enhancement(schematics): optimize formatting during codegen

This commit is contained in:
Jason Jean 2018-12-03 10:45:45 -05:00 committed by Victor Savkin
parent 9897fa1970
commit d537476b0c
55 changed files with 2097 additions and 2055 deletions

View File

@ -5,10 +5,10 @@ _[Please make sure you have read the submission guidelines before posting an iss
Please answer the following questions for yourself before submitting an issue. Please answer the following questions for yourself before submitting an issue.
**YOU MAY DELETE THE PREREQUISITES SECTION.** **YOU MAY DELETE THE PREREQUISITES SECTION.**
* [ ] I am running the latest version - [ ] I am running the latest version
* [ ] I checked the documentation and found no answer - [ ] I checked the documentation and found no answer
* [ ] I checked to make sure that this issue has not already been filed - [ ] I checked to make sure that this issue has not already been filed
* [ ] I'm reporting the issue to the correct repository (not related to Angular, AngularCLI or any dependency) - [ ] I'm reporting the issue to the correct repository (not related to Angular, AngularCLI or any dependency)
## Expected Behavior ## Expected Behavior
@ -34,12 +34,12 @@ Please provide detailed steps for reproducing the issue.
Please provide any relevant information about your setup: Please provide any relevant information about your setup:
* version of Nx used - version of Nx used
* version of Angular CLI used - version of Angular CLI used
* `angular.json` configuration - `angular.json` configuration
* version of Angular DevKit used - version of Angular DevKit used
* 3rd-party libraries and their versions - 3rd-party libraries and their versions
* and most importantly - a use-case that fails - and most importantly - a use-case that fails
**A minimal reproduce scenario using allows us to quickly confirm a bug (or point out coding problem) as well as confirm that we are fixing the right problem.** **A minimal reproduce scenario using allows us to quickly confirm a bug (or point out coding problem) as well as confirm that we are fixing the right problem.**

View File

@ -6,58 +6,58 @@
## Fixes ## Fixes
* **schematic:** add a migration to add tsconfig.tools.json ([c18baf0](https://github.com/nrwl/nx/commit/c18baf0)) - **schematic:** add a migration to add tsconfig.tools.json ([c18baf0](https://github.com/nrwl/nx/commit/c18baf0))
# 1.0.0 # 1.0.0
## Fixes ## Fixes
* **schematics:** dep-graph visual style for affected lib ([8ca8548](https://github.com/nrwl/nx/commit/8ca8548)) - **schematics:** dep-graph visual style for affected lib ([8ca8548](https://github.com/nrwl/nx/commit/8ca8548))
* **schematics:** remove strict check for "file" option for dep-graph ([6aa6702](https://github.com/nrwl/nx/commit/6aa6702)) - **schematics:** remove strict check for "file" option for dep-graph ([6aa6702](https://github.com/nrwl/nx/commit/6aa6702))
* **schematics:** add nxmodule forroot if not existing when generating ngrx for root ([e748e81](https://github.com/nrwl/nx/commit/e748e81)) - **schematics:** add nxmodule forroot if not existing when generating ngrx for root ([e748e81](https://github.com/nrwl/nx/commit/e748e81))
* **schematics:** don't leak file descriptor in shared.ts/mtime ([aa1ff78](https://github.com/nrwl/nx/commit/aa1ff78)), closes [#403](https://github.com/nrwl/nx/issues/403) - **schematics:** don't leak file descriptor in shared.ts/mtime ([aa1ff78](https://github.com/nrwl/nx/commit/aa1ff78)), closes [#403](https://github.com/nrwl/nx/issues/403)
* **schematics:** ignore errors when the tools directory already exists ([2a377fe](https://github.com/nrwl/nx/commit/2a377fe)) - **schematics:** ignore errors when the tools directory already exists ([2a377fe](https://github.com/nrwl/nx/commit/2a377fe))
# 0.11.0 # 0.11.0
## Features ## Features
* [Support to generate visualization graph](https://github.com/nrwl/nx/commit/49525efe3e31ff93dccf68499300fe35deaac78f) - [Support to generate visualization graph](https://github.com/nrwl/nx/commit/49525efe3e31ff93dccf68499300fe35deaac78f)
# 0.10.1 # 0.10.1
## Features ## Features
* [Add support for workspace-specific schematics](https://github.com/nrwl/nx/commit/bc559575f524aacbe3d25eb97210101e60de4526) - [Add support for workspace-specific schematics](https://github.com/nrwl/nx/commit/bc559575f524aacbe3d25eb97210101e60de4526)
## Fixes ## Fixes
* [Extend ngrx and Angular schematics](https://github.com/nrwl/nx/commit/0c2b01bda6904e09f0c4f997ef4a98818723b795) - [Extend ngrx and Angular schematics](https://github.com/nrwl/nx/commit/0c2b01bda6904e09f0c4f997ef4a98818723b795)
# 0.10.0 # 0.10.0
## Features ## Features
* [Use npm-run-all to run build in parallel](https://github.com/nrwl/nx/commit/7b63de662e3aa99a8688681f0e26518ca05ffcbc) - [Use npm-run-all to run build in parallel](https://github.com/nrwl/nx/commit/7b63de662e3aa99a8688681f0e26518ca05ffcbc)
* [Enhance nxEnforceModuleBoundaries check to support projects tagged with multiple tags](https://github.com/nrwl/nx/commit/d416c580b5db181cedf45ed952d9b6db49bb084d) - [Enhance nxEnforceModuleBoundaries check to support projects tagged with multiple tags](https://github.com/nrwl/nx/commit/d416c580b5db181cedf45ed952d9b6db49bb084d)
* [Add a usage information option to create-nx-workspace ](https://github.com/nrwl/nx/commit/2746f0399d0c729ff08420f46f2c2c6b4256f6a7) - [Add a usage information option to create-nx-workspace ](https://github.com/nrwl/nx/commit/2746f0399d0c729ff08420f46f2c2c6b4256f6a7)
* [Add an option to pass a custom module name when creating libs](https://github.com/nrwl/nx/commit/c1ca7df014043d0e27517b26470067677c57efa3) - [Add an option to pass a custom module name when creating libs](https://github.com/nrwl/nx/commit/c1ca7df014043d0e27517b26470067677c57efa3)
* [Affected support for uncommitted changes](https://github.com/nrwl/nx/commit/113b51bd330c2af058675386e482d38f170a6688) - [Affected support for uncommitted changes](https://github.com/nrwl/nx/commit/113b51bd330c2af058675386e482d38f170a6688)
* [Extend circular dependency check](https://github.com/nrwl/nx/commit/fff9659c3a108b613c6db89bcca4072494fbe85f) - [Extend circular dependency check](https://github.com/nrwl/nx/commit/fff9659c3a108b613c6db89bcca4072494fbe85f)
* [Add lint checks ensuring the integrity of the workspace](https://github.com/nrwl/nx/commit/ce553b732e8ca004e1ecf53cf54bcadb6ba1f7f3) - [Add lint checks ensuring the integrity of the workspace](https://github.com/nrwl/nx/commit/ce553b732e8ca004e1ecf53cf54bcadb6ba1f7f3)
* [Add ability to create bazel workspace](https://github.com/nrwl/nx/commit/5a26f241b1e8b8b87e2ab4da941d3d3c4c480046) - [Add ability to create bazel workspace](https://github.com/nrwl/nx/commit/5a26f241b1e8b8b87e2ab4da941d3d3c4c480046)
## Fixes ## Fixes
* [Fix the absolute import check](https://github.com/nrwl/nx/commit/de138bb26cb816b86afe6b8f14abb362cec2a20e) - [Fix the absolute import check](https://github.com/nrwl/nx/commit/de138bb26cb816b86afe6b8f14abb362cec2a20e)
* [Use yargs-parser to allow more types of arguments](https://github.com/nrwl/nx/commit/c747a4a0a066975bdbbda10234b78e3c63c3b44d) - [Use yargs-parser to allow more types of arguments](https://github.com/nrwl/nx/commit/c747a4a0a066975bdbbda10234b78e3c63c3b44d)
* [Fix e2e lint config in angular cli json](https://github.com/nrwl/nx/commit/fc0bec90805217d03bf2008e9b241025cad72c05) - [Fix e2e lint config in angular cli json](https://github.com/nrwl/nx/commit/fc0bec90805217d03bf2008e9b241025cad72c05)
* [Handle undefined rulesDirectory](https://github.com/nrwl/nx/commit/8beae9fa3d6d905778dd24c5e6b27fe7c534be13) - [Handle undefined rulesDirectory](https://github.com/nrwl/nx/commit/8beae9fa3d6d905778dd24c5e6b27fe7c534be13)
* [Add appRoot.path for filesystem reads/writes](https://github.com/nrwl/nx/commit/809bdd4c270baf7fd883980db8d7171ebf09508e) - [Add appRoot.path for filesystem reads/writes](https://github.com/nrwl/nx/commit/809bdd4c270baf7fd883980db8d7171ebf09508e)
* [Correct docstring for affected](https://github.com/nrwl/nx/commit/6394f9e1edf5a0b1f219050dc8b1e8269d5e5c2b) - [Correct docstring for affected](https://github.com/nrwl/nx/commit/6394f9e1edf5a0b1f219050dc8b1e8269d5e5c2b)
* [Update karma conf with app check](https://github.com/nrwl/nx/commit/fea4f48decdfa17feb5e020318abf2ac319d05da) - [Update karma conf with app check](https://github.com/nrwl/nx/commit/fea4f48decdfa17feb5e020318abf2ac319d05da)
* [Fix typo in CONTIRBUTING.md](https://github.com/nrwl/nx/commit/bd50643d60b3c2a8e8ace6ceaab5834d897b4aa3) - [Fix typo in CONTIRBUTING.md](https://github.com/nrwl/nx/commit/bd50643d60b3c2a8e8ace6ceaab5834d897b4aa3)
* [Match ng lib and update versions ](https://github.com/nrwl/nx/commit/8482177b7373844d833aaad55e15a2a10d4ae116) - [Match ng lib and update versions ](https://github.com/nrwl/nx/commit/8482177b7373844d833aaad55e15a2a10d4ae116)
# 0.9.0 # 0.9.0
@ -92,10 +92,10 @@ You can then define constraints in `tslint.json`, like this:
With this configuration in place: With this configuration in place:
* `utilslib` can depend on no libs. - `utilslib` can depend on no libs.
* `apilib` can depend on `utilslib` - `apilib` can depend on `utilslib`
* `implib` can depend on both `utilslib` and `apilib`. - `implib` can depend on both `utilslib` and `apilib`.
* `untaggedlib` can depend on no libs. - `untaggedlib` can depend on no libs.
This gets really useful once you have multiple libs with the same tag. This gets really useful once you have multiple libs with the same tag.
@ -127,27 +127,27 @@ If we change the configuration to the following:
The following will be true. The following will be true.
* `utilslib` can depend on no libs. - `utilslib` can depend on no libs.
* `apilib` can depend on `utilslib` - `apilib` can depend on `utilslib`
* `implib` can depend on both `utilslib` and `apilib`. - `implib` can depend on both `utilslib` and `apilib`.
* `untaggedlib` can depend on **all** libs. - `untaggedlib` can depend on **all** libs.
## Features ## Features
* [Cache the dependency graph to speed up "ng lint"](https://github.com/nrwl/nx/commit/93ecf24bdadfa7ae28cf802b4f3193390858db90) - [Cache the dependency graph to speed up "ng lint"](https://github.com/nrwl/nx/commit/93ecf24bdadfa7ae28cf802b4f3193390858db90)
* [Add lint check for circular deps](https://github.com/nrwl/nx/commit/53175f925e804539aacba8323af159d69205ddac) - [Add lint check for circular deps](https://github.com/nrwl/nx/commit/53175f925e804539aacba8323af159d69205ddac)
* [Add support for tagged libs](https://github.com/nrwl/nx/commit/2842cb9c387226c24c0a42a154f3c65059d730f3) - [Add support for tagged libs](https://github.com/nrwl/nx/commit/2842cb9c387226c24c0a42a154f3c65059d730f3)
* [Show warnings about importing lazy loadable libraries](https://github.com/nrwl/nx/commit/56788ba3d1a8220455b03f0c766963ae8179ce81) - [Show warnings about importing lazy loadable libraries](https://github.com/nrwl/nx/commit/56788ba3d1a8220455b03f0c766963ae8179ce81)
* [Allow users to set prettier config](https://github.com/nrwl/nx/commit/56a6611575fee7b111655d2223f3e30ea684d000) - [Allow users to set prettier config](https://github.com/nrwl/nx/commit/56a6611575fee7b111655d2223f3e30ea684d000)
* [Add store-freeze support](https://github.com/nrwl/nx/commit/e21caa0143862059d4b1089ccf3a7e3799cef0f2) - [Add store-freeze support](https://github.com/nrwl/nx/commit/e21caa0143862059d4b1089ccf3a7e3799cef0f2)
* [Extend the nrwl schematics from the angular cli schematics](https://github.com/nrwl/nx/commit/04f8e2fd4613aff4064123cacd8eb54e1da09a60) - [Extend the nrwl schematics from the angular cli schematics](https://github.com/nrwl/nx/commit/04f8e2fd4613aff4064123cacd8eb54e1da09a60)
## Fixes ## Fixes
* [Improve windows compatibility](https://github.com/nrwl/nx/commit/4f052bb3e60f20d0d676f283de2e2faece0dcef8) - [Improve windows compatibility](https://github.com/nrwl/nx/commit/4f052bb3e60f20d0d676f283de2e2faece0dcef8)
* [Dasherize the state folder](https://github.com/nrwl/nx/commit/b17b186186ea5617b1b953f1ddd4ab05ebfa7d92) - [Dasherize the state folder](https://github.com/nrwl/nx/commit/b17b186186ea5617b1b953f1ddd4ab05ebfa7d92)
* [Fix create-nx-workspace to respect the --directory](https://github.com/nrwl/nx/commit/da9310e9dbb4eb3294200ca72bfa70b14faec2c2) - [Fix create-nx-workspace to respect the --directory](https://github.com/nrwl/nx/commit/da9310e9dbb4eb3294200ca72bfa70b14faec2c2)
* [Fix protractor.conf.js schema](https://github.com/nrwl/nx/commit/37cabafb35980ce8e991abc1269387b3aaaf9f25) - [Fix protractor.conf.js schema](https://github.com/nrwl/nx/commit/37cabafb35980ce8e991abc1269387b3aaaf9f25)
# 0.8.0 # 0.8.0
@ -155,85 +155,85 @@ The following will be true.
Nx no longer uses a patched version of the CLI--we switched to using `@angular/cli@1.7.1`. We also renamed the nx-migrate command into `update` to align with the CLI. Nx no longer uses a patched version of the CLI--we switched to using `@angular/cli@1.7.1`. We also renamed the nx-migrate command into `update` to align with the CLI.
* [Switch to @angular/cli 1.7](https://github.com/nrwl/nx/commit/acdeb1b71c14e3bc7e5bd2bc925640ad7d0aa24d) - [Switch to @angular/cli 1.7](https://github.com/nrwl/nx/commit/acdeb1b71c14e3bc7e5bd2bc925640ad7d0aa24d)
* [Add update, update:skip, and update:check commands](https://github.com/nrwl/nx/commit/2fb62597514f1c82e4d80d75a7d60d11229d5aa3) - [Add update, update:skip, and update:check commands](https://github.com/nrwl/nx/commit/2fb62597514f1c82e4d80d75a7d60d11229d5aa3)
## Fixes ## Fixes
* [Fix format:write --libs-and-apps](https://github.com/nrwl/nx/commit/feeaba1a4b8d827a71731d919740135464d6e049) - [Fix format:write --libs-and-apps](https://github.com/nrwl/nx/commit/feeaba1a4b8d827a71731d919740135464d6e049)
* [Change format to default to format-all when no patterns are detected](https://github.com/nrwl/nx/commit/58e99ab04d8e9505285bdcf31014f3774a6f900a) - [Change format to default to format-all when no patterns are detected](https://github.com/nrwl/nx/commit/58e99ab04d8e9505285bdcf31014f3774a6f900a)
## Cleanup ## Cleanup
* [Remove the version property](https://github.com/nrwl/nx/commit/24063f02464b6da38e003841e04820dcc347e876) - [Remove the version property](https://github.com/nrwl/nx/commit/24063f02464b6da38e003841e04820dcc347e876)
* [Use schematic context to format the generated code](https://github.com/nrwl/nx/commit/e7481a790f5becffc46e794ec46c0835a2114319) - [Use schematic context to format the generated code](https://github.com/nrwl/nx/commit/e7481a790f5becffc46e794ec46c0835a2114319)
# 0.7.4 # 0.7.4
## Features ## Features
* [Change generate effect tests to use toBeObservable](https://github.com/nrwl/nx/commit/222efe2f2630b02f6fdb11f560c9158cd4f51d75) - [Change generate effect tests to use toBeObservable](https://github.com/nrwl/nx/commit/222efe2f2630b02f6fdb11f560c9158cd4f51d75)
## Fixes ## Fixes
* [Base projectPath on the full path instead of the current working directory of the process](https://github.com/nrwl/nx/commit/545f2ff13902a635b9c26854e9687790adff0616) - [Base projectPath on the full path instead of the current working directory of the process](https://github.com/nrwl/nx/commit/545f2ff13902a635b9c26854e9687790adff0616)
* [Ammend command in script errors](https://github.com/nrwl/nx/commit/9128fa8be1e31525acb6043ff87af73b6b884aaa) - [Ammend command in script errors](https://github.com/nrwl/nx/commit/9128fa8be1e31525acb6043ff87af73b6b884aaa)
# 0.7.2 # 0.7.2
## Features ## Features
* [Added a post install script that run nx-migrate:check](https://github.com/nrwl/nx/commit/93a6b4e94be4b1b591eb017e77b79c777bc23deb) - [Added a post install script that run nx-migrate:check](https://github.com/nrwl/nx/commit/93a6b4e94be4b1b591eb017e77b79c777bc23deb)
* [Updated create-nx-workspace to support yarn](https://github.com/nrwl/nx/commit/e5b247b573cef0c1cf5cc5163d030dbf514f4dff) - [Updated create-nx-workspace to support yarn](https://github.com/nrwl/nx/commit/e5b247b573cef0c1cf5cc5163d030dbf514f4dff)
## Cleanup ## Cleanup
* [Cleaned up the output of nx-migrate](https://github.com/nrwl/nx/commit/7ab14e3b26e0e91d660ad5bb454dccb21b9745cf) - [Cleaned up the output of nx-migrate](https://github.com/nrwl/nx/commit/7ab14e3b26e0e91d660ad5bb454dccb21b9745cf)
# 0.7.0 # 0.7.0
## Features ## Features
* [Add nx-migrate:check and nx-migrate:skip scripts](d7ba9fdc1b9f4208db6de32184e953d00f6f064f) - [Add nx-migrate:check and nx-migrate:skip scripts](d7ba9fdc1b9f4208db6de32184e953d00f6f064f)
# 0.7.0-beta.1 # 0.7.0-beta.1
## Features ## Features
* [Switch to Angular 5.2 and CLI 1.6.5](https://github.com/nrwl/nx/commit/172c18dc9b84f7ac3019694e4a0eeeb40dd1bc26) - [Switch to Angular 5.2 and CLI 1.6.5](https://github.com/nrwl/nx/commit/172c18dc9b84f7ac3019694e4a0eeeb40dd1bc26)
# 0.6.18 # 0.6.18
## Bug Fixes ## Bug Fixes
* [Fixed affected: commands. Continue traversing within irrelevant PropertyAssignment nodes](https://github.com/nrwl/nx/commit/2293e28bd031efa80566228dddc202bc437c2b03) - [Fixed affected: commands. Continue traversing within irrelevant PropertyAssignment nodes](https://github.com/nrwl/nx/commit/2293e28bd031efa80566228dddc202bc437c2b03)
* [Make affected and format windows-friendly](https://github.com/nrwl/nx/commit/9609fc675031bd4dca285ceb942a71d995d1ee7b) - [Make affected and format windows-friendly](https://github.com/nrwl/nx/commit/9609fc675031bd4dca285ceb942a71d995d1ee7b)
## Cleanup ## Cleanup
* [Clean up tslint configuration](https://github.com/nrwl/nx/commit/828e4fe75da66dc41790e55a7738192940a04cbf) - [Clean up tslint configuration](https://github.com/nrwl/nx/commit/828e4fe75da66dc41790e55a7738192940a04cbf)
* [Add a migration updating the version of prettier](https://github.com/nrwl/nx/commit/847a249980c3505845a8c597de8e9a3d78766f8b) - [Add a migration updating the version of prettier](https://github.com/nrwl/nx/commit/847a249980c3505845a8c597de8e9a3d78766f8b)
# 0.6.13 # 0.6.13
## Bug Fixes ## Bug Fixes
* [Avoid relative paths to node modules](https://github.com/nrwl/nx/commit/2c49c3029535a9b98216d367e9c2b77a8d40a4a4) - [Avoid relative paths to node modules](https://github.com/nrwl/nx/commit/2c49c3029535a9b98216d367e9c2b77a8d40a4a4)
* [Handle the case when libraries are placed in the directory with the same name as an app](https://github.com/nrwl/nx/commit/f862ed05d18e1474156779ad1dee2e7a7c785d1d) - [Handle the case when libraries are placed in the directory with the same name as an app](https://github.com/nrwl/nx/commit/f862ed05d18e1474156779ad1dee2e7a7c785d1d)
# 0.6.10 # 0.6.10
## Features ## Features
* [Improve error messages in the affected: and format: commands](https://github.com/nrwl/nx/commit/878784ae814ffea28796f458ff2f8b0b641996c0) - [Improve error messages in the affected: and format: commands](https://github.com/nrwl/nx/commit/878784ae814ffea28796f458ff2f8b0b641996c0)
## Bug Fixes ## Bug Fixes
* [Handle circular deps between apps and libs](https://github.com/nrwl/nx/commit/3531323fb5210b995b1296a198c8e76ee8bf9a07) - [Handle circular deps between apps and libs](https://github.com/nrwl/nx/commit/3531323fb5210b995b1296a198c8e76ee8bf9a07)
* [Handle projects that have similar names](https://github.com/nrwl/nx/commit/fe7032d29f4dcd66b51dbb889a1cf1751cd1d20a) - [Handle projects that have similar names](https://github.com/nrwl/nx/commit/fe7032d29f4dcd66b51dbb889a1cf1751cd1d20a)
* [Update workspace to set $schema and app name](https://github.com/nrwl/nx/commit/df5bd089b6515ea747f891bf590b46a4e00d0a92) - [Update workspace to set \$schema and app name](https://github.com/nrwl/nx/commit/df5bd089b6515ea747f891bf590b46a4e00d0a92)
* [Update workspace to copy the cli file](https://github.com/nrwl/nx/commit/ddd8de3813f374a752b0e6f47deaa58c2e9f40c8) - [Update workspace to copy the cli file](https://github.com/nrwl/nx/commit/ddd8de3813f374a752b0e6f47deaa58c2e9f40c8)
* [Disallow importing apps](https://github.com/nrwl/nx/commit/912fc81708d381f49950255eeff746a2dfd46c7b) - [Disallow importing apps](https://github.com/nrwl/nx/commit/912fc81708d381f49950255eeff746a2dfd46c7b)
# 0.6.5 # 0.6.5
@ -269,11 +269,11 @@ npm run format:write
npm run format:check npm run format:check
``` ```
* [Add format:check and format:write commands](https://github.com/nrwl/nx/commit/826a0b1056f9000425e189bad5a5d63966c3a704) - [Add format:check and format:write commands](https://github.com/nrwl/nx/commit/826a0b1056f9000425e189bad5a5d63966c3a704)
## Bug Fixes ## Bug Fixes
* [Only allow importing libs using the configured npm scope](https://github.com/nrwl/nx/commit/c836668541532e64db088ef9a984678022abb3bd) - [Only allow importing libs using the configured npm scope](https://github.com/nrwl/nx/commit/c836668541532e64db088ef9a984678022abb3bd)
# 0.6.0 # 0.6.0
@ -299,11 +299,11 @@ npm run build:affected ----files="libs/mylib/index.ts,libs/mylib2/index.ts"
npm run e2e:affected ----files="libs/mylib/index.ts,libs/mylib2/index.ts" npm run e2e:affected ----files="libs/mylib/index.ts,libs/mylib2/index.ts"
``` ```
* [Add support for building and testing only the apps affected by a commit](https://github.com/nrwl/nx/commit/428762664acc5fd155dd7be630dab09101d23542) - [Add support for building and testing only the apps affected by a commit](https://github.com/nrwl/nx/commit/428762664acc5fd155dd7be630dab09101d23542)
## Bug Fixes ## Bug Fixes
* [Make deep import check work for libs with same prefix](https://github.com/nrwl/nx/commit/3c55f34ca12a4d5338099586ffe9455c81a3b199) - [Make deep import check work for libs with same prefix](https://github.com/nrwl/nx/commit/3c55f34ca12a4d5338099586ffe9455c81a3b199)
# 0.5.3 # 0.5.3
@ -330,53 +330,53 @@ Some folks also reported having problems running Nx behind a firewall, in a corp
## Features ## Features
* [Replace install.sh with a more robust way of creating projects](https://github.com/nrwl/nx/commit/f91b5309bdaf764e436bd544ec4f10c84b99cb08) - [Replace install.sh with a more robust way of creating projects](https://github.com/nrwl/nx/commit/f91b5309bdaf764e436bd544ec4f10c84b99cb08)
* [Bump up the version of prettier](https://github.com/nrwl/nx/commit/1481d169bbb7f1fbe3df5af2bce51c4215776d93) - [Bump up the version of prettier](https://github.com/nrwl/nx/commit/1481d169bbb7f1fbe3df5af2bce51c4215776d93)
## Bug Fixes ## Bug Fixes
* [Generate an angular-cli config without the apps array, so the CLI can error properly](https://github.com/nrwl/nx/commit/a7f06edf5914212bcefbafb1198d262e9692cfdb) - [Generate an angular-cli config without the apps array, so the CLI can error properly](https://github.com/nrwl/nx/commit/a7f06edf5914212bcefbafb1198d262e9692cfdb)
# 0.5.2 # 0.5.2
## Bug Fixes ## Bug Fixes
* [Remove default prop for viewEncapsulation option flag](https://github.com/nrwl/nx/commit/b46eb1c699dd509f4be103979a5938c3f7486fb1) - [Remove default prop for viewEncapsulation option flag](https://github.com/nrwl/nx/commit/b46eb1c699dd509f4be103979a5938c3f7486fb1)
* [Fix NPM link in README](https://github.com/nrwl/nx/commit/4aa42e4772522a20df384ab9a48861a8d4f7ab0f) - [Fix NPM link in README](https://github.com/nrwl/nx/commit/4aa42e4772522a20df384ab9a48861a8d4f7ab0f)
* [Change rxjs version to use hat](https://github.com/nrwl/nx/commit/3b1942ed830ea31269a1fb9e995efb93b182870a) - [Change rxjs version to use hat](https://github.com/nrwl/nx/commit/3b1942ed830ea31269a1fb9e995efb93b182870a)
# 0.5.1 # 0.5.1
## Features ## Features
* [Disable typescript mismatch warnings](https://github.com/nrwl/nx/commit/ecb87a0dcd08f0968d77508976ce43a84f049743) - [Disable typescript mismatch warnings](https://github.com/nrwl/nx/commit/ecb87a0dcd08f0968d77508976ce43a84f049743)
## Bug Fixes ## Bug Fixes
* ["ng test" should not compile e2e tests](https://github.com/nrwl/nx/commit/ac53e9a624b01cf71f88eb412678ffc48125ff38) - ["ng test" should not compile e2e tests](https://github.com/nrwl/nx/commit/ac53e9a624b01cf71f88eb412678ffc48125ff38)
* [Fix 'npm run format'](https://github.com/nrwl/nx/commit/670cd57dfa7d5bbf6b9af4e52f2c7d40081138cb) - [Fix 'npm run format'](https://github.com/nrwl/nx/commit/670cd57dfa7d5bbf6b9af4e52f2c7d40081138cb)
# 0.5.0 # 0.5.0
## Features ## Features
* [Update the workspace to use Angular 5.1 and CLI 1.6](https://github.com/nrwl/nx/commit/a477bb9fc953e3b44d696945cc259119f701fb78) - [Update the workspace to use Angular 5.1 and CLI 1.6](https://github.com/nrwl/nx/commit/a477bb9fc953e3b44d696945cc259119f701fb78)
# 0.4.0 # 0.4.0
## Features ## Features
* [Add support for generating nested apps and libs](https://github.com/nrwl/nx/commit/013a828d1e31f55a9b3f7c69587316890ea834d4) - [Add support for generating nested apps and libs](https://github.com/nrwl/nx/commit/013a828d1e31f55a9b3f7c69587316890ea834d4)
* [Update NgRx schematic to allow the customization of the state folder](https://github.com/nrwl/nx/commit/a2d02652665f497be8958efc403d7a44bd831088) - [Update NgRx schematic to allow the customization of the state folder](https://github.com/nrwl/nx/commit/a2d02652665f497be8958efc403d7a44bd831088)
## Bug Fixes ## Bug Fixes
* [Only begin converting to workspace once files have been checked](https://github.com/nrwl/nx/commit/e7fd6b1e04f3f3387a91c53a7ac479fe72bdd72e) - [Only begin converting to workspace once files have been checked](https://github.com/nrwl/nx/commit/e7fd6b1e04f3f3387a91c53a7ac479fe72bdd72e)
* ["ng build" should only recompile the selected app](https://github.com/nrwl/nx/commit/550de7bb80f4d3f306c23fac70db52c98dadcd05) - ["ng build" should only recompile the selected app](https://github.com/nrwl/nx/commit/550de7bb80f4d3f306c23fac70db52c98dadcd05)
## Refactoring ## Refactoring
* [Eliminated single letter variable names in effects template](https://github.com/nrwl/nx/commit/996143cf60bac1a57629815d5756db9ce23193ab) - [Eliminated single letter variable names in effects template](https://github.com/nrwl/nx/commit/996143cf60bac1a57629815d5756db9ce23193ab)
# 0.3.0 # 0.3.0
@ -384,33 +384,33 @@ We want to be able to add new features to Nx without breaking existing workspace
## Features ## Features
* [add `allow` option to whitelist deep imports](https://github.com/nrwl/nx/commit/b3f67351fe8890e06402672e687b1789f279613b) - [add `allow` option to whitelist deep imports](https://github.com/nrwl/nx/commit/b3f67351fe8890e06402672e687b1789f279613b)
* [Added the nx-migrate command](https://github.com/nrwl/nx/commit/d6e66b99316181b8b67805b91cc35457c3465029) - [Added the nx-migrate command](https://github.com/nrwl/nx/commit/d6e66b99316181b8b67805b91cc35457c3465029)
* [Upgrade Prettier to 1.8.2](https://github.com/nrwl/nx/commit/cc2277e91be2ca49fb1588f1d8e29ef91fd12044) - [Upgrade Prettier to 1.8.2](https://github.com/nrwl/nx/commit/cc2277e91be2ca49fb1588f1d8e29ef91fd12044)
* [Update readme to point to example apps using Nx](https://github.com/nrwl/nx/commit/3d53e31391d5d79a0724d099c7121edb53e8b163) - [Update readme to point to example apps using Nx](https://github.com/nrwl/nx/commit/3d53e31391d5d79a0724d099c7121edb53e8b163)
# 0.2.2 # 0.2.2
## Bug Fixes ## Bug Fixes
* [Adds a schema file to allow custom properties that the default CLI distribution does not support](https://github.com/nrwl/nx/commit/7fd7594e673cf38af7668b891ed7c75b390b3330) - [Adds a schema file to allow custom properties that the default CLI distribution does not support](https://github.com/nrwl/nx/commit/7fd7594e673cf38af7668b891ed7c75b390b3330)
* [Fix issue with generating a wrong full path on windows](https://github.com/nrwl/nx/commit/11e6c055ba1211a5bee1cc73d46663985645f08e) - [Fix issue with generating a wrong full path on windows](https://github.com/nrwl/nx/commit/11e6c055ba1211a5bee1cc73d46663985645f08e)
# 0.2.1 # 0.2.1
## New Features ## New Features
* [Export jasmine-marbles getTestScheduler and time functions](https://github.com/nrwl/nx/commit/2e4613f475fc2673731540fb4724d6ba2af02aae) - [Export jasmine-marbles getTestScheduler and time functions](https://github.com/nrwl/nx/commit/2e4613f475fc2673731540fb4724d6ba2af02aae)
* [Use fetch instead of optimisticUpdate in the generated effect classes](https://github.com/nrwl/nx/commit/c9759cc4427283422e906ed19a8a2dabcb2a656b) - [Use fetch instead of optimisticUpdate in the generated effect classes](https://github.com/nrwl/nx/commit/c9759cc4427283422e906ed19a8a2dabcb2a656b)
## Bug Fixes ## Bug Fixes
* [--routing should add RouterTestingModule](https://github.com/nrwl/nx/commit/d7fc5b56054c9a4c1fbb12845bfc0803f9a9ff86) - [--routing should add RouterTestingModule](https://github.com/nrwl/nx/commit/d7fc5b56054c9a4c1fbb12845bfc0803f9a9ff86)
* [Fix wording in the documentation](https://github.com/nrwl/nx/commit/058c8995f35a9e677f88404bc9c8a2b177487080) - [Fix wording in the documentation](https://github.com/nrwl/nx/commit/058c8995f35a9e677f88404bc9c8a2b177487080)
## Refactorings ## Refactorings
* [Refactor Nx to use RxJS lettable operators](https://github.com/nrwl/nx/commit/715efa4b225b65be0052a1e6a88c5bdcd5a6cf38) - [Refactor Nx to use RxJS lettable operators](https://github.com/nrwl/nx/commit/715efa4b225b65be0052a1e6a88c5bdcd5a6cf38)
# 0.2.0 # 0.2.0

View File

@ -56,12 +56,12 @@ Before you submit an issue, please search the issue tracker, maybe an issue for
We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. Having a reproducible scenario gives us wealth of important information without going back & forth to you with additional questions like: We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. Having a reproducible scenario gives us wealth of important information without going back & forth to you with additional questions like:
* version of Nx used - version of Nx used
* version of Angular CLI used - version of Angular CLI used
* `angular.json` configuration - `angular.json` configuration
* version of Angular DevKit used - version of Angular DevKit used
* 3rd-party libraries and their versions - 3rd-party libraries and their versions
* and most importantly - a use-case that fails - and most importantly - a use-case that fails
A minimal reproduce scenario using allows us to quickly confirm a bug (or point out coding problem) as well as confirm that we are fixing the right problem. A minimal reproduce scenario using allows us to quickly confirm a bug (or point out coding problem) as well as confirm that we are fixing the right problem.
@ -73,10 +73,10 @@ You can file new issues by filling out our [issue form](https://github.com/nrwl/
Please follow the following guidelines: Please follow the following guidelines:
* Make sure unit tests pass - Make sure unit tests pass
* Make sure e2e tests pass - Make sure e2e tests pass
* Make sure you run `yarn format` - Make sure you run `yarn format`
* Update your commit message to follow the guidelines below - Update your commit message to follow the guidelines below
#### Commit Message Guidelines #### Commit Message Guidelines
@ -92,21 +92,21 @@ body
The type must be one of the following: The type must be one of the following:
* build - build
* feat - feat
* fix - fix
* refactor - refactor
* style - style
* docs - docs
* test - test
##### Scope ##### Scope
The scope must be one of the following: The scope must be one of the following:
* bazel - bazel
* nx - nx
* schematics - schematics
##### Subject ##### Subject

View File

@ -24,17 +24,17 @@ Nx is an extension for the the Angular CLI implementing the monorepo-style devel
Because Nx is built on top of the Angular CLI, you get all the conventional and loved features plus: Because Nx is built on top of the Angular CLI, you get all the conventional and loved features plus:
* Nx Workspace - Nx Workspace
* Workspace-Specific Schematics - Workspace-Specific Schematics
* Intelligent Builds and Unit Testing - Intelligent Builds and Unit Testing
* State Management - State Management
* NgRx - NgRx
* Data Persistence - Data Persistence
* Linters - Linters
* Code Formatter - Code Formatter
* UpgradeModule and downgradeModule helpers - UpgradeModule and downgradeModule helpers
* Jest unit tests integration - Jest unit tests integration
* Node build tooling - Node build tooling
# Why Nx? # Why Nx?
@ -42,8 +42,8 @@ On the surface, large and small organizations care about the same things: consis
Whats different about large organizations is that they have hundreds of Angular engineers building dozens of apps. So they have a lot of code, which changes everything. Whats different about large organizations is that they have hundreds of Angular engineers building dozens of apps. So they have a lot of code, which changes everything.
* While ten (10) developers can reach a consensus on best practices by chatting over lunch, five hundred (500) developers cannot. You have to establish best practices, team standards, **and use tools to promote them**. - While ten (10) developers can reach a consensus on best practices by chatting over lunch, five hundred (500) developers cannot. You have to establish best practices, team standards, **and use tools to promote them**.
* With three (3) projects developers will know what needs to be retested after making a change, with thirty (30) projects, however, this is no longer a simple process. Informal team rules to manage change will no longer work with large teams and multi-team, multi-project efforts. **You have to rely on the automated CI process instead.** - With three (3) projects developers will know what needs to be retested after making a change, with thirty (30) projects, however, this is no longer a simple process. Informal team rules to manage change will no longer work with large teams and multi-team, multi-project efforts. **You have to rely on the automated CI process instead.**
In other words, small organizations can often get by with informal ad-hoc processes, whereas large organizations cannot. **Large organizations must rely on tooling to enable that. Nx is this tooling.** In other words, small organizations can often get by with informal ad-hoc processes, whereas large organizations cannot. **Large organizations must rely on tooling to enable that. Nx is this tooling.**
@ -81,30 +81,30 @@ You are good to go!
### Documentation ### Documentation
* [Nx Documentation and Guides](https://nrwl.io/nx) - [Nx Documentation and Guides](https://nrwl.io/nx)
* [Nx blog posts](https://blog.nrwl.io/nx/home) - [Nx blog posts](https://blog.nrwl.io/nx/home)
### Videos ### Videos
* [5-minute video on how to get started with Nx.](http://nrwl.io/nx) - [5-minute video on how to get started with Nx.](http://nrwl.io/nx)
* [Video course on using Nx Workspaces](https://angularplaybook.com/p/nx-workspaces) - [Video course on using Nx Workspaces](https://angularplaybook.com/p/nx-workspaces)
### Talks ### Talks
* [Angular at Large Organizations](https://www.youtube.com/watch?v=piQ0EZhtus0) - [Angular at Large Organizations](https://www.youtube.com/watch?v=piQ0EZhtus0)
* [Nx: The New Way to Build Enterprise Angular Apps](https://www.youtube.com/watch?v=xo-1SDmvM8Y) - [Nx: The New Way to Build Enterprise Angular Apps](https://www.youtube.com/watch?v=xo-1SDmvM8Y)
* [Supercharging the Angular CLI](https://www.youtube.com/watch?v=bMkKz8AedHc) - [Supercharging the Angular CLI](https://www.youtube.com/watch?v=bMkKz8AedHc)
* [Hands on Full Stack development with Nx and Bazel](https://www.youtube.com/watch?v=1KDDIhcQORM) - [Hands on Full Stack development with Nx and Bazel](https://www.youtube.com/watch?v=1KDDIhcQORM)
### Podcasts and Shows ### Podcasts and Shows
* [ngAir 140: Nx for Enterprise Angular Development](https://www.youtube.com/watch?v=qYNiOKDno_I) - [ngAir 140: Nx for Enterprise Angular Development](https://www.youtube.com/watch?v=qYNiOKDno_I)
* [ngHouston: NX Demo](https://www.youtube.com/watch?v=E_UlU2Yv4G0) - [ngHouston: NX Demo](https://www.youtube.com/watch?v=E_UlU2Yv4G0)
## Misc ## Misc
* [nx-examples](https://github.com/nrwl/nx-examples) repo has branches for different nx comments to display expected behavior and example app and libraries. Check out the branch (workspace, ngrx...) to see what gets created for you. More info on readme. - [nx-examples](https://github.com/nrwl/nx-examples) repo has branches for different nx comments to display expected behavior and example app and libraries. Check out the branch (workspace, ngrx...) to see what gets created for you. More info on readme.
* [xplat - Cross-platform tools for Nx workspaces](https://nstudio.io/xplat/) - [xplat - Cross-platform tools for Nx workspaces](https://nstudio.io/xplat/)
## Want to help? ## Want to help?

View File

@ -10,20 +10,18 @@ import {
} from '../utils'; } from '../utils';
describe('Affected', () => { describe('Affected', () => {
it( it('should print, build, and test affected apps', () => {
'should print, build, and test affected apps', newProject();
() => { newApp('myapp');
newProject(); newApp('myapp2');
newApp('myapp'); newLib('mylib');
newApp('myapp2'); newLib('mylib2');
newLib('mylib'); newLib('mypublishablelib --publishable');
newLib('mylib2'); copyMissingPackages();
newLib('mypublishablelib --publishable');
copyMissingPackages();
updateFile( updateFile(
'apps/myapp/src/app/app.component.spec.ts', 'apps/myapp/src/app/app.component.spec.ts',
` `
import '@proj/mylib'; import '@proj/mylib';
describe('sample test', () => { describe('sample test', () => {
it('should test', () => { it('should test', () => {
@ -31,10 +29,10 @@ describe('Affected', () => {
}); });
}); });
` `
); );
updateFile( updateFile(
'libs/mypublishablelib/src/lib/mypublishablelib.module.spec.ts', 'libs/mypublishablelib/src/lib/mypublishablelib.module.spec.ts',
` `
import '@proj/mylib'; import '@proj/mylib';
describe('sample test', () => { describe('sample test', () => {
it('should test', () => { it('should test', () => {
@ -42,208 +40,200 @@ describe('Affected', () => {
}); });
}); });
` `
); );
const affectedApps = runCommand( const affectedApps = runCommand(
'npm run affected:apps -- --files="libs/mylib/src/index.ts"' 'npm run affected:apps -- --files="libs/mylib/src/index.ts"'
); );
expect(affectedApps).toContain('myapp'); expect(affectedApps).toContain('myapp');
expect(affectedApps).not.toContain('myapp2'); expect(affectedApps).not.toContain('myapp2');
expect(affectedApps).not.toContain('myapp-e2e'); expect(affectedApps).not.toContain('myapp-e2e');
const implicitlyAffectedApps = runCommand( const implicitlyAffectedApps = runCommand(
'npm run affected:apps -- --files="package.json"' 'npm run affected:apps -- --files="package.json"'
); );
expect(implicitlyAffectedApps).toContain('myapp'); expect(implicitlyAffectedApps).toContain('myapp');
expect(implicitlyAffectedApps).toContain('myapp2'); expect(implicitlyAffectedApps).toContain('myapp2');
const noAffectedApps = runCommand( const noAffectedApps = runCommand(
'npm run affected:apps -- --files="README.md"' 'npm run affected:apps -- --files="README.md"'
); );
expect(noAffectedApps).not.toContain('myapp'); expect(noAffectedApps).not.toContain('myapp');
expect(noAffectedApps).not.toContain('myapp2'); expect(noAffectedApps).not.toContain('myapp2');
const affectedLibs = runCommand( const affectedLibs = runCommand(
'npm run affected:libs -- --files="libs/mylib/src/index.ts"' 'npm run affected:libs -- --files="libs/mylib/src/index.ts"'
); );
expect(affectedLibs).toContain('mypublishablelib'); expect(affectedLibs).toContain('mypublishablelib');
expect(affectedLibs).toContain('mylib'); expect(affectedLibs).toContain('mylib');
expect(affectedLibs).not.toContain('mylib2'); expect(affectedLibs).not.toContain('mylib2');
const implicitlyAffectedLibs = runCommand( const implicitlyAffectedLibs = runCommand(
'npm run affected:libs -- --files="package.json"' 'npm run affected:libs -- --files="package.json"'
); );
expect(implicitlyAffectedLibs).toContain('mypublishablelib'); expect(implicitlyAffectedLibs).toContain('mypublishablelib');
expect(implicitlyAffectedLibs).toContain('mylib'); expect(implicitlyAffectedLibs).toContain('mylib');
expect(implicitlyAffectedLibs).toContain('mylib2'); expect(implicitlyAffectedLibs).toContain('mylib2');
const noAffectedLibs = runCommand( const noAffectedLibs = runCommand(
'npm run affected:libs -- --files="README.md"' 'npm run affected:libs -- --files="README.md"'
); );
expect(noAffectedLibs).not.toContain('mypublishablelib'); expect(noAffectedLibs).not.toContain('mypublishablelib');
expect(noAffectedLibs).not.toContain('mylib'); expect(noAffectedLibs).not.toContain('mylib');
expect(noAffectedLibs).not.toContain('mylib2'); expect(noAffectedLibs).not.toContain('mylib2');
const build = runCommand( const build = runCommand(
'npm run affected:build -- --files="libs/mylib/src/index.ts"' 'npm run affected:build -- --files="libs/mylib/src/index.ts"'
); );
expect(build).toContain('Running build for mypublishablelib'); expect(build).toContain('Running build for mypublishablelib');
expect(build).toContain('Running build for myapp'); expect(build).toContain('Running build for myapp');
expect(build).not.toContain('is not registered with the build command'); expect(build).not.toContain('is not registered with the build command');
expect(build).not.toContain('with flags:'); expect(build).not.toContain('with flags:');
// Should work in parallel // Should work in parallel
const buildParallel = runCommand( const buildParallel = runCommand(
'npm run affected:build -- --files="libs/mylib/src/index.ts" --parallel' 'npm run affected:build -- --files="libs/mylib/src/index.ts" --parallel'
); );
expect(buildParallel).toContain( expect(buildParallel).toContain(
'Running build for projects:\n myapp,\n mypublishablelib' 'Running build for projects:\n myapp,\n mypublishablelib'
); );
expect(buildParallel).toContain( expect(buildParallel).toContain(
'Running build for affected projects succeeded.' 'Running build for affected projects succeeded.'
); );
const buildExcluded = runCommand( const buildExcluded = runCommand(
'npm run affected:build -- --files="libs/mylib/src/index.ts" --exclude myapp' 'npm run affected:build -- --files="libs/mylib/src/index.ts" --exclude myapp'
); );
expect(buildExcluded).toContain('Running build for mypublishablelib'); expect(buildExcluded).toContain('Running build for mypublishablelib');
const buildExcludedCsv = runCommand( const buildExcludedCsv = runCommand(
'npm run affected:build -- --files="package.json" --exclude myapp,myapp2,mypublishablelib' 'npm run affected:build -- --files="package.json" --exclude myapp,myapp2,mypublishablelib'
); );
expect(buildExcludedCsv).toContain('No projects to run build'); expect(buildExcludedCsv).toContain('No projects to run build');
// affected:build should pass non-nx flags to the CLI // affected:build should pass non-nx flags to the CLI
const buildWithFlags = runCommand( const buildWithFlags = runCommand(
'npm run affected:build -- --files="libs/mylib/src/index.ts" --stats-json' 'npm run affected:build -- --files="libs/mylib/src/index.ts" --stats-json'
); );
expect(buildWithFlags).toContain('Running build for mypublishablelib'); expect(buildWithFlags).toContain('Running build for mypublishablelib');
expect(buildWithFlags).toContain('Running build for myapp'); expect(buildWithFlags).toContain('Running build for myapp');
expect(buildWithFlags).toContain('With flags: --stats-json=true'); expect(buildWithFlags).toContain('With flags: --stats-json=true');
const e2e = runCommand( const e2e = runCommand(
'npm run affected:e2e -- --files="libs/mylib/src/index.ts"' 'npm run affected:e2e -- --files="libs/mylib/src/index.ts"'
); );
expect(e2e).toContain('should display welcome message'); expect(e2e).toContain('should display welcome message');
const unitTests = runCommand( const unitTests = runCommand(
'npm run affected:test -- --files="libs/mylib/src/index.ts"' 'npm run affected:test -- --files="libs/mylib/src/index.ts"'
); );
expect(unitTests).toContain('Running test for mylib'); expect(unitTests).toContain('Running test for mylib');
expect(unitTests).toContain('Running test for mypublishablelib'); expect(unitTests).toContain('Running test for mypublishablelib');
expect(unitTests).toContain('Running test for myapp'); expect(unitTests).toContain('Running test for myapp');
// Fail a Unit Test // Fail a Unit Test
updateFile( updateFile(
'apps/myapp/src/app/app.component.spec.ts', 'apps/myapp/src/app/app.component.spec.ts',
readFile('apps/myapp/src/app/app.component.spec.ts').replace( readFile('apps/myapp/src/app/app.component.spec.ts').replace(
'.toEqual(1)', '.toEqual(1)',
'.toEqual(2)' '.toEqual(2)'
) )
); );
const failedTests = runCommand( const failedTests = runCommand(
'npm run affected:test -- --files="libs/mylib/src/index.ts"' 'npm run affected:test -- --files="libs/mylib/src/index.ts"'
); );
expect(failedTests).toContain('Running test for mylib'); expect(failedTests).toContain('Running test for mylib');
expect(failedTests).toContain('Running test for mypublishablelib'); expect(failedTests).toContain('Running test for mypublishablelib');
expect(failedTests).toContain('Running test for myapp'); expect(failedTests).toContain('Running test for myapp');
expect(failedTests).toContain('Failed projects: myapp'); expect(failedTests).toContain('Failed projects: myapp');
expect(failedTests).toContain( expect(failedTests).toContain(
'You can isolate the above projects by passing --only-failed' 'You can isolate the above projects by passing --only-failed'
); );
expect(readJson('dist/.nx-results')).toEqual({ expect(readJson('dist/.nx-results')).toEqual({
command: 'test', command: 'test',
results: { results: {
myapp: false, myapp: false,
mylib: true, mylib: true,
mypublishablelib: true mypublishablelib: true
} }
}); });
// Fix failing Unit Test // Fix failing Unit Test
updateFile( updateFile(
'apps/myapp/src/app/app.component.spec.ts', 'apps/myapp/src/app/app.component.spec.ts',
readFile('apps/myapp/src/app/app.component.spec.ts').replace( readFile('apps/myapp/src/app/app.component.spec.ts').replace(
'.toEqual(2)', '.toEqual(2)',
'.toEqual(1)' '.toEqual(1)'
) )
); );
const isolatedTests = runCommand( const isolatedTests = runCommand(
'npm run affected:test -- --files="libs/mylib/src/index.ts" --only-failed' 'npm run affected:test -- --files="libs/mylib/src/index.ts" --only-failed'
); );
expect(isolatedTests).toContain('Running test for myapp'); expect(isolatedTests).toContain('Running test for myapp');
const linting = runCommand( const linting = runCommand(
'npm run affected:lint -- --files="libs/mylib/src/index.ts"' 'npm run affected:lint -- --files="libs/mylib/src/index.ts"'
); );
expect(linting).toContain('Running lint for mylib'); expect(linting).toContain('Running lint for mylib');
expect(linting).toContain('Running lint for myapp'); expect(linting).toContain('Running lint for myapp');
expect(linting).toContain('Running lint for myapp-e2e'); expect(linting).toContain('Running lint for myapp-e2e');
expect(linting).toContain('Running lint for mypublishablelib'); expect(linting).toContain('Running lint for mypublishablelib');
const lintWithJsonFormating = runCommand( const lintWithJsonFormating = runCommand(
'npm run affected:lint -- --files="libs/mylib/src/index.ts" -- --format json' 'npm run affected:lint -- --files="libs/mylib/src/index.ts" -- --format json'
); );
expect(lintWithJsonFormating).toContain('With flags: --format json'); expect(lintWithJsonFormating).toContain('With flags: --format json');
const unitTestsExcluded = runCommand( const unitTestsExcluded = runCommand(
'npm run affected:test -- --files="libs/mylib/src/index.ts" --exclude=myapp,mypublishablelib' 'npm run affected:test -- --files="libs/mylib/src/index.ts" --exclude=myapp,mypublishablelib'
); );
expect(unitTestsExcluded).toContain('Running test for mylib'); expect(unitTestsExcluded).toContain('Running test for mylib');
const i18n = runCommand( const i18n = runCommand(
'npm run affected -- --target extract-i18n --files="libs/mylib/src/index.ts"' 'npm run affected -- --target extract-i18n --files="libs/mylib/src/index.ts"'
); );
expect(i18n).toContain('Running extract-i18n for myapp'); expect(i18n).toContain('Running extract-i18n for myapp');
}, }, 1000000);
1000000
);
it( it('should print, build, and test all apps', () => {
'should print, build, and test all apps', newProject();
() => { newApp('myapp');
newProject(); newApp('myapp2');
newApp('myapp'); newLib('mylib');
newApp('myapp2'); newLib('mypublishablelib --publishable');
newLib('mylib'); copyMissingPackages();
newLib('mypublishablelib --publishable');
copyMissingPackages();
const affectedApps = runCommand('npm run affected:apps -- --all'); const affectedApps = runCommand('npm run affected:apps -- --all');
expect(affectedApps).toContain('myapp'); expect(affectedApps).toContain('myapp');
expect(affectedApps).toContain('myapp2'); expect(affectedApps).toContain('myapp2');
expect(affectedApps).not.toContain('myapp-e2e'); expect(affectedApps).not.toContain('myapp-e2e');
const build = runCommand('npm run affected:build -- --all'); const build = runCommand('npm run affected:build -- --all');
expect(build).toContain('Running build for myapp'); expect(build).toContain('Running build for myapp');
expect(build).toContain('Running build for myapp2'); expect(build).toContain('Running build for myapp2');
expect(build).toContain('Running build for mypublishablelib'); expect(build).toContain('Running build for mypublishablelib');
expect(build).not.toContain('is not registered with the build command'); expect(build).not.toContain('is not registered with the build command');
const buildExcluded = runCommand( const buildExcluded = runCommand(
'npm run affected:build -- --files="libs/mylib/src/index.ts" --exclude myapp,myapp2,mypublishablelib' 'npm run affected:build -- --files="libs/mylib/src/index.ts" --exclude myapp,myapp2,mypublishablelib'
); );
expect(buildExcluded).toContain('No projects to run build'); expect(buildExcluded).toContain('No projects to run build');
const e2e = runCommand('npm run affected:e2e -- --all'); const e2e = runCommand('npm run affected:e2e -- --all');
expect(e2e).toContain('Running e2e for myapp-e2e'); expect(e2e).toContain('Running e2e for myapp-e2e');
expect(e2e).toContain('Running e2e for myapp2-e2e'); expect(e2e).toContain('Running e2e for myapp2-e2e');
const unitTests = runCommand('npm run affected:test -- --all'); const unitTests = runCommand('npm run affected:test -- --all');
expect(unitTests).toContain('Running test for mypublishablelib'); expect(unitTests).toContain('Running test for mypublishablelib');
expect(unitTests).toContain('Running test for myapp2'); expect(unitTests).toContain('Running test for myapp2');
expect(unitTests).toContain('Running test for myapp'); expect(unitTests).toContain('Running test for myapp');
expect(unitTests).toContain('Running test for mylib'); expect(unitTests).toContain('Running test for mylib');
const i18n = runCommand( const i18n = runCommand('npm run affected -- --target extract-i18n --all');
'npm run affected -- --target extract-i18n --all' expect(i18n).toContain('Running extract-i18n for myapp2');
); expect(i18n).toContain('Running extract-i18n for myapp');
expect(i18n).toContain('Running extract-i18n for myapp2'); }, 1000000);
expect(i18n).toContain('Running extract-i18n for myapp');
},
1000000
);
}); });

View File

@ -1,17 +1,13 @@
import { checkFilesExist, newApp, newBazelProject, newLib } from '../utils'; import { checkFilesExist, newApp, newBazelProject, newLib } from '../utils';
xdescribe('Nrwl Workspace (Bazel)', () => { xdescribe('Nrwl Workspace (Bazel)', () => {
it( it('should work', () => {
'should work', newBazelProject();
() => { newApp('myApp --directory=myDir');
newBazelProject(); newLib('myLib --directory=myDir');
newApp('myApp --directory=myDir');
newLib('myLib --directory=myDir');
checkFilesExist('WORKSPACE', 'BUILD.bazel'); checkFilesExist('WORKSPACE', 'BUILD.bazel');
}, }, 1000000);
1000000
);
}); });
// afterEach(() => { // afterEach(() => {

View File

@ -12,27 +12,25 @@ import {
} from '../utils'; } from '../utils';
describe('Command line', () => { describe('Command line', () => {
it( it('lint should ensure module boundaries', () => {
'lint should ensure module boundaries', newProject();
() => { newApp('myapp --tags=validtag');
newProject(); newApp('myapp2');
newApp('myapp --tags=validtag'); newLib('mylib');
newApp('myapp2'); newLib('lazylib');
newLib('mylib'); newLib('invalidtaglib --tags=invalidtag');
newLib('lazylib'); newLib('validtaglib --tags=validtag');
newLib('invalidtaglib --tags=invalidtag');
newLib('validtaglib --tags=validtag');
const tslint = readJson('tslint.json'); const tslint = readJson('tslint.json');
tslint.rules['nx-enforce-module-boundaries'][1].depConstraints = [ tslint.rules['nx-enforce-module-boundaries'][1].depConstraints = [
{ sourceTag: 'validtag', onlyDependOnLibsWithTags: ['validtag'] }, { sourceTag: 'validtag', onlyDependOnLibsWithTags: ['validtag'] },
...tslint.rules['nx-enforce-module-boundaries'][1].depConstraints ...tslint.rules['nx-enforce-module-boundaries'][1].depConstraints
]; ];
updateFile('tslint.json', JSON.stringify(tslint, null, 2)); updateFile('tslint.json', JSON.stringify(tslint, null, 2));
updateFile( updateFile(
'apps/myapp/src/main.ts', 'apps/myapp/src/main.ts',
` `
import '../../../libs/mylib'; import '../../../libs/mylib';
import '@proj/lazylib'; import '@proj/lazylib';
import '@proj/mylib/deep'; import '@proj/mylib/deep';
@ -42,19 +40,17 @@ describe('Command line', () => {
const s = {loadChildren: '@proj/lazylib'}; const s = {loadChildren: '@proj/lazylib'};
` `
); );
const out = runCLI('lint', { silenceError: true }); const out = runCLI('lint', { silenceError: true });
expect(out).toContain('library imports must start with @proj/'); expect(out).toContain('library imports must start with @proj/');
expect(out).toContain('imports of lazy-loaded libraries are forbidden'); expect(out).toContain('imports of lazy-loaded libraries are forbidden');
expect(out).toContain('deep imports into libraries are forbidden'); expect(out).toContain('deep imports into libraries are forbidden');
expect(out).toContain('imports of apps are forbidden'); expect(out).toContain('imports of apps are forbidden');
expect(out).toContain( expect(out).toContain(
'A project tagged with "validtag" can only depend on libs tagged with "validtag"' 'A project tagged with "validtag" can only depend on libs tagged with "validtag"'
); );
}, }, 1000000);
1000000
);
it('should run nx lint', () => { it('should run nx lint', () => {
newProject(); newProject();
@ -81,121 +77,113 @@ describe('Command line', () => {
); );
}); });
it( it('format should check and reformat the code', () => {
'format should check and reformat the code', newProject();
() => { newApp('myapp');
newProject(); newLib('mylib');
newApp('myapp'); updateFile(
newLib('mylib'); 'apps/myapp/src/main.ts',
updateFile( `
'apps/myapp/src/main.ts',
`
const x = 1111; const x = 1111;
` `
); );
updateFile( updateFile(
'apps/myapp/src/app/app.module.ts', 'apps/myapp/src/app/app.module.ts',
` `
const y = 1111; const y = 1111;
` `
); );
updateFile( updateFile(
'apps/myapp/src/app/app.component.ts', 'apps/myapp/src/app/app.component.ts',
` `
const z = 1111; const z = 1111;
` `
); );
updateFile( updateFile(
'libs/mylib/index.ts', 'libs/mylib/index.ts',
` `
const x = 1111; const x = 1111;
` `
); );
updateFile( updateFile(
'libs/mylib/src/mylib.module.ts', 'libs/mylib/src/mylib.module.ts',
` `
const y = 1111; const y = 1111;
` `
); );
let stdout = runCommand( let stdout = runCommand(
'npm run -s format:check -- --files="libs/mylib/index.ts" --libs-and-apps' 'npm run -s format:check -- --files="libs/mylib/index.ts" --libs-and-apps'
); );
expect(stdout).toContain('libs/mylib/index.ts'); expect(stdout).toContain('libs/mylib/index.ts');
expect(stdout).toContain('libs/mylib/src/mylib.module.ts'); expect(stdout).toContain('libs/mylib/src/mylib.module.ts');
stdout = runCommand('npm run -s format:check'); stdout = runCommand('npm run -s format:check');
expect(stdout).toContain('apps/myapp/src/main.ts'); expect(stdout).toContain('apps/myapp/src/main.ts');
expect(stdout).toContain('apps/myapp/src/app/app.module.ts'); expect(stdout).toContain('apps/myapp/src/app/app.module.ts');
expect(stdout).toContain('apps/myapp/src/app/app.component.ts'); expect(stdout).toContain('apps/myapp/src/app/app.component.ts');
runCommand( runCommand(
'npm run format:write -- --files="apps/myapp/src/app/app.module.ts,apps/myapp/src/app/app.component.ts"' 'npm run format:write -- --files="apps/myapp/src/app/app.module.ts,apps/myapp/src/app/app.component.ts"'
); );
stdout = runCommand('npm run -s format:check'); stdout = runCommand('npm run -s format:check');
expect(stdout).toContain('apps/myapp/src/main.ts'); expect(stdout).toContain('apps/myapp/src/main.ts');
expect(stdout).not.toContain('apps/myapp/src/app/app.module.ts'); expect(stdout).not.toContain('apps/myapp/src/app/app.module.ts');
expect(stdout).not.toContain('apps/myapp/src/app/app.component.ts'); expect(stdout).not.toContain('apps/myapp/src/app/app.component.ts');
runCommand('npm run format:write'); runCommand('npm run format:write');
expect(runCommand('npm run -s format:check')).toEqual(''); expect(runCommand('npm run -s format:check')).toEqual('');
}, }, 1000000);
1000000
);
it( it('should support workspace-specific schematics', () => {
'should support workspace-specific schematics', newProject();
() => { runCLI('g workspace-schematic custom');
newProject(); checkFilesExist(
runCLI('g workspace-schematic custom'); 'tools/schematics/custom/index.ts',
checkFilesExist( 'tools/schematics/custom/schema.json'
'tools/schematics/custom/index.ts', );
'tools/schematics/custom/schema.json'
);
const json = readJson('tools/schematics/custom/schema.json'); const json = readJson('tools/schematics/custom/schema.json');
json.properties['directory'] = { json.properties['directory'] = {
type: 'string', type: 'string',
description: 'lib directory' description: 'lib directory'
}; };
updateFile('tools/schematics/custom/schema.json', JSON.stringify(json)); updateFile('tools/schematics/custom/schema.json', JSON.stringify(json));
const indexFile = readFile('tools/schematics/custom/index.ts'); const indexFile = readFile('tools/schematics/custom/index.ts');
updateFile( updateFile(
'tools/schematics/custom/index.ts', 'tools/schematics/custom/index.ts',
indexFile.replace( indexFile.replace(
'name: schema.name', 'name: schema.name',
'name: schema.name, directory: schema.directory' 'name: schema.name, directory: schema.directory'
) )
); );
const dryRunOutput = runCommand( const dryRunOutput = runCommand(
'npm run workspace-schematic custom mylib -- --directory=dir -d' 'npm run workspace-schematic custom mylib -- --directory=dir -d'
); );
expect(exists('libs/dir/mylib/src/index.ts')).toEqual(false); expect(exists('libs/dir/mylib/src/index.ts')).toEqual(false);
expect(dryRunOutput).toContain( expect(dryRunOutput).toContain(
'create libs/dir/mylib/src/lib/dir-mylib.module.ts' 'create libs/dir/mylib/src/lib/dir-mylib.module.ts'
); );
expect(dryRunOutput).toContain('update angular.json'); expect(dryRunOutput).toContain('update angular.json');
expect(dryRunOutput).toContain('update nx.json'); expect(dryRunOutput).toContain('update nx.json');
const output = runCommand( const output = runCommand(
'npm run workspace-schematic custom mylib -- --directory=dir' 'npm run workspace-schematic custom mylib -- --directory=dir'
); );
checkFilesExist('libs/dir/mylib/src/index.ts'); checkFilesExist('libs/dir/mylib/src/index.ts');
expect(output).toContain( expect(output).toContain(
'create libs/dir/mylib/src/lib/dir-mylib.module.ts' 'create libs/dir/mylib/src/lib/dir-mylib.module.ts'
); );
expect(output).toContain('update angular.json'); expect(output).toContain('update angular.json');
expect(output).toContain('update nx.json'); expect(output).toContain('update nx.json');
}, }, 1000000);
1000000
);
describe('dep-graph', () => { describe('dep-graph', () => {
beforeEach(() => { beforeEach(() => {
newProject(); newProject();
@ -225,125 +213,109 @@ describe('Command line', () => {
); );
}); });
it( it('dep-graph should output json (without critical path) to file', () => {
'dep-graph should output json (without critical path) to file', const file = 'dep-graph.json';
() => {
const file = 'dep-graph.json';
runCommand(`npm run dep-graph -- --file="${file}"`); runCommand(`npm run dep-graph -- --file="${file}"`);
expect(() => checkFilesExist(file)).not.toThrow(); expect(() => checkFilesExist(file)).not.toThrow();
const jsonFileContents = readJson(file); const jsonFileContents = readJson(file);
expect(jsonFileContents).toEqual({ expect(jsonFileContents).toEqual({
deps: { deps: {
mylib2: [], mylib2: [],
myapp3: [], myapp3: [],
'myapp3-e2e': [ 'myapp3-e2e': [
{ {
projectName: 'myapp3', projectName: 'myapp3',
type: 'implicit' type: 'implicit'
} }
], ],
myapp2: [ myapp2: [
{ {
projectName: 'mylib', projectName: 'mylib',
type: 'es6Import' type: 'es6Import'
} }
], ],
'myapp2-e2e': [ 'myapp2-e2e': [
{ {
projectName: 'myapp2', projectName: 'myapp2',
type: 'implicit' type: 'implicit'
} }
], ],
mylib: [ mylib: [
{ {
projectName: 'mylib2', projectName: 'mylib2',
type: 'es6Import' type: 'es6Import'
} }
], ],
myapp: [ myapp: [
{ {
projectName: 'mylib', projectName: 'mylib',
type: 'es6Import' type: 'es6Import'
}, },
{ {
projectName: 'mylib2', projectName: 'mylib2',
type: 'loadChildren' type: 'loadChildren'
} }
], ],
'myapp-e2e': [ 'myapp-e2e': [
{ {
projectName: 'myapp', projectName: 'myapp',
type: 'implicit' type: 'implicit'
} }
] ]
}, },
criticalPath: [] criticalPath: []
}); });
}, }, 1000000);
1000000
);
it( it('dep-graph should output json with critical path to file', () => {
'dep-graph should output json with critical path to file', const file = 'dep-graph.json';
() => {
const file = 'dep-graph.json';
runCommand( runCommand(
`npm run affected:dep-graph -- --files="libs/mylib/src/index.ts" --file="${file}"` `npm run affected:dep-graph -- --files="libs/mylib/src/index.ts" --file="${file}"`
); );
expect(() => checkFilesExist(file)).not.toThrow(); expect(() => checkFilesExist(file)).not.toThrow();
const jsonFileContents = readJson(file); const jsonFileContents = readJson(file);
expect(jsonFileContents.criticalPath).toContain('myapp'); expect(jsonFileContents.criticalPath).toContain('myapp');
expect(jsonFileContents.criticalPath).toContain('myapp2'); expect(jsonFileContents.criticalPath).toContain('myapp2');
expect(jsonFileContents.criticalPath).toContain('mylib'); expect(jsonFileContents.criticalPath).toContain('mylib');
expect(jsonFileContents.criticalPath).not.toContain('mylib2'); expect(jsonFileContents.criticalPath).not.toContain('mylib2');
}, }, 1000000);
1000000
);
it( it('dep-graph should output dot to file', () => {
'dep-graph should output dot to file', const file = 'dep-graph.dot';
() => {
const file = 'dep-graph.dot';
runCommand( runCommand(
`npm run dep-graph -- --files="libs/mylib/index.ts" --file="${file}"` `npm run dep-graph -- --files="libs/mylib/index.ts" --file="${file}"`
); );
expect(() => checkFilesExist(file)).not.toThrow(); expect(() => checkFilesExist(file)).not.toThrow();
const fileContents = readFile(file); const fileContents = readFile(file);
expect(fileContents).toContain('"myapp" -> "mylib"'); expect(fileContents).toContain('"myapp" -> "mylib"');
expect(fileContents).toContain('"myapp2" -> "mylib"'); expect(fileContents).toContain('"myapp2" -> "mylib"');
expect(fileContents).toContain('"mylib" -> "mylib2"'); expect(fileContents).toContain('"mylib" -> "mylib2"');
}, }, 1000000);
1000000
);
it( it('dep-graph should output html to file', () => {
'dep-graph should output html to file', const file = 'dep-graph.html';
() => { runCommand(
const file = 'dep-graph.html'; `npm run dep-graph -- --files="libs/mylib/index.ts" --file="${file}"`
runCommand( );
`npm run dep-graph -- --files="libs/mylib/index.ts" --file="${file}"`
);
expect(() => checkFilesExist(file)).not.toThrow(); expect(() => checkFilesExist(file)).not.toThrow();
const fileContents = readFile(file); const fileContents = readFile(file);
expect(fileContents).toContain('<html>'); expect(fileContents).toContain('<html>');
expect(fileContents).toContain('<title>myapp&#45;&gt;mylib</title>'); expect(fileContents).toContain('<title>myapp&#45;&gt;mylib</title>');
expect(fileContents).toContain('<title>myapp&#45;&gt;mylib2</title>'); expect(fileContents).toContain('<title>myapp&#45;&gt;mylib2</title>');
expect(fileContents).toContain('<title>mylib&#45;&gt;mylib2</title>'); expect(fileContents).toContain('<title>mylib&#45;&gt;mylib2</title>');
}, }, 1000000);
1000000
);
}); });
}); });

View File

@ -11,58 +11,50 @@ import {
describe('Cypress E2E Test runner', () => { describe('Cypress E2E Test runner', () => {
describe('project scaffolding', () => { describe('project scaffolding', () => {
it( it('should generate an app with the Cypress as e2e test runner', () => {
'should generate an app with the Cypress as e2e test runner', newProject();
() => { newApp('myApp --e2eTestRunner=cypress');
newProject(); copyMissingPackages();
newApp('myApp --e2eTestRunner=cypress');
copyMissingPackages();
// Making sure the package.json file contains the Cypress dependency // Making sure the package.json file contains the Cypress dependency
const packageJson = readJson('package.json'); const packageJson = readJson('package.json');
expect(packageJson.devDependencies['cypress']).toBeTruthy(); expect(packageJson.devDependencies['cypress']).toBeTruthy();
// Making sure the cypress folders & files are created // Making sure the cypress folders & files are created
checkFilesExist('apps/my-app-e2e/cypress.json'); checkFilesExist('apps/my-app-e2e/cypress.json');
checkFilesExist('apps/my-app-e2e/tsconfig.e2e.json'); checkFilesExist('apps/my-app-e2e/tsconfig.e2e.json');
checkFilesExist('apps/my-app-e2e/src/fixtures/example.json'); checkFilesExist('apps/my-app-e2e/src/fixtures/example.json');
checkFilesExist('apps/my-app-e2e/src/integration/app.spec.ts'); checkFilesExist('apps/my-app-e2e/src/integration/app.spec.ts');
checkFilesExist('apps/my-app-e2e/src/plugins/index.ts'); checkFilesExist('apps/my-app-e2e/src/plugins/index.ts');
checkFilesExist('apps/my-app-e2e/src/support/app.po.ts'); checkFilesExist('apps/my-app-e2e/src/support/app.po.ts');
checkFilesExist('apps/my-app-e2e/src/support/index.ts'); checkFilesExist('apps/my-app-e2e/src/support/index.ts');
checkFilesExist('apps/my-app-e2e/src/support/commands.ts'); checkFilesExist('apps/my-app-e2e/src/support/commands.ts');
}, }, 1000000);
1000000
);
}); });
describe('running Cypress', () => { describe('running Cypress', () => {
it( it('should execute e2e tests using Cypress', () => {
'should execute e2e tests using Cypress', newProject();
() => { newApp('myApp --e2eTestRunner=cypress');
newProject(); copyMissingPackages();
newApp('myApp --e2eTestRunner=cypress');
copyMissingPackages();
expect( expect(
runCLI('e2e --project=my-app-e2e --headless --watch=false') runCLI('e2e --project=my-app-e2e --headless --watch=false')
).toContain('All specs passed!'); ).toContain('All specs passed!');
const originalContents = JSON.parse( const originalContents = JSON.parse(
readFile('apps/my-app-e2e/cypress.json') readFile('apps/my-app-e2e/cypress.json')
); );
delete originalContents.fixtures; delete originalContents.fixtures;
updateFile( updateFile(
'apps/my-app-e2e/cypress.json', 'apps/my-app-e2e/cypress.json',
JSON.stringify(originalContents) JSON.stringify(originalContents)
); );
expect( expect(
runCLI('e2e --project=my-app-e2e --headless --watch=false') runCLI('e2e --project=my-app-e2e --headless --watch=false')
).toContain('All specs passed!'); ).toContain('All specs passed!');
}, }, 1000000);
1000000
);
}); });
}); });

View File

@ -1,24 +1,20 @@
import { newApp, newProject, runCLI, updateFile } from '../utils'; import { newApp, newProject, runCLI, updateFile } from '../utils';
describe('DowngradeModule', () => { describe('DowngradeModule', () => {
it( it('should generate a downgradeModule setup', () => {
'should generate a downgradeModule setup', newProject();
() => { newApp('myapp');
newProject();
newApp('myapp');
updateFile( updateFile(
'apps/myapp/src/legacy.js', 'apps/myapp/src/legacy.js',
`window.angular.module('legacy', []);` `window.angular.module('legacy', []);`
); );
runCLI( runCLI(
'generate downgrade-module legacy --angularJsImport=./legacy --project=myapp' 'generate downgrade-module legacy --angularJsImport=./legacy --project=myapp'
); );
runCLI('build'); runCLI('build');
expect(runCLI('test --no-watch')).toContain('Executed 3 of 3 SUCCESS'); expect(runCLI('test --no-watch')).toContain('Executed 3 of 3 SUCCESS');
}, }, 1000000);
1000000
);
}); });

View File

@ -12,34 +12,26 @@ describe('Jest', () => {
newProject(); newProject();
}); });
it( it('should be able to generate a testable library using jest', async done => {
'should be able to generate a testable library using jest', newLib('jestlib --unit-test-runner jest');
async done => { copyMissingPackages();
newLib('jestlib --unit-test-runner jest'); await Promise.all([
copyMissingPackages(); runCLIAsync('generate service test --project jestlib'),
await Promise.all([ runCLIAsync('generate component test --project jestlib')
runCLIAsync('generate service test --project jestlib'), ]);
runCLIAsync('generate component test --project jestlib') const jestResult = await runCLIAsync('test jestlib');
]); expect(jestResult.stderr).toContain('Test Suites: 3 passed, 3 total');
const jestResult = await runCLIAsync('test jestlib'); done();
expect(jestResult.stderr).toContain('Test Suites: 3 passed, 3 total'); }, 10000);
done();
},
10000
);
it( it('should be able to generate a testable application using jest', async () => {
'should be able to generate a testable application using jest', newApp('jestapp --unit-test-runner jest');
async () => { copyMissingPackages();
newApp('jestapp --unit-test-runner jest'); await Promise.all([
copyMissingPackages(); runCLIAsync('generate service test --project jestapp'),
await Promise.all([ runCLIAsync('generate component test --project jestapp')
runCLIAsync('generate service test --project jestapp'), ]);
runCLIAsync('generate component test --project jestapp') const jestResult = await runCLIAsync('test jestapp');
]); expect(jestResult.stderr).toContain('Test Suites: 3 passed, 3 total');
const jestResult = await runCLIAsync('test jestapp'); }, 10000);
expect(jestResult.stderr).toContain('Test Suites: 3 passed, 3 total');
},
10000
);
}); });

View File

@ -13,16 +13,14 @@ import {
} from '../utils'; } from '../utils';
describe('Nrwl Workspace', () => { describe('Nrwl Workspace', () => {
it( it('should work', () => {
'should work', newProject();
() => { newApp('myApp --directory=myDir');
newProject(); newLib('myLib --directory=myDir');
newApp('myApp --directory=myDir');
newLib('myLib --directory=myDir');
updateFile( updateFile(
'apps/my-dir/my-app/src/app/app.module.ts', 'apps/my-dir/my-app/src/app/app.module.ts',
` `
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { MyDirMyLibModule } from '@proj/my-dir/my-lib'; import { MyDirMyLibModule } from '@proj/my-dir/my-lib';
@ -35,69 +33,57 @@ describe('Nrwl Workspace', () => {
}) })
export class AppModule {} export class AppModule {}
` `
); );
runCLI('build --prod --project=my-dir-my-app --output-hashing none'); runCLI('build --prod --project=my-dir-my-app --output-hashing none');
expect(exists('./tmp/proj/dist/apps/my-dir/my-app/main.js')).toEqual( expect(exists('./tmp/proj/dist/apps/my-dir/my-app/main.js')).toEqual(true);
true
);
// This is a loose requirement because there are a lot of // This is a loose requirement because there are a lot of
// influences external from this project that affect this. // influences external from this project that affect this.
const bundleSize = getSize('./tmp/proj/dist/apps/my-dir/my-app/main.js'); const bundleSize = getSize('./tmp/proj/dist/apps/my-dir/my-app/main.js');
console.log(`The current bundle size is ${bundleSize} KB`); console.log(`The current bundle size is ${bundleSize} KB`);
expect(bundleSize).toBeLessThanOrEqual(200000); expect(bundleSize).toBeLessThanOrEqual(200000);
// running tests for the app // running tests for the app
expect(runCLI('test --project=my-dir-my-app --no-watch')).toContain( expect(runCLI('test --project=my-dir-my-app --no-watch')).toContain(
'Executed 3 of 3 SUCCESS' 'Executed 3 of 3 SUCCESS'
); );
// running tests for the lib // running tests for the lib
expect(runCLI('test --project=my-dir-my-lib --no-watch')).toContain( expect(runCLI('test --project=my-dir-my-lib --no-watch')).toContain(
'Executed 1 of 1 SUCCESS' 'Executed 1 of 1 SUCCESS'
); );
// e2e tests // e2e tests
expect(runCLI('e2e --project=my-dir-my-app-e2e')).toContain( expect(runCLI('e2e --project=my-dir-my-app-e2e')).toContain(
'Executed 1 of 1 spec SUCCESS' 'Executed 1 of 1 spec SUCCESS'
); );
}, }, 1000000);
1000000
);
it( it('should support router config generation (lazy)', () => {
'should support router config generation (lazy)', newProject();
() => { newApp('myApp --directory=myDir --routing');
newProject(); newLib(
newApp('myApp --directory=myDir --routing'); 'myLib --directory=myDir --routing --lazy --parentModule=apps/my-dir/my-app/src/app/app.module.ts'
newLib( );
'myLib --directory=myDir --routing --lazy --parentModule=apps/my-dir/my-app/src/app/app.module.ts'
);
runCLI('build --aot --project=my-dir-my-app'); runCLI('build --aot --project=my-dir-my-app');
expect(runCLI('test --project=my-dir-my-app --no-watch')).toContain( expect(runCLI('test --project=my-dir-my-app --no-watch')).toContain(
'Executed 3 of 3 SUCCESS' 'Executed 3 of 3 SUCCESS'
); );
}, }, 1000000);
1000000
);
it( it('should support router config generation (eager)', () => {
'should support router config generation (eager)', newProject();
() => { newApp('myApp --directory=myDir --routing');
newProject(); newLib(
newApp('myApp --directory=myDir --routing'); 'myLib --directory=myDir --routing --parentModule=apps/my-dir/my-app/src/app/app.module.ts'
newLib( );
'myLib --directory=myDir --routing --parentModule=apps/my-dir/my-app/src/app/app.module.ts'
);
runCLI('build --aot --project=my-dir-my-app'); runCLI('build --aot --project=my-dir-my-app');
expect(runCLI('test --project=my-dir-my-app --no-watch')).toContain( expect(runCLI('test --project=my-dir-my-app --no-watch')).toContain(
'Executed 3 of 3 SUCCESS' 'Executed 3 of 3 SUCCESS'
); );
}, }, 1000000);
1000000
);
it('should support scss for styles', () => { it('should support scss for styles', () => {
cleanup(); cleanup();
@ -119,23 +105,19 @@ describe('Nrwl Workspace', () => {
}); });
// TODO: Fix this test. This test was incorrect before.. and fails after fixing it. // TODO: Fix this test. This test was incorrect before.. and fails after fixing it.
xit( xit('should not generate e2e configuration', () => {
'should not generate e2e configuration', newProject();
() => { newApp('myApp --e2eTestRunner=none');
newProject();
newApp('myApp --e2eTestRunner=none');
// Making sure the angular.json file doesn't contain e2e project // Making sure the angular.json file doesn't contain e2e project
const angularJson = readJson('angular.json'); const angularJson = readJson('angular.json');
expect(angularJson.projects['my-app-e2e']).toBeUndefined(); expect(angularJson.projects['my-app-e2e']).toBeUndefined();
// Making sure the nx.json file doesn't contain e2e project // Making sure the nx.json file doesn't contain e2e project
const nxJson = readJson('angular.json'); const nxJson = readJson('angular.json');
expect(nxJson.projects['my-app-e2e']).toBeUndefined(); expect(nxJson.projects['my-app-e2e']).toBeUndefined();
// Making sure the e2e folder is not created // Making sure the e2e folder is not created
expect(exists('./tmp/proj/apps/my-app-e2e')).toBeFalsy(); expect(exists('./tmp/proj/apps/my-app-e2e')).toBeFalsy();
}, }, 1000000);
1000000
);
}); });

View File

@ -1,34 +1,30 @@
import { newApp, newProject, runCLI, copyMissingPackages } from '../utils'; import { newApp, newProject, runCLI, copyMissingPackages } from '../utils';
describe('ngrx', () => { describe('ngrx', () => {
it( it('should work', () => {
'should work', newProject();
() => { newApp('myapp');
newProject();
newApp('myapp');
// Generate root ngrx state management // Generate root ngrx state management
runCLI( runCLI(
'generate ngrx users --module=apps/myapp/src/app/app.module.ts --root --collection=@nrwl/schematics' 'generate ngrx users --module=apps/myapp/src/app/app.module.ts --root --collection=@nrwl/schematics'
); );
copyMissingPackages(); copyMissingPackages();
// Generate feature library and ngrx state within that library // Generate feature library and ngrx state within that library
runCLI('g @nrwl/schematics:lib feature-flights --prefix=fl'); runCLI('g @nrwl/schematics:lib feature-flights --prefix=fl');
runCLI( runCLI(
'generate ngrx flights --module=libs/feature-flights/src/lib/feature-flights.module.ts --facade --collection=@nrwl/schematics' 'generate ngrx flights --module=libs/feature-flights/src/lib/feature-flights.module.ts --facade --collection=@nrwl/schematics'
); );
expect(runCLI('lint', { silenceError: true })).not.toContain('ERROR'); expect(runCLI('lint', { silenceError: true })).not.toContain('ERROR');
expect(runCLI('build')).toContain('chunk {main} main.js,'); expect(runCLI('build')).toContain('chunk {main} main.js,');
expect(runCLI('test myapp --no-watch')).toContain( expect(runCLI('test myapp --no-watch')).toContain(
'Executed 10 of 10 SUCCESS' 'Executed 10 of 10 SUCCESS'
); );
expect(runCLI('test feature-flights --no-watch')).toContain( expect(runCLI('test feature-flights --no-watch')).toContain(
'Executed 10 of 10 SUCCESS' 'Executed 10 of 10 SUCCESS'
); );
}, }, 1000000);
1000000
);
}); });

View File

@ -18,114 +18,104 @@ describe('Node Applications', () => {
copyMissingPackages(); copyMissingPackages();
}); });
it( it('should be able to generate a node application', async done => {
'should be able to generate a node application', runCLI('generate node-app node-app1');
async done => { copyMissingPackages();
runCLI('generate node-app node-app1');
copyMissingPackages();
updateFile( updateFile(
'apps/node-app1/src/app/test.spec.ts', 'apps/node-app1/src/app/test.spec.ts',
` `
describe('test', () => { describe('test', () => {
it('should work', () => { it('should work', () => {
expect(true).toEqual(true); expect(true).toEqual(true);
}) })
}) })
` `
); );
updateFile('apps/node-app1/src/assets/file.txt', ``); updateFile('apps/node-app1/src/assets/file.txt', ``);
const jestResult = await runCLIAsync('test node-app1'); const jestResult = await runCLIAsync('test node-app1');
expect(jestResult.stderr).toContain('Test Suites: 1 passed, 1 total'); expect(jestResult.stderr).toContain('Test Suites: 1 passed, 1 total');
function getData() { function getData() {
return new Promise(resolve => { return new Promise(resolve => {
http.get('http://localhost:3333', res => { http.get('http://localhost:3333', res => {
expect(res.statusCode).toEqual(200); expect(res.statusCode).toEqual(200);
let data = ''; let data = '';
res.on('data', chunk => { res.on('data', chunk => {
data += chunk; data += chunk;
}); });
res.once('end', () => { res.once('end', () => {
resolve(data); resolve(data);
});
}); });
}); });
});
}
await runCLIAsync('build node-app1');
expect(exists('./tmp/proj/dist/apps/node-app1/main.js')).toBeTruthy();
expect(
exists('./tmp/proj/dist/apps/node-app1/assets/file.txt')
).toBeTruthy();
expect(exists('./tmp/proj/dist/apps/node-app1/main.js.map')).toBeTruthy();
const server = fork(
path.join(
__dirname,
'../../../tmp/proj',
`./dist/apps/node-app1/main.js`
),
[],
{
cwd: './tmp/proj',
silent: true
} }
);
expect(server).toBeTruthy();
await runCLIAsync('build node-app1'); await new Promise(resolve => {
server.stdout.once('data', async data => {
expect(exists('./tmp/proj/dist/apps/node-app1/main.js')).toBeTruthy(); expect(data.toString()).toContain('Listening at http://localhost:3333');
expect(
exists('./tmp/proj/dist/apps/node-app1/assets/file.txt')
).toBeTruthy();
expect(exists('./tmp/proj/dist/apps/node-app1/main.js.map')).toBeTruthy();
const server = fork(
path.join(
__dirname,
'../../../tmp/proj',
`./dist/apps/node-app1/main.js`
),
[],
{
cwd: './tmp/proj',
silent: true
}
);
expect(server).toBeTruthy();
await new Promise(resolve => {
server.stdout.once('data', async data => {
expect(data.toString()).toContain(
'Listening at http://localhost:3333'
);
const result = await getData();
expect(result).toEqual('Welcome to node-app1!');
treeKill(server.pid, 'SIGTERM', err => {
expect(err).toBeFalsy();
resolve();
});
});
});
const process = spawn(
'node',
['./node_modules/.bin/ng', 'serve', 'node-app1'],
{
cwd: './tmp/proj'
}
);
process.stdout.on('data', async (data: Buffer) => {
if (!data.toString().includes('Listening at http://localhost:3333')) {
return;
}
const result = await getData(); const result = await getData();
expect(result).toEqual('Welcome to node-app1!'); expect(result).toEqual('Welcome to node-app1!');
treeKill(process.pid, 'SIGTERM', err => { treeKill(server.pid, 'SIGTERM', err => {
expect(err).toBeFalsy(); expect(err).toBeFalsy();
done(); resolve();
}); });
}); });
}, });
30000
);
it( const process = spawn(
'should be able to generate an empty application', 'node',
async () => { ['./node_modules/.bin/ng', 'serve', 'node-app1'],
runCLI('generate node-app node-app2 --framework none'); {
updateFile('apps/node-app2/src/main.ts', `console.log('Hello World!');`);
await runCLIAsync('build node-app2');
expect(exists('./tmp/proj/dist/apps/node-app2/main.js')).toBeTruthy();
const result = execSync('node dist/apps/node-app2/main.js', {
cwd: './tmp/proj' cwd: './tmp/proj'
}).toString(); }
expect(result).toContain('Hello World!'); );
},
30000 process.stdout.on('data', async (data: Buffer) => {
); if (!data.toString().includes('Listening at http://localhost:3333')) {
return;
}
const result = await getData();
expect(result).toEqual('Welcome to node-app1!');
treeKill(process.pid, 'SIGTERM', err => {
expect(err).toBeFalsy();
done();
});
});
}, 30000);
it('should be able to generate an empty application', async () => {
runCLI('generate node-app node-app2 --framework none');
updateFile('apps/node-app2/src/main.ts', `console.log('Hello World!');`);
await runCLIAsync('build node-app2');
expect(exists('./tmp/proj/dist/apps/node-app2/main.js')).toBeTruthy();
const result = execSync('node dist/apps/node-app2/main.js', {
cwd: './tmp/proj'
}).toString();
expect(result).toContain('Hello World!');
}, 30000);
}); });

View File

@ -1,41 +1,37 @@
import { newApp, newProject, runCLI, updateFile } from '../utils'; import { newApp, newProject, runCLI, updateFile } from '../utils';
describe('Upgrade', () => { describe('Upgrade', () => {
it( it('should generate an UpgradeModule setup', () => {
'should generate an UpgradeModule setup', newProject();
() => { newApp('myapp');
newProject();
newApp('myapp');
updateFile( updateFile(
'apps/myapp/src/legacy.js', 'apps/myapp/src/legacy.js',
` `
const angular = window.angular.module('legacy', []); const angular = window.angular.module('legacy', []);
angular.component('proj-root-legacy', { angular.component('proj-root-legacy', {
template: 'Expected Value' template: 'Expected Value'
}); });
` `
); );
updateFile( updateFile(
'apps/myapp/src/app/app.component.html', 'apps/myapp/src/app/app.component.html',
` `
EXPECTED [<proj-root-legacy></proj-root-legacy>] EXPECTED [<proj-root-legacy></proj-root-legacy>]
` `
); );
updateFile('apps/myapp/src/app/app.component.spec.ts', ``); updateFile('apps/myapp/src/app/app.component.spec.ts', ``);
runCLI( runCLI(
'generate upgrade-module legacy --angularJsImport=./legacy ' + 'generate upgrade-module legacy --angularJsImport=./legacy ' +
'--angularJsCmpSelector=proj-root-legacy --project=myapp' '--angularJsCmpSelector=proj-root-legacy --project=myapp'
); );
expect(runCLI('lint', { silenceError: true })).not.toContain('ERROR'); expect(runCLI('lint', { silenceError: true })).not.toContain('ERROR');
runCLI('build'); runCLI('build');
runCLI('test --no-watch'); runCLI('test --no-watch');
}, }, 1000000);
1000000
);
}); });

View File

@ -74,7 +74,7 @@
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"opn": "^5.3.0", "opn": "^5.3.0",
"precise-commits": "1.0.2", "precise-commits": "1.0.2",
"prettier": "1.10.2", "prettier": "1.15.3",
"release-it": "^7.4.0", "release-it": "^7.4.0",
"rxjs": "6.3.3", "rxjs": "6.3.3",
"semver": "5.4.1", "semver": "5.4.1",

View File

@ -12,7 +12,7 @@ node_modules/.bin/ng generate component [name] --directory=[dir]
Example: node_modules/.bin/ng generate component friends --directory=myDir Example: node_modules/.bin/ng generate component friends --directory=myDir
* must manually import the component's Bazel rule in the consuming Bazel rule - must manually import the component's Bazel rule in the consuming Bazel rule
## Add new lib ## Add new lib
@ -20,7 +20,7 @@ node_modules/.bin/ng generate lib [name]
Example: node_modules/.bin/ng generate lib mylib Example: node_modules/.bin/ng generate lib mylib
* must manually import the lib's Bazel rule in the consuming Bazel rule - must manually import the lib's Bazel rule in the consuming Bazel rule
## Run dev server ## Run dev server
@ -38,4 +38,4 @@ Example: bazel run apps/my-dir/my-app/src:prodserver
ibazel test //libs/mylib/src:test ibazel test //libs/mylib/src:test
* currently works for libs - currently works for libs

View File

@ -30,13 +30,11 @@
"lazy": { "lazy": {
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "description": "Add RouterModule.forChild when set to true, and a simple array of routes when set to false."
"Add RouterModule.forChild when set to true, and a simple array of routes when set to false."
}, },
"parentModule": { "parentModule": {
"type": "string", "type": "string",
"description": "description": "Update the router configuration of the parent module using loadChildren or children, depending on what `lazy` is set to."
"Update the router configuration of the parent module using loadChildren or children, depending on what `lazy` is set to."
} }
}, },
"required": ["name"] "required": ["name"]

View File

@ -28,8 +28,7 @@
"additionalProperties": false "additionalProperties": false
}, },
"apps": { "apps": {
"description": "description": "Properties of the different applications in this project.",
"Properties of the different applications in this project.",
"type": "array", "type": "array",
"items": { "items": {
"type": "object", "type": "object",
@ -102,8 +101,7 @@
}, },
"allowOutsideOutDir": { "allowOutsideOutDir": {
"type": "boolean", "type": "boolean",
"description": "description": "Allow assets to be copied outside the outDir.",
"Allow assets to be copied outside the outDir.",
"default": false "default": false
} }
}, },
@ -141,33 +139,27 @@
}, },
"maximumWarning": { "maximumWarning": {
"type": "string", "type": "string",
"description": "description": "The maximum threshold for warning relative to the baseline."
"The maximum threshold for warning relative to the baseline."
}, },
"maximumError": { "maximumError": {
"type": "string", "type": "string",
"description": "description": "The maximum threshold for error relative to the baseline."
"The maximum threshold for error relative to the baseline."
}, },
"minimumWarning": { "minimumWarning": {
"type": "string", "type": "string",
"description": "description": "The minimum threshold for warning relative to the baseline."
"The minimum threshold for warning relative to the baseline."
}, },
"minimumError": { "minimumError": {
"type": "string", "type": "string",
"description": "description": "The minimum threshold for error relative to the baseline."
"The minimum threshold for error relative to the baseline."
}, },
"warning": { "warning": {
"type": "string", "type": "string",
"description": "description": "The threshold for warning relative to the baseline (min & max)."
"The threshold for warning relative to the baseline (min & max)."
}, },
"error": { "error": {
"type": "string", "type": "string",
"description": "description": "The threshold for error relative to the baseline (min & max)."
"The threshold for error relative to the baseline (min & max)."
} }
} }
}, },
@ -211,16 +203,14 @@
}, },
"testTsconfig": { "testTsconfig": {
"type": "string", "type": "string",
"description": "description": "The name of the TypeScript configuration file for unit tests."
"The name of the TypeScript configuration file for unit tests."
}, },
"prefix": { "prefix": {
"type": "string", "type": "string",
"description": "The prefix to apply to generated selectors." "description": "The prefix to apply to generated selectors."
}, },
"serviceWorker": { "serviceWorker": {
"description": "description": "Experimental support for a service worker from @angular/service-worker.",
"Experimental support for a service worker from @angular/service-worker.",
"type": "boolean", "type": "boolean",
"default": false "default": false
}, },
@ -250,8 +240,7 @@
"type": "object", "type": "object",
"properties": { "properties": {
"includePaths": { "includePaths": {
"description": "description": "Paths to include. Paths will be resolved to project root.",
"Paths to include. Paths will be resolved to project root.",
"type": "array", "type": "array",
"items": { "items": {
"type": "string" "type": "string"
@ -288,8 +277,7 @@
"type": "string" "type": "string"
}, },
"environments": { "environments": {
"description": "description": "Name and corresponding file for environment config.",
"Name and corresponding file for environment config.",
"type": "object", "type": "object",
"additionalProperties": true "additionalProperties": true
} }
@ -337,8 +325,7 @@
"default": [] "default": []
}, },
"project": { "project": {
"description": "description": "Location of the tsconfig.json project file. Will also use as files to lint if 'files' property not present.",
"Location of the tsconfig.json project file. Will also use as files to lint if 'files' property not present.",
"type": "string" "type": "string"
}, },
"tslintConfig": { "tslintConfig": {
@ -444,8 +431,7 @@
"default": false "default": false
}, },
"inlineTemplate": { "inlineTemplate": {
"description": "description": "Specifies if the template will be in the ts file.",
"Specifies if the template will be in the ts file.",
"type": "boolean", "type": "boolean",
"default": false "default": false
}, },
@ -569,8 +555,7 @@
"type": "boolean" "type": "boolean"
}, },
"poll": { "poll": {
"description": "description": "Enable and define the file watching poll time period (milliseconds).",
"Enable and define the file watching poll time period (milliseconds).",
"type": "number" "type": "number"
}, },
"deleteOutputPath": { "deleteOutputPath": {
@ -589,8 +574,7 @@
"default": true "default": true
}, },
"commonChunk": { "commonChunk": {
"description": "description": "Use a separate bundle containing code used across multiple bundles.",
"Use a separate bundle containing code used across multiple bundles.",
"type": "boolean", "type": "boolean",
"default": true "default": true
}, },
@ -666,14 +650,12 @@
"type": "object", "type": "object",
"properties": { "properties": {
"hmrWarning": { "hmrWarning": {
"description": "description": "Show a warning when the user enabled the --hmr option.",
"Show a warning when the user enabled the --hmr option.",
"type": "boolean", "type": "boolean",
"default": true "default": true
}, },
"nodeDeprecation": { "nodeDeprecation": {
"description": "description": "Show a warning when the node version is incompatible.",
"Show a warning when the node version is incompatible.",
"type": "boolean", "type": "boolean",
"default": true "default": true
}, },
@ -683,20 +665,17 @@
"default": true "default": true
}, },
"versionMismatch": { "versionMismatch": {
"description": "description": "Show a warning when the global version is newer than the local one.",
"Show a warning when the global version is newer than the local one.",
"type": "boolean", "type": "boolean",
"default": true "default": true
}, },
"typescriptMismatch": { "typescriptMismatch": {
"description": "description": "Show a warning when the TypeScript version is incompatible",
"Show a warning when the TypeScript version is incompatible",
"type": "boolean", "type": "boolean",
"default": true "default": true
}, },
"servePathDefault": { "servePathDefault": {
"description": "description": "Show a warning when deploy-url/base-href use unsupported serve path values.",
"Show a warning when deploy-url/base-href use unsupported serve path values.",
"type": "boolean", "type": "boolean",
"default": true "default": true
} }

View File

@ -232,11 +232,13 @@ function findClass(
): ts.ClassDeclaration { ): ts.ClassDeclaration {
const nodes = getSourceNodes(source); const nodes = getSourceNodes(source);
const clazz = <any>nodes.filter( const clazz = <any>(
n => nodes.filter(
n.kind === ts.SyntaxKind.ClassDeclaration && n =>
(<any>n).name.text === className n.kind === ts.SyntaxKind.ClassDeclaration &&
)[0]; (<any>n).name.text === className
)[0]
);
if (!clazz) { if (!clazz) {
throw new Error(`Cannot find class '${className}'`); throw new Error(`Cannot find class '${className}'`);
@ -283,9 +285,8 @@ export function addImportToTestBed(
specPath: string, specPath: string,
symbolName: string symbolName: string
): Change[] { ): Change[] {
const allCalls: ts.CallExpression[] = <any>findNodes( const allCalls: ts.CallExpression[] = <any>(
source, findNodes(source, ts.SyntaxKind.CallExpression)
ts.SyntaxKind.CallExpression
); );
const configureTestingModuleObjectLiterals = allCalls const configureTestingModuleObjectLiterals = allCalls
@ -293,11 +294,10 @@ export function addImportToTestBed(
.filter( .filter(
(c: any) => c.expression.name.getText(source) === 'configureTestingModule' (c: any) => c.expression.name.getText(source) === 'configureTestingModule'
) )
.map( .map(c =>
c => c.arguments[0].kind === ts.SyntaxKind.ObjectLiteralExpression
c.arguments[0].kind === ts.SyntaxKind.ObjectLiteralExpression ? c.arguments[0]
? c.arguments[0] : null
: null
); );
if (configureTestingModuleObjectLiterals.length > 0) { if (configureTestingModuleObjectLiterals.length > 0) {

View File

@ -13,9 +13,8 @@ export function toClassName(str: string): string {
export function toPropertyName(s: string): string { export function toPropertyName(s: string): string {
return s return s
.replace( .replace(/(-|_|\.|\s)+(.)?/g, (_, __, chr) =>
/(-|_|\.|\s)+(.)?/g, chr ? chr.toUpperCase() : ''
(_, __, chr) => (chr ? chr.toUpperCase() : '')
) )
.replace(/^([A-Z])/, m => m.toLowerCase()); .replace(/^([A-Z])/, m => m.toLowerCase());
} }

View File

@ -83,11 +83,10 @@ export default class CypressBuilder implements Builder<CypressBuilderOptions> {
tap(() => tap(() =>
this.copyCypressFixtures(options.tsConfig, options.cypressConfig) this.copyCypressFixtures(options.tsConfig, options.cypressConfig)
), ),
concatMap( concatMap(() =>
() => !options.baseUrl && options.devServerTarget
!options.baseUrl && options.devServerTarget ? this.startDevServer(options.devServerTarget, options.watch)
? this.startDevServer(options.devServerTarget, options.watch) : of(null)
: of(null)
), ),
concatMap(() => concatMap(() =>
this.initCypress( this.initCypress(

View File

@ -22,14 +22,12 @@
}, },
"headless": { "headless": {
"type": "boolean", "type": "boolean",
"description": "description": "Whether or not the open the Cypress application to run the tests. If set to 'true', will run in headless mode",
"Whether or not the open the Cypress application to run the tests. If set to 'true', will run in headless mode",
"default": false "default": false
}, },
"baseUrl": { "baseUrl": {
"type": "string", "type": "string",
"description": "description": "Use this to pass directly the address of your distant server address with the port running your application"
"Use this to pass directly the address of your distant server address with the port running your application"
}, },
"browser": { "browser": {
"type": "string", "type": "string",

View File

@ -5,8 +5,7 @@
"properties": { "properties": {
"jestConfig": { "jestConfig": {
"type": "string", "type": "string",
"description": "description": "The path of the Jest configuration. (https://jestjs.io/docs/en/configuration.html)"
"The path of the Jest configuration. (https://jestjs.io/docs/en/configuration.html)"
}, },
"tsConfig": { "tsConfig": {
"type": "string", "type": "string",
@ -14,67 +13,55 @@
}, },
"setupFile": { "setupFile": {
"type": "string", "type": "string",
"description": "description": "The name of a setup file used by Jest. (https://jestjs.io/docs/en/configuration.html#setuptestframeworkscriptfile-string)"
"The name of a setup file used by Jest. (https://jestjs.io/docs/en/configuration.html#setuptestframeworkscriptfile-string)"
}, },
"watch": { "watch": {
"type": "boolean", "type": "boolean",
"description": "description": "Run tests when files change. (https://jestjs.io/docs/en/cli#watch)",
"Run tests when files change. (https://jestjs.io/docs/en/cli#watch)",
"default": false "default": false
}, },
"onlyChanged": { "onlyChanged": {
"type": "boolean", "type": "boolean",
"alias": "o", "alias": "o",
"description": "description": "Isolate tests affected by uncommitted changes. (https://jestjs.io/docs/en/cli#onlychanged)"
"Isolate tests affected by uncommitted changes. (https://jestjs.io/docs/en/cli#onlychanged)"
}, },
"passWithNoTests": { "passWithNoTests": {
"type": "boolean", "type": "boolean",
"description": "description": "Allow test suite to pass when no test files are found. (https://jestjs.io/docs/en/cli#passwithnotests)"
"Allow test suite to pass when no test files are found. (https://jestjs.io/docs/en/cli#passwithnotests)"
}, },
"codeCoverage": { "codeCoverage": {
"type": "boolean", "type": "boolean",
"description": "description": "Export a code coverage report. (https://jestjs.io/docs/en/cli#coverage)"
"Export a code coverage report. (https://jestjs.io/docs/en/cli#coverage)"
}, },
"updateSnapshot": { "updateSnapshot": {
"type": "boolean", "type": "boolean",
"alias": "u", "alias": "u",
"description": "description": "Re-record all failing snapshots. (https://jestjs.io/docs/en/cli#updatesnapshot)"
"Re-record all failing snapshots. (https://jestjs.io/docs/en/cli#updatesnapshot)"
}, },
"ci": { "ci": {
"type": "boolean", "type": "boolean",
"description": "description": "Fail on missing snapshots. (https://jestjs.io/docs/en/cli#ci)"
"Fail on missing snapshots. (https://jestjs.io/docs/en/cli#ci)"
}, },
"bail": { "bail": {
"type": "boolean", "type": "boolean",
"description": "description": "Exit the test suite immediately upon the first failing test suite. (https://jestjs.io/docs/en/cli#bail)"
"Exit the test suite immediately upon the first failing test suite. (https://jestjs.io/docs/en/cli#bail)"
}, },
"silent": { "silent": {
"type": "boolean", "type": "boolean",
"description": "description": "Prevent tests from printing messages through the console. (https://jestjs.io/docs/en/cli#silent)"
"Prevent tests from printing messages through the console. (https://jestjs.io/docs/en/cli#silent)"
}, },
"runInBand": { "runInBand": {
"type": "boolean", "type": "boolean",
"description": "description": "Run tests in a single process as opposed to multiple workers. Useful for CI. (https://jestjs.io/docs/en/cli.html#runinband)"
"Run tests in a single process as opposed to multiple workers. Useful for CI. (https://jestjs.io/docs/en/cli.html#runinband)"
}, },
"maxWorkers": { "maxWorkers": {
"type": "number", "type": "number",
"description": "description": "Max number of workers to run tests across. Useful for CI. (https://jestjs.io/docs/en/cli.html#maxworkers-num)"
"Max number of workers to run tests across. Useful for CI. (https://jestjs.io/docs/en/cli.html#maxworkers-num)"
}, },
"testNamePattern": { "testNamePattern": {
"type": "string", "type": "string",
"alias": "t", "alias": "t",
"description": "description": "Run only tests with a name that matches the regex. (https://jestjs.io/docs/en/cli.html#testnamepattern-regex)"
"Run only tests with a name that matches the regex. (https://jestjs.io/docs/en/cli.html#testnamepattern-regex)"
} }
}, },
"required": ["jestConfig", "tsConfig"] "required": ["jestConfig", "tsConfig"]

View File

@ -47,20 +47,17 @@
} }
} }
], ],
"description": "description": "Dependencies to keep external to the bundle. (\"all\" (default), \"none\", or an array of module names)",
"Dependencies to keep external to the bundle. (\"all\" (default), \"none\", or an array of module names)",
"default": "all" "default": "all"
}, },
"statsJson": { "statsJson": {
"type": "boolean", "type": "boolean",
"description": "description": "Generates a 'stats.json' file which can be analyzed using tools such as: #webpack-bundle-analyzer' or https: //webpack.github.io/analyse.",
"Generates a 'stats.json' file which can be analyzed using tools such as: #webpack-bundle-analyzer' or https: //webpack.github.io/analyse.",
"default": false "default": false
}, },
"extractLicenses": { "extractLicenses": {
"type": "boolean", "type": "boolean",
"description": "description": "Extract all licenses in a separate file, in the case of production builds only.",
"Extract all licenses in a separate file, in the case of production builds only.",
"default": false "default": false
}, },
"optimization": { "optimization": {
@ -75,8 +72,7 @@
}, },
"maxWorkers": { "maxWorkers": {
"type": "number", "type": "number",
"description": "description": "Number of workers to use for type checking. (defaults to # of CPUS - 2)"
"Number of workers to use for type checking. (defaults to # of CPUS - 2)"
}, },
"fileReplacements": { "fileReplacements": {
"description": "Replace files with other files in the build.", "description": "Replace files with other files in the build.",
@ -111,8 +107,7 @@
}, },
"input": { "input": {
"type": "string", "type": "string",
"description": "description": "The input directory path in which to apply 'glob'. Defaults to the project root."
"The input directory path in which to apply 'glob'. Defaults to the project root."
}, },
"ignore": { "ignore": {
"description": "An array of globs to ignore.", "description": "An array of globs to ignore.",

View File

@ -56,18 +56,19 @@ function userReducer(state: string, action: Action): string {
return 'bob'; return 'bob';
} }
@Component({ template: `ROOT[<router-outlet></router-outlet>]` }) @Component({
template: `
ROOT[<router-outlet></router-outlet>]
`
})
class RootCmp {} class RootCmp {}
@Component({ @Component({
template: ` template: `
Todo [ Todo [
<div *ngIf="(todo|async) as t"> <div *ngIf="(todo | async) as t">ID {{ t.id }} User {{ t.user }}</div>
ID {{t.id}} ]
User {{t.user}} `
</div>
]
`
}) })
class TodoComponent { class TodoComponent {
todo = this.store.select('todos', 'selected'); todo = this.store.select('todos', 'selected');
@ -113,20 +114,17 @@ describe('DataPersistence', () => {
}); });
}); });
it( it('should work', fakeAsync(() => {
'should work', const root = TestBed.createComponent(RootCmp);
fakeAsync(() => {
const root = TestBed.createComponent(RootCmp);
const router: Router = TestBed.get(Router); const router: Router = TestBed.get(Router);
router.navigateByUrl('/todo/123'); router.navigateByUrl('/todo/123');
tick(0); tick(0);
root.detectChanges(false); root.detectChanges(false);
expect(root.elementRef.nativeElement.innerHTML).toContain('ID 123'); expect(root.elementRef.nativeElement.innerHTML).toContain('ID 123');
expect(root.elementRef.nativeElement.innerHTML).toContain('User bob'); expect(root.elementRef.nativeElement.innerHTML).toContain('User bob');
}) }));
);
}); });
describe('`run` throwing an error', () => { describe('`run` throwing an error', () => {
@ -155,31 +153,26 @@ describe('DataPersistence', () => {
}); });
}); });
it( it('should work', fakeAsync(() => {
'should work', const root = TestBed.createComponent(RootCmp);
fakeAsync(() => {
const root = TestBed.createComponent(RootCmp);
const router: Router = TestBed.get(Router); const router: Router = TestBed.get(Router);
let action; let action;
TestBed.get(Actions).subscribe(a => (action = a)); TestBed.get(Actions).subscribe(a => (action = a));
router.navigateByUrl('/todo/123'); router.navigateByUrl('/todo/123');
tick(0); tick(0);
root.detectChanges(false); root.detectChanges(false);
expect(root.elementRef.nativeElement.innerHTML).not.toContain( expect(root.elementRef.nativeElement.innerHTML).not.toContain('ID 123');
'ID 123' expect(action.type).toEqual('ERROR');
); expect(action.payload.error.message).toEqual('boom');
expect(action.type).toEqual('ERROR');
expect(action.payload.error.message).toEqual('boom');
// can recover after an error // can recover after an error
router.navigateByUrl('/todo/456'); router.navigateByUrl('/todo/456');
tick(0); tick(0);
root.detectChanges(false); root.detectChanges(false);
expect(root.elementRef.nativeElement.innerHTML).toContain('ID 456'); expect(root.elementRef.nativeElement.innerHTML).toContain('ID 456');
}) }));
);
}); });
describe('`run` returning an error observable', () => { describe('`run` returning an error observable', () => {
@ -208,30 +201,25 @@ describe('DataPersistence', () => {
}); });
}); });
it( it('should work', fakeAsync(() => {
'should work', const root = TestBed.createComponent(RootCmp);
fakeAsync(() => {
const root = TestBed.createComponent(RootCmp);
const router: Router = TestBed.get(Router); const router: Router = TestBed.get(Router);
let action; let action;
TestBed.get(Actions).subscribe(a => (action = a)); TestBed.get(Actions).subscribe(a => (action = a));
router.navigateByUrl('/todo/123'); router.navigateByUrl('/todo/123');
tick(0); tick(0);
root.detectChanges(false); root.detectChanges(false);
expect(root.elementRef.nativeElement.innerHTML).not.toContain( expect(root.elementRef.nativeElement.innerHTML).not.toContain('ID 123');
'ID 123' expect(action.type).toEqual('ERROR');
); expect(action.payload.error).toEqual('boom');
expect(action.type).toEqual('ERROR');
expect(action.payload.error).toEqual('boom');
router.navigateByUrl('/todo/456'); router.navigateByUrl('/todo/456');
tick(0); tick(0);
root.detectChanges(false); root.detectChanges(false);
expect(root.elementRef.nativeElement.innerHTML).toContain('ID 456'); expect(root.elementRef.nativeElement.innerHTML).toContain('ID 456');
}) }));
);
}); });
}); });

View File

@ -580,8 +580,8 @@ const updateAngularJson = updateJsonInTree('angular.json', json => {
const type = !project.architect.build const type = !project.architect.build
? 'e2e' ? 'e2e'
: project.architect.build.options.main.startsWith('apps') : project.architect.build.options.main.startsWith('apps')
? 'application' ? 'application'
: 'library'; : 'library';
if (type !== 'e2e') { if (type !== 'e2e') {
project.projectType = type; project.projectType = type;
} }

View File

@ -29,7 +29,11 @@
"schematics": "./src/collection.json", "schematics": "./src/collection.json",
"ng-update": { "ng-update": {
"requirements": {}, "requirements": {},
"packageGroup": ["@nrwl/nx", "@nrwl/schematics", "@nrwl/builders"], "packageGroup": [
"@nrwl/nx",
"@nrwl/schematics",
"@nrwl/builders"
],
"migrations": "./migrations/migrations.json" "migrations": "./migrations/migrations.json"
}, },
"dependencies": { "dependencies": {

View File

@ -21,12 +21,10 @@ describe('app', () => {
}); });
describe('not nested', () => { describe('not nested', () => {
it('should update angular.json', () => { it('should update angular.json', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'app', .runSchematicAsync('app', { name: 'myApp' }, appTree)
{ name: 'myApp' }, .toPromise();
appTree
);
const angularJson = readJsonInTree(tree, '/angular.json'); const angularJson = readJsonInTree(tree, '/angular.json');
expect(angularJson.projects['my-app'].root).toEqual('apps/my-app/'); expect(angularJson.projects['my-app'].root).toEqual('apps/my-app/');
@ -35,12 +33,10 @@ describe('app', () => {
); );
}); });
it('should update nx.json', () => { it('should update nx.json', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'app', .runSchematicAsync('app', { name: 'myApp', tags: 'one,two' }, appTree)
{ name: 'myApp', tags: 'one,two' }, .toPromise();
appTree
);
const nxJson = readJsonInTree<NxJson>(tree, '/nx.json'); const nxJson = readJsonInTree<NxJson>(tree, '/nx.json');
expect(nxJson).toEqual({ expect(nxJson).toEqual({
npmScope: 'proj', npmScope: 'proj',
@ -55,12 +51,10 @@ describe('app', () => {
}); });
}); });
it('should generate files', () => { it('should generate files', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'app', .runSchematicAsync('app', { name: 'myApp' }, appTree)
{ name: 'myApp' }, .toPromise();
appTree
);
expect(tree.exists(`apps/my-app/karma.conf.js`)).toBeTruthy(); expect(tree.exists(`apps/my-app/karma.conf.js`)).toBeTruthy();
expect(tree.exists('apps/my-app/src/main.ts')).toBeTruthy(); expect(tree.exists('apps/my-app/src/main.ts')).toBeTruthy();
expect(tree.exists('apps/my-app/src/app/app.module.ts')).toBeTruthy(); expect(tree.exists('apps/my-app/src/app/app.module.ts')).toBeTruthy();
@ -98,17 +92,13 @@ describe('app', () => {
expect(tsconfigE2E.extends).toEqual('./tsconfig.json'); expect(tsconfigE2E.extends).toEqual('./tsconfig.json');
}); });
it('should default the prefix to npmScope', () => { it('should default the prefix to npmScope', async () => {
const noPrefix = schematicRunner.runSchematic( const noPrefix = await schematicRunner
'app', .runSchematicAsync('app', { name: 'myApp' }, appTree)
{ name: 'myApp' }, .toPromise();
appTree const withPrefix = await schematicRunner
); .runSchematicAsync('app', { name: 'myApp', prefix: 'custom' }, appTree)
const withPrefix = schematicRunner.runSchematic( .toPromise();
'app',
{ name: 'myApp', prefix: 'custom' },
appTree
);
// Testing without prefix // Testing without prefix
@ -144,11 +134,9 @@ describe('app', () => {
) )
.toPromise(); .toPromise();
const result = schematicRunner.runSchematic( const result = await schematicRunner
'app', .runSchematicAsync('app', { name: 'myApp' }, appTree)
{ name: 'myApp' }, .toPromise();
appTree
);
expect(result.exists('apps/my-app/src/main.ts')).toEqual(true); expect(result.exists('apps/my-app/src/main.ts')).toEqual(true);
expect(result.exists('apps/my-app-e2e/protractor.conf.js')).toEqual(true); expect(result.exists('apps/my-app-e2e/protractor.conf.js')).toEqual(true);
@ -156,12 +144,14 @@ describe('app', () => {
}); });
describe('nested', () => { describe('nested', () => {
it('should update angular.json', () => { it('should update angular.json', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'app', .runSchematicAsync(
{ name: 'myApp', directory: 'myDir' }, 'app',
appTree { name: 'myApp', directory: 'myDir' },
); appTree
)
.toPromise();
const angularJson = readJsonInTree(tree, '/angular.json'); const angularJson = readJsonInTree(tree, '/angular.json');
expect(angularJson.projects['my-dir-my-app'].root).toEqual( expect(angularJson.projects['my-dir-my-app'].root).toEqual(
@ -172,12 +162,14 @@ describe('app', () => {
); );
}); });
it('should update nx.json', () => { it('should update nx.json', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'app', .runSchematicAsync(
{ name: 'myApp', directory: 'myDir', tags: 'one,two' }, 'app',
appTree { name: 'myApp', directory: 'myDir', tags: 'one,two' },
); appTree
)
.toPromise();
const nxJson = readJsonInTree<NxJson>(tree, '/nx.json'); const nxJson = readJsonInTree<NxJson>(tree, '/nx.json');
expect(nxJson).toEqual({ expect(nxJson).toEqual({
npmScope: 'proj', npmScope: 'proj',
@ -192,18 +184,20 @@ describe('app', () => {
}); });
}); });
it('should generate files', () => { it('should generate files', async () => {
const hasJsonValue = ({ path, expectedValue, lookupFn }) => { const hasJsonValue = ({ path, expectedValue, lookupFn }) => {
const content = getFileContent(tree, path); const content = getFileContent(tree, path);
const config = JSON.parse(stripJsonComments(content)); const config = JSON.parse(stripJsonComments(content));
expect(lookupFn(config)).toEqual(expectedValue); expect(lookupFn(config)).toEqual(expectedValue);
}; };
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'app', .runSchematicAsync(
{ name: 'myApp', directory: 'myDir' }, 'app',
appTree { name: 'myApp', directory: 'myDir' },
); appTree
)
.toPromise();
const appModulePath = 'apps/my-dir/my-app/src/app/app.module.ts'; const appModulePath = 'apps/my-dir/my-app/src/app/app.module.ts';
expect(getFileContent(tree, appModulePath)).toContain('class AppModule'); expect(getFileContent(tree, appModulePath)).toContain('class AppModule');
@ -250,24 +244,24 @@ describe('app', () => {
}); });
}); });
it('should import NgModule', () => { it('should import NgModule', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'app', .runSchematicAsync('app', { name: 'myApp', directory: 'myDir' }, appTree)
{ name: 'myApp', directory: 'myDir' }, .toPromise();
appTree
);
expect( expect(
getFileContent(tree, 'apps/my-dir/my-app/src/app/app.module.ts') getFileContent(tree, 'apps/my-dir/my-app/src/app/app.module.ts')
).toContain('NxModule.forRoot()'); ).toContain('NxModule.forRoot()');
}); });
describe('routing', () => { describe('routing', () => {
it('should include RouterTestingModule', () => { it('should include RouterTestingModule', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'app', .runSchematicAsync(
{ name: 'myApp', directory: 'myDir', routing: true }, 'app',
appTree { name: 'myApp', directory: 'myDir', routing: true },
); appTree
)
.toPromise();
expect( expect(
getFileContent(tree, 'apps/my-dir/my-app/src/app/app.module.ts') getFileContent(tree, 'apps/my-dir/my-app/src/app/app.module.ts')
).toContain('RouterModule.forRoot'); ).toContain('RouterModule.forRoot');
@ -276,12 +270,14 @@ describe('app', () => {
).toContain('imports: [RouterTestingModule]'); ).toContain('imports: [RouterTestingModule]');
}); });
it('should not modify tests when --skip-tests is set', () => { it('should not modify tests when --skip-tests is set', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'app', .runSchematicAsync(
{ name: 'myApp', directory: 'myDir', routing: true, skipTests: true }, 'app',
appTree { name: 'myApp', directory: 'myDir', routing: true, skipTests: true },
); appTree
)
.toPromise();
expect( expect(
tree.exists('apps/my-dir/my-app/src/app/app.component.spec.ts') tree.exists('apps/my-dir/my-app/src/app/app.component.spec.ts')
).toBeFalsy(); ).toBeFalsy();
@ -289,12 +285,14 @@ describe('app', () => {
}); });
describe('template generation mode', () => { describe('template generation mode', () => {
it('should create Nx specific `app.component.html` template', () => { it('should create Nx specific `app.component.html` template', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'app', .runSchematicAsync(
{ name: 'myApp', directory: 'myDir' }, 'app',
appTree { name: 'myApp', directory: 'myDir' },
); appTree
)
.toPromise();
expect( expect(
getFileContent(tree, 'apps/my-dir/my-app/src/app/app.component.html') getFileContent(tree, 'apps/my-dir/my-app/src/app/app.component.html')
).toBeTruthy(); ).toBeTruthy();
@ -303,12 +301,14 @@ describe('app', () => {
).toContain('This is an Angular CLI app built with Nrwl Nx!'); ).toContain('This is an Angular CLI app built with Nrwl Nx!');
}); });
it("should update `template`'s property of AppComponent with Nx content", () => { it("should update `template`'s property of AppComponent with Nx content", async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'app', .runSchematicAsync(
{ name: 'myApp', directory: 'myDir', inlineTemplate: true }, 'app',
appTree { name: 'myApp', directory: 'myDir', inlineTemplate: true },
); appTree
)
.toPromise();
expect( expect(
getFileContent(tree, 'apps/my-dir/my-app/src/app/app.component.ts') getFileContent(tree, 'apps/my-dir/my-app/src/app/app.component.ts')
).toContain('This is an Angular CLI app built with Nrwl Nx!'); ).toContain('This is an Angular CLI app built with Nrwl Nx!');
@ -316,23 +316,19 @@ describe('app', () => {
}); });
describe('--style scss', () => { describe('--style scss', () => {
it('should generate scss styles', () => { it('should generate scss styles', async () => {
const result = schematicRunner.runSchematic( const result = await schematicRunner
'app', .runSchematicAsync('app', { name: 'myApp', style: 'scss' }, appTree)
{ name: 'myApp', style: 'scss' }, .toPromise();
appTree
);
expect(result.exists('apps/my-app/src/app/app.component.scss')).toEqual( expect(result.exists('apps/my-app/src/app/app.component.scss')).toEqual(
true true
); );
}); });
it('should set it as default', () => { it('should set it as default', async () => {
const result = schematicRunner.runSchematic( const result = await schematicRunner
'app', .runSchematicAsync('app', { name: 'myApp', style: 'scss' }, appTree)
{ name: 'myApp', style: 'scss' }, .toPromise();
appTree
);
const angularJson = readJsonInTree(result, 'angular.json'); const angularJson = readJsonInTree(result, 'angular.json');
@ -345,12 +341,14 @@ describe('app', () => {
}); });
describe('--unit-test-runner jest', () => { describe('--unit-test-runner jest', () => {
it('should generate a jest config', () => { it('should generate a jest config', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'app', .runSchematicAsync(
{ name: 'myApp', unitTestRunner: 'jest' }, 'app',
appTree { name: 'myApp', unitTestRunner: 'jest' },
); appTree
)
.toPromise();
expect(tree.exists('apps/my-app/src/test.ts')).toBeFalsy(); expect(tree.exists('apps/my-app/src/test.ts')).toBeFalsy();
expect(tree.exists('apps/my-app/src/test-setup.ts')).toBeTruthy(); expect(tree.exists('apps/my-app/src/test-setup.ts')).toBeTruthy();
expect(tree.exists('apps/my-app/tsconfig.spec.json')).toBeTruthy(); expect(tree.exists('apps/my-app/tsconfig.spec.json')).toBeTruthy();
@ -380,12 +378,14 @@ describe('app', () => {
}); });
describe('--unit-test-runner none', () => { describe('--unit-test-runner none', () => {
it('should not generate test configuration', () => { it('should not generate test configuration', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'app', .runSchematicAsync(
{ name: 'myApp', unitTestRunner: 'none' }, 'app',
appTree { name: 'myApp', unitTestRunner: 'none' },
); appTree
)
.toPromise();
expect(tree.exists('apps/my-app/src/test-setup.ts')).toBeFalsy(); expect(tree.exists('apps/my-app/src/test-setup.ts')).toBeFalsy();
expect(tree.exists('apps/my-app/src/test.ts')).toBeFalsy(); expect(tree.exists('apps/my-app/src/test.ts')).toBeFalsy();
expect(tree.exists('apps/my-app/tsconfig.spec.json')).toBeFalsy(); expect(tree.exists('apps/my-app/tsconfig.spec.json')).toBeFalsy();
@ -400,12 +400,14 @@ describe('app', () => {
}); });
describe('--e2e-test-runner none', () => { describe('--e2e-test-runner none', () => {
it('should not generate test configuration', () => { it('should not generate test configuration', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'app', .runSchematicAsync(
{ name: 'myApp', e2eTestRunner: 'none' }, 'app',
appTree { name: 'myApp', e2eTestRunner: 'none' },
); appTree
)
.toPromise();
expect(tree.exists('apps/my-app-e2e')).toBeFalsy(); expect(tree.exists('apps/my-app-e2e')).toBeFalsy();
const angularJson = readJsonInTree(tree, 'angular.json'); const angularJson = readJsonInTree(tree, 'angular.json');
expect(angularJson.projects['my-app-e2e']).toBeUndefined(); expect(angularJson.projects['my-app-e2e']).toBeUndefined();
@ -413,8 +415,10 @@ describe('app', () => {
}); });
describe('replaceAppNameWithPath', () => { describe('replaceAppNameWithPath', () => {
it('should protect `angular.json` commands and properties', () => { it('should protect `angular.json` commands and properties', async () => {
const tree = schematicRunner.runSchematic('app', { name: 'ui' }, appTree); const tree = await schematicRunner
.runSchematicAsync('app', { name: 'ui' }, appTree)
.toPromise();
const angularJson = readJsonInTree(tree, 'angular.json'); const angularJson = readJsonInTree(tree, 'angular.json');
expect(angularJson.projects['ui']).toBeDefined(); expect(angularJson.projects['ui']).toBeDefined();
@ -423,12 +427,10 @@ describe('app', () => {
).toEqual('@angular-devkit/build-angular:browser'); ).toEqual('@angular-devkit/build-angular:browser');
}); });
it('should protect `angular.json` sensible properties value to be renamed', () => { it('should protect `angular.json` sensible properties value to be renamed', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'app', .runSchematicAsync('app', { name: 'ui', prefix: 'ui' }, appTree)
{ name: 'ui', prefix: 'ui' }, .toPromise();
appTree
);
const angularJson = readJsonInTree(tree, 'angular.json'); const angularJson = readJsonInTree(tree, 'angular.json');
expect(angularJson.projects['ui'].prefix).toEqual('ui'); expect(angularJson.projects['ui'].prefix).toEqual('ui');

View File

@ -84,9 +84,9 @@ function addRouterRootConfiguration(options: NormalizedSchema): Rule {
const componentSpecPath = `${ const componentSpecPath = `${
options.appProjectRoot options.appProjectRoot
}/src/app/app.component.spec.ts`; }/src/app/app.component.spec.ts`;
const componentSpecSource = host.read(componentSpecPath)!.toString( const componentSpecSource = host
'utf-8' .read(componentSpecPath)!
); .toString('utf-8');
const componentSpecSourceFile = ts.createSourceFile( const componentSpecSourceFile = ts.createSourceFile(
componentSpecPath, componentSpecPath,
componentSpecSource, componentSpecSource,

View File

@ -85,8 +85,7 @@
"description": "Test runner to use for unit tests", "description": "Test runner to use for unit tests",
"default": "karma", "default": "karma",
"x-prompt": { "x-prompt": {
"message": "message": "Which Unit Test Runner would you like to use for the application?",
"Which Unit Test Runner would you like to use for the application?",
"type": "list", "type": "list",
"items": [ "items": [
{ {
@ -103,8 +102,7 @@
"description": "Test runner to use for end to end (e2e) tests", "description": "Test runner to use for end to end (e2e) tests",
"default": "protractor", "default": "protractor",
"x-prompt": { "x-prompt": {
"message": "message": "Which E2E Test Runner would you like to use for the application?",
"Which E2E Test Runner would you like to use for the application?",
"type": "list", "type": "list",
"items": [ "items": [
{ {
@ -121,8 +119,7 @@
"tags": { "tags": {
"type": "string", "type": "string",
"description": "Add tags to the application (used for linting)", "description": "Add tags to the application (used for linting)",
"x-prompt": "x-prompt": "Which tags would you like to add to the application? (used for linting)"
"Which tags would you like to add to the application? (used for linting)"
} }
}, },
"required": [] "required": []

View File

@ -18,12 +18,14 @@ describe('schematic:cypress-project', () => {
}); });
describe('generate app --e2e-test-runner=cypress', () => { describe('generate app --e2e-test-runner=cypress', () => {
it('should not contain any protractor files', () => { it('should not contain any protractor files', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'application', .runSchematicAsync(
{ name: 'myApp', e2eTestRunner: 'cypress' }, 'application',
appTree { name: 'myApp', e2eTestRunner: 'cypress' },
); appTree
)
.toPromise();
expect( expect(
tree.exists('apps/my-app-e2e/protractor.e2e.json') tree.exists('apps/my-app-e2e/protractor.e2e.json')
@ -38,12 +40,14 @@ describe('schematic:cypress-project', () => {
expect(tree.exists('apps/my-app-e2e/src/app.po.ts')).not.toBeTruthy(); expect(tree.exists('apps/my-app-e2e/src/app.po.ts')).not.toBeTruthy();
}); });
it('should generate files', () => { it('should generate files', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'application', .runSchematicAsync(
{ name: 'myApp', e2eTestRunner: 'cypress' }, 'application',
appTree { name: 'myApp', e2eTestRunner: 'cypress' },
); appTree
)
.toPromise();
expect(tree.exists('apps/my-app-e2e/cypress.json')).toBeTruthy(); expect(tree.exists('apps/my-app-e2e/cypress.json')).toBeTruthy();
expect(tree.exists('apps/my-app-e2e/tsconfig.e2e.json')).toBeTruthy(); expect(tree.exists('apps/my-app-e2e/tsconfig.e2e.json')).toBeTruthy();
@ -62,24 +66,28 @@ describe('schematic:cypress-project', () => {
expect(tree.exists('apps/my-app-e2e/src/support/index.ts')).toBeTruthy(); expect(tree.exists('apps/my-app-e2e/src/support/index.ts')).toBeTruthy();
}); });
it('should add dependencies into `package.json` file', () => { it('should add dependencies into `package.json` file', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'application', .runSchematicAsync(
{ name: 'myApp', e2eTestRunner: 'cypress' }, 'application',
appTree { name: 'myApp', e2eTestRunner: 'cypress' },
); appTree
)
.toPromise();
const packageJson = readJsonInTree(tree, 'package.json'); const packageJson = readJsonInTree(tree, 'package.json');
expect(packageJson.devDependencies.cypress).toBeDefined(); expect(packageJson.devDependencies.cypress).toBeDefined();
expect(packageJson.devDependencies['@nrwl/builders']).toBeDefined(); expect(packageJson.devDependencies['@nrwl/builders']).toBeDefined();
}); });
it('should add update `angular.json` file', () => { it('should add update `angular.json` file', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'application', .runSchematicAsync(
{ name: 'myApp', e2eTestRunner: 'cypress' }, 'application',
appTree { name: 'myApp', e2eTestRunner: 'cypress' },
); appTree
)
.toPromise();
const angularJson = readJsonInTree(tree, 'angular.json'); const angularJson = readJsonInTree(tree, 'angular.json');
const project = angularJson.projects['my-app-e2e']; const project = angularJson.projects['my-app-e2e'];
@ -91,12 +99,14 @@ describe('schematic:cypress-project', () => {
); );
}); });
it('should set right path names in `cypress.json`', () => { it('should set right path names in `cypress.json`', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'application', .runSchematicAsync(
{ name: 'myApp', e2eTestRunner: 'cypress' }, 'application',
appTree { name: 'myApp', e2eTestRunner: 'cypress' },
); appTree
)
.toPromise();
const cypressJson = readJsonInTree(tree, 'apps/my-app-e2e/cypress.json'); const cypressJson = readJsonInTree(tree, 'apps/my-app-e2e/cypress.json');
expect(cypressJson).toEqual({ expect(cypressJson).toEqual({
@ -112,12 +122,14 @@ describe('schematic:cypress-project', () => {
}); });
}); });
it('should set right path names in `tsconfig.e2e.json`', () => { it('should set right path names in `tsconfig.e2e.json`', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'application', .runSchematicAsync(
{ name: 'myApp', e2eTestRunner: 'cypress' }, 'application',
appTree { name: 'myApp', e2eTestRunner: 'cypress' },
); appTree
)
.toPromise();
const tsconfigJson = readJsonInTree( const tsconfigJson = readJsonInTree(
tree, tree,
'apps/my-app-e2e/tsconfig.e2e.json' 'apps/my-app-e2e/tsconfig.e2e.json'
@ -131,12 +143,14 @@ describe('schematic:cypress-project', () => {
}); });
describe('generate app --e2e-test-runner=cypress --directory=my-dir', () => { describe('generate app --e2e-test-runner=cypress --directory=my-dir', () => {
it('should set right path names in `cypress.json`', () => { it('should set right path names in `cypress.json`', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'application', .runSchematicAsync(
{ name: 'myApp', e2eTestRunner: 'cypress', directory: 'my-dir' }, 'application',
appTree { name: 'myApp', e2eTestRunner: 'cypress', directory: 'my-dir' },
); appTree
)
.toPromise();
const cypressJson = readJsonInTree( const cypressJson = readJsonInTree(
tree, tree,
'apps/my-dir/my-app-e2e/cypress.json' 'apps/my-dir/my-app-e2e/cypress.json'
@ -159,12 +173,14 @@ describe('schematic:cypress-project', () => {
}); });
}); });
it('should set right path names in `tsconfig.e2e.json`', () => { it('should set right path names in `tsconfig.e2e.json`', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'application', .runSchematicAsync(
{ name: 'myApp', e2eTestRunner: 'cypress', directory: 'my-dir' }, 'application',
appTree { name: 'myApp', e2eTestRunner: 'cypress', directory: 'my-dir' },
); appTree
)
.toPromise();
const tsconfigJson = readJsonInTree( const tsconfigJson = readJsonInTree(
tree, tree,
'apps/my-dir/my-app-e2e/tsconfig.e2e.json' 'apps/my-dir/my-app-e2e/tsconfig.e2e.json'

View File

@ -19,15 +19,17 @@ describe('downgrade-module', () => {
appTree = createApp(appTree, 'myapp'); appTree = createApp(appTree, 'myapp');
}); });
it('should update main.ts', () => { it('should update main.ts', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'downgrade-module', .runSchematicAsync(
{ 'downgrade-module',
name: 'legacy', {
project: 'myapp' name: 'legacy',
}, project: 'myapp'
appTree },
); appTree
)
.toPromise();
const main = getFileContent(tree, '/apps/myapp/src/main.ts'); const main = getFileContent(tree, '/apps/myapp/src/main.ts');
expect(main).toContain('downgradeModule(bootstrapAngular)'); expect(main).toContain('downgradeModule(bootstrapAngular)');
@ -37,15 +39,17 @@ describe('downgrade-module', () => {
); );
}); });
it('should update module', () => { it('should update module', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'downgrade-module', .runSchematicAsync(
{ 'downgrade-module',
name: 'legacy', {
project: 'myapp' name: 'legacy',
}, project: 'myapp'
appTree },
); appTree
)
.toPromise();
const appModule = getFileContent(tree, 'apps/myapp/src/app/app.module.ts'); const appModule = getFileContent(tree, 'apps/myapp/src/app/app.module.ts');
expect(appModule).not.toContain('bootstrap:'); expect(appModule).not.toContain('bootstrap:');
@ -53,7 +57,7 @@ describe('downgrade-module', () => {
expect(appModule).toContain('ngDoBootstrap'); expect(appModule).toContain('ngDoBootstrap');
}); });
it('should update package.json by default', () => { it('should update package.json by default', async () => {
appTree.overwrite( appTree.overwrite(
`/package.json`, `/package.json`,
JSON.stringify({ JSON.stringify({
@ -63,21 +67,23 @@ describe('downgrade-module', () => {
}) })
); );
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'downgrade-module', .runSchematicAsync(
{ 'downgrade-module',
name: 'legacy', {
project: 'myapp' name: 'legacy',
}, project: 'myapp'
appTree },
); appTree
)
.toPromise();
const packageJson = readJsonInTree(tree, '/package.json'); const packageJson = readJsonInTree(tree, '/package.json');
expect(packageJson.dependencies['@angular/upgrade']).toEqual('4.4.4'); expect(packageJson.dependencies['@angular/upgrade']).toEqual('4.4.4');
expect(packageJson.dependencies['angular']).toBeDefined(); expect(packageJson.dependencies['angular']).toBeDefined();
}); });
it('should not package.json when --skipPackageJson=true', () => { it('should not package.json when --skipPackageJson=true', async () => {
appTree.overwrite( appTree.overwrite(
`/package.json`, `/package.json`,
JSON.stringify({ JSON.stringify({
@ -87,30 +93,34 @@ describe('downgrade-module', () => {
}) })
); );
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'downgrade-module', .runSchematicAsync(
{ 'downgrade-module',
name: 'legacy', {
skipPackageJson: true, name: 'legacy',
project: 'myapp' skipPackageJson: true,
}, project: 'myapp'
appTree },
); appTree
)
.toPromise();
const packageJson = readJsonInTree(tree, 'package.json'); const packageJson = readJsonInTree(tree, 'package.json');
expect(packageJson.dependencies['@angular/upgrade']).not.toBeDefined(); expect(packageJson.dependencies['@angular/upgrade']).not.toBeDefined();
}); });
it('should support custom angularJsImport', () => { it('should support custom angularJsImport', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'downgrade-module', .runSchematicAsync(
{ 'downgrade-module',
name: 'legacy', {
angularJsImport: 'legacy-app', name: 'legacy',
project: 'myapp' angularJsImport: 'legacy-app',
}, project: 'myapp'
appTree },
); appTree
)
.toPromise();
const main = getFileContent(tree, '/apps/myapp/src/main.ts'); const main = getFileContent(tree, '/apps/myapp/src/main.ts');
expect(main).toContain(`import 'legacy-app';`); expect(main).toContain(`import 'legacy-app';`);

View File

@ -18,8 +18,7 @@
}, },
"angularJsImport": { "angularJsImport": {
"type": "string", "type": "string",
"description": "description": "Import expression of the AngularJS application (e.g., --angularJsImport=some_node_module/my_app)."
"Import expression of the AngularJS application (e.g., --angularJsImport=some_node_module/my_app)."
}, },
"skipFormat": { "skipFormat": {
"description": "Skip formatting files", "description": "Skip formatting files",
@ -29,8 +28,7 @@
"skipPackageJson": { "skipPackageJson": {
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "description": "Do not add @angular/upgrade to package.json (e.g., --skipPackageJson)"
"Do not add @angular/upgrade to package.json (e.g., --skipPackageJson)"
} }
}, },
"required": ["project"] "required": ["project"]

View File

@ -12,40 +12,46 @@ describe('lib', () => {
let appTree: Tree; let appTree: Tree;
beforeEach(() => { beforeEach(async () => {
appTree = new VirtualTree(); appTree = new VirtualTree();
appTree = createEmptyWorkspace(appTree); appTree = createEmptyWorkspace(appTree);
appTree = schematicRunner.runSchematic( appTree = await schematicRunner
'lib', .runSchematicAsync(
{ 'lib',
name: 'lib1', {
unitTestRunner: 'none' name: 'lib1',
}, unitTestRunner: 'none'
appTree },
); appTree
)
.toPromise();
}); });
it('should generate files', () => { it('should generate files', async () => {
const resultTree = schematicRunner.runSchematic( const resultTree = await schematicRunner
'jest-project', .runSchematicAsync(
{ 'jest-project',
project: 'lib1' {
}, project: 'lib1'
appTree },
); appTree
)
.toPromise();
expect(resultTree.exists('/libs/lib1/src/test-setup.ts')).toBeTruthy(); expect(resultTree.exists('/libs/lib1/src/test-setup.ts')).toBeTruthy();
expect(resultTree.exists('/libs/lib1/jest.config.js')).toBeTruthy(); expect(resultTree.exists('/libs/lib1/jest.config.js')).toBeTruthy();
expect(resultTree.exists('/libs/lib1/tsconfig.spec.json')).toBeTruthy(); expect(resultTree.exists('/libs/lib1/tsconfig.spec.json')).toBeTruthy();
}); });
it('should alter angular.json', () => { it('should alter angular.json', async () => {
const resultTree = schematicRunner.runSchematic( const resultTree = await schematicRunner
'jest-project', .runSchematicAsync(
{ 'jest-project',
project: 'lib1' {
}, project: 'lib1'
appTree },
); appTree
)
.toPromise();
const angularJson = readJsonInTree(resultTree, 'angular.json'); const angularJson = readJsonInTree(resultTree, 'angular.json');
expect(angularJson.projects.lib1.architect.test).toEqual({ expect(angularJson.projects.lib1.architect.test).toEqual({
builder: '@nrwl/builders:jest', builder: '@nrwl/builders:jest',
@ -60,14 +66,16 @@ describe('lib', () => {
); );
}); });
it('should create a jest.config.js', () => { it('should create a jest.config.js', async () => {
const resultTree = schematicRunner.runSchematic( const resultTree = await schematicRunner
'jest-project', .runSchematicAsync(
{ 'jest-project',
project: 'lib1' {
}, project: 'lib1'
appTree },
); appTree
)
.toPromise();
expect(resultTree.readContent('libs/lib1/jest.config.js')) expect(resultTree.readContent('libs/lib1/jest.config.js'))
.toBe(`module.exports = { .toBe(`module.exports = {
name: 'lib1', name: 'lib1',
@ -77,27 +85,31 @@ describe('lib', () => {
`); `);
}); });
it('should update the local tsconfig.json', () => { it('should update the local tsconfig.json', async () => {
const resultTree = schematicRunner.runSchematic( const resultTree = await schematicRunner
'jest-project', .runSchematicAsync(
{ 'jest-project',
project: 'lib1' {
}, project: 'lib1'
appTree },
); appTree
)
.toPromise();
const tsConfig = readJsonInTree(resultTree, 'libs/lib1/tsconfig.json'); const tsConfig = readJsonInTree(resultTree, 'libs/lib1/tsconfig.json');
expect(tsConfig.compilerOptions.types).toContain('jest'); expect(tsConfig.compilerOptions.types).toContain('jest');
expect(tsConfig.compilerOptions.types).toContain('node'); expect(tsConfig.compilerOptions.types).toContain('node');
}); });
it('should create a tsconfig.spec.json', () => { it('should create a tsconfig.spec.json', async () => {
const resultTree = schematicRunner.runSchematic( const resultTree = await schematicRunner
'jest-project', .runSchematicAsync(
{ 'jest-project',
project: 'lib1' {
}, project: 'lib1'
appTree },
); appTree
)
.toPromise();
const tsConfig = readJsonInTree(resultTree, 'libs/lib1/tsconfig.spec.json'); const tsConfig = readJsonInTree(resultTree, 'libs/lib1/tsconfig.spec.json');
expect(tsConfig).toEqual({ expect(tsConfig).toEqual({
extends: './tsconfig.json', extends: './tsconfig.json',
@ -112,42 +124,48 @@ describe('lib', () => {
}); });
describe('--skip-setup-file', () => { describe('--skip-setup-file', () => {
it('should generate src/test-setup.ts', () => { it('should generate src/test-setup.ts', async () => {
const resultTree = schematicRunner.runSchematic( const resultTree = await schematicRunner
'jest-project', .runSchematicAsync(
{ 'jest-project',
project: 'lib1', {
skipSetupFile: true project: 'lib1',
}, skipSetupFile: true
appTree },
); appTree
)
.toPromise();
expect(resultTree.exists('src/test-setup.ts')).toBeFalsy(); expect(resultTree.exists('src/test-setup.ts')).toBeFalsy();
}); });
it('should not list the setup file in angular.json', () => { it('should not list the setup file in angular.json', async () => {
const resultTree = schematicRunner.runSchematic( const resultTree = await schematicRunner
'jest-project', .runSchematicAsync(
{ 'jest-project',
project: 'lib1', {
skipSetupFile: true project: 'lib1',
}, skipSetupFile: true
appTree },
); appTree
)
.toPromise();
const angularJson = readJsonInTree(resultTree, 'angular.json'); const angularJson = readJsonInTree(resultTree, 'angular.json');
expect( expect(
angularJson.projects.lib1.architect.test.options.setupFile angularJson.projects.lib1.architect.test.options.setupFile
).toBeUndefined(); ).toBeUndefined();
}); });
it('should not list the setup file in tsconfig.spec.json', () => { it('should not list the setup file in tsconfig.spec.json', async () => {
const resultTree = schematicRunner.runSchematic( const resultTree = await schematicRunner
'jest-project', .runSchematicAsync(
{ 'jest-project',
project: 'lib1', {
skipSetupFile: true project: 'lib1',
}, skipSetupFile: true
appTree },
); appTree
)
.toPromise();
const tsConfig = readJsonInTree( const tsConfig = readJsonInTree(
resultTree, resultTree,
'libs/lib1/tsconfig.spec.json' 'libs/lib1/tsconfig.spec.json'

View File

@ -21,12 +21,10 @@ describe('lib', () => {
}); });
describe('not nested', () => { describe('not nested', () => {
it('should update ng-package.json', () => { it('should update ng-package.json', async () => {
const publishableTree = schematicRunner.runSchematic( const publishableTree = await schematicRunner
'lib', .runSchematicAsync('lib', { name: 'myLib', publishable: true }, appTree)
{ name: 'myLib', publishable: true }, .toPromise();
appTree
);
let ngPackage = readJsonInTree( let ngPackage = readJsonInTree(
publishableTree, publishableTree,
'libs/my-lib/ng-package.json' 'libs/my-lib/ng-package.json'
@ -35,76 +33,70 @@ describe('lib', () => {
expect(ngPackage.dest).toEqual('../../dist/libs/my-lib'); expect(ngPackage.dest).toEqual('../../dist/libs/my-lib');
}); });
it('should not update package.json by default', () => { it('should not update package.json by default', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync('lib', { name: 'myLib' }, appTree)
{ name: 'myLib' }, .toPromise();
appTree
);
const packageJson = readJsonInTree(tree, '/package.json'); const packageJson = readJsonInTree(tree, '/package.json');
expect(packageJson.devDependencies['ng-packagr']).toBeUndefined(); expect(packageJson.devDependencies['ng-packagr']).toBeUndefined();
}); });
it('should update package.json when publishable', () => { it('should update package.json when publishable', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync('lib', { name: 'myLib', publishable: true }, appTree)
{ name: 'myLib', publishable: true }, .toPromise();
appTree
);
const packageJson = readJsonInTree(tree, '/package.json'); const packageJson = readJsonInTree(tree, '/package.json');
expect(packageJson.devDependencies['ng-packagr']).toBeDefined(); expect(packageJson.devDependencies['ng-packagr']).toBeDefined();
}); });
it("should update npmScope of lib's package.json when publishable", () => { it("should update npmScope of lib's package.json when publishable", async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync('lib', { name: 'myLib', publishable: true }, appTree)
{ name: 'myLib', publishable: true }, .toPromise();
appTree
);
const packageJson = readJsonInTree(tree, '/libs/my-lib/package.json'); const packageJson = readJsonInTree(tree, '/libs/my-lib/package.json');
expect(packageJson.name).toEqual('@proj/my-lib'); expect(packageJson.name).toEqual('@proj/my-lib');
}); });
it("should update npmScope of lib's package.json when publishable", () => { it("should update npmScope of lib's package.json when publishable", async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync(
{ name: 'myLib', publishable: true, prefix: 'lib' }, 'lib',
appTree { name: 'myLib', publishable: true, prefix: 'lib' },
); appTree
)
.toPromise();
const packageJson = readJsonInTree(tree, '/libs/my-lib/package.json'); const packageJson = readJsonInTree(tree, '/libs/my-lib/package.json');
expect(packageJson.name).toEqual('@proj/my-lib'); expect(packageJson.name).toEqual('@proj/my-lib');
}); });
it('should update angular.json', () => { it('should update angular.json', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync('lib', { name: 'myLib', publishable: true }, appTree)
{ name: 'myLib', publishable: true }, .toPromise();
appTree
);
const angularJson = readJsonInTree(tree, '/angular.json'); const angularJson = readJsonInTree(tree, '/angular.json');
expect(angularJson.projects['my-lib'].root).toEqual('libs/my-lib'); expect(angularJson.projects['my-lib'].root).toEqual('libs/my-lib');
expect(angularJson.projects['my-lib'].architect.build).toBeDefined(); expect(angularJson.projects['my-lib'].architect.build).toBeDefined();
}); });
it('should remove "build" target from angular.json when a library is not publishable', () => { it('should remove "build" target from angular.json when a library is not publishable', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync(
{ name: 'myLib', publishable: false }, 'lib',
appTree { name: 'myLib', publishable: false },
); appTree
)
.toPromise();
const angularJson = readJsonInTree(tree, '/angular.json'); const angularJson = readJsonInTree(tree, '/angular.json');
expect(angularJson.projects['my-lib'].root).toEqual('libs/my-lib'); expect(angularJson.projects['my-lib'].root).toEqual('libs/my-lib');
expect(angularJson.projects['my-lib'].architect.build).not.toBeDefined(); expect(angularJson.projects['my-lib'].architect.build).not.toBeDefined();
}); });
it('should update nx.json', () => { it('should update nx.json', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync('lib', { name: 'myLib', tags: 'one,two' }, appTree)
{ name: 'myLib', tags: 'one,two' }, .toPromise();
appTree
);
const nxJson = readJsonInTree<NxJson>(tree, '/nx.json'); const nxJson = readJsonInTree<NxJson>(tree, '/nx.json');
expect(nxJson).toEqual({ expect(nxJson).toEqual({
npmScope: 'proj', npmScope: 'proj',
@ -116,24 +108,20 @@ describe('lib', () => {
}); });
}); });
it('should update root tsconfig.json', () => { it('should update root tsconfig.json', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync('lib', { name: 'myLib' }, appTree)
{ name: 'myLib' }, .toPromise();
appTree
);
const tsconfigJson = readJsonInTree(tree, '/tsconfig.json'); const tsconfigJson = readJsonInTree(tree, '/tsconfig.json');
expect(tsconfigJson.compilerOptions.paths['@proj/my-lib']).toEqual([ expect(tsconfigJson.compilerOptions.paths['@proj/my-lib']).toEqual([
'libs/my-lib/src/index.ts' 'libs/my-lib/src/index.ts'
]); ]);
}); });
it('should create a local tsconfig.json', () => { it('should create a local tsconfig.json', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync('lib', { name: 'myLib' }, appTree)
{ name: 'myLib' }, .toPromise();
appTree
);
const tsconfigJson = readJsonInTree(tree, 'libs/my-lib/tsconfig.json'); const tsconfigJson = readJsonInTree(tree, 'libs/my-lib/tsconfig.json');
expect(tsconfigJson).toEqual({ expect(tsconfigJson).toEqual({
@ -145,12 +133,10 @@ describe('lib', () => {
}); });
}); });
it('should extend the local tsconfig.json with tsconfig.spec.json', () => { it('should extend the local tsconfig.json with tsconfig.spec.json', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync('lib', { name: 'myLib' }, appTree)
{ name: 'myLib' }, .toPromise();
appTree
);
const tsconfigJson = readJsonInTree( const tsconfigJson = readJsonInTree(
tree, tree,
@ -159,12 +145,10 @@ describe('lib', () => {
expect(tsconfigJson.extends).toEqual('./tsconfig.json'); expect(tsconfigJson.extends).toEqual('./tsconfig.json');
}); });
it('should extend the local tsconfig.json with tsconfig.lib.json', () => { it('should extend the local tsconfig.json with tsconfig.lib.json', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync('lib', { name: 'myLib' }, appTree)
{ name: 'myLib' }, .toPromise();
appTree
);
const tsconfigJson = readJsonInTree( const tsconfigJson = readJsonInTree(
tree, tree,
@ -173,12 +157,10 @@ describe('lib', () => {
expect(tsconfigJson.extends).toEqual('./tsconfig.json'); expect(tsconfigJson.extends).toEqual('./tsconfig.json');
}); });
it('should generate files', () => { it('should generate files', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync('lib', { name: 'myLib' }, appTree)
{ name: 'myLib' }, .toPromise();
appTree
);
expect(tree.exists(`libs/my-lib/karma.conf.js`)).toBeTruthy(); expect(tree.exists(`libs/my-lib/karma.conf.js`)).toBeTruthy();
expect(tree.exists('libs/my-lib/src/index.ts')).toBeTruthy(); expect(tree.exists('libs/my-lib/src/index.ts')).toBeTruthy();
expect(tree.exists('libs/my-lib/src/lib/my-lib.module.ts')).toBeTruthy(); expect(tree.exists('libs/my-lib/src/lib/my-lib.module.ts')).toBeTruthy();
@ -194,11 +176,13 @@ describe('lib', () => {
tree.exists('libs/my-lib/src/lib/my-lib.service.spec.ts') tree.exists('libs/my-lib/src/lib/my-lib.service.spec.ts')
).toBeFalsy(); ).toBeFalsy();
const tree2 = schematicRunner.runSchematic( const tree2 = await schematicRunner
'lib', .runSchematicAsync(
{ name: 'myLib2', simpleModuleName: true }, 'lib',
tree { name: 'myLib2', simpleModuleName: true },
); tree
)
.toPromise();
expect(tree2.exists(`libs/my-lib2/karma.conf.js`)).toBeTruthy(); expect(tree2.exists(`libs/my-lib2/karma.conf.js`)).toBeTruthy();
expect(tree2.exists('libs/my-lib2/src/index.ts')).toBeTruthy(); expect(tree2.exists('libs/my-lib2/src/index.ts')).toBeTruthy();
expect( expect(
@ -219,12 +203,10 @@ describe('lib', () => {
).toBeFalsy(); ).toBeFalsy();
}); });
it('should not generate a module for --module false', () => { it('should not generate a module for --module false', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync('lib', { name: 'myLib', module: false }, appTree)
{ name: 'myLib', module: false }, .toPromise();
appTree
);
expect(tree.exists('libs/my-lib/src/lib/my-lib.module.ts')).toEqual( expect(tree.exists('libs/my-lib/src/lib/my-lib.module.ts')).toEqual(
false false
); );
@ -234,22 +216,18 @@ describe('lib', () => {
expect(tree.exists('libs/my-lib/src/lib/.gitkeep')).toEqual(true); expect(tree.exists('libs/my-lib/src/lib/.gitkeep')).toEqual(true);
}); });
it('should default the prefix to npmScope', () => { it('should default the prefix to npmScope', async () => {
const noPrefix = schematicRunner.runSchematic( const noPrefix = await schematicRunner
'lib', .runSchematicAsync('lib', { name: 'myLib' }, appTree)
{ name: 'myLib' }, .toPromise();
appTree
);
expect( expect(
JSON.parse(noPrefix.read('angular.json').toString()).projects['my-lib'] JSON.parse(noPrefix.read('angular.json').toString()).projects['my-lib']
.prefix .prefix
).toEqual('proj'); ).toEqual('proj');
const withPrefix = schematicRunner.runSchematic( const withPrefix = await schematicRunner
'app', .runSchematicAsync('lib', { name: 'myLib', prefix: 'custom' }, appTree)
{ name: 'myLib', prefix: 'custom' }, .toPromise();
appTree
);
expect( expect(
JSON.parse(withPrefix.read('angular.json').toString()).projects[ JSON.parse(withPrefix.read('angular.json').toString()).projects[
'my-lib' 'my-lib'
@ -259,12 +237,14 @@ describe('lib', () => {
}); });
describe('nested', () => { describe('nested', () => {
it('should update nx.json', () => { it('should update nx.json', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync(
{ name: 'myLib', directory: 'myDir', tags: 'one' }, 'lib',
appTree { name: 'myLib', directory: 'myDir', tags: 'one' },
); appTree
)
.toPromise();
const nxJson = readJsonInTree<NxJson>(tree, '/nx.json'); const nxJson = readJsonInTree<NxJson>(tree, '/nx.json');
expect(nxJson).toEqual({ expect(nxJson).toEqual({
npmScope: 'proj', npmScope: 'proj',
@ -275,16 +255,18 @@ describe('lib', () => {
} }
}); });
const tree2 = schematicRunner.runSchematic( const tree2 = await schematicRunner
'lib', .runSchematicAsync(
{ 'lib',
name: 'myLib2', {
directory: 'myDir', name: 'myLib2',
tags: 'one,two', directory: 'myDir',
simpleModuleName: true tags: 'one,two',
}, simpleModuleName: true
tree },
); tree
)
.toPromise();
const nxJson2 = readJsonInTree<NxJson>(tree2, '/nx.json'); const nxJson2 = readJsonInTree<NxJson>(tree2, '/nx.json');
expect(nxJson2).toEqual({ expect(nxJson2).toEqual({
npmScope: 'proj', npmScope: 'proj',
@ -299,12 +281,14 @@ describe('lib', () => {
}); });
}); });
it('should generate files', () => { it('should generate files', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync(
{ name: 'myLib', directory: 'myDir' }, 'lib',
appTree { name: 'myLib', directory: 'myDir' },
); appTree
)
.toPromise();
expect(tree.exists(`libs/my-dir/my-lib/karma.conf.js`)).toBeTruthy(); expect(tree.exists(`libs/my-dir/my-lib/karma.conf.js`)).toBeTruthy();
expect(tree.exists('libs/my-dir/my-lib/src/index.ts')).toBeTruthy(); expect(tree.exists('libs/my-dir/my-lib/src/index.ts')).toBeTruthy();
expect( expect(
@ -324,11 +308,13 @@ describe('lib', () => {
tree.exists('libs/my-dir/my-lib/src/lib/my-lib.service.spec.ts') tree.exists('libs/my-dir/my-lib/src/lib/my-lib.service.spec.ts')
).toBeFalsy(); ).toBeFalsy();
const tree2 = schematicRunner.runSchematic( const tree2 = await schematicRunner
'lib', .runSchematicAsync(
{ name: 'myLib2', directory: 'myDir', simpleModuleName: true }, 'lib',
tree { name: 'myLib2', directory: 'myDir', simpleModuleName: true },
); tree
)
.toPromise();
expect(tree2.exists(`libs/my-dir/my-lib2/karma.conf.js`)).toBeTruthy(); expect(tree2.exists(`libs/my-dir/my-lib2/karma.conf.js`)).toBeTruthy();
expect(tree2.exists('libs/my-dir/my-lib2/src/index.ts')).toBeTruthy(); expect(tree2.exists('libs/my-dir/my-lib2/src/index.ts')).toBeTruthy();
expect( expect(
@ -349,12 +335,14 @@ describe('lib', () => {
).toBeFalsy(); ).toBeFalsy();
}); });
it('should update ng-package.json', () => { it('should update ng-package.json', async () => {
const publishableTree = schematicRunner.runSchematic( const publishableTree = await schematicRunner
'lib', .runSchematicAsync(
{ name: 'myLib', directory: 'myDir', publishable: true }, 'lib',
appTree { name: 'myLib', directory: 'myDir', publishable: true },
); appTree
)
.toPromise();
let ngPackage = readJsonInTree( let ngPackage = readJsonInTree(
publishableTree, publishableTree,
@ -363,12 +351,14 @@ describe('lib', () => {
expect(ngPackage.dest).toEqual('../../../dist/libs/my-dir/my-lib'); expect(ngPackage.dest).toEqual('../../../dist/libs/my-dir/my-lib');
}); });
it('should update angular.json', () => { it('should update angular.json', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync(
{ name: 'myLib', directory: 'myDir' }, 'lib',
appTree { name: 'myLib', directory: 'myDir' },
); appTree
)
.toPromise();
const angularJson = readJsonInTree(tree, '/angular.json'); const angularJson = readJsonInTree(tree, '/angular.json');
expect(angularJson.projects['my-dir-my-lib'].root).toEqual( expect(angularJson.projects['my-dir-my-lib'].root).toEqual(
@ -376,12 +366,14 @@ describe('lib', () => {
); );
}); });
it('should update tsconfig.json', () => { it('should update tsconfig.json', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync(
{ name: 'myLib', directory: 'myDir' }, 'lib',
appTree { name: 'myLib', directory: 'myDir' },
); appTree
)
.toPromise();
const tsconfigJson = readJsonInTree(tree, '/tsconfig.json'); const tsconfigJson = readJsonInTree(tree, '/tsconfig.json');
expect(tsconfigJson.compilerOptions.paths['@proj/my-dir/my-lib']).toEqual( expect(tsconfigJson.compilerOptions.paths['@proj/my-dir/my-lib']).toEqual(
['libs/my-dir/my-lib/src/index.ts'] ['libs/my-dir/my-lib/src/index.ts']
@ -391,12 +383,14 @@ describe('lib', () => {
).toBeUndefined(); ).toBeUndefined();
}); });
it('should create a local tsconfig.json', () => { it('should create a local tsconfig.json', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync(
{ name: 'myLib', directory: 'myDir' }, 'lib',
appTree { name: 'myLib', directory: 'myDir' },
); appTree
)
.toPromise();
const tsconfigJson = readJsonInTree( const tsconfigJson = readJsonInTree(
tree, tree,
@ -411,12 +405,14 @@ describe('lib', () => {
}); });
}); });
it('should not generate a module for --module false', () => { it('should not generate a module for --module false', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync(
{ name: 'myLib', directory: 'myDir', module: false }, 'lib',
appTree { name: 'myLib', directory: 'myDir', module: false },
); appTree
)
.toPromise();
expect( expect(
tree.exists('libs/my-dir/my-lib/src/lib/my-dir-my-lib.module.ts') tree.exists('libs/my-dir/my-lib/src/lib/my-dir-my-lib.module.ts')
).toEqual(false); ).toEqual(false);
@ -428,23 +424,26 @@ describe('lib', () => {
}); });
describe('router', () => { describe('router', () => {
it('should error when lazy is set without routing', () => { it('should error when lazy is set without routing', async () => {
expect(() => try {
schematicRunner.runSchematic( await schematicRunner
'lib', .runSchematicAsync('lib', { name: 'myLib', lazy: true }, appTree)
{ name: 'myLib', lazy: true }, .toPromise();
appTree fail();
) } catch (e) {
).toThrow('routing must be set'); expect(e.message).toEqual('routing must be set');
}
}); });
describe('lazy', () => { describe('lazy', () => {
it('should add RouterModule.forChild', () => { it('should add RouterModule.forChild', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync(
{ name: 'myLib', directory: 'myDir', routing: true, lazy: true }, 'lib',
appTree { name: 'myLib', directory: 'myDir', routing: true, lazy: true },
); appTree
)
.toPromise();
expect( expect(
tree.exists('libs/my-dir/my-lib/src/lib/my-dir-my-lib.module.ts') tree.exists('libs/my-dir/my-lib/src/lib/my-dir-my-lib.module.ts')
@ -456,17 +455,19 @@ describe('lib', () => {
) )
).toContain('RouterModule.forChild'); ).toContain('RouterModule.forChild');
const tree2 = schematicRunner.runSchematic( const tree2 = await schematicRunner
'lib', .runSchematicAsync(
{ 'lib',
name: 'myLib2', {
directory: 'myDir', name: 'myLib2',
routing: true, directory: 'myDir',
lazy: true, routing: true,
simpleModuleName: true lazy: true,
}, simpleModuleName: true
tree },
); tree
)
.toPromise();
expect( expect(
tree2.exists('libs/my-dir/my-lib2/src/lib/my-lib2.module.ts') tree2.exists('libs/my-dir/my-lib2/src/lib/my-lib2.module.ts')
@ -476,24 +477,30 @@ describe('lib', () => {
).toContain('RouterModule.forChild'); ).toContain('RouterModule.forChild');
}); });
it('should update the parent module', () => { it('should update the parent module', async () => {
appTree = createApp(appTree, 'myapp'); appTree = createApp(appTree, 'myapp');
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync(
{ 'lib',
name: 'myLib', {
directory: 'myDir', name: 'myLib',
routing: true, directory: 'myDir',
lazy: true, routing: true,
parentModule: 'apps/myapp/src/app/app.module.ts' lazy: true,
}, parentModule: 'apps/myapp/src/app/app.module.ts'
appTree },
); appTree
expect( )
getFileContent(tree, 'apps/myapp/src/app/app.module.ts') .toPromise();
).toContain( const moduleContents = getFileContent(
`RouterModule.forRoot([{path: 'my-dir-my-lib', loadChildren: '@proj/my-dir/my-lib#MyDirMyLibModule'}])` tree,
'apps/myapp/src/app/app.module.ts'
); );
expect(moduleContents).toContain('RouterModule.forRoot([');
expect(moduleContents).toContain(`{
path: 'my-dir-my-lib',
loadChildren: '@proj/my-dir/my-lib#MyDirMyLibModule'
}`);
const tsConfigAppJson = JSON.parse( const tsConfigAppJson = JSON.parse(
stripJsonComments( stripJsonComments(
@ -505,22 +512,32 @@ describe('lib', () => {
'../../libs/my-dir/my-lib/src/index.ts' '../../libs/my-dir/my-lib/src/index.ts'
]); ]);
const tree2 = schematicRunner.runSchematic( const tree2 = await schematicRunner
'lib', .runSchematicAsync(
{ 'lib',
name: 'myLib2', {
directory: 'myDir', name: 'myLib2',
routing: true, directory: 'myDir',
lazy: true, routing: true,
parentModule: 'apps/myapp/src/app/app.module.ts' lazy: true,
}, parentModule: 'apps/myapp/src/app/app.module.ts'
tree },
); tree
expect( )
getFileContent(tree2, 'apps/myapp/src/app/app.module.ts') .toPromise();
).toContain( const moduleContents2 = getFileContent(
`RouterModule.forRoot([{path: 'my-dir-my-lib', loadChildren: '@proj/my-dir/my-lib#MyDirMyLibModule'}, {path: 'my-dir-my-lib2', loadChildren: '@proj/my-dir/my-lib2#MyDirMyLib2Module'}])` tree2,
'apps/myapp/src/app/app.module.ts'
); );
expect(moduleContents2).toContain('RouterModule.forRoot([');
expect(moduleContents2).toContain(`{
path: 'my-dir-my-lib',
loadChildren: '@proj/my-dir/my-lib#MyDirMyLibModule'
}`);
expect(moduleContents2).toContain(`{
path: 'my-dir-my-lib2',
loadChildren: '@proj/my-dir/my-lib2#MyDirMyLib2Module'
}`);
const tsConfigAppJson2 = JSON.parse( const tsConfigAppJson2 = JSON.parse(
stripJsonComments( stripJsonComments(
@ -533,22 +550,35 @@ describe('lib', () => {
'../../libs/my-dir/my-lib2/src/index.ts' '../../libs/my-dir/my-lib2/src/index.ts'
]); ]);
const tree3 = schematicRunner.runSchematic( const tree3 = await schematicRunner
'lib', .runSchematicAsync(
{ 'lib',
name: 'myLib3', {
directory: 'myDir', name: 'myLib3',
routing: true, directory: 'myDir',
lazy: true, routing: true,
parentModule: 'apps/myapp/src/app/app.module.ts', lazy: true,
simpleModuleName: true parentModule: 'apps/myapp/src/app/app.module.ts',
}, simpleModuleName: true
tree2 },
tree2
)
.toPromise();
const moduleContents3 = getFileContent(
tree3,
'apps/myapp/src/app/app.module.ts'
); );
expect( expect(moduleContents3).toContain('RouterModule.forRoot([');
getFileContent(tree3, 'apps/myapp/src/app/app.module.ts') expect(moduleContents3).toContain(`{
).toContain( path: 'my-dir-my-lib',
`RouterModule.forRoot([{path: 'my-dir-my-lib', loadChildren: '@proj/my-dir/my-lib#MyDirMyLibModule'}, {path: 'my-dir-my-lib2', loadChildren: '@proj/my-dir/my-lib2#MyDirMyLib2Module'}, {path: 'my-lib3', loadChildren: '@proj/my-dir/my-lib3#MyLib3Module'}])` loadChildren: '@proj/my-dir/my-lib#MyDirMyLibModule'
}`);
expect(moduleContents3).toContain(`{
path: 'my-dir-my-lib2',
loadChildren: '@proj/my-dir/my-lib2#MyDirMyLib2Module'
}`);
expect(moduleContents3).toContain(
`{ path: 'my-lib3', loadChildren: '@proj/my-dir/my-lib3#MyLib3Module' }`
); );
const tsConfigAppJson3 = JSON.parse( const tsConfigAppJson3 = JSON.parse(
@ -566,12 +596,14 @@ describe('lib', () => {
}); });
describe('eager', () => { describe('eager', () => {
it('should add RouterModule and define an array of routes', () => { it('should add RouterModule and define an array of routes', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync(
{ name: 'myLib', directory: 'myDir', routing: true }, 'lib',
appTree { name: 'myLib', directory: 'myDir', routing: true },
); appTree
)
.toPromise();
expect( expect(
tree.exists('libs/my-dir/my-lib/src/lib/my-dir-my-lib.module.ts') tree.exists('libs/my-dir/my-lib/src/lib/my-dir-my-lib.module.ts')
).toBeTruthy(); ).toBeTruthy();
@ -588,16 +620,18 @@ describe('lib', () => {
) )
).toContain('const myDirMyLibRoutes: Route[] = '); ).toContain('const myDirMyLibRoutes: Route[] = ');
const tree2 = schematicRunner.runSchematic( const tree2 = await schematicRunner
'lib', .runSchematicAsync(
{ 'lib',
name: 'myLib2', {
directory: 'myDir', name: 'myLib2',
routing: true, directory: 'myDir',
simpleModuleName: true routing: true,
}, simpleModuleName: true
tree },
); tree
)
.toPromise();
expect( expect(
tree2.exists('libs/my-dir/my-lib2/src/lib/my-lib2.module.ts') tree2.exists('libs/my-dir/my-lib2/src/lib/my-lib2.module.ts')
).toBeTruthy(); ).toBeTruthy();
@ -609,67 +643,89 @@ describe('lib', () => {
).toContain('const myLib2Routes: Route[] = '); ).toContain('const myLib2Routes: Route[] = ');
}); });
it('should update the parent module', () => { it('should update the parent module', async () => {
appTree = createApp(appTree, 'myapp'); appTree = createApp(appTree, 'myapp');
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'lib', .runSchematicAsync(
{ 'lib',
name: 'myLib', {
directory: 'myDir', name: 'myLib',
routing: true, directory: 'myDir',
parentModule: 'apps/myapp/src/app/app.module.ts' routing: true,
}, parentModule: 'apps/myapp/src/app/app.module.ts'
appTree },
appTree
)
.toPromise();
const moduleContents = getFileContent(
tree,
'apps/myapp/src/app/app.module.ts'
); );
expect( expect(moduleContents).toContain('RouterModule.forRoot([');
getFileContent(tree, 'apps/myapp/src/app/app.module.ts') expect(moduleContents).toContain(
).toContain( "{ path: 'my-dir-my-lib', children: myDirMyLibRoutes }"
`RouterModule.forRoot([{path: 'my-dir-my-lib', children: myDirMyLibRoutes}])`
); );
const tree2 = schematicRunner.runSchematic( const tree2 = await schematicRunner
'lib', .runSchematicAsync(
{ 'lib',
name: 'myLib2', {
directory: 'myDir', name: 'myLib2',
routing: true, directory: 'myDir',
parentModule: 'apps/myapp/src/app/app.module.ts' routing: true,
}, parentModule: 'apps/myapp/src/app/app.module.ts'
tree },
tree
)
.toPromise();
const moduleContents2 = getFileContent(
tree2,
'apps/myapp/src/app/app.module.ts'
); );
expect( expect(moduleContents2).toContain('RouterModule.forRoot([');
getFileContent(tree2, 'apps/myapp/src/app/app.module.ts') expect(moduleContents2).toContain(
).toContain( "{ path: 'my-dir-my-lib', children: myDirMyLibRoutes }"
`RouterModule.forRoot([{path: 'my-dir-my-lib', children: myDirMyLibRoutes}, {path: 'my-dir-my-lib2', children: myDirMyLib2Routes}])` );
expect(moduleContents2).toContain(
"{ path: 'my-dir-my-lib2', children: myDirMyLib2Routes }"
); );
const tree3 = schematicRunner.runSchematic( const tree3 = await schematicRunner
'lib', .runSchematicAsync(
{ 'lib',
name: 'myLib3', {
directory: 'myDir', name: 'myLib3',
routing: true, directory: 'myDir',
parentModule: 'apps/myapp/src/app/app.module.ts', routing: true,
simpleModuleName: true parentModule: 'apps/myapp/src/app/app.module.ts',
}, simpleModuleName: true
tree2 },
tree2
)
.toPromise();
const moduleContents3 = getFileContent(
tree3,
'apps/myapp/src/app/app.module.ts'
); );
expect( expect(moduleContents3).toContain('RouterModule.forRoot([');
getFileContent(tree3, 'apps/myapp/src/app/app.module.ts') expect(moduleContents3).toContain(
).toContain( "{ path: 'my-dir-my-lib', children: myDirMyLibRoutes }"
`RouterModule.forRoot([{path: 'my-dir-my-lib', children: myDirMyLibRoutes}, {path: 'my-dir-my-lib2', children: myDirMyLib2Routes}, {path: 'my-lib3', children: myLib3Routes}])` );
expect(moduleContents3).toContain(
"{ path: 'my-dir-my-lib2', children: myDirMyLib2Routes }"
);
expect(moduleContents3).toContain(
"{ path: 'my-lib3', children: myLib3Routes }"
); );
}); });
}); });
}); });
describe('--style scss', () => { describe('--style scss', () => {
it('should set it as default', () => { it('should set it as default', async () => {
const result = schematicRunner.runSchematic( const result = await schematicRunner
'lib', .runSchematicAsync('lib', { name: 'myLib', style: 'scss' }, appTree)
{ name: 'myLib', style: 'scss' }, .toPromise();
appTree
);
const angularJson = readJsonInTree(result, 'angular.json'); const angularJson = readJsonInTree(result, 'angular.json');
@ -682,12 +738,14 @@ describe('lib', () => {
}); });
describe('--unit-test-runner jest', () => { describe('--unit-test-runner jest', () => {
it('should generate jest configuration', () => { it('should generate jest configuration', async () => {
const resultTree = schematicRunner.runSchematic( const resultTree = await schematicRunner
'lib', .runSchematicAsync(
{ name: 'myLib', unitTestRunner: 'jest' }, 'lib',
appTree { name: 'myLib', unitTestRunner: 'jest' },
); appTree
)
.toPromise();
expect(resultTree.exists('libs/my-lib/src/test.ts')).toBeFalsy(); expect(resultTree.exists('libs/my-lib/src/test.ts')).toBeFalsy();
expect(resultTree.exists('libs/my-lib/src/test-setup.ts')).toBeTruthy(); expect(resultTree.exists('libs/my-lib/src/test-setup.ts')).toBeTruthy();
expect(resultTree.exists('libs/my-lib/tsconfig.spec.json')).toBeTruthy(); expect(resultTree.exists('libs/my-lib/tsconfig.spec.json')).toBeTruthy();
@ -704,23 +762,27 @@ describe('lib', () => {
]); ]);
}); });
it('should skip the setup file if no module is generated', () => { it('should skip the setup file if no module is generated', async () => {
const resultTree = schematicRunner.runSchematic( const resultTree = await schematicRunner
'lib', .runSchematicAsync(
{ name: 'myLib', unitTestRunner: 'jest', module: false }, 'lib',
appTree { name: 'myLib', unitTestRunner: 'jest', module: false },
); appTree
)
.toPromise();
expect(resultTree.exists('libs/my-lib/src/test-setup.ts')).toBeFalsy(); expect(resultTree.exists('libs/my-lib/src/test-setup.ts')).toBeFalsy();
}); });
}); });
describe('--unit-test-runner none', () => { describe('--unit-test-runner none', () => {
it('should not generate test configuration', () => { it('should not generate test configuration', async () => {
const resultTree = schematicRunner.runSchematic( const resultTree = await schematicRunner
'lib', .runSchematicAsync(
{ name: 'myLib', unitTestRunner: 'none' }, 'lib',
appTree { name: 'myLib', unitTestRunner: 'none' },
); appTree
)
.toPromise();
expect( expect(
resultTree.exists('libs/my-lib/src/lib/my-lib.module.spec.ts') resultTree.exists('libs/my-lib/src/lib/my-lib.module.spec.ts')
).toBeFalsy(); ).toBeFalsy();

View File

@ -51,8 +51,7 @@
}, },
"parentModule": { "parentModule": {
"type": "string", "type": "string",
"description": "description": "Update the router configuration of the parent module using loadChildren or children, depending on what `lazy` is set to.",
"Update the router configuration of the parent module using loadChildren or children, depending on what `lazy` is set to.",
"x-prompt": "Which module should import the library?" "x-prompt": "Which module should import the library?"
}, },
"style": { "style": {
@ -75,14 +74,12 @@
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "Add router configuration. See lazy for more information.", "description": "Add router configuration. See lazy for more information.",
"x-prompt": "x-prompt": "Would you like to add a routing configuration to the library?"
"Would you like to add a routing configuration to the library?"
}, },
"lazy": { "lazy": {
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "description": "Add RouterModule.forChild when set to true, and a simple array of routes when set to false.",
"Add RouterModule.forChild when set to true, and a simple array of routes when set to false.",
"x-prompt": "Will this library be lazy loaded?" "x-prompt": "Will this library be lazy loaded?"
}, },
"module": { "module": {
@ -94,8 +91,7 @@
"tags": { "tags": {
"type": "string", "type": "string",
"description": "Add tags to the library (used for linting)", "description": "Add tags to the library (used for linting)",
"x-prompt": "x-prompt": "Which tags would you like to add to the library? (used for linting)"
"Which tags would you like to add to the library? (used for linting)"
}, },
"unitTestRunner": { "unitTestRunner": {
"type": "string", "type": "string",
@ -103,8 +99,7 @@
"description": "Test runner to use for unit tests", "description": "Test runner to use for unit tests",
"default": "karma", "default": "karma",
"x-prompt": { "x-prompt": {
"message": "message": "Which Unit Test Runner would you like to use for the library?",
"Which Unit Test Runner would you like to use for the library?",
"type": "list", "type": "list",
"items": [ "items": [
{ "value": "karma", "label": "Karma" }, { "value": "karma", "label": "Karma" },

View File

@ -8,8 +8,7 @@
"npmScope": { "npmScope": {
"type": "string", "type": "string",
"description": "Npm scope for importing libs.", "description": "Npm scope for importing libs.",
"x-prompt": "x-prompt": "What is the npm scope you would like to use for your Nx Workspace?"
"What is the npm scope you would like to use for your Nx Workspace?"
}, },
"skipInstall": { "skipInstall": {
"type": "boolean", "type": "boolean",

View File

@ -62,7 +62,9 @@ function addTasks(options: Schema, context: SchematicContext) {
const commit = const commit =
typeof options.commit == 'object' typeof options.commit == 'object'
? options.commit ? options.commit
: !!options.commit ? {} : false; : !!options.commit
? {}
: false;
context.addTask( context.addTask(
new RepositoryInitializerTask(options.directory, commit), new RepositoryInitializerTask(options.directory, commit),
packageTask ? [packageTask] : [] packageTask ? [packageTask] : []

View File

@ -39,8 +39,7 @@
"npmScope": { "npmScope": {
"type": "string", "type": "string",
"description": "Npm scope for importing libs.", "description": "Npm scope for importing libs.",
"x-prompt": "x-prompt": "What is the npm scope you would like to use for your Nx Workspace?"
"What is the npm scope you would like to use for your Nx Workspace?"
}, },
"skipInstall": { "skipInstall": {
"description": "Skip installing dependency packages.", "description": "Skip installing dependency packages.",
@ -57,8 +56,7 @@
"type": "string", "type": "string",
"description": "Package manager used in the project.", "description": "Package manager used in the project.",
"x-prompt": { "x-prompt": {
"message": "message": "Which Package Manager would you like to use for your workspace?",
"Which Package Manager would you like to use for your workspace?",
"type": "list", "type": "list",
"items": [ "items": [
{ "value": "npm", "label": "" }, { "value": "npm", "label": "" },

View File

@ -31,38 +31,43 @@ describe('ngrx', () => {
appTree = createApp(appTree, 'myapp'); appTree = createApp(appTree, 'myapp');
}); });
it('should add empty root', () => { it('should add empty root', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'ngrx', .runSchematicAsync(
{ 'ngrx',
name: 'state', {
module: 'apps/myapp/src/app/app.module.ts', name: 'state',
onlyEmptyRoot: true module: 'apps/myapp/src/app/app.module.ts',
}, onlyEmptyRoot: true
appTree },
); appTree
)
.toPromise();
const appModule = getFileContent(tree, '/apps/myapp/src/app/app.module.ts'); const appModule = getFileContent(tree, '/apps/myapp/src/app/app.module.ts');
expect( expect(
tree.exists('apps/myapp/src/app/+state/state.actions.ts') tree.exists('apps/myapp/src/app/+state/state.actions.ts')
).toBeFalsy(); ).toBeFalsy();
expect(appModule).toContain('StoreModule.forRoot(');
expect(appModule).toContain( expect(appModule).toContain(
'StoreModule.forRoot({},{ metaReducers : !environment.production ? [storeFreeze] : [] })' '{ metaReducers: !environment.production ? [storeFreeze] : [] }'
); );
expect(appModule).toContain('EffectsModule.forRoot'); expect(appModule).toContain('EffectsModule.forRoot');
}); });
it('should add root', () => { it('should add root', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'ngrx', .runSchematicAsync(
{ 'ngrx',
name: 'app', {
module: 'apps/myapp/src/app/app.module.ts', name: 'app',
root: true module: 'apps/myapp/src/app/app.module.ts',
}, root: true
appTree },
); appTree
)
.toPromise();
[ [
'/apps/myapp/src/app/+state/app.actions.ts', '/apps/myapp/src/app/+state/app.actions.ts',
@ -91,20 +96,22 @@ describe('ngrx', () => {
expect(appModule).toContain('!environment.production ? [storeFreeze] : []'); expect(appModule).toContain('!environment.production ? [storeFreeze] : []');
expect(appModule).toContain('app: appReducer'); expect(appModule).toContain('app: appReducer');
expect(appModule).toContain('initialState : { app : appInitialState }'); expect(appModule).toContain('initialState: { app: appInitialState }');
}); });
it('should add facade to root', () => { it('should add facade to root', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'ngrx', .runSchematicAsync(
{ 'ngrx',
name: 'app', {
module: 'apps/myapp/src/app/app.module.ts', name: 'app',
root: true, module: 'apps/myapp/src/app/app.module.ts',
facade: true root: true,
}, facade: true
appTree },
); appTree
)
.toPromise();
const appModule = getFileContent(tree, '/apps/myapp/src/app/app.module.ts'); const appModule = getFileContent(tree, '/apps/myapp/src/app/app.module.ts');
@ -118,7 +125,7 @@ describe('ngrx', () => {
expect(appModule).toContain('providers: [AppFacade]'); expect(appModule).toContain('providers: [AppFacade]');
expect(appModule).toContain('app: appReducer'); expect(appModule).toContain('app: appReducer');
expect(appModule).toContain('initialState : { app : appInitialState }'); expect(appModule).toContain('initialState: { app: appInitialState }');
[ [
'/apps/myapp/src/app/+state/app.actions.ts', '/apps/myapp/src/app/+state/app.actions.ts',
@ -135,17 +142,19 @@ describe('ngrx', () => {
}); });
}); });
it('should not add RouterStoreModule only if the module does not reference the router', () => { it('should not add RouterStoreModule only if the module does not reference the router', async () => {
const newTree = createApp(appTree, 'myapp-norouter', false); const newTree = createApp(appTree, 'myapp-norouter', false);
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'ngrx', .runSchematicAsync(
{ 'ngrx',
name: 'app', {
module: 'apps/myapp-norouter/src/app/app.module.ts', name: 'app',
root: true module: 'apps/myapp-norouter/src/app/app.module.ts',
}, root: true
newTree },
); newTree
)
.toPromise();
const appModule = getFileContent( const appModule = getFileContent(
tree, tree,
'/apps/myapp-norouter/src/app/app.module.ts' '/apps/myapp-norouter/src/app/app.module.ts'
@ -153,21 +162,23 @@ describe('ngrx', () => {
expect(appModule).not.toContain('StoreRouterConnectingModule'); expect(appModule).not.toContain('StoreRouterConnectingModule');
}); });
it('should add feature', () => { it('should add feature', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'ngrx', .runSchematicAsync(
{ 'ngrx',
name: 'state', {
module: 'apps/myapp/src/app/app.module.ts' name: 'state',
}, module: 'apps/myapp/src/app/app.module.ts'
appTree },
); appTree
)
.toPromise();
const appModule = getFileContent(tree, '/apps/myapp/src/app/app.module.ts'); const appModule = getFileContent(tree, '/apps/myapp/src/app/app.module.ts');
expect(appModule).toContain('StoreModule.forFeature'); expect(appModule).toContain('StoreModule.forFeature');
expect(appModule).toContain('EffectsModule.forFeature'); expect(appModule).toContain('EffectsModule.forFeature');
expect(appModule).toContain('STATE_FEATURE_KEY, stateReducer'); expect(appModule).toContain('STATE_FEATURE_KEY, stateReducer');
expect(appModule).toContain('{ initialState: stateInitialState }'); expect(appModule).toContain('initialState: stateInitialState');
expect(appModule).not.toContain( expect(appModule).not.toContain(
'!environment.production ? [storeFreeze] : []' '!environment.production ? [storeFreeze] : []'
); );
@ -177,16 +188,18 @@ describe('ngrx', () => {
).toBeTruthy(); ).toBeTruthy();
}); });
it('should add with custom directoryName', () => { it('should add with custom directoryName', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'ngrx', .runSchematicAsync(
{ 'ngrx',
name: 'state', {
module: 'apps/myapp/src/app/app.module.ts', name: 'state',
directory: 'myCustomState' module: 'apps/myapp/src/app/app.module.ts',
}, directory: 'myCustomState'
appTree },
); appTree
)
.toPromise();
const appModule = getFileContent(tree, '/apps/myapp/src/app/app.module.ts'); const appModule = getFileContent(tree, '/apps/myapp/src/app/app.module.ts');
expect(appModule).toContain('StoreModule.forFeature'); expect(appModule).toContain('StoreModule.forFeature');
@ -200,17 +213,19 @@ describe('ngrx', () => {
).toBeTruthy(); ).toBeTruthy();
}); });
it('should only add files', () => { it('should only add files', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'ngrx', .runSchematicAsync(
{ 'ngrx',
name: 'state', {
module: 'apps/myapp/src/app/app.module.ts', name: 'state',
onlyAddFiles: true, module: 'apps/myapp/src/app/app.module.ts',
facade: true onlyAddFiles: true,
}, facade: true
appTree },
); appTree
)
.toPromise();
const appModule = getFileContent(tree, '/apps/myapp/src/app/app.module.ts'); const appModule = getFileContent(tree, '/apps/myapp/src/app/app.module.ts');
expect(appModule).not.toContain('StoreModule'); expect(appModule).not.toContain('StoreModule');
@ -231,15 +246,17 @@ describe('ngrx', () => {
}); });
}); });
it('should update package.json', () => { it('should update package.json', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'ngrx', .runSchematicAsync(
{ 'ngrx',
name: 'state', {
module: 'apps/myapp/src/app/app.module.ts' name: 'state',
}, module: 'apps/myapp/src/app/app.module.ts'
appTree },
); appTree
)
.toPromise();
const packageJson = readJsonInTree(tree, 'package.json'); const packageJson = readJsonInTree(tree, 'package.json');
expect(packageJson.dependencies['@ngrx/store']).toBeDefined(); expect(packageJson.dependencies['@ngrx/store']).toBeDefined();
@ -249,40 +266,49 @@ describe('ngrx', () => {
expect(packageJson.devDependencies['ngrx-store-freeze']).toBeDefined(); expect(packageJson.devDependencies['ngrx-store-freeze']).toBeDefined();
}); });
it('should error when no module is provided', () => { it('should error when no module is provided', async () => {
expect(() => try {
schematicRunner.runSchematic( await schematicRunner
'ngrx', .runSchematicAsync(
{ 'ngrx',
name: 'state', {
module: '' name: 'state',
}, module: ''
appTree },
) appTree
).toThrow('The required --module option must be passed'); )
.toPromise();
fail();
} catch (e) {
expect(e.message).toEqual('The required --module option must be passed');
}
}); });
it('should error the module could not be found', () => { it('should error the module could not be found', async () => {
expect(() => try {
schematicRunner.runSchematic( await schematicRunner
'ngrx', .runSchematicAsync(
{ 'ngrx',
name: 'state', {
module: 'does-not-exist.ts' name: 'state',
}, module: 'does-not-exist.ts'
appTree },
) appTree
).toThrow('Path does not exist: does-not-exist.ts'); )
.toPromise();
} catch (e) {
expect(e.message).toEqual('Path does not exist: does-not-exist.ts');
}
}); });
describe('code generation', () => { describe('code generation', () => {
it('should scaffold the ngrx "user" files without a facade', () => { it('should scaffold the ngrx "user" files without a facade', async () => {
const appConfig = getAppConfig(); const appConfig = getAppConfig();
const hasFile = file => expect(tree.exists(file)).toBeTruthy(); const hasFile = file => expect(tree.exists(file)).toBeTruthy();
const missingFile = file => expect(tree.exists(file)).not.toBeTruthy(); const missingFile = file => expect(tree.exists(file)).not.toBeTruthy();
const statePath = `${findModuleParent(appConfig.appModule)}/+state`; const statePath = `${findModuleParent(appConfig.appModule)}/+state`;
const tree = buildNgrxTree(appConfig); const tree = await buildNgrxTree(appConfig);
hasFile(`${statePath}/user.actions.ts`); hasFile(`${statePath}/user.actions.ts`);
hasFile(`${statePath}/user.effects.ts`); hasFile(`${statePath}/user.effects.ts`);
@ -294,10 +320,10 @@ describe('ngrx', () => {
hasFile(`${statePath}/user.selectors.ts`); hasFile(`${statePath}/user.selectors.ts`);
}); });
it('should scaffold the ngrx "user" files WITH a facade', () => { it('should scaffold the ngrx "user" files WITH a facade', async () => {
const appConfig = getAppConfig(); const appConfig = getAppConfig();
const hasFile = file => expect(tree.exists(file)).toBeTruthy(); const hasFile = file => expect(tree.exists(file)).toBeTruthy();
const tree = buildNgrxTree(appConfig, 'user', true); const tree = await buildNgrxTree(appConfig, 'user', true);
const statePath = `${findModuleParent(appConfig.appModule)}/+state`; const statePath = `${findModuleParent(appConfig.appModule)}/+state`;
hasFile(`${statePath}/user.actions.ts`); hasFile(`${statePath}/user.actions.ts`);
@ -312,18 +338,18 @@ describe('ngrx', () => {
hasFile(`${statePath}/user.facade.spec.ts`); hasFile(`${statePath}/user.facade.spec.ts`);
}); });
it('should build the ngrx actions', () => { it('should build the ngrx actions', async () => {
const appConfig = getAppConfig(); const appConfig = getAppConfig();
const tree = buildNgrxTree(appConfig, 'users'); const tree = await buildNgrxTree(appConfig, 'users');
const statePath = `${findModuleParent(appConfig.appModule)}/+state`; const statePath = `${findModuleParent(appConfig.appModule)}/+state`;
const content = getFileContent(tree, `${statePath}/users.actions.ts`); const content = getFileContent(tree, `${statePath}/users.actions.ts`);
expect(content).toContain('UsersActionTypes'); expect(content).toContain('UsersActionTypes');
expect(content).toContain('LoadUsers = "[Users] Load Users"'); expect(content).toContain("LoadUsers = '[Users] Load Users'");
expect(content).toContain('UsersLoaded = "[Users] Users Loaded"'); expect(content).toContain("UsersLoaded = '[Users] Users Loaded'");
expect(content).toContain('UsersLoadError = "[Users] Users Load Error"'); expect(content).toContain("UsersLoadError = '[Users] Users Load Error'");
expect(content).toContain('class LoadUsers implements Action'); expect(content).toContain('class LoadUsers implements Action');
expect(content).toContain('class UsersLoaded implements Action'); expect(content).toContain('class UsersLoaded implements Action');
@ -333,9 +359,9 @@ describe('ngrx', () => {
expect(content).toContain('export const fromUsersActions'); expect(content).toContain('export const fromUsersActions');
}); });
it('should build the ngrx selectors', () => { it('should build the ngrx selectors', async () => {
const appConfig = getAppConfig(); const appConfig = getAppConfig();
const tree = buildNgrxTree(appConfig, 'users'); const tree = await buildNgrxTree(appConfig, 'users');
const statePath = `${findModuleParent(appConfig.appModule)}/+state`; const statePath = `${findModuleParent(appConfig.appModule)}/+state`;
const content = getFileContent(tree, `${statePath}/users.selectors.ts`); const content = getFileContent(tree, `${statePath}/users.selectors.ts`);
@ -348,10 +374,10 @@ describe('ngrx', () => {
}); });
}); });
it('should build the ngrx facade', () => { it('should build the ngrx facade', async () => {
const appConfig = getAppConfig(); const appConfig = getAppConfig();
const includeFacade = true; const includeFacade = true;
const tree = buildNgrxTree(appConfig, 'users', includeFacade); const tree = await buildNgrxTree(appConfig, 'users', includeFacade);
const statePath = `${findModuleParent(appConfig.appModule)}/+state`; const statePath = `${findModuleParent(appConfig.appModule)}/+state`;
const content = getFileContent(tree, `${statePath}/users.facade.ts`); const content = getFileContent(tree, `${statePath}/users.facade.ts`);
@ -365,9 +391,9 @@ describe('ngrx', () => {
}); });
}); });
it('should build the ngrx reducer', () => { it('should build the ngrx reducer', async () => {
const appConfig = getAppConfig(); const appConfig = getAppConfig();
const tree = buildNgrxTree(appConfig, 'user'); const tree = await buildNgrxTree(appConfig, 'user');
const statePath = `${findModuleParent(appConfig.appModule)}/+state`; const statePath = `${findModuleParent(appConfig.appModule)}/+state`;
const content = getFileContent(tree, `${statePath}/user.reducer.ts`); const content = getFileContent(tree, `${statePath}/user.reducer.ts`);
@ -380,28 +406,34 @@ describe('ngrx', () => {
`export interface UserState`, `export interface UserState`,
'export function userReducer', 'export function userReducer',
'state: UserState = initialState', 'state: UserState = initialState',
'action: UserAction): UserState', 'action: UserAction',
'): UserState',
'case UserActionTypes.UserLoaded' 'case UserActionTypes.UserLoaded'
].forEach(text => { ].forEach(text => {
expect(content).toContain(text); expect(content).toContain(text);
}); });
}); });
it('should build the ngrx effects', () => { it('should build the ngrx effects', async () => {
const appConfig = getAppConfig(); const appConfig = getAppConfig();
const tree = buildNgrxTree(appConfig, 'users'); const tree = await buildNgrxTree(appConfig, 'users');
const statePath = `${findModuleParent(appConfig.appModule)}/+state`; const statePath = `${findModuleParent(appConfig.appModule)}/+state`;
const content = getFileContent(tree, `${statePath}/users.effects.ts`); const content = getFileContent(tree, `${statePath}/users.effects.ts`);
[ [
`import { DataPersistence } from \'@nrwl/nx\'`, `import { DataPersistence } from \'@nrwl/nx\'`,
`import { LoadUsers, UsersLoaded, UsersLoadError, UsersActionTypes } from \'./users.actions\'`, `import {
LoadUsers,
UsersLoaded,
UsersLoadError,
UsersActionTypes
} from \'./users.actions\';`,
`loadUsers$`, `loadUsers$`,
`run: (action: LoadUsers, state: UsersPartialState)`, `run: (action: LoadUsers, state: UsersPartialState)`,
`return new UsersLoaded([])`, `return new UsersLoaded([])`,
`return new UsersLoadError(error)`, `return new UsersLoadError(error)`,
'private actions$: Actions', 'private actions$: Actions',
'private dataPersistence: DataPersistence<UsersPartialState>)' 'private dataPersistence: DataPersistence<UsersPartialState>'
].forEach(text => { ].forEach(text => {
expect(content).toContain(text); expect(content).toContain(text);
}); });
@ -409,9 +441,9 @@ describe('ngrx', () => {
}); });
describe('unit tests', () => { describe('unit tests', () => {
it('should produce proper specs for the ngrx reducer', () => { it('should produce proper specs for the ngrx reducer', async () => {
const appConfig = getAppConfig(); const appConfig = getAppConfig();
const tree = buildNgrxTree(appConfig); const tree = await buildNgrxTree(appConfig);
const statePath = `${findModuleParent(appConfig.appModule)}/+state`; const statePath = `${findModuleParent(appConfig.appModule)}/+state`;
const contents = tree.readContent(`${statePath}/user.reducer.spec.ts`); const contents = tree.readContent(`${statePath}/user.reducer.spec.ts`);
@ -422,18 +454,20 @@ describe('ngrx', () => {
); );
}); });
it('should update the barrel API with exports for ngrx facade, selector, and reducer', () => { it('should update the barrel API with exports for ngrx facade, selector, and reducer', async () => {
appTree = createLib(appTree, 'flights'); appTree = createLib(appTree, 'flights');
let libConfig = getLibConfig(); let libConfig = getLibConfig();
let tree = schematicRunner.runSchematic( let tree = await schematicRunner
'ngrx', .runSchematicAsync(
{ 'ngrx',
name: 'super-users', {
module: libConfig.module, name: 'super-users',
facade: true module: libConfig.module,
}, facade: true
appTree },
); appTree
)
.toPromise();
const barrel = tree.readContent(libConfig.barrel); const barrel = tree.readContent(libConfig.barrel);
expect(barrel).toContain( expect(barrel).toContain(
@ -441,18 +475,20 @@ describe('ngrx', () => {
); );
}); });
it('should not update the barrel API with a facade', () => { it('should not update the barrel API with a facade', async () => {
appTree = createLib(appTree, 'flights'); appTree = createLib(appTree, 'flights');
let libConfig = getLibConfig(); let libConfig = getLibConfig();
let tree = schematicRunner.runSchematic( let tree = await schematicRunner
'ngrx', .runSchematicAsync(
{ 'ngrx',
name: 'super-users', {
module: libConfig.module, name: 'super-users',
facade: false module: libConfig.module,
}, facade: false
appTree },
); appTree
)
.toPromise();
const barrel = tree.readContent(libConfig.barrel); const barrel = tree.readContent(libConfig.barrel);
expect(barrel).not.toContain( expect(barrel).not.toContain(
@ -460,16 +496,18 @@ describe('ngrx', () => {
); );
}); });
it('should produce proper tests for the ngrx reducer for a name with a dash', () => { it('should produce proper tests for the ngrx reducer for a name with a dash', async () => {
const appConfig = getAppConfig(); const appConfig = getAppConfig();
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'ngrx', .runSchematicAsync(
{ 'ngrx',
name: 'super-users', {
module: appConfig.appModule name: 'super-users',
}, module: appConfig.appModule
appTree },
); appTree
)
.toPromise();
const statePath = `${findModuleParent(appConfig.appModule)}/+state`; const statePath = `${findModuleParent(appConfig.appModule)}/+state`;
const contents = tree.readContent( const contents = tree.readContent(
@ -483,19 +521,21 @@ describe('ngrx', () => {
}); });
}); });
function buildNgrxTree( async function buildNgrxTree(
appConfig: AppConfig, appConfig: AppConfig,
featureName: string = 'user', featureName: string = 'user',
withFacade = false withFacade = false
): UnitTestTree { ): Promise<UnitTestTree> {
return schematicRunner.runSchematic( return await schematicRunner
'ngrx', .runSchematicAsync(
{ 'ngrx',
name: featureName, {
module: appConfig.appModule, name: featureName,
facade: withFacade module: appConfig.appModule,
}, facade: withFacade
appTree },
); appTree
)
.toPromise();
} }
}); });

View File

@ -6,8 +6,7 @@
"properties": { "properties": {
"name": { "name": {
"type": "string", "type": "string",
"description": "description": "Name of the NgRx feature (e.g., Products, Users, etc.). Recommended to use plural form for name.",
"Name of the NgRx feature (e.g., Products, Users, etc.). Recommended to use plural form for name.",
"$default": { "$default": {
"$source": "argv", "$source": "argv",
"index": 0 "index": 0
@ -16,42 +15,35 @@
}, },
"module": { "module": {
"type": "string", "type": "string",
"description": "description": "Path to ngModule; host directory will contain the new '+state' directory (e.g., libs/comments/src/lib/comments-state.module.ts).",
"Path to ngModule; host directory will contain the new '+state' directory (e.g., libs/comments/src/lib/comments-state.module.ts).", "x-prompt": "What is the path to the module where this ngrx state should be added to?"
"x-prompt":
"What is the path to the module where this ngrx state should be added to?"
}, },
"directory": { "directory": {
"type": "string", "type": "string",
"default": "+state", "default": "+state",
"description": "description": "Override the name of the folder used to contain/group the NgRx files: contains actions, effects, reducers. selectors. (e.g., +state)"
"Override the name of the folder used to contain/group the NgRx files: contains actions, effects, reducers. selectors. (e.g., +state)"
}, },
"root": { "root": {
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "description": "Add StoreModule.forRoot and EffectsModule.forRoot() instead of forFeature (e.g., --root).",
"Add StoreModule.forRoot and EffectsModule.forRoot() instead of forFeature (e.g., --root).",
"x-prompt": "Is this the root state of the application?" "x-prompt": "Is this the root state of the application?"
}, },
"facade": { "facade": {
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "description": "Create a Facade class for the the Feature (e.g., --facade).",
"Create a Facade class for the the Feature (e.g., --facade).",
"x-prompt": "Would you like to add a Facade to your ngrx state" "x-prompt": "Would you like to add a Facade to your ngrx state"
}, },
"onlyAddFiles": { "onlyAddFiles": {
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "description": "Only add new NgRx files, without changing the module file (e.g., --onlyAddFiles)."
"Only add new NgRx files, without changing the module file (e.g., --onlyAddFiles)."
}, },
"onlyEmptyRoot": { "onlyEmptyRoot": {
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "description": "Do not generate any files. Only generate StoreModule.forRoot and EffectsModule.forRoot (e.g., --onlyEmptyRoot)."
"Do not generate any files. Only generate StoreModule.forRoot and EffectsModule.forRoot (e.g., --onlyEmptyRoot)."
}, },
"skipFormat": { "skipFormat": {
"description": "Skip formatting files", "description": "Skip formatting files",
@ -61,8 +53,7 @@
"skipPackageJson": { "skipPackageJson": {
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "description": "Do not add NgRx dependencies to package.json (e.g., --skipPackageJson)"
"Do not add NgRx dependencies to package.json (e.g., --skipPackageJson)"
} }
}, },
"required": ["module"] "required": ["module"]

View File

@ -43,8 +43,7 @@
"tags": { "tags": {
"type": "string", "type": "string",
"description": "Add tags to the application (used for linting)", "description": "Add tags to the application (used for linting)",
"x-prompt": "x-prompt": "Which tags would you like to add to the node application? (used for linting)"
"Which tags would you like to add to the node application? (used for linting)"
} }
}, },
"required": [] "required": []

View File

@ -18,13 +18,11 @@
}, },
"angularJsImport": { "angularJsImport": {
"type": "string", "type": "string",
"description": "description": "Import expression of the AngularJS application (e.g., --angularJsImport=some_node_module/my_app)."
"Import expression of the AngularJS application (e.g., --angularJsImport=some_node_module/my_app)."
}, },
"angularJsCmpSelector": { "angularJsCmpSelector": {
"type": "string", "type": "string",
"description": "description": "The selector of an AngularJS component (e.g., --angularJsCmpSelector=myComponent)"
"The selector of an AngularJS component (e.g., --angularJsCmpSelector=myComponent)"
}, },
"skipFormat": { "skipFormat": {
"description": "Skip formatting files", "description": "Skip formatting files",
@ -34,8 +32,7 @@
"skipPackageJson": { "skipPackageJson": {
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "description": "Do not add @angular/upgrade to package.json (e.g., --skipPackageJson)"
"Do not add @angular/upgrade to package.json (e.g., --skipPackageJson)"
}, },
"router": { "router": {
"type": "boolean", "type": "boolean",

View File

@ -19,15 +19,17 @@ describe('upgrade-module', () => {
appTree = createApp(appTree, 'myapp'); appTree = createApp(appTree, 'myapp');
}); });
it('should update the bootstrap logic', () => { it('should update the bootstrap logic', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'upgrade-module', .runSchematicAsync(
{ 'upgrade-module',
name: 'legacy', {
project: 'myapp' name: 'legacy',
}, project: 'myapp'
appTree },
); appTree
)
.toPromise();
const appModule = getFileContent(tree, '/apps/myapp/src/app/app.module.ts'); const appModule = getFileContent(tree, '/apps/myapp/src/app/app.module.ts');
expect(appModule).toContain( expect(appModule).toContain(
@ -41,7 +43,7 @@ describe('upgrade-module', () => {
expect(tree.exists('/apps/myapp/src/hybrid.spec.ts')).toBeTruthy(); expect(tree.exists('/apps/myapp/src/hybrid.spec.ts')).toBeTruthy();
}); });
it('should update package.json by default', () => { it('should update package.json by default', async () => {
appTree.overwrite( appTree.overwrite(
`/package.json`, `/package.json`,
JSON.stringify({ JSON.stringify({
@ -51,21 +53,23 @@ describe('upgrade-module', () => {
}) })
); );
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'upgrade-module', .runSchematicAsync(
{ 'upgrade-module',
name: 'legacy', {
project: 'myapp' name: 'legacy',
}, project: 'myapp'
appTree },
); appTree
)
.toPromise();
const packageJson = readJsonInTree(tree, '/package.json'); const packageJson = readJsonInTree(tree, '/package.json');
expect(packageJson.dependencies['@angular/upgrade']).toEqual('4.4.4'); expect(packageJson.dependencies['@angular/upgrade']).toEqual('4.4.4');
expect(packageJson.dependencies['angular']).toBeDefined(); expect(packageJson.dependencies['angular']).toBeDefined();
}); });
it('should not package.json when --skipPackageJson=true', () => { it('should not package.json when --skipPackageJson=true', async () => {
appTree.overwrite( appTree.overwrite(
`/package.json`, `/package.json`,
JSON.stringify({ JSON.stringify({
@ -75,45 +79,51 @@ describe('upgrade-module', () => {
}) })
); );
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'upgrade-module', .runSchematicAsync(
{ 'upgrade-module',
name: 'legacy', {
skipPackageJson: true, name: 'legacy',
project: 'myapp' skipPackageJson: true,
}, project: 'myapp'
appTree },
); appTree
)
.toPromise();
const packageJson = readJsonInTree(tree, '/package.json'); const packageJson = readJsonInTree(tree, '/package.json');
expect(packageJson.dependencies['@angular/upgrade']).not.toBeDefined(); expect(packageJson.dependencies['@angular/upgrade']).not.toBeDefined();
}); });
it('should add router configuration when --router=true', () => { it('should add router configuration when --router=true', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'upgrade-module', .runSchematicAsync(
{ 'upgrade-module',
name: 'legacy', {
router: true, name: 'legacy',
project: 'myapp' router: true,
}, project: 'myapp'
appTree },
); appTree
)
.toPromise();
const legacySetup = getFileContent(tree, '/apps/myapp/src/legacy-setup.ts'); const legacySetup = getFileContent(tree, '/apps/myapp/src/legacy-setup.ts');
expect(legacySetup).toContain(`setUpLocationSync`); expect(legacySetup).toContain(`setUpLocationSync`);
}); });
it('should support custom angularJsImport', () => { it('should support custom angularJsImport', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'upgrade-module', .runSchematicAsync(
{ 'upgrade-module',
name: 'legacy', {
angularJsImport: 'legacy-app', name: 'legacy',
project: 'myapp' angularJsImport: 'legacy-app',
}, project: 'myapp'
appTree },
); appTree
)
.toPromise();
const legacySetup = getFileContent(tree, '/apps/myapp/src/legacy-setup.ts'); const legacySetup = getFileContent(tree, '/apps/myapp/src/legacy-setup.ts');
expect(legacySetup).toContain(`import 'legacy-app';`); expect(legacySetup).toContain(`import 'legacy-app';`);

View File

@ -16,12 +16,10 @@ describe('workspace-schematic', () => {
appTree = createEmptyWorkspace(appTree); appTree = createEmptyWorkspace(appTree);
}); });
it('should generate files', () => { it('should generate files', async () => {
const tree = schematicRunner.runSchematic( const tree = await schematicRunner
'workspace-schematic', .runSchematicAsync('workspace-schematic', { name: 'custom' }, appTree)
{ name: 'custom' }, .toPromise();
appTree
);
expect(tree.exists('tools/schematics/custom/index.ts')).toBeTruthy(); expect(tree.exists('tools/schematics/custom/index.ts')).toBeTruthy();
expect(tree.exists('tools/schematics/custom/schema.json')).toBeTruthy(); expect(tree.exists('tools/schematics/custom/schema.json')).toBeTruthy();
}); });

View File

@ -91,12 +91,10 @@ function hasDependencyOnTouchedProjects(
deps[project] deps[project]
.map(d => d.projectName) .map(d => d.projectName)
.filter(k => .filter(k =>
hasDependencyOnTouchedProjects( hasDependencyOnTouchedProjects(k, touchedProjects, deps, [
k, ...visisted,
touchedProjects, project
deps, ])
[...visisted, project]
)
).length > 0 ).length > 0
); );
} }

View File

@ -208,12 +208,14 @@ function withDepGraphOptions(yargs: yargs.Argv): yargs.Argv {
} }
function parseCSV(args: string[]) { function parseCSV(args: string[]) {
return args.map(arg => arg.split(',')).reduce( return args
(acc, value) => { .map(arg => arg.split(','))
return [...acc, ...value]; .reduce(
}, (acc, value) => {
[] as string[] return [...acc, ...value];
); },
[] as string[]
);
} }
function withParallel(yargs: yargs.Argv): yargs.Argv { function withParallel(yargs: yargs.Argv): yargs.Argv {

View File

@ -267,7 +267,9 @@ export function getProjectNodes(angularJson, nxJson): ProjectNode[] {
const projectType = const projectType =
p.projectType === 'application' p.projectType === 'application'
? key.endsWith('-e2e') ? ProjectType.e2e : ProjectType.app ? key.endsWith('-e2e')
? ProjectType.e2e
: ProjectType.app
: ProjectType.lib; : ProjectType.lib;
let implicitDependencies = nxJson.projects[key].implicitDependencies || []; let implicitDependencies = nxJson.projects[key].implicitDependencies || [];

View File

@ -224,14 +224,13 @@ class EnforceModuleBoundariesWalker extends Lint.RuleWalker {
if (done[sourceProjectName]) return false; if (done[sourceProjectName]) return false;
if (!this.deps[sourceProjectName]) return false; if (!this.deps[sourceProjectName]) return false;
return this.deps[sourceProjectName] return this.deps[sourceProjectName]
.map( .map(dep =>
dep => dep.projectName === targetProjectName
dep.projectName === targetProjectName ? true
? true : this.isDependingOn(dep.projectName, targetProjectName, {
: this.isDependingOn(dep.projectName, targetProjectName, { ...done,
...done, [`${sourceProjectName}`]: true
[`${sourceProjectName}`]: true })
})
) )
.some(result => result); .some(result => result);
} }

View File

@ -264,11 +264,13 @@ export function findClass(
): ts.ClassDeclaration { ): ts.ClassDeclaration {
const nodes = getSourceNodes(source); const nodes = getSourceNodes(source);
const clazz = <any>nodes.filter( const clazz = <any>(
n => nodes.filter(
n.kind === ts.SyntaxKind.ClassDeclaration && n =>
(<any>n).name.text === className n.kind === ts.SyntaxKind.ClassDeclaration &&
)[0]; (<any>n).name.text === className
)[0]
);
if (!clazz && !silent) { if (!clazz && !silent) {
throw new Error(`Cannot find class '${className}'`); throw new Error(`Cannot find class '${className}'`);
@ -315,9 +317,8 @@ export function addImportToTestBed(
specPath: string, specPath: string,
symbolName: string symbolName: string
): Change[] { ): Change[] {
const allCalls: ts.CallExpression[] = <any>findNodes( const allCalls: ts.CallExpression[] = <any>(
source, findNodes(source, ts.SyntaxKind.CallExpression)
ts.SyntaxKind.CallExpression
); );
const configureTestingModuleObjectLiterals = allCalls const configureTestingModuleObjectLiterals = allCalls
@ -325,11 +326,10 @@ export function addImportToTestBed(
.filter( .filter(
(c: any) => c.expression.name.getText(source) === 'configureTestingModule' (c: any) => c.expression.name.getText(source) === 'configureTestingModule'
) )
.map( .map(c =>
c => c.arguments[0].kind === ts.SyntaxKind.ObjectLiteralExpression
c.arguments[0].kind === ts.SyntaxKind.ObjectLiteralExpression ? c.arguments[0]
? c.arguments[0] : null
: null
); );
if (configureTestingModuleObjectLiterals.length > 0) { if (configureTestingModuleObjectLiterals.length > 0) {

View File

@ -24,9 +24,8 @@ export function toClassName(str: string): string {
*/ */
export function toPropertyName(s: string): string { export function toPropertyName(s: string): string {
return s return s
.replace( .replace(/(-|_|\.|\s)+(.)?/g, (_, __, chr) =>
/(-|_|\.|\s)+(.)?/g, chr ? chr.toUpperCase() : ''
(_, __, chr) => (chr ? chr.toUpperCase() : '')
) )
.replace(/^([A-Z])/, m => m.toLowerCase()); .replace(/^([A-Z])/, m => m.toLowerCase());
} }

View File

@ -4,7 +4,9 @@ import { forEach, FileEntry, Rule } from '@angular-devkit/schematics';
* Remove a file from the Virtual Schematic Tree * Remove a file from the Virtual Schematic Tree
*/ */
export function deleteFile(from: string): Rule { export function deleteFile(from: string): Rule {
return forEach((entry: FileEntry): FileEntry | null => { return forEach(
return entry.path === from ? null : entry; (entry: FileEntry): FileEntry | null => {
}); return entry.path === from ? null : entry;
}
);
} }

View File

@ -1,11 +1,13 @@
import { SchematicTestRunner } from '@angular-devkit/schematics/testing'; import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
import { Tree } from '@angular-devkit/schematics'; import { Tree } from '@angular-devkit/schematics';
import * as prettier from 'prettier';
import * as path from 'path'; import * as path from 'path';
import { createEmptyWorkspace } from '../testing-utils'; import { createEmptyWorkspace } from '../testing-utils';
import { formatFiles } from './format-files'; import { formatFiles } from './format-files';
import { serializeJson } from '../fileutils'; import { serializeJson } from '../fileutils';
import * as appRoot from 'app-root-path';
describe('formatFiles', () => { describe('formatFiles', () => {
let tree: Tree; let tree: Tree;
@ -15,52 +17,89 @@ describe('formatFiles', () => {
'@nrwl/schematics', '@nrwl/schematics',
path.join(__dirname, '../../collection.json') path.join(__dirname, '../../collection.json')
); );
tree = createEmptyWorkspace(Tree.empty()); spyOn(prettier, 'format').and.callFake(input => 'formated :: ' + input);
tree.overwrite( tree = Tree.empty();
'package.json', });
serializeJson({
scripts: { it('should format created files', async () => {
format: 'prettier' spyOn(prettier, 'resolveConfig').and.returnValue(
} Promise.resolve({
printWidth: 80
}) })
); );
tree.create('a.ts', 'const a=a');
const result = await schematicRunner
.callRule(formatFiles(), tree)
.toPromise();
expect(prettier.format).toHaveBeenCalledWith('const a=a', {
printWidth: 80,
filepath: appRoot.resolve('a.ts')
});
expect(result.read('a.ts').toString()).toEqual('formated :: const a=a');
});
it('should not format deleted files', async () => {
spyOn(prettier, 'resolveConfig').and.returnValue(
Promise.resolve({
printWidth: 80
})
);
tree.create('b.ts', '');
tree.delete('b.ts');
await schematicRunner.callRule(formatFiles(), tree).toPromise();
expect(prettier.format).not.toHaveBeenCalledWith(
'const b=b',
jasmine.anything()
);
}); });
it('should format files', done => { it('should format overwritten files', async () => {
schematicRunner.callRule(formatFiles(), tree).subscribe(result => { spyOn(prettier, 'resolveConfig').and.returnValue(Promise.resolve(null));
expect(schematicRunner.tasks.length).toBe(1); tree.create('a.ts', 'const a=a');
expect(schematicRunner.tasks[0]).toEqual({ tree.overwrite('a.ts', 'const a=b');
name: 'node-package', const result = await schematicRunner
options: { .callRule(formatFiles(), tree)
packageName: 'run format -- --untracked', .toPromise();
quiet: true expect(prettier.format).toHaveBeenCalledWith('const a=b', {
} filepath: appRoot.resolve('a.ts')
});
done();
}); });
expect(result.read('a.ts').toString()).toEqual('formated :: const a=b');
}); });
it('should not format files if there is no format npm script', done => { it('should not format renamed files', async () => {
tree.overwrite( spyOn(prettier, 'resolveConfig').and.returnValue(Promise.resolve(null));
'package.json', tree.create('a.ts', 'const a=a');
serializeJson({ tree.rename('a.ts', 'b.ts');
scripts: {} const result = await schematicRunner
}) .callRule(formatFiles(), tree)
); .toPromise();
schematicRunner.callRule(formatFiles(), tree).subscribe(result => { expect(prettier.format).toHaveBeenCalledWith('const a=a', {
expect(schematicRunner.tasks.length).toBe(0); filepath: appRoot.resolve('b.ts')
//TODO: test that a warning is emitted.
done();
}); });
expect(result.read('b.ts').toString()).toEqual('formated :: const a=a');
}); });
it('should not format files if skipFormat is passed', done => { describe('--skip-format', () => {
schematicRunner it('should not format created files', async () => {
.callRule(formatFiles({ skipFormat: true }), tree) spyOn(prettier, 'resolveConfig').and.returnValue(
.subscribe(result => { Promise.resolve({
expect(schematicRunner.tasks.length).toBe(0); printWidth: 80
//TODO: test that a warning is not emitted. })
done(); );
tree.create('a.ts', 'const a=a');
const result = await schematicRunner
.callRule(
formatFiles({
skipFormat: true
}),
tree
)
.toPromise();
expect(prettier.format).not.toHaveBeenCalledWith('const a=a', {
printWidth: 80,
filepath: appRoot.resolve('a.ts')
}); });
expect(result.read('a.ts').toString()).toEqual('const a=a');
});
}); });
}); });

View File

@ -1,25 +1,15 @@
import { readJsonInTree } from '../ast-utils';
import { import {
TaskConfigurationGenerator,
TaskConfiguration,
Tree, Tree,
SchematicContext, SchematicContext,
Rule, Rule,
noop noop,
OverwriteFileAction,
CreateFileAction
} from '@angular-devkit/schematics'; } from '@angular-devkit/schematics';
import { stripIndents } from '@angular-devkit/core/src/utils/literals'; import { format, resolveConfig, getFileInfo } from 'prettier';
import * as appRoot from 'app-root-path';
class FormatFiles implements TaskConfigurationGenerator<any> { import { from } from 'rxjs';
toConfiguration(): TaskConfiguration<any> { import { filter, map, mergeMap } from 'rxjs/operators';
return {
name: 'node-package',
options: {
packageName: 'run format -- --untracked', // workaround. we should define a custom task executor.
quiet: true
}
};
}
}
export function formatFiles( export function formatFiles(
options: { skipFormat: boolean } = { skipFormat: false } options: { skipFormat: boolean } = { skipFormat: false }
@ -28,15 +18,45 @@ export function formatFiles(
return noop(); return noop();
} }
return (host: Tree, context: SchematicContext) => { return (host: Tree, context: SchematicContext) => {
const packageJson = readJsonInTree(host, 'package.json'); const files = new Set(
if (packageJson.scripts && packageJson.scripts.format) { host.actions
context.addTask(new FormatFiles()); .filter(action => action.kind !== 'd' && action.kind !== 'r')
} else { .map((action: OverwriteFileAction | CreateFileAction) => ({
context.logger.warn(stripIndents` path: action.path,
Files were not formated during this code generation. content: action.content.toString()
The "format" npm script is missing in your package.json. }))
Please either add a format script or pass --skip-format. );
`); if (files.size === 0) {
return host;
} }
return from(files).pipe(
filter(file => host.exists(file.path)),
mergeMap(async file => {
const systemPath = appRoot.resolve(file.path);
let options: any = {
filepath: systemPath
};
const resolvedOptions = await resolveConfig(systemPath);
if (resolvedOptions) {
options = {
...options,
...resolvedOptions
};
}
const support = await getFileInfo(systemPath, options);
if (support.ignored || !support.inferredParser) {
return;
}
try {
host.overwrite(file.path, format(file.content, options));
} catch (e) {
context.logger.warn(
`Could not format ${file.path} because ${e.message}`
);
}
}),
map(() => host)
);
}; };
} }

View File

@ -9151,10 +9151,10 @@ preserve@^0.2.0:
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=
prettier@1.10.2: prettier@1.15.3:
version "1.10.2" version "1.15.3"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.10.2.tgz#1af8356d1842276a99a5b5529c82dd9e9ad3cc93" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.15.3.tgz#1feaac5bdd181237b54dbe65d874e02a1472786a"
integrity sha512-TcdNoQIWFoHblurqqU6d1ysopjq7UX0oRcT/hJ8qvBAELiYWn+Ugf0AXdnzISEJ7vuhNnQ98N8jR8Sh53x4IZg== integrity sha512-gAU9AGAPMaKb3NNSUUuhhFAS7SCO4ALTN4nRIn6PJ075Qd28Yn2Ig2ahEJWdJwJmlEBTUfC7mMUSFy8MwsOCfg==
pretty-format@^23.6.0: pretty-format@^23.6.0:
version "23.6.0" version "23.6.0"