diff --git a/docs/reference/commands.md b/docs/reference/commands.md index f69d19f1c8..84543a33f5 100644 --- a/docs/reference/commands.md +++ b/docs/reference/commands.md @@ -74,6 +74,70 @@ Note: Currently only supports HTTP/HTTPS endpoints. | -------- | -------- | ----------- | | `serviceAndPath` | Yes | The name of the service(s) to call followed by the endpoint path (e.g. my-container/somepath). +### garden create project + +Creates a new Garden project. + +The 'create project' command walks the user through setting up a new Garden project and +generates scaffolding based on user input. + +Examples: + + garden create project # creates a new Garden project in the current directory (project name defaults to + directory name) + garden create project my-project # creates a new Garden project in my-project directory + garden create project --module-dirs=path/to/modules1,path/to/modules2 + # creates a new Garden project and looks for pre-existing modules in the modules1 and modules2 directories + garden create project --name my-project + # creates a new Garden project in the current directory and names it my-project + +##### Usage + + garden create project [project-dir] [options] + +##### Arguments + +| Argument | Required | Description | +| -------- | -------- | ----------- | + | `project-dir` | No | Directory of the project. (Defaults to current directory.) + +##### Options + +| Argument | Alias | Type | Description | +| -------- | ----- | ---- | ----------- | + | `--module-dirs` | | string | Relative path to modules directory. Use comma as a separator to specify multiple directories + | `--name` | | string | Assigns a custom name to the project. (Defaults to name of the current directory.) + +### garden create module + +Creates a new Garden module. + +Creates a new Garden module of the given type + +Examples: + + garden create module # creates a new module in the current directory (module name defaults to directory name) + garden create module my-module # creates a new module in my-module directory + garden create module --type=container # creates a new container module + garden create module --name=my-module # creates a new module in current directory and names it my-module + +##### Usage + + garden create module [module-dir] [options] + +##### Arguments + +| Argument | Required | Description | +| -------- | -------- | ----------- | + | `module-dir` | No | Directory of the module. (Defaults to current directory.) + +##### Options + +| Argument | Alias | Type | Description | +| -------- | ----- | ---- | ----------- | + | `--name` | | boolean | Assigns a custom name to the module. (Defaults to name of the current directory.) + | `--type` | | string | Type of module. Check out 'https://docs.garden.io' for available types + ### garden delete config Delete a configuration variable from the environment. diff --git a/package-lock.json b/package-lock.json index 7e29b7be85..35447e48a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "garden-cli", - "version": "0.2.0-0", + "version": "0.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -6969,7 +6969,7 @@ "dependencies": { "align-text": { "version": "0.1.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, "requires": { @@ -6980,7 +6980,7 @@ }, "amdefine": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", "dev": true }, @@ -6992,7 +6992,7 @@ }, "append-transform": { "version": "0.4.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-0.4.0.tgz", "integrity": "sha1-126/jKlNJ24keja61EpLdKthGZE=", "dev": true, "requires": { @@ -7001,67 +7001,67 @@ }, "archy": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, "arr-diff": { "version": "4.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", "dev": true }, "arr-flatten": { "version": "1.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", "dev": true }, "arr-union": { "version": "3.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, "array-unique": { "version": "0.3.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, "arrify": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, "assign-symbols": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, "async": { "version": "1.5.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true }, "atob": { "version": "2.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=", "dev": true }, "balanced-match": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, "base": { "version": "0.11.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, "requires": { @@ -7076,7 +7076,7 @@ "dependencies": { "define-property": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { @@ -7085,7 +7085,7 @@ }, "is-accessor-descriptor": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { @@ -7094,7 +7094,7 @@ }, "is-data-descriptor": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { @@ -7103,7 +7103,7 @@ }, "is-descriptor": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { @@ -7114,7 +7114,7 @@ }, "kind-of": { "version": "6.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } @@ -7122,7 +7122,7 @@ }, "brace-expansion": { "version": "1.1.11", - "resolved": false, + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { @@ -7132,7 +7132,7 @@ }, "braces": { "version": "2.3.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { @@ -7150,7 +7150,7 @@ "dependencies": { "extend-shallow": { "version": "2.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { @@ -7161,13 +7161,13 @@ }, "builtin-modules": { "version": "1.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "dev": true }, "cache-base": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": true, "requires": { @@ -7184,7 +7184,7 @@ }, "caching-transform": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-1.0.1.tgz", "integrity": "sha1-bb2y8g+Nj7znnz6U6dF0Lc31wKE=", "dev": true, "requires": { @@ -7195,14 +7195,14 @@ }, "camelcase": { "version": "1.2.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", "dev": true, "optional": true }, "center-align": { "version": "0.1.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", "dev": true, "optional": true, @@ -7213,7 +7213,7 @@ }, "class-utils": { "version": "0.3.6", - "resolved": false, + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, "requires": { @@ -7225,7 +7225,7 @@ "dependencies": { "define-property": { "version": "0.2.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { @@ -7236,7 +7236,7 @@ }, "cliui": { "version": "2.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", "dev": true, "optional": true, @@ -7248,7 +7248,7 @@ "dependencies": { "wordwrap": { "version": "0.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", "dev": true, "optional": true @@ -7257,13 +7257,13 @@ }, "code-point-at": { "version": "1.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, "collection-visit": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "requires": { @@ -7273,37 +7273,37 @@ }, "commondir": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, "component-emitter": { "version": "1.2.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", "dev": true }, "concat-map": { "version": "0.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, "convert-source-map": { "version": "1.5.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", "dev": true }, "copy-descriptor": { "version": "0.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, "cross-spawn": { "version": "4.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", "dev": true, "requires": { @@ -7322,25 +7322,25 @@ }, "debug-log": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", "dev": true }, "decamelize": { "version": "1.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, "decode-uri-component": { "version": "0.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", "dev": true }, "default-require-extensions": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-1.0.0.tgz", "integrity": "sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=", "dev": true, "requires": { @@ -7349,7 +7349,7 @@ }, "define-property": { "version": "2.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, "requires": { @@ -7359,7 +7359,7 @@ "dependencies": { "is-accessor-descriptor": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { @@ -7368,7 +7368,7 @@ }, "is-data-descriptor": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { @@ -7377,7 +7377,7 @@ }, "is-descriptor": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { @@ -7388,7 +7388,7 @@ }, "kind-of": { "version": "6.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } @@ -7396,7 +7396,7 @@ }, "error-ex": { "version": "1.3.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", "dev": true, "requires": { @@ -7405,7 +7405,7 @@ }, "execa": { "version": "0.7.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { @@ -7420,7 +7420,7 @@ "dependencies": { "cross-spawn": { "version": "5.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { @@ -7433,7 +7433,7 @@ }, "expand-brackets": { "version": "2.1.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { @@ -7457,7 +7457,7 @@ }, "define-property": { "version": "0.2.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { @@ -7466,7 +7466,7 @@ }, "extend-shallow": { "version": "2.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { @@ -7477,7 +7477,7 @@ }, "extend-shallow": { "version": "3.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "requires": { @@ -7487,7 +7487,7 @@ "dependencies": { "is-extendable": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { @@ -7498,7 +7498,7 @@ }, "extglob": { "version": "2.0.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { @@ -7514,7 +7514,7 @@ "dependencies": { "define-property": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { @@ -7523,7 +7523,7 @@ }, "extend-shallow": { "version": "2.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { @@ -7532,7 +7532,7 @@ }, "is-accessor-descriptor": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { @@ -7541,7 +7541,7 @@ }, "is-data-descriptor": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { @@ -7550,7 +7550,7 @@ }, "is-descriptor": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { @@ -7561,7 +7561,7 @@ }, "kind-of": { "version": "6.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } @@ -7569,7 +7569,7 @@ }, "fill-range": { "version": "4.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { @@ -7581,7 +7581,7 @@ "dependencies": { "extend-shallow": { "version": "2.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { @@ -7592,7 +7592,7 @@ }, "find-cache-dir": { "version": "0.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", "dev": true, "requires": { @@ -7603,7 +7603,7 @@ }, "find-up": { "version": "2.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { @@ -7612,13 +7612,13 @@ }, "for-in": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", "dev": true }, "foreground-child": { "version": "1.5.6", - "resolved": false, + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", "dev": true, "requires": { @@ -7628,7 +7628,7 @@ }, "fragment-cache": { "version": "0.2.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, "requires": { @@ -7637,31 +7637,31 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, "get-caller-file": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", "dev": true }, "get-stream": { "version": "3.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true }, "get-value": { "version": "2.0.6", - "resolved": false, + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", "dev": true }, "glob": { "version": "7.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { @@ -7675,13 +7675,13 @@ }, "graceful-fs": { "version": "4.1.11", - "resolved": false, + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, "handlebars": { "version": "4.0.11", - "resolved": false, + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz", "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", "dev": true, "requires": { @@ -7693,7 +7693,7 @@ "dependencies": { "source-map": { "version": "0.4.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { @@ -7704,7 +7704,7 @@ }, "has-value": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, "requires": { @@ -7715,7 +7715,7 @@ }, "has-values": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, "requires": { @@ -7725,7 +7725,7 @@ "dependencies": { "kind-of": { "version": "4.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { @@ -7736,19 +7736,19 @@ }, "hosted-git-info": { "version": "2.6.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==", "dev": true }, "imurmurhash": { "version": "0.1.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, "inflight": { "version": "1.0.6", - "resolved": false, + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { @@ -7758,19 +7758,19 @@ }, "inherits": { "version": "2.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, "invert-kv": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", "dev": true }, "is-accessor-descriptor": { "version": "0.1.6", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { @@ -7779,19 +7779,19 @@ }, "is-arrayish": { "version": "0.2.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, "is-buffer": { "version": "1.1.6", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, "is-builtin-module": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { @@ -7800,7 +7800,7 @@ }, "is-data-descriptor": { "version": "0.1.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { @@ -7809,7 +7809,7 @@ }, "is-descriptor": { "version": "0.1.6", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { @@ -7820,7 +7820,7 @@ "dependencies": { "kind-of": { "version": "5.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true } @@ -7828,19 +7828,19 @@ }, "is-extendable": { "version": "0.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, "is-number": { "version": "3.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { @@ -7849,7 +7849,7 @@ }, "is-odd": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", "dev": true, "requires": { @@ -7858,7 +7858,7 @@ "dependencies": { "is-number": { "version": "4.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", "dev": true } @@ -7866,7 +7866,7 @@ }, "is-plain-object": { "version": "2.0.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { @@ -7875,49 +7875,49 @@ }, "is-stream": { "version": "1.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, "is-utf8": { "version": "0.2.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", "dev": true }, "is-windows": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, "isarray": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, "isexe": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, "isobject": { "version": "3.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, "istanbul-lib-coverage": { "version": "1.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz", "integrity": "sha512-GvgM/uXRwm+gLlvkWHTjDAvwynZkL9ns15calTrmhGgowlwJBbWMYzWbKqE2DT6JDP1AFXKa+Zi0EkqNCUqY0A==", "dev": true }, "istanbul-lib-hook": { "version": "1.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-1.1.0.tgz", "integrity": "sha512-U3qEgwVDUerZ0bt8cfl3dSP3S6opBoOtk3ROO5f2EfBr/SRiD9FQqzwaZBqFORu8W7O0EXpai+k7kxHK13beRg==", "dev": true, "requires": { @@ -7926,7 +7926,7 @@ }, "istanbul-lib-report": { "version": "1.1.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-1.1.3.tgz", "integrity": "sha512-D4jVbMDtT2dPmloPJS/rmeP626N5Pr3Rp+SovrPn1+zPChGHcggd/0sL29jnbm4oK9W0wHjCRsdch9oLd7cm6g==", "dev": true, "requires": { @@ -7944,7 +7944,7 @@ }, "supports-color": { "version": "3.2.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { @@ -7977,7 +7977,7 @@ }, "kind-of": { "version": "3.2.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { @@ -7986,14 +7986,14 @@ }, "lazy-cache": { "version": "1.0.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", "dev": true, "optional": true }, "lcid": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "dev": true, "requires": { @@ -8002,7 +8002,7 @@ }, "load-json-file": { "version": "1.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -8015,7 +8015,7 @@ }, "locate-path": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { @@ -8025,7 +8025,7 @@ "dependencies": { "path-exists": { "version": "3.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true } @@ -8033,13 +8033,13 @@ }, "longest": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", "dev": true }, "lru-cache": { "version": "4.1.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", "dev": true, "requires": { @@ -8049,13 +8049,13 @@ }, "map-cache": { "version": "0.2.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", "dev": true }, "map-visit": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, "requires": { @@ -8064,7 +8064,7 @@ }, "md5-hex": { "version": "1.3.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-1.3.0.tgz", "integrity": "sha1-0sSv6YPENwZiF5uMrRRSGRNQRsQ=", "dev": true, "requires": { @@ -8073,13 +8073,13 @@ }, "md5-o-matic": { "version": "0.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/md5-o-matic/-/md5-o-matic-0.1.1.tgz", "integrity": "sha1-givM1l4RfFFPqxdrJZRdVBAKA8M=", "dev": true }, "mem": { "version": "1.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "dev": true, "requires": { @@ -8088,7 +8088,7 @@ }, "merge-source-map": { "version": "1.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", "dev": true, "requires": { @@ -8097,7 +8097,7 @@ "dependencies": { "source-map": { "version": "0.6.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } @@ -8105,7 +8105,7 @@ }, "micromatch": { "version": "3.1.10", - "resolved": false, + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "requires": { @@ -8126,7 +8126,7 @@ "dependencies": { "kind-of": { "version": "6.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } @@ -8134,13 +8134,13 @@ }, "mimic-fn": { "version": "1.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, "minimatch": { "version": "3.0.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { @@ -8149,13 +8149,13 @@ }, "minimist": { "version": "0.0.8", - "resolved": false, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, "mixin-deep": { "version": "1.3.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", "dev": true, "requires": { @@ -8165,7 +8165,7 @@ "dependencies": { "is-extendable": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { @@ -8176,7 +8176,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { @@ -8185,13 +8185,13 @@ }, "ms": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, "nanomatch": { "version": "1.2.9", - "resolved": false, + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", "dev": true, "requires": { @@ -8211,7 +8211,7 @@ "dependencies": { "kind-of": { "version": "6.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } @@ -8219,7 +8219,7 @@ }, "normalize-package-data": { "version": "2.4.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "dev": true, "requires": { @@ -8231,7 +8231,7 @@ }, "npm-run-path": { "version": "2.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { @@ -8240,19 +8240,19 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, "object-assign": { "version": "4.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, "object-copy": { "version": "0.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, "requires": { @@ -8263,7 +8263,7 @@ "dependencies": { "define-property": { "version": "0.2.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { @@ -8274,7 +8274,7 @@ }, "object-visit": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, "requires": { @@ -8283,7 +8283,7 @@ }, "object.pick": { "version": "1.3.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { @@ -8292,7 +8292,7 @@ }, "once": { "version": "1.4.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { @@ -8301,7 +8301,7 @@ }, "optimist": { "version": "0.6.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { @@ -8311,13 +8311,13 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, "os-locale": { "version": "2.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "dev": true, "requires": { @@ -8328,13 +8328,13 @@ }, "p-finally": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "dev": true }, "p-limit": { "version": "1.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", "dev": true, "requires": { @@ -8343,7 +8343,7 @@ }, "p-locate": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { @@ -8352,13 +8352,13 @@ }, "p-try": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, "parse-json": { "version": "2.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { @@ -8367,13 +8367,13 @@ }, "pascalcase": { "version": "0.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, "path-exists": { "version": "2.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { @@ -8382,25 +8382,25 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, "path-key": { "version": "2.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", "dev": true }, "path-parse": { "version": "1.0.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", "dev": true }, "path-type": { "version": "1.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { @@ -8411,19 +8411,19 @@ }, "pify": { "version": "2.3.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true }, "pinkie": { "version": "2.0.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", "dev": true }, "pinkie-promise": { "version": "2.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { @@ -8432,7 +8432,7 @@ }, "pkg-dir": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", "dev": true, "requires": { @@ -8441,7 +8441,7 @@ "dependencies": { "find-up": { "version": "1.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { @@ -8453,19 +8453,19 @@ }, "posix-character-classes": { "version": "0.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, "pseudomap": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, "read-pkg": { "version": "1.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { @@ -8476,7 +8476,7 @@ }, "read-pkg-up": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { @@ -8486,7 +8486,7 @@ "dependencies": { "find-up": { "version": "1.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { @@ -8498,7 +8498,7 @@ }, "regex-not": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, "requires": { @@ -8508,49 +8508,49 @@ }, "repeat-element": { "version": "1.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", "dev": true }, "repeat-string": { "version": "1.6.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, "require-directory": { "version": "2.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, "require-main-filename": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", "dev": true }, "resolve-from": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=", "dev": true }, "resolve-url": { "version": "0.2.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "dev": true }, "ret": { "version": "0.1.15", - "resolved": false, + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, "right-align": { "version": "0.1.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", "dev": true, "optional": true, @@ -8560,7 +8560,7 @@ }, "rimraf": { "version": "2.6.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { @@ -8569,7 +8569,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -8578,19 +8578,19 @@ }, "semver": { "version": "5.5.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true }, "set-blocking": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, "set-value": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", "dev": true, "requires": { @@ -8602,7 +8602,7 @@ "dependencies": { "extend-shallow": { "version": "2.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { @@ -8613,7 +8613,7 @@ }, "shebang-command": { "version": "1.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { @@ -8622,25 +8622,25 @@ }, "shebang-regex": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, "signal-exit": { "version": "3.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, "slide": { "version": "1.1.6", - "resolved": false, + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", "dev": true }, "snapdragon": { "version": "0.8.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": true, "requires": { @@ -8665,7 +8665,7 @@ }, "define-property": { "version": "0.2.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { @@ -8674,7 +8674,7 @@ }, "extend-shallow": { "version": "2.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { @@ -8685,7 +8685,7 @@ }, "snapdragon-node": { "version": "2.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, "requires": { @@ -8696,7 +8696,7 @@ "dependencies": { "define-property": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { @@ -8705,7 +8705,7 @@ }, "is-accessor-descriptor": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { @@ -8714,7 +8714,7 @@ }, "is-data-descriptor": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { @@ -8723,7 +8723,7 @@ }, "is-descriptor": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { @@ -8734,7 +8734,7 @@ }, "kind-of": { "version": "6.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } @@ -8742,7 +8742,7 @@ }, "snapdragon-util": { "version": "3.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, "requires": { @@ -8751,7 +8751,7 @@ }, "source-map": { "version": "0.5.7", - "resolved": false, + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, @@ -8770,13 +8770,13 @@ }, "source-map-url": { "version": "0.4.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, "spawn-wrap": { "version": "1.4.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.2.tgz", "integrity": "sha512-vMwR3OmmDhnxCVxM8M+xO/FtIp6Ju/mNaDfCMMW7FDcLRTPFWUswec4LXJHTJE2hwTI9O0YBfygu4DalFl7Ylg==", "dev": true, "requires": { @@ -8790,7 +8790,7 @@ }, "spdx-correct": { "version": "3.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", "dev": true, "requires": { @@ -8800,13 +8800,13 @@ }, "spdx-exceptions": { "version": "2.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", "dev": true }, "spdx-expression-parse": { "version": "3.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", "dev": true, "requires": { @@ -8816,13 +8816,13 @@ }, "spdx-license-ids": { "version": "3.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", "dev": true }, "split-string": { "version": "3.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dev": true, "requires": { @@ -8831,7 +8831,7 @@ }, "static-extend": { "version": "0.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, "requires": { @@ -8841,7 +8841,7 @@ "dependencies": { "define-property": { "version": "0.2.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { @@ -8852,7 +8852,7 @@ }, "string-width": { "version": "2.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { @@ -8871,7 +8871,7 @@ }, "strip-bom": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { @@ -8880,13 +8880,13 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, "test-exclude": { "version": "4.2.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-4.2.1.tgz", "integrity": "sha512-qpqlP/8Zl+sosLxBcVKl9vYy26T9NPalxSzzCP/OY6K7j938ui2oKgo+kRZYfxAeIpLqpbVnsHq1tyV70E4lWQ==", "dev": true, "requires": { @@ -8899,7 +8899,7 @@ }, "to-object-path": { "version": "0.3.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "dev": true, "requires": { @@ -8908,7 +8908,7 @@ }, "to-regex": { "version": "3.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "dev": true, "requires": { @@ -8920,7 +8920,7 @@ }, "to-regex-range": { "version": "2.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, "requires": { @@ -8930,7 +8930,7 @@ }, "uglify-js": { "version": "2.8.29", - "resolved": false, + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", "dev": true, "optional": true, @@ -8942,7 +8942,7 @@ "dependencies": { "yargs": { "version": "3.10.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "dev": true, "optional": true, @@ -8957,14 +8957,14 @@ }, "uglify-to-browserify": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", "dev": true, "optional": true }, "union-value": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", "dev": true, "requires": { @@ -8976,7 +8976,7 @@ "dependencies": { "extend-shallow": { "version": "2.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { @@ -8985,7 +8985,7 @@ }, "set-value": { "version": "0.4.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", "dev": true, "requires": { @@ -8999,7 +8999,7 @@ }, "unset-value": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, "requires": { @@ -9009,7 +9009,7 @@ "dependencies": { "has-value": { "version": "0.3.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "dev": true, "requires": { @@ -9020,7 +9020,7 @@ "dependencies": { "isobject": { "version": "2.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", "dev": true, "requires": { @@ -9031,7 +9031,7 @@ }, "has-values": { "version": "0.1.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", "dev": true } @@ -9039,13 +9039,13 @@ }, "urix": { "version": "0.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, "use": { "version": "3.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", "dev": true, "requires": { @@ -9054,7 +9054,7 @@ "dependencies": { "kind-of": { "version": "6.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } @@ -9062,7 +9062,7 @@ }, "validate-npm-package-license": { "version": "3.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", "dev": true, "requires": { @@ -9081,26 +9081,26 @@ }, "which-module": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, "window-size": { "version": "0.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", "dev": true, "optional": true }, "wordwrap": { "version": "0.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", "dev": true }, "wrap-ansi": { "version": "2.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { @@ -9116,7 +9116,7 @@ }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { @@ -9125,7 +9125,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { @@ -9147,13 +9147,13 @@ }, "wrappy": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, "write-file-atomic": { "version": "1.3.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", "dev": true, "requires": { @@ -9164,19 +9164,19 @@ }, "y18n": { "version": "3.2.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", "dev": true }, "yallist": { "version": "2.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true }, "yargs": { "version": "11.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", "dev": true, "requires": { @@ -9196,13 +9196,13 @@ "dependencies": { "camelcase": { "version": "4.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", "dev": true }, "cliui": { "version": "4.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "dev": true, "requires": { @@ -9213,7 +9213,7 @@ }, "yargs-parser": { "version": "9.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", "dev": true, "requires": { @@ -9224,7 +9224,7 @@ }, "yargs-parser": { "version": "8.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz", "integrity": "sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==", "dev": true, "requires": { @@ -9233,7 +9233,7 @@ "dependencies": { "camelcase": { "version": "4.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", "dev": true } @@ -11402,6 +11402,37 @@ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, + "supports-hyperlinks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz", + "integrity": "sha512-HHi5kVSefKaJkGYXbDuKbUGRVxqnWGn3J2e39CYcNJEfWciGq2zYtOhXLTlvrOZW1QU7VX67w7fMmWafHX9Pfw==", + "requires": { + "has-flag": "^2.0.0", + "supports-color": "^5.0.0" + }, + "dependencies": { + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "requires": { + "has-flag": "^3.0.0" + }, + "dependencies": { + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + } + } + } + } + }, "sver-compat": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", @@ -11452,6 +11483,15 @@ "uuid": "^3.0.1" } }, + "terminal-link": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-1.1.0.tgz", + "integrity": "sha512-sOZb3eUbMEcBeuA+TePxEiyueKHNoFOdU8gJtw6vXBKQEgj2ZeyQfWT0aXqjSDI1a/xEZfjzTZMApcSgV70KGg==", + "requires": { + "ansi-escapes": "^3.1.0", + "supports-hyperlinks": "^1.0.1" + } + }, "testdouble": { "version": "3.8.1", "resolved": "https://registry.npmjs.org/testdouble/-/testdouble-3.8.1.tgz", diff --git a/package.json b/package.json index db18709cfe..e1e8d3fa0d 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "split": "^1.0.1", "strip-ansi": "^4.0.0", "sywac": "^1.2.0", + "terminal-link": "^1.1.0", "ts-stream": "^1.0.1", "typescript-memoize": "^1.0.0-alpha.3", "uniqid": "^5.0.3", diff --git a/src/cli/cli.ts b/src/cli/cli.ts index b2b0f4edde..87ead5fcb2 100644 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -33,7 +33,7 @@ import { PluginError, toGardenError, } from "../exceptions" -import { Garden } from "../garden" +import { Garden, ContextOpts } from "../garden" import { RootLogNode, getLogger } from "../logger/logger" import { LogLevel, LoggerType } from "../logger/types" @@ -53,6 +53,8 @@ import { prepareOptionConfig, styleConfig, } from "./helpers" +import { GardenConfig } from "../types/config" +import { defaultEnvironments } from "../types/project" const OUTPUT_RENDERERS = { json: (data: DeepPrimitiveMap) => { @@ -75,6 +77,26 @@ const getLogLevelFromArg = (level: string) => { return LogLevel[level] } +// For initializing garden without a project config +export const MOCK_CONFIG: GardenConfig = { + version: "0", + dirname: "/", + path: "/", + project: { + name: "mock-project", + defaultEnvironment: "local", + environments: defaultEnvironments, + environmentDefaults: { + providers: [ + { + name: "local-kubernetes", + }, + ], + variables: {}, + }, + }, +} + export const GLOBAL_OPTIONS = { root: new StringParameter({ alias: "r", @@ -225,7 +247,11 @@ export class GardenCli { let garden let result do { - garden = await Garden.factory(root, { env, logger }) + const contextOpts: ContextOpts = { env, logger } + if (command.noProject) { + contextOpts.config = MOCK_CONFIG + } + garden = await Garden.factory(root, contextOpts) // TODO: enforce that commands always output DeepPrimitiveMap result = await command.action(garden.pluginContext, parsedArgs, parsedOpts) } while (result.restartRequired) diff --git a/src/commands/base.ts b/src/commands/base.ts index 3f4b9e7544..997172757a 100644 --- a/src/commands/base.ts +++ b/src/commands/base.ts @@ -148,6 +148,7 @@ export abstract class Command + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { capitalize, camelCase, uniq } from "lodash" +import * as Joi from "joi" + +import { DeepPartial } from "../../util/util" +import { ContainerModuleSpec } from "../../plugins/container" +import { GcfModuleSpec } from "../../plugins/google/google-cloud-functions" +import { ProjectConfig } from "../../types/project" +import { BaseModuleSpec, ModuleConfig, baseModuleSpecSchema } from "../../types/module" + +/** + * Ideally there would be some mechanism to discover available module types, + * and for plugins to expose a minimal config for the given type along with + * a list of providers per environment, rather than hard coding these values. + * + * Alternatively, consider co-locating the templates with the plugins. + */ +export const MODULE_PROVIDER_MAP = { + container: "local-kubernetes", + function: "local-google-cloud-functions", + "npm-package": "npm-package", +} + +export const availableModuleTypes = Object.keys(MODULE_PROVIDER_MAP) + +export type ModuleType = keyof typeof MODULE_PROVIDER_MAP + +export const moduleSchema = Joi.object().keys({ + module: baseModuleSpecSchema, +}) + +export interface ConfigOpts { + name: string + path: string + config: { module: Partial } | Partial +} + +export interface ModuleConfigOpts extends ConfigOpts { + type: ModuleType + config: { module: Partial } +} + +export interface ProjectConfigOpts extends ConfigOpts { + config: Partial +} + +const noCase = (str: string) => str.replace(/-|_/g, " ") +const titleize = (str: string) => capitalize(noCase(str)) + +export function containerTemplate(moduleName: string): DeepPartial { + return { + services: [ + { + name: `${moduleName}-service`, + ports: [{ + name: "http", + containerPort: 8080, + }], + endpoints: [{ + paths: ["/"], + port: "http", + }], + }, + ], + } +} + +export function functionTemplate(moduleName: string): DeepPartial { + return { + functions: [{ + name: `${moduleName}-function`, + entrypoint: camelCase(`${moduleName}-function`), + }], + } +} + +export function npmPackageTemplate(_moduleName: string): any { + return {} +} + +export const projectTemplate = (name: string, moduleTypes: ModuleType[]): Partial => { + const providers = uniq(moduleTypes).map(type => ({ name: MODULE_PROVIDER_MAP[type] })) + return { + name, + environments: [ + { + name: "local", + providers, + variables: {}, + }, + ], + } +} + +export const moduleTemplate = (name: string, type: ModuleType): Partial => ({ + name, + type, + description: `${titleize(name)} ${noCase(type)}`, +}) diff --git a/src/commands/create/create.ts b/src/commands/create/create.ts new file mode 100644 index 0000000000..f79de0465a --- /dev/null +++ b/src/commands/create/create.ts @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2018 Garden Technologies, Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { Command } from "../base" +import { CreateProjectCommand } from "./project" +import { CreateModuleCommand } from "./module" + +export class CreateCommand extends Command { + name = "create" + alias = "r" + help = "Create a new project or add a new module" + + subCommands = [ + CreateProjectCommand, + CreateModuleCommand, + ] + + async action() { return {} } +} diff --git a/src/commands/create/helpers.ts b/src/commands/create/helpers.ts new file mode 100644 index 0000000000..49e5797e89 --- /dev/null +++ b/src/commands/create/helpers.ts @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2018 Garden Technologies, Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import * as Joi from "joi" +import { + containerTemplate, + functionTemplate, + npmPackageTemplate, + ModuleConfigOpts, + ModuleType, + moduleTemplate, + ConfigOpts, +} from "./config-templates" +import { join } from "path" +import { pathExists } from "fs-extra" +import { validate } from "../../types/common" +import { EntryStyle } from "../../logger/types" +import { LogNode } from "../../logger/logger" +import { dumpYaml } from "../../util/util" +import { MODULE_CONFIG_FILENAME } from "../../constants" + +export function prepareNewModuleConfig(name: string, type: ModuleType, path: string): ModuleConfigOpts { + const moduleTypeTemplate = { + container: containerTemplate, + function: functionTemplate, + "npm-package": npmPackageTemplate, + }[type] + return { + name, + type, + path, + config: { + module: { + ...moduleTemplate(name, type), + ...moduleTypeTemplate(name), + }, + }, + } +} + +export async function dumpConfig(configOpts: ConfigOpts, schema: Joi.Schema, logger: LogNode) { + const { config, name, path } = configOpts + const yamlPath = join(path, MODULE_CONFIG_FILENAME) + const task = logger.info({ + msg: `Writing config for ${name}`, + entryStyle: EntryStyle.activity, + }) + + if (await pathExists(yamlPath)) { + task.setWarn({ msg: `Garden config file already exists at path, skipping`, append: true }) + return + } + + try { + validate(config, schema) + await dumpYaml(yamlPath, config) + task.setSuccess() + } catch (err) { + task.setError({ msg: `Generated config is invalid, skipping`, append: true }) + throw err + } +} diff --git a/src/commands/create/module.ts b/src/commands/create/module.ts new file mode 100644 index 0000000000..9e51c0452e --- /dev/null +++ b/src/commands/create/module.ts @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2018 Garden Technologies, Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { basename, join } from "path" +import dedent = require("dedent") + +import { + PluginContext, +} from "../../plugin-context" +import { + Command, + CommandResult, + StringParameter, + ParameterValues, + BooleanParameter, +} from "../base" +import { ParameterError, GardenBaseError } from "../../exceptions" +import { availableModuleTypes, ModuleType, moduleSchema, ModuleConfigOpts } from "./config-templates" +import { + prepareNewModuleConfig, + dumpConfig, +} from "./helpers" +import { prompts } from "./prompts" +import { validate, joiIdentifier } from "../../types/common" +import { ensureDir } from "fs-extra" + +export const createModuleOptions = { + name: new BooleanParameter({ + help: "Assigns a custom name to the module. (Defaults to name of the current directory.)", + }), + type: new StringParameter({ + help: "Type of module. Check out 'https://docs.garden.io' for available types", + }), +} + +export const createModuleArguments = { + "module-dir": new StringParameter({ + help: "Directory of the module. (Defaults to current directory.)", + }), +} + +export type Args = ParameterValues +export type Opts = ParameterValues + +interface CreateModuleResult extends CommandResult { + result: { + module?: ModuleConfigOpts, + } +} + +export class CreateModuleCommand extends Command { + name = "module" + alias = "m" + help = "Creates a new Garden module." + + description = dedent` + Creates a new Garden module of the given type + + Examples: + + garden create module # creates a new module in the current directory (module name defaults to directory name) + garden create module my-module # creates a new module in my-module directory + garden create module --type=container # creates a new container module + garden create module --name=my-module # creates a new module in current directory and names it my-module + ` + + noProject = true + arguments = createModuleArguments + options = createModuleOptions + + async action(ctx: PluginContext, args: Args, opts: Opts): Promise { + let errors: GardenBaseError[] = [] + + const moduleRoot = join(ctx.projectRoot, (args["module-dir"] || "").trim()) + const moduleName = validate( + opts.name || basename(moduleRoot), + joiIdentifier(), + { context: "module name" }, + ) + + await ensureDir(moduleRoot) + + ctx.log.header({ emoji: "house_with_garden", command: "create" }) + ctx.log.info(`Initializing new module ${moduleName}`) + + let type: ModuleType + + if (opts.type) { + // Type passed as parameter + type = opts.type + if (!availableModuleTypes.includes(type)) { + throw new ParameterError("Module type not available", {}) + } + } else { + // Prompt for type + ctx.log.info("---------") + ctx.log.stop() + type = (await prompts.addConfigForModule(moduleName)).type + ctx.log.info("---------") + if (!type) { + return { result: {} } + } + } + + const module = prepareNewModuleConfig(moduleName, type, moduleRoot) + try { + await dumpConfig(module, moduleSchema, ctx.log) + } catch (err) { + errors.push(err) + } + return { + result: { module }, + errors, + } + } +} diff --git a/src/commands/create/project.ts b/src/commands/create/project.ts new file mode 100644 index 0000000000..d3c69eeada --- /dev/null +++ b/src/commands/create/project.ts @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2018 Garden Technologies, Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import { resolve, join, basename } from "path" +import { pathExists, ensureDir } from "fs-extra" +import Bluebird = require("bluebird") +import dedent = require("dedent") +import terminalLink = require("terminal-link") + +import { + PluginContext, +} from "../../plugin-context" +import { + Command, + CommandResult, + StringParameter, + ParameterValues, +} from "../base" +import { ParameterError, GardenBaseError } from "../../exceptions" +import { EntryStyle } from "../../logger/types" +import { + prepareNewModuleConfig, + dumpConfig, +} from "./helpers" +import { prompts } from "./prompts" +import { + projectTemplate, + ModuleConfigOpts, + ProjectConfigOpts, + moduleSchema, +} from "../create/config-templates" +import { getChildDirNames } from "../../util/util" +import { validate, joiIdentifier } from "../../types/common" +import { projectSchema } from "../../types/project" + +export const createProjectOptions = { + "module-dirs": new StringParameter({ + help: "Relative path to modules directory. Use comma as a separator to specify multiple directories", + }), + name: new StringParameter({ + help: "Assigns a custom name to the project. (Defaults to name of the current directory.)", + }), +} + +export const createProjectArguments = { + "project-dir": new StringParameter({ + help: "Directory of the project. (Defaults to current directory.)", + }), +} + +export type Args = ParameterValues +export type Opts = ParameterValues + +const flatten = (acc, val) => acc.concat(val) + +interface CreateProjectResult extends CommandResult { + result: { + projectConfig: ProjectConfigOpts, + moduleConfigs: ModuleConfigOpts[], + } +} + +export class CreateProjectCommand extends Command { + name = "project" + alias = "p" + help = "Creates a new Garden project." + + description = dedent` + The 'create project' command walks the user through setting up a new Garden project and + generates scaffolding based on user input. + + Examples: + + garden create project # creates a new Garden project in the current directory (project name defaults to + directory name) + garden create project my-project # creates a new Garden project in my-project directory + garden create project --module-dirs=path/to/modules1,path/to/modules2 + # creates a new Garden project and looks for pre-existing modules in the modules1 and modules2 directories + garden create project --name my-project + # creates a new Garden project in the current directory and names it my-project + ` + + noProject = true + arguments = createProjectArguments + options = createProjectOptions + + async action(ctx: PluginContext, args: Args, opts: Opts): Promise { + let moduleConfigs: ModuleConfigOpts[] = [] + let errors: GardenBaseError[] = [] + + const projectRoot = args["project-dir"] ? join(ctx.projectRoot, args["project-dir"].trim()) : ctx.projectRoot + const projectName = validate( + opts.name || basename(projectRoot), + joiIdentifier(), + { context: "project name" }, + ) + + await ensureDir(projectRoot) + + // Resolve and validate dirs that contain modules + let moduleParentDirs: string[] = [] + if (opts["module-dirs"]) { + const dirs = opts["module-dirs"].split(",") + moduleParentDirs = await Bluebird + .map(dirs, (dir: string) => resolve(projectRoot, dir)) + .each(async (dir: string) => { + if (!(await pathExists(dir))) { + throw new ParameterError(`Directory ${dir} not found`, {}) + } + }) + } + + ctx.log.header({ emoji: "house_with_garden", command: "create" }) + ctx.log.info(`Initializing new Garden project ${projectName}`) + ctx.log.info("---------") + // Stop logger while prompting + ctx.log.stop() + + if (moduleParentDirs.length > 0) { + // If module-dirs option provided we scan for modules in the parent dir(s) and add them one by one + moduleConfigs = (await Bluebird.mapSeries(moduleParentDirs, async parentDir => { + const moduleNames = await getChildDirNames(parentDir) + + return Bluebird.reduce(moduleNames, async (acc: ModuleConfigOpts[], moduleName: string) => { + const { type } = await prompts.addConfigForModule(moduleName) + if (type) { + acc.push(prepareNewModuleConfig(moduleName, type, join(parentDir, moduleName))) + } + return acc + }, []) + })) + .reduce(flatten, []) + .filter(m => m) + } else { + // Otherwise we prompt the user for modules to add + moduleConfigs = (await prompts.repeatAddModule()) + .map(({ name, type }) => prepareNewModuleConfig(name, type, join(projectRoot, name))) + } + + ctx.log.info("---------") + const task = ctx.log.info({ msg: "Setting up project", entryStyle: EntryStyle.activity }) + + for (const module of moduleConfigs) { + await ensureDir(module.path) + try { + await dumpConfig(module, moduleSchema, ctx.log) + } catch (err) { + errors.push(err) + } + } + + const projectConfig: ProjectConfigOpts = { + path: projectRoot, + name: projectName, + config: projectTemplate(projectName, moduleConfigs.map(module => module.type)), + } + + try { + await dumpConfig(projectConfig, projectSchema, ctx.log) + } catch (err) { + errors.push(err) + } + + if (errors.length === 0) { + task.setSuccess() + } else { + task.setWarn({ msg: "Finished with errors", append: true }) + } + + ctx.log.info(`Project created! Be sure to check out our ${terminalLink("docs", "https://docs.garden.io")}!`) + + return { + result: { + moduleConfigs, + projectConfig, + }, + errors, + } + } +} diff --git a/src/commands/create/prompts.ts b/src/commands/create/prompts.ts new file mode 100644 index 0000000000..f9e7d46964 --- /dev/null +++ b/src/commands/create/prompts.ts @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2018 Garden Technologies, Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import * as inquirer from "inquirer" +import * as Joi from "joi" +import chalk from "chalk" + +import { joiIdentifier } from "../../types/common" +import { ModuleType } from "./config-templates" + +export interface ModuleTypeChoice extends inquirer.objects.ChoiceOption { + value: ModuleType +} + +export interface ModuleTypeMap { + type: ModuleType +} + +export interface ModuleTypeAndName extends ModuleTypeMap { + name: string +} + +export interface Prompts { + addConfigForModule: (...args: any[]) => Promise + addModule: (...args: any[]) => Promise + repeatAddModule: (...args: any[]) => Promise +} + +const moduleTypeChoices: ModuleTypeChoice[] = [ + { + name: "container", + value: "container", + }, + { + name: `google-cloud-function (${chalk.red.italic("experimental")})`, + value: "function", + }, + { + name: `npm package (${chalk.red.italic("experimental")})`, + value: "npm-package", + }, +] + +// Create config for an existing module +async function addConfigForModule(dir: string): Promise { + const qNames = { + ADD_MODULE: "addModule", + TYPE: "type", + } + const questions: inquirer.Questions = [ + { + name: qNames.ADD_MODULE, + message: `Add module config for ${chalk.italic(dir)}?`, + type: "confirm", + }, + { + name: qNames.TYPE, + message: "Module type", + choices: moduleTypeChoices, + when: ans => ans[qNames.ADD_MODULE], + type: "list", + }, + ] + return await inquirer.prompt(questions) as ModuleTypeMap +} + +// Create a new module with config +async function addModule(addModuleMessage: string): Promise { + const qNames = { + ADD_MODULE: "addModule", + NAME: "name", + TYPE: "type", + } + const questions: inquirer.Questions = [ + { + name: qNames.ADD_MODULE, + message: addModuleMessage, + type: "confirm", + }, + { + name: qNames.NAME, + message: "Enter module name", + type: "input", + validate: input => { + try { + Joi.attempt(input.trim(), joiIdentifier()) + } catch (err) { + return `Invalid module name, please try again\nError: ${err.message}` + } + return true + }, + filter: input => input.trim(), + when: ans => ans[qNames.ADD_MODULE], + }, + { + name: qNames.TYPE, + message: "Module type", + choices: moduleTypeChoices, + when: ans => ans[qNames.NAME], + type: "list", + }, + ] + return await inquirer.prompt(questions) as ModuleTypeAndName +} + +export async function repeatAddModule(): Promise { + let modules: ModuleTypeAndName[] = [] + let addModuleMessage = "Would you like to add a module to your project?" + let ans = await addModule(addModuleMessage) + + while (ans.type) { + modules.push({ name: ans.name, type: ans.type }) + addModuleMessage = `Add another module? (current modules: ${modules.map(m => m.name).join(", ")})` + ans = await addModule(addModuleMessage) + } + return modules +} + +export const prompts: Prompts = { + addConfigForModule, + addModule, + repeatAddModule, +} diff --git a/src/logger/renderers.ts b/src/logger/renderers.ts index ce60ee0d1d..7f79af8edf 100644 --- a/src/logger/renderers.ts +++ b/src/logger/renderers.ts @@ -10,7 +10,7 @@ import * as logSymbols from "log-symbols" import * as nodeEmoji from "node-emoji" import * as yaml from "js-yaml" import chalk from "chalk" -import { curryRight, flow, isArray, padEnd, padStart } from "lodash" +import { curryRight, flow, isArray, isEmpty, padEnd, padStart } from "lodash" import hasAnsi = require("has-ansi") import { duration } from "./util" @@ -74,7 +74,7 @@ export function renderError(entry: LogEntry) { if (error) { const { detail, message, stack } = error let out = `${stack || message}` - if (detail) { + if (!isEmpty(detail)) { const yamlDetail = yaml.safeDump(detail, { noRefs: true, skipInvalid: true }) out += `\nError Details:\n${yamlDetail}` } diff --git a/src/util/util.ts b/src/util/util.ts index 2e01ccf3a0..6cc4d7d4c9 100644 --- a/src/util/util.ts +++ b/src/util/util.ts @@ -16,7 +16,7 @@ import * as yaml from "js-yaml" import * as Cryo from "cryo" import { spawn as _spawn } from "child_process" import { pathExists, readFile, writeFile } from "fs-extra" -import { join } from "path" +import { join, basename } from "path" import { find } from "lodash" import { TimeoutError, @@ -41,6 +41,14 @@ const exitHookNames: string[] = [] // For debugging/testing/inspection purposes export type Omit = Pick> export type Diff = T extends U ? never : T export type Nullable = { [P in keyof T]: T[P] | null } +// From: https://stackoverflow.com/a/49936686/5629940 +export type DeepPartial = { + [P in keyof T]?: T[P] extends Array + ? Array> + : T[P] extends ReadonlyArray + ? ReadonlyArray> + : DeepPartial +} export function shutdown(code) { // This is a good place to log exitHookNames if needed. @@ -118,6 +126,19 @@ export async function* scanDirectory(path: string, opts?: klaw.Options): AsyncIt } } +export async function getChildDirNames(parentDir: string): Promise { + let dirNames: string[] = [] + // Filter on hidden dirs by default. We could make the filter function a param if needed later + const filter = (item: string) => !basename(item).startsWith(".") + + for await (const item of scanDirectory(parentDir, { depthLimit: 0, filter })) { + if (!item || !item.stats.isDirectory()) { + continue + } + dirNames.push(basename(item.path)) + } + return dirNames +} export async function getIgnorer(rootPath: string) { // TODO: this doesn't handle nested .gitignore files, we should revisit const gitignorePath = join(rootPath, ".gitignore") diff --git a/test/data/get-child-dir-names/.hidden/.keep b/test/data/get-child-dir-names/.hidden/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/data/get-child-dir-names/.keep b/test/data/get-child-dir-names/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/data/get-child-dir-names/a/.keep b/test/data/get-child-dir-names/a/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/data/get-child-dir-names/a/sub-a/.keep b/test/data/get-child-dir-names/a/sub-a/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/data/get-child-dir-names/b/.keep b/test/data/get-child-dir-names/b/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/data/test-project-create-command/garden.yml b/test/data/test-project-create-command/garden.yml new file mode 100644 index 0000000000..a445562ade --- /dev/null +++ b/test/data/test-project-create-command/garden.yml @@ -0,0 +1,2 @@ +project: + name: create-command diff --git a/test/data/test-project-create-command/module-a/child-module-a/garden.yml b/test/data/test-project-create-command/module-a/child-module-a/garden.yml new file mode 100644 index 0000000000..ac0625d2be --- /dev/null +++ b/test/data/test-project-create-command/module-a/child-module-a/garden.yml @@ -0,0 +1,13 @@ +module: + name: child-module-a + type: container + description: Child Module a container + services: + - name: child-module-a-service + ports: + - name: http + containerPort: 8080 + endpoints: + - paths: + - / + port: http diff --git a/test/data/test-project-create-command/module-a/garden.yml b/test/data/test-project-create-command/module-a/garden.yml new file mode 100644 index 0000000000..a3c4ec1b59 --- /dev/null +++ b/test/data/test-project-create-command/module-a/garden.yml @@ -0,0 +1,13 @@ +module: + name: module-parent-a + type: container + description: Module parent a container + services: + - name: module-parent-a-service + ports: + - name: http + containerPort: 8080 + endpoints: + - paths: + - / + port: http diff --git a/test/data/test-project-create-command/module-b/child-module-b/garden.yml b/test/data/test-project-create-command/module-b/child-module-b/garden.yml new file mode 100644 index 0000000000..e433fd2b69 --- /dev/null +++ b/test/data/test-project-create-command/module-b/child-module-b/garden.yml @@ -0,0 +1,13 @@ +module: + name: child-module-b + type: container + description: Child module b container + services: + - name: child-module-b-service + ports: + - name: http + containerPort: 8080 + endpoints: + - paths: + - / + port: http diff --git a/test/data/test-project-create-command/module-b/garden.yml b/test/data/test-project-create-command/module-b/garden.yml new file mode 100644 index 0000000000..6cbc60d223 --- /dev/null +++ b/test/data/test-project-create-command/module-b/garden.yml @@ -0,0 +1,13 @@ +module: + name: module-parent-b + type: container + description: Module parent b container + services: + - name: module-parent-b-service + ports: + - name: http + containerPort: 8080 + endpoints: + - paths: + - / + port: http diff --git a/test/helpers.ts b/test/helpers.ts index 5af169cade..9f7b0cce83 100644 --- a/test/helpers.ts +++ b/test/helpers.ts @@ -256,7 +256,7 @@ export async function expectError(fn: Function, typeOrCallback: string | ((err: throw new Error(`Expected GardenError with type ${typeOrCallback}, got: ${err}`) } if (err.type !== typeOrCallback) { - throw new Error(`Expected ${typeOrCallback} error, got: ${err}`) + throw new Error(`Expected ${typeOrCallback} error, got: ${err.type} error`) } } return diff --git a/test/src/commands/create/config-templates.ts b/test/src/commands/create/config-templates.ts new file mode 100644 index 0000000000..4e2a238cd2 --- /dev/null +++ b/test/src/commands/create/config-templates.ts @@ -0,0 +1,41 @@ +import { expect } from "chai" + +import { + availableModuleTypes, + projectTemplate, + moduleTemplate, +} from "../../../../src/commands/create/config-templates" +import { validate } from "../../../../src/types/common" +import { baseModuleSpecSchema } from "../../../../src/types/module" +import { projectSchema } from "../../../../src/types/project" + +describe("ConfigTemplates", () => { + describe("projectTemplate", () => { + for (const moduleType of availableModuleTypes) { + it(`should be valid for module type ${moduleType}`, async () => { + const config = projectTemplate("my-project", [moduleType]) + expect(() => validate(config, projectSchema)).to.not.throw() + }) + } + it("should be valid for multiple module types", async () => { + const config = projectTemplate("my-project", availableModuleTypes) + expect(() => validate(config, projectSchema)).to.not.throw() + }) + it("should be valid for multiple modules of same type", async () => { + const config = projectTemplate("my-project", [availableModuleTypes[0], availableModuleTypes[0]]) + expect(() => validate(config, projectSchema)).to.not.throw() + }) + it("should be valid if no modules", async () => { + const config = projectTemplate("my-project", []) + expect(() => validate(config, projectSchema)).to.not.throw() + }) + }) + describe("moduleTemplate", () => { + for (const moduleType of availableModuleTypes) { + it(`should be valid for module type ${moduleType}`, async () => { + const config = moduleTemplate("my-module", moduleType) + expect(() => validate(config, baseModuleSpecSchema)).to.not.throw() + }) + } + }) +}) diff --git a/test/src/commands/create/module.ts b/test/src/commands/create/module.ts new file mode 100644 index 0000000000..36ccd1b8f1 --- /dev/null +++ b/test/src/commands/create/module.ts @@ -0,0 +1,105 @@ +import { expect } from "chai" +import { + expectError, + makeTestContext, +} from "../../../helpers" +import { pick } from "lodash" +import { join } from "path" +import * as td from "testdouble" + +import { CreateModuleCommand } from "../../../../src/commands/create/module" +import { + prompts, + ModuleTypeMap, +} from "../../../../src/commands/create/prompts" +import { remove } from "fs-extra" + +const projectRoot = join(__dirname, "../../..", "data", "test-project-create-command") + +const replaceAddConfigForModule = (returnVal?: ModuleTypeMap) => { + if (!returnVal) { + returnVal = { + type: "container", + } + td.replace(prompts, "addConfigForModule", async () => returnVal) + } +} + +afterEach(async () => { + await remove(join(projectRoot, "new-module")) + td.reset() +}) + +describe("CreateModuleCommand", () => { + const cmd = new CreateModuleCommand() + // garden create module + it("should add a module config to the current directory", async () => { + replaceAddConfigForModule() + const ctx = await makeTestContext(projectRoot) + const { result } = await cmd.action(ctx, { "module-dir": "" }, { name: "", type: "" }) + expect(pick(result.module, ["name", "type", "path"])).to.eql({ + name: "test-project-create-command", + type: "container", + path: ctx.projectRoot, + }) + }) + // garden create module new-module + it("should add a module config to new-module directory", async () => { + replaceAddConfigForModule() + const ctx = await makeTestContext(projectRoot) + const { result } = await cmd.action(ctx, { "module-dir": "new-module" }, { name: "", type: "" }) + expect(pick(result.module, ["name", "type", "path"])).to.eql({ + name: "new-module", + type: "container", + path: join(ctx.projectRoot, "new-module"), + }) + }) + // garden create module --name=my-module + it("should optionally name the module my-module", async () => { + replaceAddConfigForModule() + const ctx = await makeTestContext(projectRoot) + const { result } = await cmd.action(ctx, { "module-dir": "" }, { name: "my-module", type: "" }) + expect(pick(result.module, ["name", "type", "path"])).to.eql({ + name: "my-module", + type: "container", + path: ctx.projectRoot, + }) + }) + // garden create module --type=function + it("should optionally create a module of a specific type (without prompting)", async () => { + const ctx = await makeTestContext(projectRoot) + const { result } = await cmd.action(ctx, { "module-dir": "" }, { name: "", type: "function" }) + expect(pick(result.module, ["name", "type", "path"])).to.eql({ + name: "test-project-create-command", + type: "function", + path: ctx.projectRoot, + }) + }) + // garden create module ___ + it("should throw if module name is invalid when inherited from current directory", async () => { + replaceAddConfigForModule() + const ctx = await makeTestContext(projectRoot) + await expectError( + async () => await cmd.action(ctx, { "module-dir": "___" }, { name: "", type: "" }), + "configuration", + ) + }) + // garden create --name=___ + it("should throw if module name is invalid when explicitly specified", async () => { + replaceAddConfigForModule() + const ctx = await makeTestContext(projectRoot) + await expectError( + async () => await cmd.action(ctx, { "module-dir": "" }, { name: "___", type: "" }), + "configuration", + ) + }) + // garden create module --type=banana + it("should throw if invalid type provided", async () => { + replaceAddConfigForModule() + const ctx = await makeTestContext(projectRoot) + await expectError( + async () => await cmd.action(ctx, { "module-dir": "" }, { name: "", type: "banana" }), + "parameter", + ) + }) +}) diff --git a/test/src/commands/create/project.ts b/test/src/commands/create/project.ts new file mode 100644 index 0000000000..80986f7e03 --- /dev/null +++ b/test/src/commands/create/project.ts @@ -0,0 +1,139 @@ +import { expect } from "chai" +import { + expectError, + makeTestContext, +} from "../../../helpers" +import { pick } from "lodash" +import { remove } from "fs-extra" +import { join } from "path" +import * as td from "testdouble" + +import { CreateProjectCommand } from "../../../../src/commands/create/project" +import { + prompts, + ModuleTypeAndName, + ModuleTypeMap, +} from "../../../../src/commands/create/prompts" + +const projectRoot = join(__dirname, "../../..", "data", "test-project-create-command") + +const replaceRepeatAddModule = (returnVal?: ModuleTypeAndName[]) => { + if (!returnVal) { + returnVal = [ + { + type: "container", + name: "module-a", + }, + { + type: "container", + name: "module-b", + }, + ] + } + td.replace(prompts, "repeatAddModule", async () => returnVal) +} + +const replaceAddConfigForModule = (returnVal?: ModuleTypeMap) => { + if (!returnVal) { + returnVal = { + type: "container", + } + td.replace(prompts, "addConfigForModule", async () => returnVal) + } +} + +afterEach(async () => { + await remove(join(projectRoot, "new-project")) + td.reset() +}) + +describe("CreateProjectCommand", () => { + const cmd = new CreateProjectCommand() + + // garden create project + it("should create a project in the current directory", async () => { + replaceRepeatAddModule() + const ctx = await makeTestContext(projectRoot) + const { result } = await cmd.action(ctx, { "project-dir": "" }, { name: "", "module-dirs": "" }) + const modules = result.moduleConfigs.map(m => pick(m, ["name", "type", "path"])) + const project = pick(result.projectConfig, ["name", "path"]) + + expect({ modules, project }).to.eql({ + modules: [ + { type: "container", name: "module-a", path: join(ctx.projectRoot, "module-a") }, + { type: "container", name: "module-b", path: join(ctx.projectRoot, "module-b") }, + ], + project: { + name: "test-project-create-command", + path: ctx.projectRoot, + }, + }) + }) + // garden create project new-project + it("should create a project in directory new-project", async () => { + replaceRepeatAddModule() + const ctx = await makeTestContext(projectRoot) + const { result } = await cmd.action(ctx, { "project-dir": "new-project" }, { name: "", "module-dirs": "" }) + expect(pick(result.projectConfig, ["name", "path"])).to.eql({ + name: "new-project", + path: join(ctx.projectRoot, "new-project"), + }) + }) + // garden create project --name=my-project + it("should optionally create a project named my-project", async () => { + replaceRepeatAddModule() + const ctx = await makeTestContext(projectRoot) + const { result } = await cmd.action(ctx, { "project-dir": "" }, { name: "my-project", "module-dirs": "" }) + expect(pick(result.projectConfig, ["name", "path"])).to.eql({ + name: "my-project", + path: join(ctx.projectRoot), + }) + }) + // garden create project --module-dirs=. + it("should optionally create module configs for modules in current directory", async () => { + replaceAddConfigForModule() + const ctx = await makeTestContext(projectRoot) + const { result } = await cmd.action(ctx, { "project-dir": "" }, { name: "", "module-dirs": "." }) + expect(result.moduleConfigs.map(m => pick(m, ["name", "type", "path"]))).to.eql([ + { type: "container", name: "module-a", path: join(ctx.projectRoot, "module-a") }, + { type: "container", name: "module-b", path: join(ctx.projectRoot, "module-b") }, + ]) + }) + // garden create project --module-dirs=module-a,module-b + it("should optionally create module configs for modules in specified directories", async () => { + replaceAddConfigForModule() + const ctx = await makeTestContext(projectRoot) + const { result } = await cmd.action(ctx, { "project-dir": "" }, { name: "", "module-dirs": "module-a,module-b" }) + expect(result.moduleConfigs.map(m => pick(m, ["name", "type", "path"]))).to.eql([ + { type: "container", name: "child-module-a", path: join(ctx.projectRoot, "module-a", "child-module-a") }, + { type: "container", name: "child-module-b", path: join(ctx.projectRoot, "module-b", "child-module-b") }, + ]) + }) + // garden create project ___ + it("should throw if project name is invalid when inherited from current directory", async () => { + replaceRepeatAddModule() + const ctx = await makeTestContext(projectRoot) + await expectError( + async () => await cmd.action(ctx, { "project-dir": "___" }, { name: "", "module-dirs": "" }), + "configuration", + ) + }) + // garden create project --name=____ + it("should throw if project name is invalid when explicitly specified", async () => { + replaceRepeatAddModule() + const ctx = await makeTestContext(projectRoot) + await expectError( + async () => await cmd.action(ctx, { "project-dir": "" }, { name: "___", "module-dirs": "" }), + "configuration", + ) + }) + // garden create project --module-dirs=banana + it("should throw if module parent directory does not exist", async () => { + replaceRepeatAddModule() + const ctx = await makeTestContext(projectRoot) + await expectError( + async () => await cmd.action(ctx, { "project-dir": "" }, { name: "", "module-dirs": "banana" }), + "parameter", + ) + }) +}) diff --git a/test/src/garden.ts b/test/src/garden.ts index 6457932a86..78561d4ebf 100644 --- a/test/src/garden.ts +++ b/test/src/garden.ts @@ -13,6 +13,7 @@ import { testPluginB, } from "../helpers" import { getNames } from "../../src/util/util" +import { MOCK_CONFIG } from "../../src/cli/cli" describe("Garden", () => { describe("factory", () => { @@ -27,6 +28,11 @@ describe("Garden", () => { expect(ctx.actionHandlers.configureEnvironment["test-plugin-b"]).to.be.ok }) + it("should initialize with MOCK_CONFIG", async () => { + const garden = await Garden.factory("./", { config: MOCK_CONFIG }) + expect(garden).to.be.ok + }) + it("should throw if registering same plugin twice", async () => { try { await Garden.factory(projectRootA, { diff --git a/test/src/util.ts b/test/src/util.ts index 08b2572c98..9e9974fc65 100644 --- a/test/src/util.ts +++ b/test/src/util.ts @@ -1,6 +1,6 @@ import { expect } from "chai" import { join } from "path" -import { scanDirectory } from "../../src/util/util" +import { scanDirectory, getChildDirNames } from "../../src/util/util" describe("util", () => { describe("scanDirectory", () => { @@ -34,4 +34,10 @@ describe("util", () => { }) }) + describe("getChildDirNames", () => { + it("should return the names of all none hidden directories in the parent directory", async () => { + const testPath = join(__dirname, "..", "data", "get-child-dir-names") + expect(await getChildDirNames(testPath)).to.eql(["a", "b"]) + }) + }) })