├── src ├── Respect_callbacks.re ├── Respect_clihelper.re ├── Respect_matchersV2.re ├── Respect_domain.re ├── Respect_matcher.re ├── Respect_ctx.re ├── Respect_dsl.re └── Respect.re ├── watch.rb ├── bsconfig.json ├── tests ├── Respect_matchersV2_test.re ├── AsyncDslSpecs.re ├── TestContext_test.re ├── TestHelpers.re ├── DslSpecs.re └── RunnerSpecs.re ├── .gitignore ├── .travis.yml ├── package.json ├── bin └── respect ├── README.md ├── CHANGELOG.md ├── Documentation.md └── yarn.lock /src/Respect_callbacks.re: -------------------------------------------------------------------------------- 1 | type doneCallback = (~err: string=?, unit) => unit; 2 | -------------------------------------------------------------------------------- /watch.rb: -------------------------------------------------------------------------------- 1 | watch('lib\/.*\.js') do |md| 2 | system("node ./lib/js/src/demo.js") 3 | end 4 | -------------------------------------------------------------------------------- /bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@stroiman/respect", 3 | "refmt": 3, 4 | "sources": [ 5 | {"dir": "bin"}, 6 | {"dir": "src"}, 7 | { 8 | "dir": "tests", 9 | "type": "dev" 10 | } 11 | ], 12 | "bs-dependencies" : [ 13 | "@stroiman/async" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tests/Respect_matchersV2_test.re: -------------------------------------------------------------------------------- 1 | open Respect.Dsl.Async; 2 | open Respect.MatchersV2; 3 | open TestHelpers.MatchHelper; 4 | 5 | describe("MatchersV2", [ 6 | describe("equal", [ 7 | it("matches when args are equal", (_) => { 8 | expect(5)#to_(equal(5)) 9 | |> shouldMatch 10 | }), 11 | 12 | it("fails when args are not equal", (_) => { 13 | expect(5)#to_(equal(6)) 14 | |> shouldNotMatch 15 | }) 16 | ]) 17 | ]) |> register 18 | -------------------------------------------------------------------------------- /tests/AsyncDslSpecs.re: -------------------------------------------------------------------------------- 1 | open Respect; 2 | open Respect.Dsl.Async; 3 | open Respect.Matcher; 4 | 5 | describe( 6 | "Async dsl", 7 | [ 8 | it( "Example group has metadata", 9 | (_) => { 10 | let grp = DslSpecs.parse(("name", "value") **> describe("Group", [])); 11 | let expected = Ctx.ContextMap.empty |> Ctx.ContextMap.add("name", Obj.repr("value")); 12 | grp.metadata |> shoulda(equal(expected)) 13 | } 14 | ) 15 | ] 16 | ) 17 | |> register; 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.obj 3 | *.out 4 | *.compile 5 | *.native 6 | *.byte 7 | *.cmo 8 | *.annot 9 | *.cmi 10 | *.cmx 11 | *.cmt 12 | *.cmti 13 | *.cma 14 | *.a 15 | *.cmxa 16 | *.obj 17 | *~ 18 | *.annot 19 | *.cmj 20 | *.bak 21 | lib/js 22 | lib/bs 23 | lib/ocaml 24 | *.mlast 25 | *.mliast 26 | .vscode 27 | .merlin 28 | node_modules 29 | package-lock.json 30 | *.log 31 | .bsb.lock 32 | 33 | # This folder contains some custom .js files to simulate binding to 34 | # bs modules with async functions 35 | !lib/node_modules 36 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '8' 4 | deploy: 5 | provider: npm 6 | email: peter@stroiman.com 7 | api_key: 8 | secure: "gUmpEe4HZemiHAK2GSdHBnxL2lW4zeu56y3MKnF2fcYUOMi4pIrAGk3ay5NnokyzMsGhbRoeDAXm6St7gy4w7XxTHwYlixkkwrv7IbbZMYBhF9FbKK5hP4mA3/aeLw+E2K2I8dCbf0ho0HvqrkW5ojXro6Kyqh9dcd6fov1OMQQLxJ2H8t2/xTF58X+KU/IL1xTiNofbdp64uFT6SQi16sED97oaFFB3XJIV5W9KnNqgsiZUwAgVkMeuhJvpb87WrM7zIWWE6qjGS2yrkTKUgZRohwESLjo7dbb911AqIG9owUyNfw7tkqKVkhoW+5GAa75Hy7ueR4Hbnx5vjKchF2CLGao+fjcO7eYEppiZex84LMv5o5I54lyfIOfREVs7uvKv6PV4zf8zfZocl9q3SH0D0AX5FKGgGkkr3gsnzDiWyKMmIEbid/wMAeoRKHFl2qyxZJ5lblAO7jdDiigXJYzSAQ5MWNco7C58nJ0OPGnTuYII0SAv9dvoPYM1nNIVqQJrnLm6w46T3eYxIvhEuV+jlZ/wnYhiK86FZfJX8OZ04rre0nM2ewizW9FK+WZFYdlrGr98GlieUXNxJa3sSQbzVRS+o8oY628XAsZCABpvnpFCVFJI54LPOXZ9dnou5qOgUQAlZlHO6xUD268aTQwrt03y3PCqQYh7kLB1PQY=" 9 | on: 10 | tags: true 11 | repo: stroiman/respect 12 | -------------------------------------------------------------------------------- /src/Respect_clihelper.re: -------------------------------------------------------------------------------- 1 | open Respect.Runner; 2 | 3 | let runRoot = callback => 4 | runRoot() 5 | |> Async.run( 6 | (result: RunResult.t) => { 7 | Js.logMany([| 8 | result |> RunResult.getNoOfPassedTests |> Js.Int.toString, 9 | "passed tests,", 10 | result |> RunResult.getNoOfPendingTests |> Js.Int.toString, 11 | "pending tests, ", 12 | result |> RunResult.getNoOfFailedTests |> Js.Int.toString, 13 | "failed tests", 14 | |]); 15 | if (result |> Respect.TestResult.isSuccess) { 16 | Js.log("Test run succeeded"); 17 | callback(0); 18 | } else { 19 | Js.log("Test run failed"); 20 | callback(1); 21 | }; 22 | }, 23 | ~fe= 24 | _ => { 25 | Js.log("Test run failed"); 26 | callback(1); 27 | }, 28 | ); 29 | -------------------------------------------------------------------------------- /src/Respect_matchersV2.re: -------------------------------------------------------------------------------- 1 | open Respect_callbacks; 2 | 3 | type matchResult('a) = 4 | | MatchSuccess('a) 5 | | MatchFailure(Obj.t); 6 | 7 | type async('a) = Async.t('a); 8 | 9 | type matcher('a, 'b) = {. f: 'a => async(matchResult('b))}; 10 | 11 | let createCallback = (don: doneCallback) => 12 | fun 13 | | MatchSuccess(_) => don() 14 | | MatchFailure(_) => don(~err="Match error", ()); 15 | 16 | let expect = actual => { 17 | pri actual = actual; /* avoid warning */ 18 | pub to_ = (matcher: matcher('a, 'b), don: doneCallback) => 19 | matcher#f(this#actual) 20 | |> Async.run(createCallback(don), ~fe=_ => 21 | don(~err="Exception occurred", ()) 22 | ) 23 | }; 24 | 25 | let equal = (expected: 'a): matcher('a, 'a) => { 26 | pri expected = expected; 27 | pub f = actual => 28 | if (actual == this#expected) { 29 | MatchSuccess(actual) |> Async.return; 30 | } else { 31 | MatchFailure(actual |> Obj.repr) |> Async.return; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@stroiman/respect", 3 | "version": "0.10.0", 4 | "scripts": { 5 | "clean": "bsb -clean-world", 6 | "build": "bsb -make-world", 7 | "watch": "bsb -make-world -w", 8 | "test:watch": "nodemon bin/respect", 9 | "test:run": "node bin/respect", 10 | "test": "run-s build test:run", 11 | "dev": "run-p watch test:watch" 12 | }, 13 | "bin": { 14 | "respect": "./bin/respect" 15 | }, 16 | "keywords": [ 17 | "BuckleScript", 18 | "reason", 19 | "reasonml", 20 | "test", 21 | "testing", 22 | "tdd", 23 | "bdd" 24 | ], 25 | "license": "MIT", 26 | "devDependencies": { 27 | "bs-platform": "5", 28 | "nodemon": "^1.12.1", 29 | "npm-run-all": "^4.1.1" 30 | }, 31 | "dependencies": { 32 | "@stroiman/async": "^0.7", 33 | "cli": "^1.0.1", 34 | "glob": "^7.1.2" 35 | }, 36 | "repository": { 37 | "type": "git", 38 | "url": "https://github.com/stroiman/respect.git" 39 | }, 40 | "bugs": { 41 | "url": "https://github.com/stroiman/respect/issues" 42 | }, 43 | "homepage": "https://github.com/stroiman/respect#readme", 44 | "publishConfig": { 45 | "access": "public" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /bin/respect: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | const cli = require('cli'); 5 | const glob = require('glob'); 6 | 7 | // The placement of compiled reason .re and .ml files depends on 8 | // the compilation settings of the including project 9 | const inSource = fs.existsSync(path.join(__dirname, '../src/Respect_clihelper.js')); 10 | const cliHelper = inSource ? '../src/Respect_clihelper' : '../lib/js/src/Respect_clihelper'; 11 | const runner = require(cliHelper); 12 | 13 | const usage = `respect [OPTIONS] [TEST FILES] 14 | 15 | The test files must be the compiled .js files. Defaults to lib/js/tests/**/*.js. 16 | Default may change over time, so an explicit list is recommended.`; 17 | 18 | const options = cli 19 | .setUsage(usage) 20 | .enable('glob') 21 | .parse({ }); 22 | 23 | 24 | let defaultFiles = inSource ? "tests/**/*.js" : "lib/js/tests/**/*.js"; 25 | let files = cli.args; 26 | if (files.length === 0) { 27 | files = glob.sync(defaultFiles); 28 | } else { 29 | files = files.map(f => glob.sync(f)).reduce((x,y) => x.concat(y), []); 30 | } 31 | 32 | for (let f of files) { 33 | require(path.resolve(f)); 34 | } 35 | 36 | runner.runRoot(function(result) { 37 | process.exit(result); 38 | }); 39 | -------------------------------------------------------------------------------- /src/Respect_domain.re: -------------------------------------------------------------------------------- 1 | /* 2 | Module Domain describes the internal structure that represents examples 3 | and groups of examples. 4 | */ 5 | 6 | /* Represents the outcome of running a test */ 7 | type executionResult = 8 | | TestPending 9 | | TestSucceeded 10 | | TestFailed; 11 | type executionCallback = executionResult => unit; 12 | /* Internal implementation, a nicer callback is exposed via the DSL */ 13 | type testFunc = (Respect_ctx.t, executionCallback) => unit; 14 | type example = { 15 | name: string, 16 | func: testFunc, 17 | metadata: Respect_ctx.contextMap, 18 | focused: bool, 19 | skipped: bool, 20 | }; 21 | type setup = 22 | | Setup(testFunc); 23 | /* A group of examples, and nested groups */ 24 | type exampleGroup = { 25 | name: string, 26 | children: list(exampleGroup), 27 | setups: list(setup), 28 | examples: list(example), 29 | metadata: Respect_ctx.contextMap, 30 | }; 31 | module ExampleGroup = { 32 | let empty = { 33 | name: "", 34 | children: [], 35 | setups: [], 36 | examples: [], 37 | metadata: Respect_ctx.ContextMap.empty, 38 | }; 39 | let addChild = (child, root) => { 40 | ...root, 41 | children: root.children @ [child], 42 | }; 43 | let addExample = (ex, grp) => {...grp, examples: grp.examples @ [ex]}; 44 | let addSetup = (code, grp) => {...grp, setups: grp.setups @ [code]}; 45 | 46 | let rec hasFocusedExamples = grp => { 47 | let tmp = 48 | List.fold_left( 49 | (acc, ex) => ex.focused ? true : acc, 50 | false, 51 | grp.examples, 52 | ); 53 | List.fold_left( 54 | (acc, child) => hasFocusedExamples(child) ? true : acc, 55 | tmp, 56 | grp.children, 57 | ); 58 | }; 59 | }; 60 | 61 | module Example = { 62 | let isFocused = x => x.focused; 63 | let isSkipped = x => x.skipped; 64 | }; 65 | -------------------------------------------------------------------------------- /src/Respect_matcher.re: -------------------------------------------------------------------------------- 1 | type matchResult('t) = 2 | | MatchSuccess('t) 3 | | MatchFailure(Obj.t, Obj.t); 4 | 5 | exception MatchFailedException(string); 6 | 7 | type matcher('a, 'b) = ('a, matchResult('b) => unit) => unit; 8 | 9 | let (>=>) = (a: matcher('a, 'b), b: matcher('b, 'c), x: 'a, cb) => 10 | a( 11 | x, 12 | fun 13 | | MatchFailure(x, y) => cb(MatchFailure(x, y)) 14 | | MatchSuccess(x) => b(x, cb), 15 | ); 16 | 17 | let matchSuccess = (a, cb) => cb(MatchSuccess(a)); 18 | let matchFailure = (actual, exp, cb) => 19 | cb(MatchFailure(actual |> Obj.repr, exp |> Obj.repr)); 20 | 21 | let equal = (expected, actual) => 22 | actual == expected ? matchSuccess(actual) : matchFailure(actual, expected); 23 | 24 | let beGreaterThan = (expected, actual) => 25 | actual > expected ? matchSuccess(actual) : matchFailure(actual, expected); 26 | 27 | let beLessThan = (expected, actual) => 28 | actual < expected ? matchSuccess(actual) : matchFailure(actual, expected); 29 | 30 | let should = (matcher: matcher('a, 'b), actual: 'a) => { 31 | let result = ref(None); 32 | matcher(actual, r => result := Some(r)); 33 | switch (result^) { 34 | | Some(MatchSuccess(_)) => () 35 | | Some(MatchFailure(a, e)) => 36 | Js.log("Match failed"); 37 | Js.log2("Expected: ", e); 38 | Js.log2("Actual: ", a); 39 | MatchFailedException("Match failed") |> raise; 40 | | None => failwith("Matcher did not eval synchronously") 41 | }; 42 | }; 43 | 44 | let shoulda = (matcher, actual, don: Respect_callbacks.doneCallback) => { 45 | let handleMatch = result => 46 | switch (result) { 47 | | MatchSuccess(_) => don() 48 | | MatchFailure(actual, expected) => 49 | Js.log("Match failed"); 50 | Js.log2("Expected: ", expected); 51 | Js.log2("Actual: ", actual); 52 | don(~err="match failed", ()); 53 | }; 54 | (matcher(actual))(handleMatch); 55 | }; 56 | -------------------------------------------------------------------------------- /src/Respect_ctx.re: -------------------------------------------------------------------------------- 1 | open Respect_callbacks; 2 | 3 | module ContextMap = { 4 | include Map.Make(String); 5 | let merge = (a, b) => 6 | merge( 7 | (_, x, y) => 8 | switch (x) { 9 | | None => y 10 | | _ => x 11 | }, 12 | a, 13 | b, 14 | ); 15 | }; 16 | 17 | type contextMap = ContextMap.t(Obj.t); 18 | 19 | type t = { 20 | . 21 | add: 'a. (string, 'a) => t, 22 | get: 'a. string => 'a, 23 | tryGet: 'a. string => option('a), 24 | setSubj: 'a. (t => 'a) => t, 25 | subject: 'a. unit => 'a, 26 | don: (unit, doneCallback) => unit, 27 | }; 28 | 29 | let create = metaData: t => { 30 | val mutable data = metaData; 31 | val mutable subjFn: option(t => Obj.t) = None; 32 | val mutable subj: option(Obj.t) = None; 33 | pub add: 'a. (string, 'a) => t = 34 | (key, x) => { 35 | data = data |> ContextMap.add(key, x |> Obj.repr); 36 | this; 37 | }; 38 | pub get: 'a. string => 'a = key => data |> ContextMap.find(key) |> Obj.obj; 39 | pub tryGet: 'a. string => option('a) = 40 | key => data |> ContextMap.mem(key) ? Some(this#get(key)) : None; 41 | pub setSubj: 'a. (t => 'a) => t = 42 | fn => { 43 | subjFn = Some(x => fn(x) |> Obj.repr); 44 | this; 45 | }; 46 | pub subject: 'a. unit => 'a = 47 | () => 48 | switch (subj) { 49 | | None => 50 | let s = Js.Option.getExn(subjFn, this); 51 | subj = Some(s); 52 | s |> Obj.obj; 53 | | Some(x) => x |> Obj.obj 54 | }; 55 | pub don = ((), d) => d() 56 | }; 57 | 58 | let add = (key: string, x: 'a, ctx: t) => ctx#add(key, x); 59 | let get = (key: string, ctx: t): 'a => ctx#get(key); 60 | let setSubj = (fn: t => 'a, ctx: t) => ctx#setSubj(fn); 61 | let subject = (ctx: t) => ctx#subject(); 62 | let tryGet = (key, ctx: t) => ctx#tryGet(key); 63 | 64 | let map = (key: string, f: 'a => 'b, ctx: t) => { 65 | let updated = ctx |> get(key) |> f; 66 | ctx |> add(key, updated); 67 | }; 68 | 69 | let don = (ctx: t): (doneCallback => unit) => ctx#don(); 70 | -------------------------------------------------------------------------------- /tests/TestContext_test.re: -------------------------------------------------------------------------------- 1 | open Respect; 2 | open Respect.Dsl.Async; 3 | open Respect.Matcher; 4 | open Respect.Domain; 5 | 6 | let beFailure = (result) => 7 | switch result { 8 | | TestFailed => MatchSuccess() 9 | | _ => MatchFailure(Obj.repr(), Obj.repr()) 10 | }; 11 | 12 | let create = () => Ctx.create(Ctx.ContextMap.empty); 13 | 14 | describe("TestContext", [ 15 | describe("done helper for setup", [ 16 | beforeEach(ctx => ctx |> Ctx.add("key",42) |> Ctx.don), 17 | 18 | it("Uses value from setup", (ctx) => { 19 | ctx 20 | |> Ctx.get("key") 21 | |> shoulda(equal(42)) 22 | }) 23 | ]), 24 | 25 | describe("Get/set data to context", [ 26 | it("Data added to context is retrievable", (_) => { 27 | let ctx = create(); 28 | ctx 29 | #add("key", 42) 30 | #get("key") |> shoulda(equal(42)); 31 | }), 32 | it("Data new values overwrite old values", (_) => { 33 | let ctx = create(); 34 | let ctx = ctx#add("key2", 42); 35 | ctx#add("key", 43)#get("key") |> shoulda(equal(43)) 36 | }), 37 | it("Subjec to change!!! Context is mutated", (_) => { 38 | let ctx = create(); 39 | ctx#add("key", 42) 40 | #get("key") |> shoulda(equal(42)); 41 | }), 42 | 43 | describe("Subject", [ 44 | it("is not evaluated until used", (_) => { 45 | let ctx = create(); 46 | let ctx = ctx |> Ctx.setSubj(ctx => ctx#get("key") + 1); 47 | let ctx = ctx#add("key", 42); 48 | Ctx.subject(ctx) |> shoulda(equal(43)); 49 | }) 50 | ]) 51 | ]), 52 | describe("Update data", [ 53 | it("modifies a key based on a function", (_) => { 54 | let ctx = create(); 55 | ctx 56 | |> Ctx.add("key", 42) 57 | |> Ctx.map("key", x => x+1) 58 | |> Ctx.get("key") |> shoulda(equal(43)) 59 | }) 60 | ]), 61 | describe("Access with piping", [ 62 | it("Can be piped", (_) => 63 | create() 64 | |> Ctx.add("key", 42) 65 | |> Ctx.add("key", 43) 66 | |> Ctx.get("key") 67 | |> shoulda(equal(43)) 68 | ) 69 | ]), 70 | describe("tryGet", [ 71 | it("returns Some when data exists", (_) => { 72 | create() 73 | |> Ctx.add("key", 42) 74 | |> Ctx.tryGet("key") 75 | |> shoulda(equal(Some(42))) 76 | }), 77 | it("returns None when data doesn't exist", (_) => { 78 | create() 79 | |> Ctx.tryGet("key") 80 | |> shoulda(equal(None)) 81 | }) 82 | ]) 83 | ]) |> register; 84 | -------------------------------------------------------------------------------- /tests/TestHelpers.re: -------------------------------------------------------------------------------- 1 | open Respect; 2 | open Respect.Domain; 3 | 4 | let passingExample = (~onRun=?, (), ctx, cb) => { 5 | switch (onRun) { 6 | | Some(f) => f(ctx) 7 | | None => () 8 | }; 9 | cb(TestSucceeded); 10 | }; 11 | 12 | let failingExample = (~onRun=?, (), ctx, cb) => { 13 | switch (onRun) { 14 | | Some(f) => f(ctx) 15 | | None => () 16 | }; 17 | cb(TestFailed); 18 | }; 19 | 20 | let pendingExample = (~onRun=?, (), ctx, cb) => { 21 | switch (onRun) { 22 | | Some(f) => f(ctx) 23 | | None => () 24 | }; 25 | cb(TestPending); 26 | }; 27 | 28 | let anExampleWithCode = fn => { 29 | name: "dummy", 30 | func: Dsl.wrapTest(fn), 31 | metadata: Ctx.ContextMap.empty, 32 | focused: false, 33 | skipped: false, 34 | }; 35 | 36 | let passingSetup = passingExample; 37 | 38 | let anExampleGroup = ExampleGroup.empty; 39 | 40 | let withAnExample = ExampleGroup.addExample; 41 | 42 | let withExampleCode = f => f |> anExampleWithCode |> withAnExample; 43 | 44 | let withMetadata = ((name, value), grp) => { 45 | ...grp, 46 | metadata: grp.metadata |> Ctx.ContextMap.add(name, value |> Obj.repr), 47 | }; 48 | 49 | let withSetup = code => ExampleGroup.addSetup(Setup(code)); 50 | 51 | let withChildGroup = (child, grp) => grp |> ExampleGroup.addChild(child); 52 | 53 | let withExample = 54 | ( 55 | ~metadata=?, 56 | ~name="Dummy example", 57 | ~skipped=false, 58 | ~focused=false, 59 | ~code=passingExample(), 60 | grp, 61 | ) => { 62 | let md = 63 | switch (metadata) { 64 | | None => Ctx.ContextMap.empty 65 | | Some((name, value)) => 66 | Ctx.ContextMap.empty |> Ctx.ContextMap.add(name, value |> Obj.repr) 67 | }; 68 | let ex: example = {name, func: code, metadata: md, focused, skipped}; 69 | grp |> ExampleGroup.addExample(ex); 70 | }; 71 | 72 | module MatchHelper = { 73 | open Respect_callbacks; 74 | let shouldMatch = (fn: doneCallback => unit, don: doneCallback) => 75 | fn((~err=?, ()) => 76 | switch (err) { 77 | | None => don() 78 | | Some(x) => don(~err=x, ()) 79 | } 80 | ); 81 | 82 | let shouldNotMatch = (fn: doneCallback => unit, don: doneCallback) => 83 | fn((~err=?, ()) => 84 | switch (err) { 85 | | None => don(~err="Expected match error, but none was received", ()) 86 | | Some(_) => don() 87 | } 88 | ); 89 | }; 90 | 91 | module AsyncMatchers = { 92 | open Respect.Matcher; 93 | 94 | let asyncResolve = (actual: Async.t('a), cb) => { 95 | let successCb = x => cb(MatchSuccess(x)); 96 | let exnCb = x => cb(MatchFailure(x |> Obj.repr, x |> Obj.repr)); 97 | actual |> Async.run(successCb, ~fe=exnCb); 98 | }; 99 | 100 | let asyncThrow = (actual: Async.t('a), cb) => { 101 | let successCb = x => cb(MatchFailure(x |> Obj.repr, x |> Obj.repr)); 102 | let exnCb = x => cb(MatchSuccess(x)); 103 | actual |> Async.run(successCb, ~fe=exnCb); 104 | }; 105 | }; 106 | -------------------------------------------------------------------------------- /tests/DslSpecs.re: -------------------------------------------------------------------------------- 1 | open Respect; 2 | open Respect.Dsl; 3 | open Respect.Matcher; 4 | 5 | let dummy = _ => (); 6 | 7 | let parse = op => 8 | Respect.Domain.ExampleGroup.empty 9 | |> applyOperation(op) 10 | |> (grp => grp.children) 11 | |> List.hd; 12 | 13 | let exampleCode = _ => (); 14 | 15 | describe( 16 | "Dsl", 17 | [ 18 | it("Example has a setup", _ => { 19 | let grp = parse(describe("Group", [beforeEach(dummy)])); 20 | grp.setups |> List.length |> should(equal(1)); 21 | }), 22 | it("Example group has metadata", _ => { 23 | let grp = 24 | parse( 25 | ("name", "value") **> describe("Group", [beforeEach(dummy)]), 26 | ); 27 | let expected = 28 | Ctx.ContextMap.empty |> Ctx.ContextMap.add("name", Obj.repr("value")); 29 | grp.metadata |> should(equal(expected)); 30 | }), 31 | it("Example has metadata", _ => { 32 | let grp = 33 | parse( 34 | describe( 35 | "Group", 36 | [("name", "value") **> it("has example", exampleCode)], 37 | ), 38 | ); 39 | let expected = 40 | Ctx.ContextMap.empty |> Ctx.ContextMap.add("name", Obj.repr("value")); 41 | (grp.examples |> List.hd).metadata |> should(equal(expected)); 42 | }), 43 | it("Example group has metadata", _ => { 44 | let grp = 45 | parse( 46 | ("name", "value") **> describe("Group", [beforeEach(dummy)]), 47 | ); 48 | let expected = 49 | Ctx.ContextMap.empty |> Ctx.ContextMap.add("name", Obj.repr("value")); 50 | grp.metadata |> should(equal(expected)); 51 | }), 52 | describe( 53 | "Skipped", 54 | [ 55 | it("is true when example has 'skip @@'", _ => { 56 | let grp = 57 | parse( 58 | describe("group", [skip @@ it("has example", exampleCode)]), 59 | ); 60 | grp.examples 61 | |> List.map(Domain.Example.isSkipped) 62 | |> should(equal([true])); 63 | }), 64 | it("is false when example doesn't have `skip @@`", _ => { 65 | let grp = 66 | parse(describe("group", [it("has example", exampleCode)])); 67 | grp.examples 68 | |> List.map(Domain.Example.isSkipped) 69 | |> should(equal([false])); 70 | }), 71 | ], 72 | ),describe( 73 | "Focused", 74 | [ 75 | it("is true when example has 'focus @@'", _ => { 76 | let grp = 77 | parse( 78 | describe("group", [focus @@ it("has example", exampleCode)]), 79 | ); 80 | grp.examples 81 | |> List.map(Example.isFocused) 82 | |> should(equal([true])); 83 | }), 84 | it("is false when example doesn't have `focus @@`", _ => { 85 | let grp = 86 | parse(describe("group", [it("has example", exampleCode)])); 87 | grp.examples 88 | |> List.map(Example.isFocused) 89 | |> should(equal([false])); 90 | }), 91 | ], 92 | ), 93 | ], 94 | ) 95 | |> register; 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ReSpect 2 | 3 | BDD-style test framework for Reason/bucklescript 4 | 5 | __Attention__ - _The NPM package has moved to `@stroiman/respect`. Remember to 6 | update both _package.json_ AND `bsconfig.json`._ 7 | 8 | _The source repo was moved from to `https://github.com/stroiman/respect.git`_ 9 | 10 | [![Build Status](https://travis-ci.org/stroiman/respect.svg?branch=master)](https://travis-ci.org/stroiman/respect) 11 | 12 | This is an RSpec inspired test framework for ReasonML/OCaml/Bucklescript. The 13 | runner uses raw javascript code, so it will only run in node environments at the moment. 14 | 15 | I base this on a lot of experience I gained from a similar project for F#, FSpec. 16 | 17 | This project is still in a very early stage, so use at your own risk. Breaking 18 | changes occur from time to time. 19 | 20 | ## Features 21 | 22 | * The framework places tests in "examples", which are grouped in "example 23 | groups", just like mocha, jest, jasmine, etc. 24 | * Example groups and their examples are build using immutable data structures, 25 | allowing for metaprogramming to modify/generate the examples. 26 | * Support for testing async code. 27 | * The framework provides a context object that is unique to each individual 28 | test case, and provides a place where each test case can store state 29 | necessary for that case. 30 | * Each example or group can have metadata atteched that will be made available 31 | through the context object. This can be used modify what happens in the 32 | setup code. 33 | 34 | ## Getting Started 35 | 36 | Run `npm install --save-dev @stroiman/respect` and add `@stroiman/respect` to the `bs-dev-dependencies` in `bsconfig.json`. 37 | 38 | ```Reason 39 | open Respect.Dsl.Sync; 40 | 41 | describe "My first test" [ 42 | it "should be a failing test" (fun _ => { 43 | 1 |> should (equal (2)) 44 | }) 45 | ] |> register 46 | ``` 47 | 48 | Add a test script to `package.json`: 49 | 50 | ```json 51 | "scripts": { 52 | ... 53 | "test": "respect" 54 | } 55 | ``` 56 | 57 | Build the code and run the tests with: `npm run test` 58 | 59 | For more info, including tips on test watcher functionality, see the full 60 | [Documentation](https://github.com/PeteProgrammer/respect/blob/master/Documentation.md) 61 | 62 | ## TODO 63 | 64 | * "Finalize" DSL for building test suites. 65 | * Determine whether or not to allow mutation of `TestContext`. This worked well 66 | in F#/FSpec, but we don't have runtime type checking in Reason/Bucklescript. 67 | * Finalize assertion framework. 68 | * Nicer test output when running. 69 | * Nicer test output when assertions fail. 70 | * Internally, figure out how to report progresss. 71 | * More flexible runner, e.g. configurable location of test files 72 | * ✓ Handle async timeout to avoid hanging when async tests don't call back 73 | * Make timeout configurable through example metadata. 74 | * Support tear-down code 75 | 76 | Although, I had learned from many mistakes when building FSpec, there are some 77 | problems that demand different solutions in Reason/Bucklescript. Async support 78 | in particular. 79 | 80 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # ReSpect Version History 2 | 3 | ### 0.10.0 4 | 5 | * Create a `Dsl.Base` functor - which is used to create `Dsl.Sync` and 6 | `Dsl.Async`. 7 | 8 | ### 0.9.0 9 | 10 | * Add support for focused and skipped examples 11 | * Upgrade to bs-platform@5 12 | 13 | ### 0.8.0 14 | 15 | * Remove compiler warnings caused by module aliases 16 | * Support being used in projects compiled with inSource: true 17 | * Fix source file glob 18 | 19 | ### 0.7.2 20 | 21 | * Update `@stroiman/async` to 0.7 22 | 23 | ### 0.7.1 24 | 25 | * Attempt to fix missing documentation, links, etc. on npmjs.com 26 | 27 | ### 0.7.0 28 | 29 | * Changed dependency from `re-sync` to `@stroiman/async` 30 | * Build against bs-platform 2.2.1 31 | 32 | ### 0.6.0 33 | 34 | * cli now allows you to specify test files on the command line. (was hardcoded 35 | to `lib/js/tests/*.js` before. 36 | 37 | ### 0.5.4 38 | 39 | * Moved package from `re-respect` to `@stroiman/respect`. 40 | 41 | ### 0.5.1 42 | 43 | * No of passed, pending, and failed tests are written to the console after the 44 | test run. 45 | 46 | ### 0.5.0 47 | 48 | * Breaking change: Renamed `TestContext` module to `Respect_ctx` and made it 49 | available under `Respect.Ctx`. A quick fix to get your code to compile 50 | is to add the this to your own modules `module TestContext = Respect.Ctx`. 51 | 52 | ### 0.4.3 53 | 54 | * Added `Ctx.tryGet` - Returns `None` if no entry with the specified key exists 55 | in the context data. 56 | 57 | ### 0.4.2 58 | 59 | * Added Respect.Ctx module as alias for TestContext 60 | 61 | ### 0.4.1 62 | 63 | * TestContext.don function to help accept a done callback from setup functions 64 | 65 | ### 0.4.0 66 | 67 | * Refactor: Test context is now represented by an object instead of a mutable 68 | record. But the TestContext module still has functions for operating on the 69 | actual context, so existing code should be compatible. 70 | * Testcontext subject: The subject is a function that evaluates to an actual 71 | value when requested. The function receives the test context as input. You 72 | can assign the subject in a parent group, and modify the context in a child 73 | group. 74 | * TextContext.map: Allows you to easily modify objects in the context. 75 | 76 | ### 0.3.0 77 | 78 | * Breaking change: In order to get better error messages when match fails, the 79 | MatchFailure constructor now takes two args, the actual object, and the 80 | expected object of the failed matcher. This was in particular for testing 81 | async results. 82 | 83 | ## 0.2.0 84 | 85 | * Breaking change: A match result can now only be asynchronous, but helper 86 | functions exists for matchers that evaluate synchronously. See the readme 87 | for guidance. 88 | 89 | ## 0.1.0 90 | 91 | * Support for pending tests 92 | 93 | ## 0.0.9 94 | 95 | * Removed dependency to 're-sync' npm package. This caused cyclic deps 96 | when building 're-sync' 97 | 98 | ## 0.0.8 99 | 100 | * Only show failed tests in the console 101 | * Added a 1 second timout for examples (not yet configurable) 102 | * Bugfix: Test runniner wasn't working on case sensitive file system 103 | 104 | ## 0.0.6 105 | 106 | * Added test metadata support 107 | 108 | ## 0.0.5 109 | 110 | * Implemented nicer callback for tests `don ()` or `don err::"Failed" ()` 111 | * Created `Respect.Dsl.Async` module to have nice naming for async test suites. 112 | * Implemented async support in assertion framework. 113 | 114 | ## 0.0.4 115 | 116 | * Rewrote runner engine to use continuations intenally to allow for async tests. 117 | * Created an `it_a` construct to create async tests. 118 | 119 | ## 0.0.3 120 | 121 | First version with a test runner that was able to execute tests 122 | -------------------------------------------------------------------------------- /src/Respect_dsl.re: -------------------------------------------------------------------------------- 1 | open Respect_domain; 2 | 3 | let wrapTest = (fn: Respect_ctx.t => unit): testFunc => 4 | (ctx, callback) => 5 | try ( 6 | { 7 | fn(ctx); 8 | callback(TestSucceeded); 9 | } 10 | ) { 11 | | _ => callback(TestFailed) 12 | }; 13 | 14 | type doneCallback = Respect_callbacks.doneCallback; 15 | let wrapW = (fn: (Respect_ctx.t, doneCallback) => unit): testFunc => 16 | (ctx: Respect_ctx.t, callback) => 17 | fn(ctx, (~err=?, ()) => 18 | switch (err) { 19 | | None => callback(TestSucceeded) 20 | | Some(_) => callback(TestFailed) 21 | } 22 | ); 23 | 24 | type operator = 25 | | Skip 26 | | Focus; 27 | 28 | type operation = 29 | | AddOperator(operator, operation) 30 | | WrapMetadata((string, Obj.t), operation) 31 | | AddChildGroupOperation(string, list(operation)) 32 | | AddExampleOperation(string, testFunc) 33 | | AddSetupOperation(testFunc); 34 | 35 | let rec applyOperation = (operation, context, metadata, focus, skipped) => 36 | switch (operation) { 37 | | AddOperator(Focus, op) => 38 | applyOperation(op, context, metadata, true, skipped) 39 | | AddOperator(Skip, op) => 40 | applyOperation(op, context, metadata, focus, true) 41 | | WrapMetadata((key, value), op) => 42 | applyOperation( 43 | op, 44 | context, 45 | metadata |> Respect_ctx.ContextMap.add(key, value), 46 | focus, 47 | skipped, 48 | ) 49 | | AddSetupOperation(fn) => context |> ExampleGroup.addSetup(Setup(fn)) 50 | | AddExampleOperation(name, func) => { 51 | ...context, 52 | examples: [ 53 | {name, func, metadata, focused: focus, skipped}, 54 | ...context.examples, 55 | ], 56 | } 57 | | AddChildGroupOperation(name, ops) => 58 | let initial = {...ExampleGroup.empty, name, metadata}; 59 | let newChild = 60 | List.fold_left( 61 | (grp, op) => 62 | applyOperation( 63 | op, 64 | grp, 65 | Respect_ctx.ContextMap.empty, 66 | focus, 67 | skipped, 68 | ), 69 | initial, 70 | ops, 71 | ); 72 | let newChild' = { 73 | ...newChild, 74 | children: newChild.children |> List.rev, 75 | examples: newChild.examples |> List.rev, 76 | }; 77 | {...context, children: [newChild', ...context.children]}; 78 | }; 79 | let applyOperation = (operation, context) => 80 | applyOperation( 81 | operation, 82 | context, 83 | Respect_ctx.ContextMap.empty, 84 | false, 85 | false, 86 | ); 87 | let rootContext = ref(ExampleGroup.empty); 88 | 89 | module Example = { 90 | let isFocused = (x: example) => x.focused; 91 | }; 92 | 93 | module type DslMapper = { 94 | type t('a); 95 | let mapTestfunc: (Respect_ctx.t => t('a)) => testFunc; 96 | }; 97 | 98 | module Make = (M: DslMapper) => { 99 | type t('a) = M.t('a); 100 | let it = (name, f) => AddExampleOperation(name, M.mapTestfunc(f)); 101 | let describe = (name, ops) => AddChildGroupOperation(name, ops); 102 | let beforeEach = f => AddSetupOperation(M.mapTestfunc(f)); 103 | let ( **> ) = ((key, value), op) => 104 | WrapMetadata((key, Obj.repr(value)), op); 105 | let focus = op => AddOperator(Focus, op); 106 | let skip = op => AddOperator(Skip, op); 107 | let pending = name => 108 | AddExampleOperation(name, (_, cb) => cb(TestPending)); 109 | let register = op => rootContext := rootContext^ |> applyOperation(op); 110 | }; 111 | 112 | module SyncMapper = { 113 | type t('a) = unit; 114 | let mapTestfunc = wrapTest; 115 | }; 116 | 117 | module AsyncMapper = { 118 | type t('a) = doneCallback => unit; 119 | let mapTestfunc = (fn: Respect_ctx.t => t('a)): testFunc => 120 | (ctx: Respect_ctx.t, callback) => 121 | fn(ctx, (~err=?, ()) => 122 | switch (err) { 123 | | None => callback(TestSucceeded) 124 | | Some(_) => callback(TestFailed) 125 | } 126 | ); 127 | }; 128 | 129 | module Sync = Make(SyncMapper); 130 | include Sync; 131 | 132 | module Async = { 133 | include Make(AsyncMapper); 134 | 135 | let register = register; 136 | }; 137 | -------------------------------------------------------------------------------- /src/Respect.re: -------------------------------------------------------------------------------- 1 | module Matcher = Respect_matcher; 2 | module MatchersV2 = Respect_matchersV2; 3 | module Callbacks = Respect_callbacks; 4 | module Ctx = Respect_ctx; 5 | module Domain = Respect_domain; 6 | module Dsl = Respect_dsl; 7 | 8 | module AsyncInfix = { 9 | open Async; 10 | 11 | let (>>=) = (x, f) => x |> bind(f); 12 | }; 13 | 14 | module Runner = { 15 | open Domain; 16 | 17 | let mergeResult = (a, b) => 18 | switch (a, b) { 19 | | (TestFailed, _) => TestFailed 20 | | (_, TestFailed) => TestFailed 21 | | (TestPending, _) => TestPending 22 | | (_, TestPending) => TestPending 23 | | (TestSucceeded, TestSucceeded) => TestSucceeded 24 | }; 25 | 26 | type runResult = { 27 | noOfPassed: int, 28 | noOfPending: int, 29 | noOfFailed: int, 30 | }; 31 | 32 | module RunResult = { 33 | type t = runResult; 34 | let empty = {noOfPassed: 0, noOfPending: 0, noOfFailed: 0}; 35 | let recordResult = (result, carry) => 36 | switch (result) { 37 | | TestSucceeded => {...carry, noOfPassed: carry.noOfPassed + 1} 38 | | TestPending => {...carry, noOfPending: carry.noOfPending + 1} 39 | | TestFailed => {...carry, noOfFailed: carry.noOfFailed + 1} 40 | }; 41 | let merge = (a, b) => { 42 | noOfPassed: a.noOfPassed + b.noOfPassed, 43 | noOfPending: a.noOfPending + b.noOfPending, 44 | noOfFailed: a.noOfFailed + b.noOfFailed, 45 | }; 46 | let getNoOfPassedTests = x => x.noOfPassed; 47 | let getNoOfPendingTests = x => x.noOfPending; 48 | let getNoOfFailedTests = x => x.noOfFailed; 49 | let getResult = x => 50 | x.noOfFailed > 0 ? 51 | TestFailed : x.noOfPending > 0 ? TestPending : TestSucceeded; 52 | }; 53 | 54 | let runExample = (groupStack, ex: example): Async.t(executionResult) => { 55 | let ctx = { 56 | let mdStack = groupStack |> List.map(x => x.metadata); 57 | let mdStack' = [ex.metadata, ...mdStack]; 58 | let md = 59 | List.fold_left(Ctx.ContextMap.merge, Ctx.ContextMap.empty, mdStack'); 60 | Ctx.create(md); 61 | }; 62 | 63 | let logError = r => { 64 | if (r == TestPending) { 65 | let groupNames = 66 | List.fold_left( 67 | (acc, grp) => 68 | if (grp.name == "") { 69 | acc; 70 | } else { 71 | grp.name ++ " - " ++ acc; 72 | }, 73 | "", 74 | groupStack, 75 | ); 76 | Js.log("EXAMPLE: " ++ groupNames ++ ex.name ++ " - PENDING"); 77 | }; 78 | if (r == TestFailed) { 79 | let groupNames = 80 | List.fold_left( 81 | (acc, grp) => 82 | if (grp.name == "") { 83 | acc; 84 | } else { 85 | grp.name ++ " - " ++ acc; 86 | }, 87 | "", 88 | groupStack, 89 | ); 90 | Js.log("EXAMPLE: " ++ groupNames ++ ex.name ++ " - FAILED"); 91 | }; 92 | r; 93 | }; 94 | 95 | let doRun = () => ex.func(ctx) |> Async.from_callback; 96 | 97 | let rec runParentGroups = grps: Async.t(executionResult) => 98 | switch (grps) { 99 | | [] => doRun() 100 | | [grp, ...parents] => 101 | let rec runSetups = setups: Async.t(executionResult) => 102 | switch (setups) { 103 | | [] => runParentGroups(parents) 104 | | [Setup(x), ...rest] => 105 | x(ctx) 106 | |> Async.from_callback 107 | |> Async.bind( 108 | fun 109 | | TestFailed => Async.return(TestFailed) 110 | | TestPending => Async.return(TestPending) 111 | | TestSucceeded => runSetups(rest), 112 | ) 113 | }; 114 | runSetups(grp.setups); 115 | }; 116 | runParentGroups(groupStack |> List.rev) 117 | |> Async.timeout(Async.Seconds(1)) 118 | |> Async.tryCatch(_ => Some(TestFailed)) 119 | |> Async.map(logError); 120 | }; 121 | let rec run = (grp, filter, parents): Async.t(runResult) => { 122 | open AsyncInfix; 123 | let groupStack = [grp, ...parents]; 124 | let rec iter = (state: RunResult.t, tests): Async.t(runResult) => 125 | switch (tests |> List.filter(filter)) { 126 | | [] => Async.return(state) 127 | | [ex, ...rest] => 128 | runExample(groupStack, ex) 129 | >>= (result => iter(RunResult.recordResult(result, state), rest)) 130 | }; 131 | let rec iterGrps = (state, grps): Async.t(runResult) => 132 | switch (grps) { 133 | | [] => Async.return(state) 134 | | [grp, ...rest] => 135 | run(grp, filter, groupStack) 136 | >>= (result => iterGrps(RunResult.merge(result, state), rest)) 137 | }; 138 | iter(RunResult.empty, grp.examples) 139 | >>= (exampleResults => iterGrps(exampleResults, grp.children)); 140 | }; 141 | /* Runs all tests in a single example group. Since a group has no knowledge 142 | of its parents, using this function will not run setup code registered in 143 | parents */ 144 | let run = grp: Async.t(runResult) => { 145 | let filter = 146 | ExampleGroup.hasFocusedExamples(grp) ? 147 | x => Example.isFocused(x) && !Example.isSkipped(x) : 148 | (x => !Example.isSkipped(x)); 149 | run(grp, filter, []); 150 | }; 151 | /* Runs all tests registered in the root example group */ 152 | let runRoot = (): Async.t(runResult) => run(Dsl.rootContext^); 153 | }; 154 | 155 | module TestResult = { 156 | open Runner; 157 | let isSuccess = result => result.noOfFailed > 0 ? false : true; 158 | }; 159 | -------------------------------------------------------------------------------- /tests/RunnerSpecs.re: -------------------------------------------------------------------------------- 1 | module Ctx = Respect.Ctx; 2 | open Respect.Dsl.Async; 3 | open Respect.Domain; 4 | /*open Respect.Runner;*/ 5 | open Respect.Matcher; 6 | open TestHelpers; 7 | open TestHelpers.AsyncMatchers; 8 | exception MockFailure(string); 9 | 10 | let run = Respect.Runner.run; 11 | let runAndReturn = (res,examples) => run(examples) |> Async.map((_) => res^); 12 | 13 | let beSuccess = actual => { 14 | open Respect.Runner; 15 | switch(actual |> RunResult.getResult) { 16 | | TestSucceeded => matchSuccess(actual) 17 | | _ => matchFailure(actual, actual) 18 | }; 19 | }; 20 | 21 | let beFailure = actual => { 22 | open Respect.Runner; 23 | switch(actual |> RunResult.getResult) { 24 | | TestFailed => matchSuccess(actual) 25 | | _ => matchFailure(actual, actual) 26 | }; 27 | }; 28 | 29 | let bePending = actual => { 30 | open Respect.Runner; 31 | switch(actual |> RunResult.getResult) { 32 | | TestPending => matchSuccess(actual) 33 | | _ => matchFailure(actual, actual) 34 | }; 35 | }; 36 | 37 | describe("Example filtering", [ 38 | it("only runs focused examples, if present", (_) => { 39 | let ex = anExampleGroup 40 | |> withExample( ~code = passingExample(), ~focused=true) 41 | |> withExample( ~code = passingExample()); 42 | run(ex) 43 | |> Async.map((x: Respect.Runner.runResult) => x.noOfPassed) 44 | |> shoulda(asyncResolve >=> equal(1)); 45 | }), 46 | it("skips skipped examples", (_) => { 47 | let ex = anExampleGroup 48 | |> withExample( ~code = passingExample(), ~skipped=true) 49 | |> withExample( ~code = passingExample()); 50 | run(ex) 51 | |> Async.map((x: Respect.Runner.runResult) => x.noOfPassed) 52 | |> shoulda(asyncResolve >=> equal(1)); 53 | }), 54 | ]) |> register; 55 | 56 | describe("Runner", [ 57 | describe("Group has 3 passing, two pending, and one failing test", [ 58 | beforeEach((ctx,don) => { 59 | let ex = anExampleGroup 60 | |> withExample( ~code = passingExample()) 61 | |> withExample( ~code = passingExample()) 62 | |> withExample( ~code = passingExample()) 63 | |> withExample( ~code = pendingExample()) 64 | |> withExample( ~code = pendingExample()) 65 | |> withExample( ~code = failingExample()); 66 | run(ex) 67 | |> Async.run(x => { 68 | ctx |> Ctx.add("result", x) |> ignore; 69 | don() 70 | }); 71 | }), 72 | 73 | it("reports 3 passed", ctx => { 74 | open Respect.Runner.RunResult; 75 | ctx |> Ctx.get("result") |> getNoOfPassedTests |> shoulda(equal(3)) 76 | }), 77 | 78 | it("Is success when all tests succeed", (_) => { 79 | let ex = anExampleGroup 80 | |> withExample( ~code = passingExample()) 81 | |> withExample( ~code = passingExample()); 82 | run(ex) |> shoulda(asyncResolve >=> beSuccess) 83 | }), 84 | it("Is pending when one test is pending", (_) => { 85 | let ex = anExampleGroup 86 | |> withExample( ~code = passingExample() ) 87 | |> withExample( ~code = pendingExample() ); 88 | run(ex) |> shoulda(asyncResolve >=> bePending) 89 | }), 90 | it("Is a failure when one test is pending", (_) => { 91 | let ex = anExampleGroup 92 | |> withExample( ~code = passingExample() ) 93 | |> withExample( ~code = pendingExample() ) 94 | |> withExample( ~code = failingExample() ); 95 | run(ex) |> shoulda(asyncResolve >=> beFailure) 96 | }) 97 | ]), 98 | 99 | describe("Group has a setup", [ 100 | it("Doesn't execute example code when setup code fails", (_) => { 101 | let lines = ref([]); 102 | let append = (line) => lines := lines^ @ [line]; 103 | let ex = 104 | anExampleGroup 105 | |> withSetup( (_, cb) => { append("setup"); cb(TestFailed) }) 106 | |> withExample( ~code= (_, cb) => { append("test"); cb(TestSucceeded) }); 107 | ex |> runAndReturn(lines) 108 | |> shoulda(asyncResolve >=> equal(["setup"])) 109 | }), 110 | 111 | it("Executes multiple setups before the example", (_) => { 112 | let lines = ref([]); 113 | let append = (line) => (_) => lines := lines^ @ [line]; 114 | let ex = 115 | anExampleGroup 116 | |> withSetup(passingSetup(~onRun=append("setup 1"),()) ) 117 | |> withSetup(passingSetup(~onRun=append("setup 2"),()) ) 118 | |> withExample(~code=passingExample(~onRun=append("test"),()) ); 119 | let expected = ["setup 1", "setup 2", "test"]; 120 | ex |> runAndReturn(lines) 121 | |> shoulda(asyncResolve >=> equal(expected)) 122 | }), 123 | 124 | it("Executes the setup code before the example", (_) => { 125 | let lines = ref([]); 126 | let append = (line) => (_) => lines := lines^ @ [line]; 127 | let ex = 128 | anExampleGroup 129 | |> withSetup(passingSetup(~onRun=append("setup"),())) 130 | |> withExample(~code=passingExample(~onRun=append("test"),())); 131 | ex |> runAndReturn(lines) 132 | |> shoulda(asyncResolve >=> equal(["setup", "test"])) 133 | }), 134 | 135 | describe("Group has two examples", [ 136 | it("Runs the setup before each example", (_) => { 137 | let lines = ref([]); 138 | let append = (line) => (_) => lines := lines^ @ [line]; 139 | let ex = 140 | anExampleGroup 141 | |> withSetup(passingSetup(~onRun=append("setup"),())) 142 | |> withExample( ~code=passingExample(~onRun=append("test 1"),())) 143 | |> withExample( ~code=passingExample(~onRun=append("test 2"),())); 144 | let expected = ["setup", "test 1", "setup", "test 2"]; 145 | ex |> runAndReturn(lines) 146 | |> shoulda(asyncResolve >=> equal(expected)) 147 | }) 148 | ]), 149 | 150 | describe("Nested groups", [ 151 | it("Runs the setups from outermost to innermost group", (_) => { 152 | let lines = ref([]); 153 | let append = (line) => (_) => lines := lines^ @ [line]; 154 | let innerGroup = 155 | anExampleGroup 156 | |> withSetup(passingSetup(~onRun=append("inner setup"),())) 157 | |> withExample(~code=passingExample(~onRun=append("inner test"),())); 158 | let outerGroup = 159 | anExampleGroup 160 | |> withSetup(passingSetup(~onRun=append("outer setup"),())) 161 | |> withExample(~code=passingExample(~onRun=append("outer test"),())) 162 | |> withChildGroup(innerGroup); 163 | let expected = [ 164 | "outer setup", "outer test", "outer setup", 165 | "inner setup", "inner test" 166 | ]; 167 | outerGroup |> runAndReturn(lines) 168 | |> shoulda(asyncResolve >=> equal(expected)) 169 | }) 170 | ]) 171 | ]), 172 | 173 | describe("ExampleGroup has metadata", [ 174 | it("Initializes the metadata on the test context", (_) => { 175 | let data = ref(""); 176 | let grp = 177 | anExampleGroup 178 | |> withMetadata(("data", "value")) 179 | |> withExample(~code=passingExample( 180 | ~onRun=ctx => data := ctx |> Ctx.get("data"),())); 181 | grp |> runAndReturn(data) 182 | |> shoulda(asyncResolve >=> equal("value")) 183 | }) 184 | ]), 185 | 186 | describe("Parent group has metadata", [ 187 | it("uses the metadata closest to the example", (_) => { 188 | let lines = ref([]); 189 | let append = (line) => lines := lines^ @ [line]; 190 | let innerGroup = 191 | anExampleGroup 192 | |> withMetadata(("data2", "inner")) 193 | |> withMetadata(("data3", "inner")) 194 | |> withExample( 195 | ~metadata=("data3", "test"), 196 | ~code=passingExample(~onRun=ctx => { 197 | append(ctx |> Ctx.get("data1")); 198 | append(ctx |> Ctx.get("data2")); 199 | append(ctx |> Ctx.get("data3")); 200 | },())); 201 | let outerGroup = 202 | anExampleGroup 203 | |> withMetadata(("data1", "outer")) 204 | |> withMetadata(("data2", "outer")) 205 | |> withMetadata(("data3", "outer")) 206 | |> withChildGroup(innerGroup); 207 | outerGroup |> runAndReturn(lines) 208 | |> shoulda(asyncResolve >=> equal(["outer", "inner", "test"])) 209 | }) 210 | ]), 211 | 212 | describe("example throws an exception", [ 213 | it("returns an error message", (_) => { 214 | let ex = anExampleGroup |> withExampleCode((_) => raise(MockFailure(""))); 215 | run(ex) |> shoulda(asyncResolve >=> beFailure) 216 | }) 217 | ]), 218 | 219 | it("executes the example code", (_) => { 220 | let lines = ref([]); 221 | let append = (line) => lines := [line, ...lines^]; 222 | let ex = anExampleGroup |> withExampleCode((_) => append("x")); 223 | ex |> runAndReturn(lines) 224 | |> shoulda(asyncResolve >=> equal(["x"])) 225 | }) 226 | ]) |> register; 227 | -------------------------------------------------------------------------------- /Documentation.md: -------------------------------------------------------------------------------- 1 | # Respect documentation 2 | 3 | ## Installation 4 | 5 | This guide will help you get `Respect` and having a test-watcher setup. 6 | _Respect_ in itself does not implement test-watcher functionality, but it is 7 | easily added with the _nodemon_ package. 8 | 9 | ### Basic installation 10 | 11 | First, add _respect_, the npm package is named "@stroiman/respect" 12 | 13 | ```shell 14 | npm install --save-dev @stroiman/respect 15 | ``` 16 | 17 | As this is a package with Reason code, you need to add a reference to the 18 | package in the _bsconfig.json_ file, as well. 19 | 20 | You also need a folder to contain your test files. 21 | 22 | ```json 23 | "files": [ 24 | {"dir": "src"}, 25 | {"dir": "tests", 26 | "type": "dev" } 27 | ], 28 | "bs-dev-dependencies": [ 29 | "@stroiman/respect" 30 | ] 31 | ``` 32 | 33 | Create a skeleton test, "./tests/tests.re": 34 | 35 | ```Reason 36 | open Respect.Dsl.Sync; 37 | 38 | describe "My first test" [ 39 | it "runs" (fun _ => {()}) 40 | ] |> register 41 | ``` 42 | 43 | The functions `describe` and `it` helps build an immutable data structure 44 | describing your tests. `register` adds this to a global list, so they can be 45 | found by the runner. 46 | 47 | Now, let's add a test target to _package.json_ to call the test runner. The 48 | runner needs to find the compiled `.js` files. 49 | 50 | ```json 51 | "scripts": { 52 | ... 53 | "test": "respect" 54 | } 55 | ``` 56 | 57 | Execute `npm run build` to build the code, and `npm run test` to run the tests. 58 | 59 | ### Adding test watcher functionality 60 | 61 | The npm package _nodemon_ can trigger running _.js_ files when the file system 62 | changes. We can use this to implement filesystem watcher functionality. First 63 | install the package 64 | 65 | ```sh 66 | npm install --save-dev nodemon 67 | ``` 68 | 69 | And then add a script to the _package.json_ file (remember to add a file-glob if 70 | the tests are not in `lib/js/tests`. 71 | 72 | ```Reason 73 | "scripts": { 74 | ... 75 | "test:watch": "nodemon node_modules/.bin/respect" 76 | } 77 | ``` 78 | 79 | And now, you can have the tests run automatically when a _.js_ file changes 80 | with the command `npm run test:watch`. Of course, when you edit reason source 81 | files, that will not trigger a test run, so you need to run `npm run watch` in a 82 | different terminal. 83 | 84 | ### Optionally, create a _dev_ task 85 | 86 | In the previous section, you had to run two watchers in two separate terminals 87 | in order to have full watcher implementation. We can create an npm script that 88 | does both of these tasks with the help of the npm package _npm-run-all_, which 89 | allows parallel execution of multiple scripts. 90 | 91 | ```sh 92 | npm install --save-dev npm-run-all 93 | ``` 94 | 95 | In the _package.json_ file, add a new script: 96 | 97 | ```json 98 | "scripts": { 99 | ... 100 | "dev": "run-p watch test:watch" 101 | } 102 | ``` 103 | 104 | The command `run-p` is part of _npm-run-all_, and it runs the two scripts in 105 | parallel. 106 | 107 | Now you can run `npm run dev` in one terminal, and it will compile reason files, 108 | and run tests, as files are written on disk. 109 | 110 | **Caution** When implementing this target, you can experience false positives. 111 | The tests are executed whenever a `.js` file has changed. And sometimes when the 112 | bucklescript build fails, it still touches some of the `.js` files, causing a 113 | test run to execute, but it's running on old files. 114 | 115 | ## Syntax 116 | 117 | Instead of using mutating nested function calls, _Respect_ uses immutable data 118 | structures for building up the test context and tests. Therefore, the 119 | `desribe`-operation takes nested operations in a list. 120 | 121 | ```Reason 122 | register( 123 | describe("Parent context", [ 124 | it("has some test", (_) => 125 | ... 126 | ), 127 | 128 | it("has some test", (_) => 129 | ... 130 | ), 131 | 132 | describe("Child context", [ 133 | it("has more tests", (_) => 134 | ... 135 | ) 136 | ]) 137 | ]) 138 | ``` 139 | 140 | The only mutating construct here is the function `register` which adds the group 141 | of examples to an implicit root group. 142 | 143 | ### Pending tests 144 | 145 | Often it is useful to write pending tests, small skeleton descriptions of 146 | functionality you need to implement. This can turn the test framework into a 147 | small todo list: 148 | 149 | ```Reason 150 | describe("Register user", [ 151 | pending("Returns Ok(user) if registration succeeded"), 152 | pending("Returns Error(DuplicateEmail) if email already registered"), 153 | ]) |> register 154 | ``` 155 | 156 | Pending tests will not result in failure when running the tests. 157 | 158 | ### Sync and Async tests 159 | 160 | Internally, respect works asynchonously. A test function takes two curried 161 | arguments, a context object (more on that later) and a callback. 162 | 163 | ```reason 164 | /* Represents the outcome of running a test */ 165 | type executionResult = 166 | | TestPending 167 | | TestSucceeded 168 | | TestFailed; 169 | type executionCallback = executionResult => unit; 170 | /* Internal implementation, a nicer callback is exposed via the DSL */ 171 | type testFunc = (Respect_ctx.t, executionCallback) => unit; 172 | ``` 173 | 174 | The DSL functions, `it`, `beforeEach`, etc. though are generated by a functor, 175 | `Respect.Dsl.Make`, mapping generating the internal representation from a nicer format. 176 | 177 | This allows you to modify the DSL, but also create both sync and async versions 178 | of the DSL, and use custom async mechanisms, e.g. promises. 179 | 180 | #### Sync module 181 | 182 | The sync DSL is available in `Respect.Dsl.Sync`. It also exists in 183 | `Respect.Dsl`, but that is legacy support and will be removed. 184 | 185 | The `Sync` module will simply fail the test if an exception is thrown. (The 186 | matcher library will throw exceptions) 187 | 188 | #### Async module 189 | 190 | The module `Respect.Dsl.Async` supports async test execution, however this is a 191 | bit clunky interface. This was the first attempt at an async implementation and 192 | predates the use of the `Make`-functor for increased flexibility. 193 | 194 | ```Reason 195 | open Respect.Dsl.Async; 196 | 197 | describe ("Parent context", [ 198 | it("has an async test", (_,don) => { 199 | if (success) { 200 | don () 201 | }else { 202 | don (~err="Error",()) 203 | } 204 | }) 205 | ]) |> register; 206 | ``` 207 | 208 | There is currently async matcher support through the function `shoulda` 209 | (should-async). The function has the signature: 210 | 211 | ```Reason 212 | (matcher : matcher 'a 'b) => (actual : 'a) => (cb : doneCallback) => unit 213 | ``` 214 | 215 | This signature plays nicely with the callback allowing you to write tests like 216 | this: 217 | 218 | ```Reason 219 | describe("Register User", [ 220 | describe("Posting valid user", [ 221 | it("creates a user", (_) => { 222 | createValidInput () 223 | |> UserFeature.registerUser 224 | |> shoulda(asyncSucceed) 225 | }) 226 | ]) 227 | ]) |> register 228 | ``` 229 | 230 | This is a bit cryptic but I'll try to explain 231 | 232 | * Our test function didn't explicitly specify a done callback 233 | * We didn't pass a done callback to to the `shoulda` function either. This makes 234 | the result of the `shoulda` function another function, which takes a done 235 | callback. 236 | * So the result of our test function is the function returned by `should`, the 237 | one that takes done callback. Thus our test function has the exact shape that `it` expects. 238 | * The `registerUser` is an async function that expects a callback that we didn't supply. 239 | * The asyncSucceed takes an async function as argument and supplies the right 240 | callback that binds it to the done callback. 241 | 242 | This doesn't play nice however, if you want to have multiple assertions in the 243 | same test :( 244 | 245 | Please be aware that the matcher syntax is likely to change, but I will try 246 | to keep backward compatibility by moving alternate matcher framework into separate 247 | modules. 248 | 249 | #### Creating a custom DSL 250 | 251 | The following piece of code will generate a new DSL supporting my [own async 252 | library](https://github.com/stroiman/resync/blob/master/src/Async.re) 253 | 254 | ```reason 255 | module Resync = { 256 | module Mapper = { 257 | type t('a) = Async.t('a); 258 | let mapTestfunc = 259 | (fn, ctx: Respect.Ctx.t, callback: Respect.Domain.executionCallback) => { 260 | let f = _ => callback(TestSucceeded); 261 | let fe = _ => callback(TestFailed); 262 | fn(ctx) |> Async.run(~fe, f); 263 | }; 264 | }; 265 | 266 | include Make(Mapper); 267 | }; 268 | ``` 269 | 270 | ### Focused/skipped examples 271 | 272 | You can focus an example with the `focus` function. When there are focused 273 | examples, only those will be executed. 274 | 275 | You can skip an example with the `skip` function. Skipped examples will not run. 276 | 277 | You can apply the functions to both examples, and example groups. 278 | 279 | ```reason 280 | describe("Group with focused examples", [ 281 | focus @@ 282 | it("This example is focused", ...), 283 | 284 | skip @@ 285 | it("This example is skipped", ...), 286 | 287 | focus @@ 288 | describe("Group with more focused examples", [ 289 | it("This example is focused", ...), 290 | 291 | it("This example is also focused", ...), 292 | ]) 293 | ]) |> register 294 | ``` 295 | 296 | ## Matchers 297 | 298 | The matchers framework is based on these types: 299 | 300 | ```Reason 301 | type matchResult('t) = 302 | | MatchSuccess('t) 303 | | MatchFailure(Obj.t); 304 | 305 | type matcher('a, 'b) = 'a => (matchResult('b) => unit) => unit; 306 | 307 | exception MatchFailedException(string); 308 | ``` 309 | 310 | So a matcher takes an actual value and provides a matchresult asyncrounously 311 | through a callback. Matchers that evaluate synchronously can use these helper 312 | functions 313 | 314 | ```Reason 315 | let matchSuccess = (a) => cb => cb(MatchSuccess(a)); 316 | let matchFailure = (a) => cb => cb(MatchFailure(a |> Obj.repr)); 317 | ``` 318 | 319 | So if we look at the `equal` match constructor: 320 | 321 | ```Reason 322 | let equal = (expected, actual) => 323 | actual == expected ? matchSuccess(actual) : matchFailure(expected); 324 | ``` 325 | 326 | So it takes an expected value and returns a matcher based on this. 327 | 328 | ### Composing Matchers 329 | 330 | Matchers can be composed using the "fish" operator `>=>`, so a `matcher('a,'b)` 331 | can be composed with a `matcher('b,'c)` into a `matcher('a,'c)`. 332 | 333 | This can be particularly useful when the value passed with the success is 334 | different from the actual value passed to the matcher. Here is an example from a 335 | piece of production code I am working on: 336 | 337 | ```Reason 338 | /* General types to handle errors and async code */ 339 | type result('a, 'b) = Js.Result.t('a, 'b) = | Ok('a) | Error('b); 340 | 341 | type async('a) = ('a => unit) => unit; 342 | type asyncResult('a,'b) = async(result('a,'b)); 343 | 344 | /* Specific error types returned by repository layer */ 345 | type databaseError 'id = 346 | | DocumentNotFound(string,'id) 347 | | MongoErr(MongoError.t); 348 | 349 | /* This is a matcher that verifies that an async function fails. "actual" is a 350 | function that takes a result callback */ 351 | let asyncFail = actual => cb => { 352 | actual 353 | |> AsyncResult.run (fun 354 | | Error(y) => cb(MatchSuccess(y)) 355 | | Ok(y) => cb(MatchFailure (Obj.repr(y)))); 356 | }; 357 | ``` 358 | 359 | The interesting thing is that the `asyncFail` matcher passes the error to the 360 | `MatchResult` constructor, to be used by a new matcher. In this tests we compose 361 | it with a new matcher that verifies that we actually get the expected error. 362 | 363 | ```Reason 364 | describe("UserRepository", [ 365 | describe("findById", [ 366 | describe("record doesn't exist", [ 367 | it("returns DocumentNotFound", (_) => { 368 | let id = "dummy"; 369 | UserRepository.getById(id) 370 | |> shoulda (asyncFail >=> (equal (DocumentNotFound("users",id)))) 371 | }) 372 | ]) 373 | ]) 374 | ]) |> register; 375 | ``` 376 | 377 | 378 | ## TestContext 379 | 380 | The context object passed to both setup functions and test functions provides a 381 | place setup code can place data that can be read by examples or setup code in 382 | nested contexts. 383 | 384 | **Note** I'm working at completely rewriting how to work with dynamic data. This feature is very 385 | much inspired by the solution I chose for FSpec, where it worked _reasonably_ 386 | well. But F# has runtime type information - OCaml/ReasonML does not. 387 | 388 | Currently, all data stored in the test context is stored as `Obj.t`. And as 389 | there is no run-time type check, the following piece of code will neither 390 | generate a compile-time, nor a run-time error. 391 | 392 | ```Reason 393 | let newCtx = ctx |> Ctx.add("key", 42); 394 | let s : string = newCtx |> Ctx.get("key"); 395 | ``` 396 | 397 | We might get an exception further down the line, where the source of the error 398 | is difficult to determine. But even worse, we could get a false positive. 399 | 400 | ### Test Metadata 401 | 402 | You can add metadata to a group or an example. And if you have metadata on a 403 | parent group, you can override it in a child group. The metadata is added using 404 | the strange looking `**>` operator (I chose this because the `**` makes it right 405 | associative, which I need in order to avoid parenthesis hell, and the `>` is a 406 | visual aid, that it binds with the group to come. 407 | 408 | The interesting thing is that the metadata is initialized before the example 409 | starts executing, which means that metadata specified on an example can effect 410 | the setup code executed in a parent group. The following example shows how: 411 | 412 | ```Reason 413 | open Respect.Dsl.Async; 414 | module Ctx = Respect.Ctx; 415 | 416 | describe("Register user", [ 417 | beforeEach ((ctx,don) => { 418 | ctx 419 | |> Ctx.get("userName") 420 | |> /* do something interesting with the user */ 421 | don() 422 | }), 423 | 424 | ("userName", "johndoe") **> 425 | describe("A valid user name was entered", [ 426 | it("Correctly registers the user", (ctx,don) => { 427 | ... 428 | don 429 | }) 430 | ]), 431 | 432 | ("userName", "!@#$") **> 433 | describe("An invalid user name was entered", [ 434 | it("Returns a sensible error message", (ctx, don) => { 435 | ... 436 | don () 437 | }) 438 | ]) 439 | ]) |> register 440 | ``` 441 | 442 | Multiple pieces of metadata can be added to the same example or group, and 443 | values can be overwritten in nested groups/examples. 444 | 445 | ```Reason 446 | /* Pass sensible defaults for a happy case to the root example */ 447 | ("userName", "johndoe") **> 448 | ("password", "agoodlongpassword*42!X") **> 449 | describe("Register user", [ 450 | beforeEach((ctx, don) => { 451 | let userName = ctx |> Ctx.get("userName"); 452 | let password = ctx |> Ctx.get("password"); 453 | }), 454 | 455 | it("succeeds when username and password are ok", (ctx, don) => { 456 | ... 457 | }), 458 | 459 | /* Create various examples that deviate from the happy case to test 460 | that the code handles these cases correctly */ 461 | 462 | ("userName", "!@#$") **> 463 | it("Rejects the attempt when username is invalid", (ctx, don) => { 464 | ... 465 | }), 466 | 467 | ("password", "xyz") **> 468 | it("Rejects the attempt when password is too short", (ctx, don) => { 469 | }) 470 | ]) |> register 471 | ``` 472 | 473 | 474 | #### Subject 475 | 476 | A special property exists on the test context, the `subject` which is meant to 477 | mean "the thing that we are verifying". The subject is a lazily evaluated value, 478 | and the evaluation receives the test context as input. Therefore a setup 479 | function in a parent group can setup the subject, and setup functions in nested 480 | groups can modify the input. 481 | 482 | **Note** this feature might be dropped. 483 | 484 | This shows how (this is for the sake of the example only, there are nicer ways 485 | of doing this). 486 | 487 | ```Reason 488 | describe("create user", [ 489 | beforeEach((ctx) => { 490 | ctx 491 | |> Ctx.setSubj(ctx => { 492 | let username : string = ctx |> Ctx.get("username"); 493 | let password : string = ctx |> Ctx.get("password"); 494 | createUser({username, password}); 495 | /* Assume that createuser returns an Ok/Error indicating the result */ 496 | }) 497 | |> Ctx.don 498 | }), 499 | 500 | describe("Valid credentials", [ 501 | /* this nested context modifies the context before the subject evaluation */ 502 | beforeEach(ctx => { 503 | ctx 504 | |> Ctx.add("username", "validusername") 505 | |> Ctx.add("password", "validpassword") 506 | }) 507 | 508 | it("successfully creates the user", ctx => { 509 | ctx 510 | |> Ctx.subject 511 | |> shoulda(equal(Ok({...}))) 512 | }) 513 | ]) 514 | ]) 515 | ``` 516 | 517 | #### Ctx module functions 518 | 519 | ```Reason 520 | 521 | /* The don function helps return a curried function for the done callback */ 522 | beforeEach(ctx => ctx |> Ctx.don) 523 | 524 | /* Adding data to the context */ 525 | beforeEach(ctx => ctx 526 | |> Ctx.add("Key", value) 527 | |> Ctx.add("key", value) 528 | |> Ctx.don); 529 | 530 | /* Retrieving data */ 531 | let x : string = ctx |> Ctx.get("key") 532 | 533 | /* Retrieve Some(_) if a key exists, None if it doesn't */ 534 | let x : option(string) = ctx |> Ctx.tryGet("key") 535 | 536 | /* Mapping data in the context */ 537 | describe("email is empty", [ 538 | beforeEach(ctx => ctx 539 | |> Ctx.map("user", user => {...user, email: ""}) 540 | |> Ctx.don) 541 | ]) 542 | 543 | /* Setting the subject */ 544 | beforeEach(ctx => ctx 545 | |> Ctx.setSubj(ctx => { 546 | Login(ctx |> Ctx.get("username"), ctx |> Ctx.get("password")) 547 | }) 548 | |> Ctx.don) 549 | 550 | /* Getting the subject */ 551 | let subject : option(Domain.user) = ctx |> Ctx.subject; 552 | ``` 553 | 554 | #### Test context as an object 555 | 556 | The test context itself is implemented as an object, meaning that you can use 557 | the object methods to access the data. 558 | 559 | This could be changed however, so I would recommend using the module function. 560 | 561 | ```Reason 562 | ctx#add("key", value) 563 | /* add returns the context, making it possible to chain calls to #add */ 564 | ctx#add("key", value)#add("key2", value2) /* do notice the bug mentioned below */ 565 | 566 | let s: string = ctx#get("key") 567 | /* get will automatically cast to whatever type you assign it to */ 568 | 569 | let maybeS: option(string) = ctx#tryGet("key") 570 | /* returns Some(_) if the key exists, otherwise returns None */ 571 | 572 | ctx#setSub(ctx => Login(ctx#get("username"), ctx#get("password"))) 573 | /* Sets the function that will be used to evaluate the subject. The function 574 | receives the context (which may have been updated) when it is eventually run */ 575 | 576 | let s = ctx#subject() 577 | /* Evaluates and retrieves the subject. The value is cached, so multiple calls 578 | will return the same value. */ 579 | 580 | ctx#don() 581 | /* helper for ending a setup function */ 582 | 583 | beforeEach(ctx => { 584 | ctx 585 | #add("key", 42) 586 | #don() 587 | }) 588 | ``` 589 | 590 | ### Extending the context 591 | 592 | One technique to help reduce code duplication in the test is to write a 593 | specialed context module in a code file with tests, and add useful functions to 594 | this. 595 | 596 | ```reason 597 | open Respect.Dsl.Async; 598 | open Respect.Matcher; 599 | 600 | module Ctx = { 601 | include Respect.Ctx; 602 | 603 | let createUser = ctx => { 604 | open CreateUserModule; 605 | let username = ctx |> get("username"); 606 | let password = ctx |> get("password"); 607 | createUser({username, password}) 608 | } 609 | }; 610 | 611 | ("username", "goodusername") **> 612 | ("password", "goodpassword") **> 613 | describe("createUser", [ 614 | it("succeeds when user is a success", ctx => 615 | ctx 616 | |> Ctx.createUser 617 | |> shoulda(beSuccess)) 618 | 619 | ("username", "") **> 620 | it("fails when username is empty", ctx => 621 | ctx 622 | |> Ctx.createUser 623 | |> shoulda(beFailure)) 624 | ]) 625 | ``` 626 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@stroiman/async@^0.7": 6 | version "0.7.0" 7 | resolved "https://registry.yarnpkg.com/@stroiman/async/-/async-0.7.0.tgz#b046a0f9e7421ad7411f0eb0b17eaaefc56a530a" 8 | 9 | abbrev@1: 10 | version "1.1.1" 11 | resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" 12 | 13 | ansi-align@^2.0.0: 14 | version "2.0.0" 15 | resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" 16 | dependencies: 17 | string-width "^2.0.0" 18 | 19 | ansi-regex@^2.0.0: 20 | version "2.1.1" 21 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" 22 | 23 | ansi-regex@^3.0.0: 24 | version "3.0.0" 25 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" 26 | 27 | ansi-styles@^3.2.0, ansi-styles@^3.2.1: 28 | version "3.2.1" 29 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 30 | dependencies: 31 | color-convert "^1.9.0" 32 | 33 | anymatch@^2.0.0: 34 | version "2.0.0" 35 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" 36 | dependencies: 37 | micromatch "^3.1.4" 38 | normalize-path "^2.1.1" 39 | 40 | aproba@^1.0.3: 41 | version "1.2.0" 42 | resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" 43 | 44 | are-we-there-yet@~1.1.2: 45 | version "1.1.5" 46 | resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" 47 | dependencies: 48 | delegates "^1.0.0" 49 | readable-stream "^2.0.6" 50 | 51 | arr-diff@^4.0.0: 52 | version "4.0.0" 53 | resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" 54 | 55 | arr-flatten@^1.1.0: 56 | version "1.1.0" 57 | resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" 58 | 59 | arr-union@^3.1.0: 60 | version "3.1.0" 61 | resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" 62 | 63 | array-unique@^0.3.2: 64 | version "0.3.2" 65 | resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" 66 | 67 | assign-symbols@^1.0.0: 68 | version "1.0.0" 69 | resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" 70 | 71 | async-each@^1.0.0: 72 | version "1.0.1" 73 | resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" 74 | 75 | atob@^2.1.1: 76 | version "2.1.1" 77 | resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.1.tgz#ae2d5a729477f289d60dd7f96a6314a22dd6c22a" 78 | 79 | balanced-match@^1.0.0: 80 | version "1.0.0" 81 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 82 | 83 | base@^0.11.1: 84 | version "0.11.2" 85 | resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" 86 | dependencies: 87 | cache-base "^1.0.1" 88 | class-utils "^0.3.5" 89 | component-emitter "^1.2.1" 90 | define-property "^1.0.0" 91 | isobject "^3.0.1" 92 | mixin-deep "^1.2.0" 93 | pascalcase "^0.1.1" 94 | 95 | binary-extensions@^1.0.0: 96 | version "1.11.0" 97 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" 98 | 99 | boxen@^1.2.1: 100 | version "1.3.0" 101 | resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" 102 | dependencies: 103 | ansi-align "^2.0.0" 104 | camelcase "^4.0.0" 105 | chalk "^2.0.1" 106 | cli-boxes "^1.0.0" 107 | string-width "^2.0.0" 108 | term-size "^1.2.0" 109 | widest-line "^2.0.0" 110 | 111 | brace-expansion@^1.1.7: 112 | version "1.1.11" 113 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 114 | dependencies: 115 | balanced-match "^1.0.0" 116 | concat-map "0.0.1" 117 | 118 | braces@^2.3.0, braces@^2.3.1: 119 | version "2.3.2" 120 | resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" 121 | dependencies: 122 | arr-flatten "^1.1.0" 123 | array-unique "^0.3.2" 124 | extend-shallow "^2.0.1" 125 | fill-range "^4.0.0" 126 | isobject "^3.0.1" 127 | repeat-element "^1.1.2" 128 | snapdragon "^0.8.1" 129 | snapdragon-node "^2.0.1" 130 | split-string "^3.0.2" 131 | to-regex "^3.0.1" 132 | 133 | bs-platform@5: 134 | version "5.0.0" 135 | resolved "https://registry.yarnpkg.com/bs-platform/-/bs-platform-5.0.0.tgz#f9fded818bafc3891aeb85eb4e0d95b8d8f8b4d7" 136 | integrity sha512-zxLobdIaf/r7go47hOgxcd6C0ANh7MYMEtZNb9Al7JdoekzFZI50F4GpZpP8kMh9Z+M5PoPE1WuryT4S8mT8Kg== 137 | 138 | builtin-modules@^1.0.0: 139 | version "1.1.1" 140 | resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" 141 | 142 | cache-base@^1.0.1: 143 | version "1.0.1" 144 | resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" 145 | dependencies: 146 | collection-visit "^1.0.0" 147 | component-emitter "^1.2.1" 148 | get-value "^2.0.6" 149 | has-value "^1.0.0" 150 | isobject "^3.0.1" 151 | set-value "^2.0.0" 152 | to-object-path "^0.3.0" 153 | union-value "^1.0.0" 154 | unset-value "^1.0.0" 155 | 156 | camelcase@^4.0.0: 157 | version "4.1.0" 158 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" 159 | 160 | capture-stack-trace@^1.0.0: 161 | version "1.0.0" 162 | resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d" 163 | 164 | chalk@^2.0.1, chalk@^2.1.0: 165 | version "2.4.1" 166 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" 167 | dependencies: 168 | ansi-styles "^3.2.1" 169 | escape-string-regexp "^1.0.5" 170 | supports-color "^5.3.0" 171 | 172 | chokidar@^2.0.2: 173 | version "2.0.4" 174 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26" 175 | dependencies: 176 | anymatch "^2.0.0" 177 | async-each "^1.0.0" 178 | braces "^2.3.0" 179 | glob-parent "^3.1.0" 180 | inherits "^2.0.1" 181 | is-binary-path "^1.0.0" 182 | is-glob "^4.0.0" 183 | lodash.debounce "^4.0.8" 184 | normalize-path "^2.1.1" 185 | path-is-absolute "^1.0.0" 186 | readdirp "^2.0.0" 187 | upath "^1.0.5" 188 | optionalDependencies: 189 | fsevents "^1.2.2" 190 | 191 | chownr@^1.1.1: 192 | version "1.1.4" 193 | resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" 194 | integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== 195 | 196 | ci-info@^1.0.0: 197 | version "1.1.3" 198 | resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.3.tgz#710193264bb05c77b8c90d02f5aaf22216a667b2" 199 | 200 | class-utils@^0.3.5: 201 | version "0.3.6" 202 | resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" 203 | dependencies: 204 | arr-union "^3.1.0" 205 | define-property "^0.2.5" 206 | isobject "^3.0.0" 207 | static-extend "^0.1.1" 208 | 209 | cli-boxes@^1.0.0: 210 | version "1.0.0" 211 | resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" 212 | 213 | cli@^1.0.1: 214 | version "1.0.1" 215 | resolved "https://registry.yarnpkg.com/cli/-/cli-1.0.1.tgz#22817534f24bfa4950c34d532d48ecbc621b8c14" 216 | dependencies: 217 | exit "0.1.2" 218 | glob "^7.1.1" 219 | 220 | code-point-at@^1.0.0: 221 | version "1.1.0" 222 | resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" 223 | 224 | collection-visit@^1.0.0: 225 | version "1.0.0" 226 | resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" 227 | dependencies: 228 | map-visit "^1.0.0" 229 | object-visit "^1.0.0" 230 | 231 | color-convert@^1.9.0: 232 | version "1.9.2" 233 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.2.tgz#49881b8fba67df12a96bdf3f56c0aab9e7913147" 234 | dependencies: 235 | color-name "1.1.1" 236 | 237 | color-name@1.1.1: 238 | version "1.1.1" 239 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" 240 | 241 | component-emitter@^1.2.1: 242 | version "1.2.1" 243 | resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" 244 | 245 | concat-map@0.0.1: 246 | version "0.0.1" 247 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 248 | 249 | configstore@^3.0.0: 250 | version "3.1.2" 251 | resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.2.tgz#c6f25defaeef26df12dd33414b001fe81a543f8f" 252 | dependencies: 253 | dot-prop "^4.1.0" 254 | graceful-fs "^4.1.2" 255 | make-dir "^1.0.0" 256 | unique-string "^1.0.0" 257 | write-file-atomic "^2.0.0" 258 | xdg-basedir "^3.0.0" 259 | 260 | console-control-strings@^1.0.0, console-control-strings@~1.1.0: 261 | version "1.1.0" 262 | resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" 263 | 264 | copy-descriptor@^0.1.0: 265 | version "0.1.1" 266 | resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" 267 | 268 | core-util-is@~1.0.0: 269 | version "1.0.2" 270 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 271 | 272 | create-error-class@^3.0.0: 273 | version "3.0.2" 274 | resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" 275 | dependencies: 276 | capture-stack-trace "^1.0.0" 277 | 278 | cross-spawn@^5.0.1: 279 | version "5.1.0" 280 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" 281 | dependencies: 282 | lru-cache "^4.0.1" 283 | shebang-command "^1.2.0" 284 | which "^1.2.9" 285 | 286 | cross-spawn@^6.0.4: 287 | version "6.0.5" 288 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" 289 | dependencies: 290 | nice-try "^1.0.4" 291 | path-key "^2.0.1" 292 | semver "^5.5.0" 293 | shebang-command "^1.2.0" 294 | which "^1.2.9" 295 | 296 | crypto-random-string@^1.0.0: 297 | version "1.0.0" 298 | resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" 299 | 300 | debug@^2.1.2, debug@^2.2.0, debug@^2.3.3: 301 | version "2.6.9" 302 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 303 | dependencies: 304 | ms "2.0.0" 305 | 306 | debug@^3.1.0: 307 | version "3.1.0" 308 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" 309 | dependencies: 310 | ms "2.0.0" 311 | 312 | decode-uri-component@^0.2.0: 313 | version "0.2.0" 314 | resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" 315 | 316 | deep-extend@^0.6.0: 317 | version "0.6.0" 318 | resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" 319 | 320 | define-properties@^1.1.2: 321 | version "1.1.2" 322 | resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" 323 | dependencies: 324 | foreach "^2.0.5" 325 | object-keys "^1.0.8" 326 | 327 | define-property@^0.2.5: 328 | version "0.2.5" 329 | resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" 330 | dependencies: 331 | is-descriptor "^0.1.0" 332 | 333 | define-property@^1.0.0: 334 | version "1.0.0" 335 | resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" 336 | dependencies: 337 | is-descriptor "^1.0.0" 338 | 339 | define-property@^2.0.2: 340 | version "2.0.2" 341 | resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" 342 | dependencies: 343 | is-descriptor "^1.0.2" 344 | isobject "^3.0.1" 345 | 346 | delegates@^1.0.0: 347 | version "1.0.0" 348 | resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" 349 | 350 | detect-libc@^1.0.2: 351 | version "1.0.3" 352 | resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" 353 | 354 | dot-prop@^4.1.0: 355 | version "4.2.1" 356 | resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.1.tgz#45884194a71fc2cda71cbb4bceb3a4dd2f433ba4" 357 | integrity sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ== 358 | dependencies: 359 | is-obj "^1.0.0" 360 | 361 | duplexer3@^0.1.4: 362 | version "0.1.4" 363 | resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" 364 | 365 | duplexer@~0.1.1: 366 | version "0.1.1" 367 | resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" 368 | 369 | error-ex@^1.3.1: 370 | version "1.3.2" 371 | resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" 372 | dependencies: 373 | is-arrayish "^0.2.1" 374 | 375 | es-abstract@^1.4.3: 376 | version "1.12.0" 377 | resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165" 378 | dependencies: 379 | es-to-primitive "^1.1.1" 380 | function-bind "^1.1.1" 381 | has "^1.0.1" 382 | is-callable "^1.1.3" 383 | is-regex "^1.0.4" 384 | 385 | es-to-primitive@^1.1.1: 386 | version "1.1.1" 387 | resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d" 388 | dependencies: 389 | is-callable "^1.1.1" 390 | is-date-object "^1.0.1" 391 | is-symbol "^1.0.1" 392 | 393 | escape-string-regexp@^1.0.5: 394 | version "1.0.5" 395 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 396 | 397 | event-stream@~3.3.0: 398 | version "3.3.4" 399 | resolved "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" 400 | dependencies: 401 | duplexer "~0.1.1" 402 | from "~0" 403 | map-stream "~0.1.0" 404 | pause-stream "0.0.11" 405 | split "0.3" 406 | stream-combiner "~0.0.4" 407 | through "~2.3.1" 408 | 409 | execa@^0.7.0: 410 | version "0.7.0" 411 | resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" 412 | dependencies: 413 | cross-spawn "^5.0.1" 414 | get-stream "^3.0.0" 415 | is-stream "^1.1.0" 416 | npm-run-path "^2.0.0" 417 | p-finally "^1.0.0" 418 | signal-exit "^3.0.0" 419 | strip-eof "^1.0.0" 420 | 421 | exit@0.1.2: 422 | version "0.1.2" 423 | resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" 424 | 425 | expand-brackets@^2.1.4: 426 | version "2.1.4" 427 | resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" 428 | dependencies: 429 | debug "^2.3.3" 430 | define-property "^0.2.5" 431 | extend-shallow "^2.0.1" 432 | posix-character-classes "^0.1.0" 433 | regex-not "^1.0.0" 434 | snapdragon "^0.8.1" 435 | to-regex "^3.0.1" 436 | 437 | extend-shallow@^2.0.1: 438 | version "2.0.1" 439 | resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" 440 | dependencies: 441 | is-extendable "^0.1.0" 442 | 443 | extend-shallow@^3.0.0, extend-shallow@^3.0.2: 444 | version "3.0.2" 445 | resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" 446 | dependencies: 447 | assign-symbols "^1.0.0" 448 | is-extendable "^1.0.1" 449 | 450 | extglob@^2.0.4: 451 | version "2.0.4" 452 | resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" 453 | dependencies: 454 | array-unique "^0.3.2" 455 | define-property "^1.0.0" 456 | expand-brackets "^2.1.4" 457 | extend-shallow "^2.0.1" 458 | fragment-cache "^0.2.1" 459 | regex-not "^1.0.0" 460 | snapdragon "^0.8.1" 461 | to-regex "^3.0.1" 462 | 463 | fill-range@^4.0.0: 464 | version "4.0.0" 465 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" 466 | dependencies: 467 | extend-shallow "^2.0.1" 468 | is-number "^3.0.0" 469 | repeat-string "^1.6.1" 470 | to-regex-range "^2.1.0" 471 | 472 | for-in@^1.0.2: 473 | version "1.0.2" 474 | resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" 475 | integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= 476 | 477 | foreach@^2.0.5: 478 | version "2.0.5" 479 | resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" 480 | 481 | fragment-cache@^0.2.1: 482 | version "0.2.1" 483 | resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" 484 | dependencies: 485 | map-cache "^0.2.2" 486 | 487 | from@~0: 488 | version "0.1.7" 489 | resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" 490 | 491 | fs-minipass@^1.2.5: 492 | version "1.2.7" 493 | resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" 494 | integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== 495 | dependencies: 496 | minipass "^2.6.0" 497 | 498 | fs.realpath@^1.0.0: 499 | version "1.0.0" 500 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 501 | 502 | fsevents@^1.2.2: 503 | version "1.2.4" 504 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426" 505 | dependencies: 506 | nan "^2.9.2" 507 | node-pre-gyp "^0.10.0" 508 | 509 | function-bind@^1.0.2, function-bind@^1.1.1: 510 | version "1.1.1" 511 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 512 | 513 | gauge@~2.7.3: 514 | version "2.7.4" 515 | resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" 516 | dependencies: 517 | aproba "^1.0.3" 518 | console-control-strings "^1.0.0" 519 | has-unicode "^2.0.0" 520 | object-assign "^4.1.0" 521 | signal-exit "^3.0.0" 522 | string-width "^1.0.1" 523 | strip-ansi "^3.0.1" 524 | wide-align "^1.1.0" 525 | 526 | get-stream@^3.0.0: 527 | version "3.0.0" 528 | resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" 529 | 530 | get-value@^2.0.3, get-value@^2.0.6: 531 | version "2.0.6" 532 | resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" 533 | 534 | glob-parent@^3.1.0: 535 | version "3.1.0" 536 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" 537 | dependencies: 538 | is-glob "^3.1.0" 539 | path-dirname "^1.0.0" 540 | 541 | glob@^7.0.5, glob@^7.1.1, glob@^7.1.2: 542 | version "7.1.2" 543 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" 544 | dependencies: 545 | fs.realpath "^1.0.0" 546 | inflight "^1.0.4" 547 | inherits "2" 548 | minimatch "^3.0.4" 549 | once "^1.3.0" 550 | path-is-absolute "^1.0.0" 551 | 552 | global-dirs@^0.1.0: 553 | version "0.1.1" 554 | resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" 555 | dependencies: 556 | ini "^1.3.4" 557 | 558 | got@^6.7.1: 559 | version "6.7.1" 560 | resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" 561 | dependencies: 562 | create-error-class "^3.0.0" 563 | duplexer3 "^0.1.4" 564 | get-stream "^3.0.0" 565 | is-redirect "^1.0.0" 566 | is-retry-allowed "^1.0.0" 567 | is-stream "^1.0.0" 568 | lowercase-keys "^1.0.0" 569 | safe-buffer "^5.0.1" 570 | timed-out "^4.0.0" 571 | unzip-response "^2.0.1" 572 | url-parse-lax "^1.0.0" 573 | 574 | graceful-fs@^4.1.11, graceful-fs@^4.1.2: 575 | version "4.1.11" 576 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" 577 | 578 | has-flag@^3.0.0: 579 | version "3.0.0" 580 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 581 | 582 | has-unicode@^2.0.0: 583 | version "2.0.1" 584 | resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" 585 | 586 | has-value@^0.3.1: 587 | version "0.3.1" 588 | resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" 589 | dependencies: 590 | get-value "^2.0.3" 591 | has-values "^0.1.4" 592 | isobject "^2.0.0" 593 | 594 | has-value@^1.0.0: 595 | version "1.0.0" 596 | resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" 597 | dependencies: 598 | get-value "^2.0.6" 599 | has-values "^1.0.0" 600 | isobject "^3.0.0" 601 | 602 | has-values@^0.1.4: 603 | version "0.1.4" 604 | resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" 605 | 606 | has-values@^1.0.0: 607 | version "1.0.0" 608 | resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" 609 | dependencies: 610 | is-number "^3.0.0" 611 | kind-of "^4.0.0" 612 | 613 | has@^1.0.1: 614 | version "1.0.3" 615 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 616 | dependencies: 617 | function-bind "^1.1.1" 618 | 619 | hosted-git-info@^2.1.4: 620 | version "2.8.9" 621 | resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" 622 | integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== 623 | 624 | iconv-lite@^0.4.4: 625 | version "0.4.23" 626 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" 627 | dependencies: 628 | safer-buffer ">= 2.1.2 < 3" 629 | 630 | ignore-by-default@^1.0.1: 631 | version "1.0.1" 632 | resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" 633 | 634 | ignore-walk@^3.0.1: 635 | version "3.0.1" 636 | resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" 637 | dependencies: 638 | minimatch "^3.0.4" 639 | 640 | import-lazy@^2.1.0: 641 | version "2.1.0" 642 | resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" 643 | 644 | imurmurhash@^0.1.4: 645 | version "0.1.4" 646 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" 647 | 648 | inflight@^1.0.4: 649 | version "1.0.6" 650 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 651 | dependencies: 652 | once "^1.3.0" 653 | wrappy "1" 654 | 655 | inherits@2, inherits@^2.0.1, inherits@~2.0.3: 656 | version "2.0.3" 657 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 658 | 659 | ini@^1.3.4, ini@~1.3.0: 660 | version "1.3.8" 661 | resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" 662 | integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== 663 | 664 | is-accessor-descriptor@^0.1.6: 665 | version "0.1.6" 666 | resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" 667 | dependencies: 668 | kind-of "^3.0.2" 669 | 670 | is-accessor-descriptor@^1.0.0: 671 | version "1.0.0" 672 | resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" 673 | dependencies: 674 | kind-of "^6.0.0" 675 | 676 | is-arrayish@^0.2.1: 677 | version "0.2.1" 678 | resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" 679 | 680 | is-binary-path@^1.0.0: 681 | version "1.0.1" 682 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" 683 | dependencies: 684 | binary-extensions "^1.0.0" 685 | 686 | is-buffer@^1.1.5: 687 | version "1.1.6" 688 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" 689 | 690 | is-builtin-module@^1.0.0: 691 | version "1.0.0" 692 | resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" 693 | dependencies: 694 | builtin-modules "^1.0.0" 695 | 696 | is-callable@^1.1.1, is-callable@^1.1.3: 697 | version "1.1.4" 698 | resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" 699 | 700 | is-ci@^1.0.10: 701 | version "1.1.0" 702 | resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.1.0.tgz#247e4162e7860cebbdaf30b774d6b0ac7dcfe7a5" 703 | dependencies: 704 | ci-info "^1.0.0" 705 | 706 | is-data-descriptor@^0.1.4: 707 | version "0.1.4" 708 | resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" 709 | dependencies: 710 | kind-of "^3.0.2" 711 | 712 | is-data-descriptor@^1.0.0: 713 | version "1.0.0" 714 | resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" 715 | dependencies: 716 | kind-of "^6.0.0" 717 | 718 | is-date-object@^1.0.1: 719 | version "1.0.1" 720 | resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" 721 | 722 | is-descriptor@^0.1.0: 723 | version "0.1.6" 724 | resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" 725 | dependencies: 726 | is-accessor-descriptor "^0.1.6" 727 | is-data-descriptor "^0.1.4" 728 | kind-of "^5.0.0" 729 | 730 | is-descriptor@^1.0.0, is-descriptor@^1.0.2: 731 | version "1.0.2" 732 | resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" 733 | dependencies: 734 | is-accessor-descriptor "^1.0.0" 735 | is-data-descriptor "^1.0.0" 736 | kind-of "^6.0.2" 737 | 738 | is-extendable@^0.1.0, is-extendable@^0.1.1: 739 | version "0.1.1" 740 | resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" 741 | 742 | is-extendable@^1.0.1: 743 | version "1.0.1" 744 | resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" 745 | integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== 746 | dependencies: 747 | is-plain-object "^2.0.4" 748 | 749 | is-extglob@^2.1.0, is-extglob@^2.1.1: 750 | version "2.1.1" 751 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 752 | 753 | is-fullwidth-code-point@^1.0.0: 754 | version "1.0.0" 755 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" 756 | dependencies: 757 | number-is-nan "^1.0.0" 758 | 759 | is-fullwidth-code-point@^2.0.0: 760 | version "2.0.0" 761 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" 762 | 763 | is-glob@^3.1.0: 764 | version "3.1.0" 765 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" 766 | dependencies: 767 | is-extglob "^2.1.0" 768 | 769 | is-glob@^4.0.0: 770 | version "4.0.0" 771 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" 772 | dependencies: 773 | is-extglob "^2.1.1" 774 | 775 | is-installed-globally@^0.1.0: 776 | version "0.1.0" 777 | resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80" 778 | dependencies: 779 | global-dirs "^0.1.0" 780 | is-path-inside "^1.0.0" 781 | 782 | is-npm@^1.0.0: 783 | version "1.0.0" 784 | resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" 785 | 786 | is-number@^3.0.0: 787 | version "3.0.0" 788 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" 789 | dependencies: 790 | kind-of "^3.0.2" 791 | 792 | is-obj@^1.0.0: 793 | version "1.0.1" 794 | resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" 795 | integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= 796 | 797 | is-path-inside@^1.0.0: 798 | version "1.0.1" 799 | resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" 800 | dependencies: 801 | path-is-inside "^1.0.1" 802 | 803 | is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: 804 | version "2.0.4" 805 | resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" 806 | integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== 807 | dependencies: 808 | isobject "^3.0.1" 809 | 810 | is-redirect@^1.0.0: 811 | version "1.0.0" 812 | resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" 813 | 814 | is-regex@^1.0.4: 815 | version "1.0.4" 816 | resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" 817 | dependencies: 818 | has "^1.0.1" 819 | 820 | is-retry-allowed@^1.0.0: 821 | version "1.1.0" 822 | resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" 823 | 824 | is-stream@^1.0.0, is-stream@^1.1.0: 825 | version "1.1.0" 826 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" 827 | 828 | is-symbol@^1.0.1: 829 | version "1.0.1" 830 | resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" 831 | 832 | is-windows@^1.0.2: 833 | version "1.0.2" 834 | resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" 835 | 836 | isarray@1.0.0, isarray@~1.0.0: 837 | version "1.0.0" 838 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 839 | 840 | isexe@^2.0.0: 841 | version "2.0.0" 842 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 843 | 844 | isobject@^2.0.0: 845 | version "2.1.0" 846 | resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" 847 | dependencies: 848 | isarray "1.0.0" 849 | 850 | isobject@^3.0.0, isobject@^3.0.1: 851 | version "3.0.1" 852 | resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" 853 | integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= 854 | 855 | json-parse-better-errors@^1.0.1: 856 | version "1.0.2" 857 | resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" 858 | 859 | kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: 860 | version "3.2.2" 861 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" 862 | dependencies: 863 | is-buffer "^1.1.5" 864 | 865 | kind-of@^4.0.0: 866 | version "4.0.0" 867 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" 868 | dependencies: 869 | is-buffer "^1.1.5" 870 | 871 | kind-of@^5.0.0: 872 | version "5.1.0" 873 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" 874 | 875 | kind-of@^6.0.0, kind-of@^6.0.2: 876 | version "6.0.2" 877 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" 878 | 879 | latest-version@^3.0.0: 880 | version "3.1.0" 881 | resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15" 882 | dependencies: 883 | package-json "^4.0.0" 884 | 885 | load-json-file@^4.0.0: 886 | version "4.0.0" 887 | resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" 888 | dependencies: 889 | graceful-fs "^4.1.2" 890 | parse-json "^4.0.0" 891 | pify "^3.0.0" 892 | strip-bom "^3.0.0" 893 | 894 | lodash.debounce@^4.0.8: 895 | version "4.0.8" 896 | resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" 897 | 898 | lowercase-keys@^1.0.0: 899 | version "1.0.1" 900 | resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" 901 | 902 | lru-cache@^4.0.1: 903 | version "4.1.3" 904 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c" 905 | dependencies: 906 | pseudomap "^1.0.2" 907 | yallist "^2.1.2" 908 | 909 | make-dir@^1.0.0: 910 | version "1.3.0" 911 | resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" 912 | dependencies: 913 | pify "^3.0.0" 914 | 915 | map-cache@^0.2.2: 916 | version "0.2.2" 917 | resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" 918 | 919 | map-stream@~0.1.0: 920 | version "0.1.0" 921 | resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" 922 | 923 | map-visit@^1.0.0: 924 | version "1.0.0" 925 | resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" 926 | dependencies: 927 | object-visit "^1.0.0" 928 | 929 | memorystream@^0.3.1: 930 | version "0.3.1" 931 | resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" 932 | 933 | micromatch@^3.1.4: 934 | version "3.1.10" 935 | resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" 936 | dependencies: 937 | arr-diff "^4.0.0" 938 | array-unique "^0.3.2" 939 | braces "^2.3.1" 940 | define-property "^2.0.2" 941 | extend-shallow "^3.0.2" 942 | extglob "^2.0.4" 943 | fragment-cache "^0.2.1" 944 | kind-of "^6.0.2" 945 | nanomatch "^1.2.9" 946 | object.pick "^1.3.0" 947 | regex-not "^1.0.0" 948 | snapdragon "^0.8.1" 949 | to-regex "^3.0.2" 950 | 951 | minimatch@^3.0.2, minimatch@^3.0.4: 952 | version "3.0.4" 953 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 954 | dependencies: 955 | brace-expansion "^1.1.7" 956 | 957 | minimist@^1.2.0, minimist@^1.2.5: 958 | version "1.2.6" 959 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" 960 | integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== 961 | 962 | minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: 963 | version "2.9.0" 964 | resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" 965 | integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== 966 | dependencies: 967 | safe-buffer "^5.1.2" 968 | yallist "^3.0.0" 969 | 970 | minizlib@^1.2.1: 971 | version "1.3.3" 972 | resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" 973 | integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== 974 | dependencies: 975 | minipass "^2.9.0" 976 | 977 | mixin-deep@^1.2.0: 978 | version "1.3.2" 979 | resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" 980 | integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== 981 | dependencies: 982 | for-in "^1.0.2" 983 | is-extendable "^1.0.1" 984 | 985 | mkdirp@^0.5.0, mkdirp@^0.5.1: 986 | version "0.5.5" 987 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" 988 | integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== 989 | dependencies: 990 | minimist "^1.2.5" 991 | 992 | ms@2.0.0: 993 | version "2.0.0" 994 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 995 | 996 | nan@^2.9.2: 997 | version "2.10.0" 998 | resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" 999 | 1000 | nanomatch@^1.2.9: 1001 | version "1.2.13" 1002 | resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" 1003 | dependencies: 1004 | arr-diff "^4.0.0" 1005 | array-unique "^0.3.2" 1006 | define-property "^2.0.2" 1007 | extend-shallow "^3.0.2" 1008 | fragment-cache "^0.2.1" 1009 | is-windows "^1.0.2" 1010 | kind-of "^6.0.2" 1011 | object.pick "^1.3.0" 1012 | regex-not "^1.0.0" 1013 | snapdragon "^0.8.1" 1014 | to-regex "^3.0.1" 1015 | 1016 | needle@^2.2.1: 1017 | version "2.2.1" 1018 | resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.1.tgz#b5e325bd3aae8c2678902fa296f729455d1d3a7d" 1019 | dependencies: 1020 | debug "^2.1.2" 1021 | iconv-lite "^0.4.4" 1022 | sax "^1.2.4" 1023 | 1024 | nice-try@^1.0.4: 1025 | version "1.0.4" 1026 | resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4" 1027 | 1028 | node-pre-gyp@^0.10.0: 1029 | version "0.10.3" 1030 | resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc" 1031 | dependencies: 1032 | detect-libc "^1.0.2" 1033 | mkdirp "^0.5.1" 1034 | needle "^2.2.1" 1035 | nopt "^4.0.1" 1036 | npm-packlist "^1.1.6" 1037 | npmlog "^4.0.2" 1038 | rc "^1.2.7" 1039 | rimraf "^2.6.1" 1040 | semver "^5.3.0" 1041 | tar "^4" 1042 | 1043 | nodemon@^1.12.1: 1044 | version "1.17.5" 1045 | resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.17.5.tgz#e6a665c872fdf09d48bf2a81f3e85f8cfb39322a" 1046 | dependencies: 1047 | chokidar "^2.0.2" 1048 | debug "^3.1.0" 1049 | ignore-by-default "^1.0.1" 1050 | minimatch "^3.0.4" 1051 | pstree.remy "^1.1.0" 1052 | semver "^5.5.0" 1053 | supports-color "^5.2.0" 1054 | touch "^3.1.0" 1055 | undefsafe "^2.0.2" 1056 | update-notifier "^2.3.0" 1057 | 1058 | nopt@^4.0.1: 1059 | version "4.0.1" 1060 | resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" 1061 | dependencies: 1062 | abbrev "1" 1063 | osenv "^0.1.4" 1064 | 1065 | nopt@~1.0.10: 1066 | version "1.0.10" 1067 | resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" 1068 | dependencies: 1069 | abbrev "1" 1070 | 1071 | normalize-package-data@^2.3.2: 1072 | version "2.4.0" 1073 | resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" 1074 | dependencies: 1075 | hosted-git-info "^2.1.4" 1076 | is-builtin-module "^1.0.0" 1077 | semver "2 || 3 || 4 || 5" 1078 | validate-npm-package-license "^3.0.1" 1079 | 1080 | normalize-path@^2.1.1: 1081 | version "2.1.1" 1082 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" 1083 | dependencies: 1084 | remove-trailing-separator "^1.0.1" 1085 | 1086 | npm-bundled@^1.0.1: 1087 | version "1.0.3" 1088 | resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.3.tgz#7e71703d973af3370a9591bafe3a63aca0be2308" 1089 | 1090 | npm-packlist@^1.1.6: 1091 | version "1.1.10" 1092 | resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.10.tgz#1039db9e985727e464df066f4cf0ab6ef85c398a" 1093 | dependencies: 1094 | ignore-walk "^3.0.1" 1095 | npm-bundled "^1.0.1" 1096 | 1097 | npm-run-all@^4.1.1: 1098 | version "4.1.3" 1099 | resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.3.tgz#49f15b55a66bb4101664ce270cb18e7103f8f185" 1100 | dependencies: 1101 | ansi-styles "^3.2.0" 1102 | chalk "^2.1.0" 1103 | cross-spawn "^6.0.4" 1104 | memorystream "^0.3.1" 1105 | minimatch "^3.0.4" 1106 | ps-tree "^1.1.0" 1107 | read-pkg "^3.0.0" 1108 | shell-quote "^1.6.1" 1109 | string.prototype.padend "^3.0.0" 1110 | 1111 | npm-run-path@^2.0.0: 1112 | version "2.0.2" 1113 | resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" 1114 | dependencies: 1115 | path-key "^2.0.0" 1116 | 1117 | npmlog@^4.0.2: 1118 | version "4.1.2" 1119 | resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" 1120 | dependencies: 1121 | are-we-there-yet "~1.1.2" 1122 | console-control-strings "~1.1.0" 1123 | gauge "~2.7.3" 1124 | set-blocking "~2.0.0" 1125 | 1126 | number-is-nan@^1.0.0: 1127 | version "1.0.1" 1128 | resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" 1129 | 1130 | object-assign@^4.1.0: 1131 | version "4.1.1" 1132 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 1133 | 1134 | object-copy@^0.1.0: 1135 | version "0.1.0" 1136 | resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" 1137 | dependencies: 1138 | copy-descriptor "^0.1.0" 1139 | define-property "^0.2.5" 1140 | kind-of "^3.0.3" 1141 | 1142 | object-keys@^1.0.8: 1143 | version "1.0.12" 1144 | resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2" 1145 | 1146 | object-visit@^1.0.0: 1147 | version "1.0.1" 1148 | resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" 1149 | dependencies: 1150 | isobject "^3.0.0" 1151 | 1152 | object.pick@^1.3.0: 1153 | version "1.3.0" 1154 | resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" 1155 | dependencies: 1156 | isobject "^3.0.1" 1157 | 1158 | once@^1.3.0: 1159 | version "1.4.0" 1160 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 1161 | dependencies: 1162 | wrappy "1" 1163 | 1164 | os-homedir@^1.0.0: 1165 | version "1.0.2" 1166 | resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" 1167 | 1168 | os-tmpdir@^1.0.0: 1169 | version "1.0.2" 1170 | resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" 1171 | 1172 | osenv@^0.1.4: 1173 | version "0.1.5" 1174 | resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" 1175 | dependencies: 1176 | os-homedir "^1.0.0" 1177 | os-tmpdir "^1.0.0" 1178 | 1179 | p-finally@^1.0.0: 1180 | version "1.0.0" 1181 | resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" 1182 | 1183 | package-json@^4.0.0: 1184 | version "4.0.1" 1185 | resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed" 1186 | dependencies: 1187 | got "^6.7.1" 1188 | registry-auth-token "^3.0.1" 1189 | registry-url "^3.0.3" 1190 | semver "^5.1.0" 1191 | 1192 | parse-json@^4.0.0: 1193 | version "4.0.0" 1194 | resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" 1195 | dependencies: 1196 | error-ex "^1.3.1" 1197 | json-parse-better-errors "^1.0.1" 1198 | 1199 | pascalcase@^0.1.1: 1200 | version "0.1.1" 1201 | resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" 1202 | 1203 | path-dirname@^1.0.0: 1204 | version "1.0.2" 1205 | resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" 1206 | 1207 | path-is-absolute@^1.0.0: 1208 | version "1.0.1" 1209 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 1210 | 1211 | path-is-inside@^1.0.1: 1212 | version "1.0.2" 1213 | resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" 1214 | 1215 | path-key@^2.0.0, path-key@^2.0.1: 1216 | version "2.0.1" 1217 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" 1218 | 1219 | path-type@^3.0.0: 1220 | version "3.0.0" 1221 | resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" 1222 | dependencies: 1223 | pify "^3.0.0" 1224 | 1225 | pause-stream@0.0.11: 1226 | version "0.0.11" 1227 | resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" 1228 | dependencies: 1229 | through "~2.3" 1230 | 1231 | pify@^3.0.0: 1232 | version "3.0.0" 1233 | resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" 1234 | 1235 | posix-character-classes@^0.1.0: 1236 | version "0.1.1" 1237 | resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" 1238 | 1239 | prepend-http@^1.0.1: 1240 | version "1.0.4" 1241 | resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" 1242 | 1243 | process-nextick-args@~2.0.0: 1244 | version "2.0.0" 1245 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" 1246 | 1247 | ps-tree@^1.1.0: 1248 | version "1.1.0" 1249 | resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.1.0.tgz#b421b24140d6203f1ed3c76996b4427b08e8c014" 1250 | dependencies: 1251 | event-stream "~3.3.0" 1252 | 1253 | pseudomap@^1.0.2: 1254 | version "1.0.2" 1255 | resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" 1256 | 1257 | pstree.remy@^1.1.0: 1258 | version "1.1.0" 1259 | resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.0.tgz#f2af27265bd3e5b32bbfcc10e80bac55ba78688b" 1260 | dependencies: 1261 | ps-tree "^1.1.0" 1262 | 1263 | rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: 1264 | version "1.2.8" 1265 | resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" 1266 | dependencies: 1267 | deep-extend "^0.6.0" 1268 | ini "~1.3.0" 1269 | minimist "^1.2.0" 1270 | strip-json-comments "~2.0.1" 1271 | 1272 | read-pkg@^3.0.0: 1273 | version "3.0.0" 1274 | resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" 1275 | dependencies: 1276 | load-json-file "^4.0.0" 1277 | normalize-package-data "^2.3.2" 1278 | path-type "^3.0.0" 1279 | 1280 | readable-stream@^2.0.2, readable-stream@^2.0.6: 1281 | version "2.3.6" 1282 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" 1283 | dependencies: 1284 | core-util-is "~1.0.0" 1285 | inherits "~2.0.3" 1286 | isarray "~1.0.0" 1287 | process-nextick-args "~2.0.0" 1288 | safe-buffer "~5.1.1" 1289 | string_decoder "~1.1.1" 1290 | util-deprecate "~1.0.1" 1291 | 1292 | readdirp@^2.0.0: 1293 | version "2.1.0" 1294 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" 1295 | dependencies: 1296 | graceful-fs "^4.1.2" 1297 | minimatch "^3.0.2" 1298 | readable-stream "^2.0.2" 1299 | set-immediate-shim "^1.0.1" 1300 | 1301 | regex-not@^1.0.0, regex-not@^1.0.2: 1302 | version "1.0.2" 1303 | resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" 1304 | dependencies: 1305 | extend-shallow "^3.0.2" 1306 | safe-regex "^1.1.0" 1307 | 1308 | registry-auth-token@^3.0.1: 1309 | version "3.3.2" 1310 | resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20" 1311 | dependencies: 1312 | rc "^1.1.6" 1313 | safe-buffer "^5.0.1" 1314 | 1315 | registry-url@^3.0.3: 1316 | version "3.1.0" 1317 | resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" 1318 | dependencies: 1319 | rc "^1.0.1" 1320 | 1321 | remove-trailing-separator@^1.0.1: 1322 | version "1.1.0" 1323 | resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" 1324 | 1325 | repeat-element@^1.1.2: 1326 | version "1.1.2" 1327 | resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" 1328 | 1329 | repeat-string@^1.6.1: 1330 | version "1.6.1" 1331 | resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" 1332 | 1333 | resolve-url@^0.2.1: 1334 | version "0.2.1" 1335 | resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" 1336 | 1337 | ret@~0.1.10: 1338 | version "0.1.15" 1339 | resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" 1340 | 1341 | rimraf@^2.6.1: 1342 | version "2.6.2" 1343 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" 1344 | dependencies: 1345 | glob "^7.0.5" 1346 | 1347 | safe-buffer@^5.0.1, safe-buffer@^5.1.2: 1348 | version "5.2.1" 1349 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 1350 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 1351 | 1352 | safe-buffer@~5.1.0, safe-buffer@~5.1.1: 1353 | version "5.1.2" 1354 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 1355 | 1356 | safe-regex@^1.1.0: 1357 | version "1.1.0" 1358 | resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" 1359 | dependencies: 1360 | ret "~0.1.10" 1361 | 1362 | "safer-buffer@>= 2.1.2 < 3": 1363 | version "2.1.2" 1364 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 1365 | 1366 | sax@^1.2.4: 1367 | version "1.2.4" 1368 | resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" 1369 | 1370 | semver-diff@^2.0.0: 1371 | version "2.1.0" 1372 | resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" 1373 | dependencies: 1374 | semver "^5.0.3" 1375 | 1376 | "semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.5.0: 1377 | version "5.5.0" 1378 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" 1379 | 1380 | set-blocking@~2.0.0: 1381 | version "2.0.0" 1382 | resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" 1383 | 1384 | set-immediate-shim@^1.0.1: 1385 | version "1.0.1" 1386 | resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" 1387 | 1388 | set-value@^0.4.3: 1389 | version "0.4.3" 1390 | resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" 1391 | dependencies: 1392 | extend-shallow "^2.0.1" 1393 | is-extendable "^0.1.1" 1394 | is-plain-object "^2.0.1" 1395 | to-object-path "^0.3.0" 1396 | 1397 | set-value@^2.0.0: 1398 | version "2.0.0" 1399 | resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" 1400 | dependencies: 1401 | extend-shallow "^2.0.1" 1402 | is-extendable "^0.1.1" 1403 | is-plain-object "^2.0.3" 1404 | split-string "^3.0.1" 1405 | 1406 | shebang-command@^1.2.0: 1407 | version "1.2.0" 1408 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" 1409 | dependencies: 1410 | shebang-regex "^1.0.0" 1411 | 1412 | shebang-regex@^1.0.0: 1413 | version "1.0.0" 1414 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" 1415 | 1416 | shell-quote@^1.6.1: 1417 | version "1.7.3" 1418 | resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" 1419 | integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== 1420 | 1421 | signal-exit@^3.0.0, signal-exit@^3.0.2: 1422 | version "3.0.2" 1423 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" 1424 | 1425 | snapdragon-node@^2.0.1: 1426 | version "2.1.1" 1427 | resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" 1428 | dependencies: 1429 | define-property "^1.0.0" 1430 | isobject "^3.0.0" 1431 | snapdragon-util "^3.0.1" 1432 | 1433 | snapdragon-util@^3.0.1: 1434 | version "3.0.1" 1435 | resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" 1436 | dependencies: 1437 | kind-of "^3.2.0" 1438 | 1439 | snapdragon@^0.8.1: 1440 | version "0.8.2" 1441 | resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" 1442 | dependencies: 1443 | base "^0.11.1" 1444 | debug "^2.2.0" 1445 | define-property "^0.2.5" 1446 | extend-shallow "^2.0.1" 1447 | map-cache "^0.2.2" 1448 | source-map "^0.5.6" 1449 | source-map-resolve "^0.5.0" 1450 | use "^3.1.0" 1451 | 1452 | source-map-resolve@^0.5.0: 1453 | version "0.5.2" 1454 | resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" 1455 | dependencies: 1456 | atob "^2.1.1" 1457 | decode-uri-component "^0.2.0" 1458 | resolve-url "^0.2.1" 1459 | source-map-url "^0.4.0" 1460 | urix "^0.1.0" 1461 | 1462 | source-map-url@^0.4.0: 1463 | version "0.4.0" 1464 | resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" 1465 | 1466 | source-map@^0.5.6: 1467 | version "0.5.7" 1468 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" 1469 | 1470 | spdx-correct@^3.0.0: 1471 | version "3.0.0" 1472 | resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82" 1473 | dependencies: 1474 | spdx-expression-parse "^3.0.0" 1475 | spdx-license-ids "^3.0.0" 1476 | 1477 | spdx-exceptions@^2.1.0: 1478 | version "2.1.0" 1479 | resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz#2c7ae61056c714a5b9b9b2b2af7d311ef5c78fe9" 1480 | 1481 | spdx-expression-parse@^3.0.0: 1482 | version "3.0.0" 1483 | resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" 1484 | dependencies: 1485 | spdx-exceptions "^2.1.0" 1486 | spdx-license-ids "^3.0.0" 1487 | 1488 | spdx-license-ids@^3.0.0: 1489 | version "3.0.0" 1490 | resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87" 1491 | 1492 | split-string@^3.0.1, split-string@^3.0.2: 1493 | version "3.1.0" 1494 | resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" 1495 | dependencies: 1496 | extend-shallow "^3.0.0" 1497 | 1498 | split@0.3: 1499 | version "0.3.3" 1500 | resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" 1501 | dependencies: 1502 | through "2" 1503 | 1504 | static-extend@^0.1.1: 1505 | version "0.1.2" 1506 | resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" 1507 | dependencies: 1508 | define-property "^0.2.5" 1509 | object-copy "^0.1.0" 1510 | 1511 | stream-combiner@~0.0.4: 1512 | version "0.0.4" 1513 | resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" 1514 | dependencies: 1515 | duplexer "~0.1.1" 1516 | 1517 | string-width@^1.0.1: 1518 | version "1.0.2" 1519 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" 1520 | dependencies: 1521 | code-point-at "^1.0.0" 1522 | is-fullwidth-code-point "^1.0.0" 1523 | strip-ansi "^3.0.0" 1524 | 1525 | "string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1: 1526 | version "2.1.1" 1527 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" 1528 | dependencies: 1529 | is-fullwidth-code-point "^2.0.0" 1530 | strip-ansi "^4.0.0" 1531 | 1532 | string.prototype.padend@^3.0.0: 1533 | version "3.0.0" 1534 | resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz#f3aaef7c1719f170c5eab1c32bf780d96e21f2f0" 1535 | dependencies: 1536 | define-properties "^1.1.2" 1537 | es-abstract "^1.4.3" 1538 | function-bind "^1.0.2" 1539 | 1540 | string_decoder@~1.1.1: 1541 | version "1.1.1" 1542 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" 1543 | dependencies: 1544 | safe-buffer "~5.1.0" 1545 | 1546 | strip-ansi@^3.0.0, strip-ansi@^3.0.1: 1547 | version "3.0.1" 1548 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" 1549 | dependencies: 1550 | ansi-regex "^2.0.0" 1551 | 1552 | strip-ansi@^4.0.0: 1553 | version "4.0.0" 1554 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" 1555 | dependencies: 1556 | ansi-regex "^3.0.0" 1557 | 1558 | strip-bom@^3.0.0: 1559 | version "3.0.0" 1560 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" 1561 | 1562 | strip-eof@^1.0.0: 1563 | version "1.0.0" 1564 | resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" 1565 | 1566 | strip-json-comments@~2.0.1: 1567 | version "2.0.1" 1568 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" 1569 | 1570 | supports-color@^5.2.0, supports-color@^5.3.0: 1571 | version "5.4.0" 1572 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" 1573 | dependencies: 1574 | has-flag "^3.0.0" 1575 | 1576 | tar@^4: 1577 | version "4.4.15" 1578 | resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.15.tgz#3caced4f39ebd46ddda4d6203d48493a919697f8" 1579 | integrity sha512-ItbufpujXkry7bHH9NpQyTXPbJ72iTlXgkBAYsAjDXk3Ds8t/3NfO5P4xZGy7u+sYuQUbimgzswX4uQIEeNVOA== 1580 | dependencies: 1581 | chownr "^1.1.1" 1582 | fs-minipass "^1.2.5" 1583 | minipass "^2.8.6" 1584 | minizlib "^1.2.1" 1585 | mkdirp "^0.5.0" 1586 | safe-buffer "^5.1.2" 1587 | yallist "^3.0.3" 1588 | 1589 | term-size@^1.2.0: 1590 | version "1.2.0" 1591 | resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" 1592 | dependencies: 1593 | execa "^0.7.0" 1594 | 1595 | through@2, through@~2.3, through@~2.3.1: 1596 | version "2.3.8" 1597 | resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" 1598 | 1599 | timed-out@^4.0.0: 1600 | version "4.0.1" 1601 | resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" 1602 | 1603 | to-object-path@^0.3.0: 1604 | version "0.3.0" 1605 | resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" 1606 | dependencies: 1607 | kind-of "^3.0.2" 1608 | 1609 | to-regex-range@^2.1.0: 1610 | version "2.1.1" 1611 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" 1612 | dependencies: 1613 | is-number "^3.0.0" 1614 | repeat-string "^1.6.1" 1615 | 1616 | to-regex@^3.0.1, to-regex@^3.0.2: 1617 | version "3.0.2" 1618 | resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" 1619 | dependencies: 1620 | define-property "^2.0.2" 1621 | extend-shallow "^3.0.2" 1622 | regex-not "^1.0.2" 1623 | safe-regex "^1.1.0" 1624 | 1625 | touch@^3.1.0: 1626 | version "3.1.0" 1627 | resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" 1628 | dependencies: 1629 | nopt "~1.0.10" 1630 | 1631 | undefsafe@^2.0.2: 1632 | version "2.0.2" 1633 | resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.2.tgz#225f6b9e0337663e0d8e7cfd686fc2836ccace76" 1634 | dependencies: 1635 | debug "^2.2.0" 1636 | 1637 | union-value@^1.0.0: 1638 | version "1.0.0" 1639 | resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" 1640 | dependencies: 1641 | arr-union "^3.1.0" 1642 | get-value "^2.0.6" 1643 | is-extendable "^0.1.1" 1644 | set-value "^0.4.3" 1645 | 1646 | unique-string@^1.0.0: 1647 | version "1.0.0" 1648 | resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a" 1649 | dependencies: 1650 | crypto-random-string "^1.0.0" 1651 | 1652 | unset-value@^1.0.0: 1653 | version "1.0.0" 1654 | resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" 1655 | dependencies: 1656 | has-value "^0.3.1" 1657 | isobject "^3.0.0" 1658 | 1659 | unzip-response@^2.0.1: 1660 | version "2.0.1" 1661 | resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" 1662 | 1663 | upath@^1.0.5: 1664 | version "1.1.0" 1665 | resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" 1666 | 1667 | update-notifier@^2.3.0: 1668 | version "2.5.0" 1669 | resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.5.0.tgz#d0744593e13f161e406acb1d9408b72cad08aff6" 1670 | dependencies: 1671 | boxen "^1.2.1" 1672 | chalk "^2.0.1" 1673 | configstore "^3.0.0" 1674 | import-lazy "^2.1.0" 1675 | is-ci "^1.0.10" 1676 | is-installed-globally "^0.1.0" 1677 | is-npm "^1.0.0" 1678 | latest-version "^3.0.0" 1679 | semver-diff "^2.0.0" 1680 | xdg-basedir "^3.0.0" 1681 | 1682 | urix@^0.1.0: 1683 | version "0.1.0" 1684 | resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" 1685 | 1686 | url-parse-lax@^1.0.0: 1687 | version "1.0.0" 1688 | resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" 1689 | dependencies: 1690 | prepend-http "^1.0.1" 1691 | 1692 | use@^3.1.0: 1693 | version "3.1.0" 1694 | resolved "https://registry.yarnpkg.com/use/-/use-3.1.0.tgz#14716bf03fdfefd03040aef58d8b4b85f3a7c544" 1695 | dependencies: 1696 | kind-of "^6.0.2" 1697 | 1698 | util-deprecate@~1.0.1: 1699 | version "1.0.2" 1700 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 1701 | 1702 | validate-npm-package-license@^3.0.1: 1703 | version "3.0.3" 1704 | resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz#81643bcbef1bdfecd4623793dc4648948ba98338" 1705 | dependencies: 1706 | spdx-correct "^3.0.0" 1707 | spdx-expression-parse "^3.0.0" 1708 | 1709 | which@^1.2.9: 1710 | version "1.3.1" 1711 | resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" 1712 | dependencies: 1713 | isexe "^2.0.0" 1714 | 1715 | wide-align@^1.1.0: 1716 | version "1.1.3" 1717 | resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" 1718 | dependencies: 1719 | string-width "^1.0.2 || 2" 1720 | 1721 | widest-line@^2.0.0: 1722 | version "2.0.0" 1723 | resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.0.tgz#0142a4e8a243f8882c0233aa0e0281aa76152273" 1724 | dependencies: 1725 | string-width "^2.1.1" 1726 | 1727 | wrappy@1: 1728 | version "1.0.2" 1729 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1730 | 1731 | write-file-atomic@^2.0.0: 1732 | version "2.3.0" 1733 | resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab" 1734 | dependencies: 1735 | graceful-fs "^4.1.11" 1736 | imurmurhash "^0.1.4" 1737 | signal-exit "^3.0.2" 1738 | 1739 | xdg-basedir@^3.0.0: 1740 | version "3.0.0" 1741 | resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" 1742 | 1743 | yallist@^2.1.2: 1744 | version "2.1.2" 1745 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" 1746 | 1747 | yallist@^3.0.0, yallist@^3.0.3: 1748 | version "3.1.1" 1749 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" 1750 | integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== 1751 | --------------------------------------------------------------------------------