feat(nx): standalone version of nx

This commit is contained in:
Victor Savkin 2019-07-23 12:27:29 -04:00
parent 3a2c56f21a
commit 2b646f8eb4
158 changed files with 5268 additions and 3644 deletions

View File

@ -34,7 +34,23 @@ matrix:
- yarn install --network-timeout 1000000 - yarn install --network-timeout 1000000
script: script:
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then yarn checkformat --head=$TRAVIS_PULL_REQUEST_SHA --base=$(git merge-base HEAD $TRAVIS_BRANCH); fi' - 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then yarn checkformat --head=$TRAVIS_PULL_REQUEST_SHA --base=$(git merge-base HEAD $TRAVIS_BRANCH); fi'
- yarn e2e - yarn e2e --cli nx
- os: linux
language: node_js
node_js: 10
dist: trusty
sudo: required
cache:
npm: false
addons:
chrome: stable
before_install:
- export DISPLAY=:99.0; sh -e /etc/init.d/xvfb start;
install:
- yarn install --network-timeout 1000000
script:
- 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then yarn checkformat --head=$TRAVIS_PULL_REQUEST_SHA --base=$(git merge-base HEAD $TRAVIS_BRANCH); fi'
- yarn e2e --cli angular
notifications: notifications:
email: false email: false

View File

@ -17,6 +17,14 @@ Type: `string`
A directory where the app is placed A directory where the app is placed
### linter
Default: `tslint`
Type: `string`
The tool to use for running lint checks.
### name ### name
Type: `string` Type: `string`

View File

@ -23,6 +23,14 @@ Type: `string`
Frontend project that needs to access this application. This sets up proxy configuration. Frontend project that needs to access this application. This sets up proxy configuration.
### linter
Default: `tslint`
Type: `string`
The tool to use for running lint checks.
### name ### name
Type: `string` Type: `string`

View File

@ -33,6 +33,14 @@ Type: `string`
Test runner to use for end to end (e2e) tests Test runner to use for end to end (e2e) tests
### linter
Default: `tslint`
Type: `string`
The tool to use for running lint checks.
### name ### name
Type: `string` Type: `string`

View File

@ -45,7 +45,7 @@ Use pascal case component file name (e.g. App.tsx)
Type: `string` Type: `string`
The name of the project (as specified in angular.json). The name of the project.
### routing ### routing

View File

@ -17,6 +17,14 @@ Type: `string`
A directory where the app is placed A directory where the app is placed
### linter
Default: `tslint`
Type: `string`
The tool to use for running lint checks.
### name ### name
Type: `string` Type: `string`

View File

@ -25,6 +25,14 @@ Type: `string`
Test runner to use for end to end (e2e) tests Test runner to use for end to end (e2e) tests
### linter
Default: `tslint`
Type: `string`
The tool to use for running lint checks.
### name ### name
Type: `string` Type: `string`

View File

@ -8,7 +8,7 @@ Run commands
Type: `string` Type: `string`
Extra arguments. You can pass them as follows: ng run project:target --args='--wait=100'. You can them use {args.wait} syntax to interpolate them in angular.json Extra arguments. You can pass them as follows: ng run project:target --args='--wait=100'. You can them use {args.wait} syntax to interpolate them in the workspace config file.
### commands ### commands

View File

@ -17,6 +17,14 @@ Type: `string`
A directory where the app is placed A directory where the app is placed
### linter
Default: `tslint`
Type: `string`
The tool to use for running lint checks.
### name ### name
Type: `string` Type: `string`

View File

@ -0,0 +1,72 @@
# tao-new [hidden]
Create a workspace
## Usage
```bash
ng generate tao-new ...
```
## Options
### commit
Default: `true`
Type: `boolean`
Initial repository commit information.
### directory
Type: `string`
The directory name to create the workspace in.
### name
Type: `string`
The name of the workspace.
### npmScope
Type: `string`
Npm scope for importing libs.
### preset
Default: `empty`
Type: `string`
What to create in the new workspace
### skipGit
Alias(es): g
Default: `false`
Type: `boolean`
Skip initializing a git repository.
### skipInstall
Default: `false`
Type: `boolean`
Skip installing dependency packages.
### style
Default: `css`
Type: `string`
The file extension to be used for style files.

View File

@ -11,6 +11,14 @@ ng generate workspace ...
## Options ## Options
### cli
Default: `nx`
Type: `string`
CLI used for generating code and running tasks
### commit ### commit
Default: `true` Default: `true`

View File

@ -3,15 +3,16 @@ import {
readFile, readFile,
readJson, readJson,
runCommand, runCommand,
runsInWSL,
uniq, uniq,
updateFile, updateFile,
runCLI runCLI,
forEachCli,
supportUi
} from './utils'; } from './utils';
let originalCIValue; let originalCIValue;
describe('Affected', () => { forEachCli(() => {
/** /**
* Setting CI=true makes it simpler to configure assertions around output, as there * Setting CI=true makes it simpler to configure assertions around output, as there
* won't be any colors. * won't be any colors.
@ -24,210 +25,210 @@ describe('Affected', () => {
process.env.CI = originalCIValue; process.env.CI = originalCIValue;
}); });
it('should print, build, and test affected apps', () => { describe('Affected', () => {
ensureProject(); it('should print, build, and test affected apps', () => {
const myapp = uniq('myapp'); ensureProject();
const myapp2 = uniq('myapp2'); const myapp = uniq('myapp');
const mylib = uniq('mylib'); const myapp2 = uniq('myapp2');
const mylib2 = uniq('mylib2'); const mylib = uniq('mylib');
const mypublishablelib = uniq('mypublishablelib'); const mylib2 = uniq('mylib2');
runCLI(`generate @nrwl/angular:app ${myapp}`); const mypublishablelib = uniq('mypublishablelib');
runCLI(`generate @nrwl/angular:app ${myapp2}`); runCLI(`generate @nrwl/angular:app ${myapp}`);
runCLI(`generate @nrwl/angular:lib ${mylib}`); runCLI(`generate @nrwl/angular:app ${myapp2}`);
runCLI(`generate @nrwl/angular:lib ${mylib2}`); runCLI(`generate @nrwl/angular:lib ${mylib}`);
runCLI(`generate @nrwl/angular:lib ${mypublishablelib} --publishable`); runCLI(`generate @nrwl/angular:lib ${mylib2}`);
runCLI(`generate @nrwl/angular:lib ${mypublishablelib} --publishable`);
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', () => {
expect(1).toEqual(1); expect(1).toEqual(1);
});
}); });
}); `
` );
); 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', () => { expect(1).toEqual(1);
expect(1).toEqual(1); });
}); });
}); `
`
);
const affectedApps = runCommand(
`npm run affected:apps -- --files="libs/${mylib}/src/index.ts"`
);
expect(affectedApps).toContain(myapp);
expect(affectedApps).not.toContain(myapp2);
expect(affectedApps).not.toContain(`${myapp}-e2e`);
const implicitlyAffectedApps = runCommand(
'npm run affected:apps -- --files="package.json"'
);
expect(implicitlyAffectedApps).toContain(myapp);
expect(implicitlyAffectedApps).toContain(myapp2);
const noAffectedApps = runCommand(
'npm run affected:apps -- --files="README.md"'
);
expect(noAffectedApps).not.toContain(myapp);
expect(noAffectedApps).not.toContain(myapp2);
const affectedLibs = runCommand(
`npm run affected:libs -- --files="libs/${mylib}/src/index.ts"`
);
expect(affectedLibs).toContain(mypublishablelib);
expect(affectedLibs).toContain(mylib);
expect(affectedLibs).not.toContain(mylib2);
const implicitlyAffectedLibs = runCommand(
'npm run affected:libs -- --files="package.json"'
);
expect(implicitlyAffectedLibs).toContain(mypublishablelib);
expect(implicitlyAffectedLibs).toContain(mylib);
expect(implicitlyAffectedLibs).toContain(mylib2);
const noAffectedLibs = runCommand(
'npm run affected:libs -- --files="README.md"'
);
expect(noAffectedLibs).not.toContain(mypublishablelib);
expect(noAffectedLibs).not.toContain(mylib);
expect(noAffectedLibs).not.toContain(mylib2);
const build = runCommand(
`npm run affected:build -- --files="libs/${mylib}/src/index.ts"`
);
expect(build).toContain(`Running target build for projects:`);
expect(build).toContain(myapp);
expect(build).toContain(mypublishablelib);
expect(build).not.toContain('is not registered with the build command');
expect(build).not.toContain('with flags:');
// Should work in parallel
const buildParallel = runCommand(
`npm run affected:build -- --files="libs/${mylib}/src/index.ts" --parallel`
);
expect(buildParallel).toContain(`Running target build for projects:`);
expect(buildParallel).toContain(myapp);
expect(buildParallel).toContain(mypublishablelib);
expect(buildParallel).toContain(
'Running target "build" for affected projects succeeded'
);
const buildExcluded = runCommand(
`npm run affected:build -- --files="libs/${mylib}/src/index.ts" --exclude ${myapp}`
);
expect(buildExcluded).toContain(`Running target build for projects:`);
expect(buildExcluded).toContain(mypublishablelib);
// affected:build should pass non-nx flags to the CLI
const buildWithFlags = runCommand(
`npm run affected:build -- --files="libs/${mylib}/src/index.ts" --stats-json`
);
expect(buildWithFlags).toContain(`Running target build for projects:`);
expect(buildWithFlags).toContain(myapp);
expect(buildWithFlags).toContain(mypublishablelib);
expect(buildWithFlags).toContain('With flags: --stats-json=true');
if (!runsInWSL()) {
const e2e = runCommand(
`npm run affected:e2e -- --files="libs/${mylib}/src/index.ts" --headless --no-watch`
); );
expect(e2e).toContain('should display welcome message');
}
const unitTests = runCommand( const affectedApps = runCommand(
`npm run affected:test -- --files="libs/${mylib}/src/index.ts"` `npm run affected:apps -- --files="libs/${mylib}/src/index.ts"`
); );
expect(unitTests).toContain(`Running target test for projects:`); expect(affectedApps).toContain(myapp);
expect(unitTests).toContain(mylib); expect(affectedApps).not.toContain(myapp2);
expect(unitTests).toContain(myapp); expect(affectedApps).not.toContain(`${myapp}-e2e`);
expect(unitTests).toContain(mypublishablelib);
// Fail a Unit Test const implicitlyAffectedApps = runCommand(
updateFile( 'npm run affected:apps -- --files="package.json"'
`apps/${myapp}/src/app/app.component.spec.ts`, );
readFile(`apps/${myapp}/src/app/app.component.spec.ts`).replace( expect(implicitlyAffectedApps).toContain(myapp);
'.toEqual(1)', expect(implicitlyAffectedApps).toContain(myapp2);
'.toEqual(2)'
)
);
const failedTests = runCommand( const noAffectedApps = runCommand(
`npm run affected:test -- --files="libs/${mylib}/src/index.ts"` 'npm run affected:apps -- --files="README.md"'
); );
expect(failedTests).toContain(`Running target test for projects:`); expect(noAffectedApps).not.toContain(myapp);
expect(failedTests).toContain(mylib); expect(noAffectedApps).not.toContain(myapp2);
expect(failedTests).toContain(myapp);
expect(failedTests).toContain(mypublishablelib);
expect(failedTests).toContain(`Failed projects:`); const affectedLibs = runCommand(
expect(failedTests).toContain(myapp); `npm run affected:libs -- --files="libs/${mylib}/src/index.ts"`
expect(failedTests).toContain( );
'You can isolate the above projects by passing: --only-failed' expect(affectedLibs).toContain(mypublishablelib);
); expect(affectedLibs).toContain(mylib);
expect(readJson('dist/.nx-results')).toEqual({ expect(affectedLibs).not.toContain(mylib2);
command: 'test',
results: { const implicitlyAffectedLibs = runCommand(
[myapp]: false, 'npm run affected:libs -- --files="package.json"'
[mylib]: true, );
[mypublishablelib]: true expect(implicitlyAffectedLibs).toContain(mypublishablelib);
expect(implicitlyAffectedLibs).toContain(mylib);
expect(implicitlyAffectedLibs).toContain(mylib2);
const noAffectedLibs = runCommand(
'npm run affected:libs -- --files="README.md"'
);
expect(noAffectedLibs).not.toContain(mypublishablelib);
expect(noAffectedLibs).not.toContain(mylib);
expect(noAffectedLibs).not.toContain(mylib2);
const build = runCommand(
`npm run affected:build -- --files="libs/${mylib}/src/index.ts"`
);
expect(build).toContain(`Running target build for projects:`);
expect(build).toContain(`- ${myapp}`);
expect(build).toContain(`- ${mypublishablelib}`);
expect(build).not.toContain('is not registered with the build command');
expect(build).not.toContain('with flags:');
// Should work in parallel
const buildParallel = runCommand(
`npm run affected:build -- --files="libs/${mylib}/src/index.ts" --parallel`
);
expect(buildParallel).toContain(`Running target build for projects:`);
expect(buildParallel).toContain(`- ${myapp}`);
expect(buildParallel).toContain(`- ${mypublishablelib}`);
expect(buildParallel).toContain(
'Running target "build" for affected projects succeeded'
);
const buildExcluded = runCommand(
`npm run affected:build -- --files="libs/${mylib}/src/index.ts" --exclude ${myapp}`
);
expect(buildExcluded).toContain(`Running target build for projects:`);
expect(buildExcluded).toContain(`- ${mypublishablelib}`);
// affected:build should pass non-nx flags to the CLI
const buildWithFlags = runCommand(
`npm run affected:build -- --files="libs/${mylib}/src/index.ts" --stats-json`
);
expect(buildWithFlags).toContain(`Running target build for projects:`);
expect(buildWithFlags).toContain(`- ${myapp}`);
expect(buildWithFlags).toContain(`- ${mypublishablelib}`);
expect(buildWithFlags).toContain('With flags: --stats-json=true');
if (supportUi()) {
const e2e = runCommand(
`npm run affected:e2e -- --files="libs/${mylib}/src/index.ts" --headless`
);
expect(e2e).toContain('should display welcome message');
} }
});
// Fix failing Unit Test const unitTests = runCommand(
updateFile( `npm run affected:test -- --files="libs/${mylib}/src/index.ts"`
`apps/${myapp}/src/app/app.component.spec.ts`, );
readFile(`apps/${myapp}/src/app/app.component.spec.ts`).replace( expect(unitTests).toContain(`Running target test for projects:`);
'.toEqual(2)', expect(unitTests).toContain(`- ${mylib}`);
'.toEqual(1)' expect(unitTests).toContain(`- ${myapp}`);
) expect(unitTests).toContain(`- ${mypublishablelib}`);
); // Fail a Unit Test
updateFile(
`apps/${myapp}/src/app/app.component.spec.ts`,
readFile(`apps/${myapp}/src/app/app.component.spec.ts`).replace(
'.toEqual(1)',
'.toEqual(2)'
)
);
const isolatedTests = runCommand( const failedTests = runCommand(
`npm run affected:test -- --files="libs/${mylib}/src/index.ts" --only-failed` `npm run affected:test -- --files="libs/${mylib}/src/index.ts"`
); );
expect(isolatedTests).toContain(`Running target test for projects:`); expect(failedTests).toContain(`Running target test for projects:`);
expect(isolatedTests).toContain(myapp); expect(failedTests).toContain(`- ${mylib}`);
expect(failedTests).toContain(`- ${myapp}`);
expect(failedTests).toContain(`- ${mypublishablelib}`);
expect(failedTests).toContain(`Failed projects:`);
expect(failedTests).toContain(
'You can isolate the above projects by passing: --only-failed'
);
expect(readJson('dist/.nx-results')).toEqual({
command: 'test',
results: {
[myapp]: false,
[mylib]: true,
[mypublishablelib]: true
}
});
const linting = runCommand( // Fix failing Unit Test
`npm run affected:lint -- --files="libs/${mylib}/src/index.ts"` updateFile(
); `apps/${myapp}/src/app/app.component.spec.ts`,
expect(linting).toContain(`Running target lint for projects:`); readFile(`apps/${myapp}/src/app/app.component.spec.ts`).replace(
expect(linting).toContain(mylib); '.toEqual(2)',
expect(linting).toContain(myapp); '.toEqual(1)'
expect(linting).toContain(`${myapp}-e2e`); )
expect(linting).toContain(mypublishablelib); );
const lintWithJsonFormating = runCommand( const isolatedTests = runCommand(
`npm run affected:lint -- --files="libs/${mylib}/src/index.ts" -- --format json` `npm run affected:test -- --files="libs/${mylib}/src/index.ts" --only-failed`
); );
expect(lintWithJsonFormating).toContain('With flags: --format json'); expect(isolatedTests).toContain(`Running target test for projects`);
expect(isolatedTests).toContain(`- ${myapp}`);
const unitTestsExcluded = runCommand( const linting = runCommand(
`npm run affected:test -- --files="libs/${mylib}/src/index.ts" --exclude=${myapp},${mypublishablelib}` `npm run affected:lint -- --files="libs/${mylib}/src/index.ts"`
); );
expect(unitTestsExcluded).toContain(`Running target test for projects:`); expect(linting).toContain(`Running target lint for projects:`);
expect(unitTestsExcluded).toContain(mylib); expect(linting).toContain(`- ${mylib}`);
expect(linting).toContain(`- ${myapp}`);
expect(linting).toContain(`- ${myapp}-e2e`);
expect(linting).toContain(`- ${mypublishablelib}`);
const i18n = runCommand( const lintWithJsonFormating = runCommand(
`npm run affected -- --target extract-i18n --files="libs/${mylib}/src/index.ts"` `npm run affected:lint -- --files="libs/${mylib}/src/index.ts" -- --format json`
); );
expect(i18n).toContain(`Running target extract-i18n for projects:`); expect(lintWithJsonFormating).toContain('With flags: --format json');
expect(i18n).toContain(myapp);
const interpolatedTests = runCommand( const unitTestsExcluded = runCommand(
`npm run affected -- --target test --files="libs/${mylib}/src/index.ts" -- --jest-config {project.root}/jest.config.js` `npm run affected:test -- --files="libs/${mylib}/src/index.ts" --exclude=${myapp},${mypublishablelib}`
); );
expect(interpolatedTests).toContain( expect(unitTestsExcluded).toContain(`Running target test for projects:`);
`Running target "test" for affected projects succeeded` expect(unitTestsExcluded).toContain(`- ${mylib}`);
);
}, 1000000); const i18n = runCommand(
`npm run affected -- --target extract-i18n --files="libs/${mylib}/src/index.ts"`
);
expect(i18n).toContain(`Running target extract-i18n for projects:`);
expect(i18n).toContain(`- ${myapp}`);
const interpolatedTests = runCommand(
`npm run affected -- --target test --files="libs/${mylib}/src/index.ts" -- --jest-config {project.root}/jest.config.js`
);
expect(interpolatedTests).toContain(
`Running target \"test\" for affected projects succeeded`
);
}, 1000000);
});
}); });

View File

@ -8,37 +8,40 @@ import {
updateFile, updateFile,
exists, exists,
ensureProject, ensureProject,
uniq uniq,
forEachCli,
workspaceConfigName
} from './utils'; } from './utils';
describe('Command line', () => { forEachCli(() => {
it('lint should ensure module boundaries', () => { describe('Command line', () => {
ensureProject(); it('lint should ensure module boundaries', () => {
ensureProject();
const myapp = uniq('myapp'); const myapp = uniq('myapp');
const myapp2 = uniq('myapp2'); const myapp2 = uniq('myapp2');
const mylib = uniq('mylib'); const mylib = uniq('mylib');
const lazylib = uniq('lazylib'); const lazylib = uniq('lazylib');
const invalidtaglib = uniq('invalidtaglib'); const invalidtaglib = uniq('invalidtaglib');
const validtaglib = uniq('validtaglib'); const validtaglib = uniq('validtaglib');
runCLI(`generate @nrwl/angular:app ${myapp} --tags=validtag`); runCLI(`generate @nrwl/angular:app ${myapp} --tags=validtag`);
runCLI(`generate @nrwl/angular:app ${myapp2}`); runCLI(`generate @nrwl/angular:app ${myapp2}`);
runCLI(`generate @nrwl/angular:lib ${mylib}`); runCLI(`generate @nrwl/angular:lib ${mylib}`);
runCLI(`generate @nrwl/angular:lib ${lazylib}`); runCLI(`generate @nrwl/angular:lib ${lazylib}`);
runCLI(`generate @nrwl/angular:lib ${invalidtaglib} --tags=invalidtag`); runCLI(`generate @nrwl/angular:lib ${invalidtaglib} --tags=invalidtag`);
runCLI(`generate @nrwl/angular:lib ${validtaglib} --tags=validtag`); runCLI(`generate @nrwl/angular:lib ${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';
@ -48,305 +51,313 @@ describe('Command line', () => {
const s = {loadChildren: '@proj/${lazylib}'}; const s = {loadChildren: '@proj/${lazylib}'};
` `
); );
const out = runCLI(`lint ${myapp}`, { silenceError: true }); const out = runCLI(`lint ${myapp}`, { 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);
describe('nx lint', () => { describe('nx lint', () => {
afterAll(() => { afterAll(() => {
newProject(); newProject();
});
it('should run nx lint', () => {
ensureProject();
const appBefore = uniq('before');
const appAfter = uniq('after');
runCLI(`generate @nrwl/angular:app ${appBefore}`);
runCommand(`mv apps/${appBefore} apps/${appAfter}`);
const stdout = runCommand('./node_modules/.bin/nx workspace-lint');
expect(stdout).toContain(
`- Cannot find project '${appBefore}' in 'apps/${appBefore}'`
);
expect(stdout).toContain(
'The following file(s) do not belong to any projects:'
);
expect(stdout).toContain(`- apps/${appAfter}/browserslist`);
expect(stdout).toContain(
`- apps/${appAfter}/src/app/app.component.css`
);
expect(stdout).toContain(
`- apps/${appAfter}/src/app/app.component.html`
);
expect(stdout).toContain(
`- apps/${appAfter}/src/app/app.component.spec.ts`
);
});
}); });
it('should run nx lint', () => { it('format should check and reformat the code', () => {
ensureProject(); ensureProject();
const appBefore = uniq('before'); const myapp = uniq('myapp');
const appAfter = uniq('after'); const mylib = uniq('mylib');
runCLI(`generate @nrwl/angular:app ${appBefore}`); runCLI(`generate @nrwl/angular:app ${myapp}`);
runCommand(`mv apps/${appBefore} apps/${appAfter}`); runCLI(`generate @nrwl/angular:lib ${mylib}`);
updateFile(
const stdout = runCommand('./node_modules/.bin/nx workspace-lint'); `apps/${myapp}/src/main.ts`,
expect(stdout).toContain( `
`- Cannot find project '${appBefore}' in 'apps/${appBefore}'`
);
expect(stdout).toContain(
'The following file(s) do not belong to any projects:'
);
expect(stdout).toContain(`- apps/${appAfter}/browserslist`);
expect(stdout).toContain(`- apps/${appAfter}/src/app/app.component.css`);
expect(stdout).toContain(`- apps/${appAfter}/src/app/app.component.html`);
expect(stdout).toContain(
`- apps/${appAfter}/src/app/app.component.spec.ts`
);
});
});
it('format should check and reformat the code', () => {
ensureProject();
const myapp = uniq('myapp');
const mylib = uniq('mylib');
runCLI(`generate @nrwl/angular:app ${myapp}`);
runCLI(`generate @nrwl/angular:lib ${mylib}`);
updateFile(
`apps/${myapp}/src/main.ts`,
`
const x = 1111; const x = 1111;
` `
); );
updateFile(
`apps/${myapp}/src/app/app.module.ts`,
`
const y = 1111;
`
);
updateFile(
`apps/${myapp}/src/app/app.component.ts`,
`
const z = 1111;
`
);
updateFile(
`libs/${mylib}/index.ts`,
`
const x = 1111;
`
);
updateFile(
`libs/${mylib}/src/${mylib}.module.ts`,
`
const y = 1111;
`
);
let stdout = runCommand(
`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}/src/${mylib}.module.ts`);
stdout = runCommand(`npm run -s format:check -- --all`);
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.component.ts`);
runCommand(
`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 -- --all');
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.component.ts`);
runCommand('npm run format:write -- --all');
expect(runCommand('npm run -s format:check -- --all')).toEqual('');
});
it('should support workspace-specific schematics', () => {
ensureProject();
const custom = uniq('custom');
runCLI(`g workspace-schematic ${custom} --no-interactive`);
checkFilesExist(
`tools/schematics/${custom}/index.ts`,
`tools/schematics/${custom}/schema.json`
);
const json = readJson(`tools/schematics/${custom}/schema.json`);
json.properties['directory'] = {
type: 'string',
description: 'lib directory'
};
updateFile(`tools/schematics/${custom}/schema.json`, JSON.stringify(json));
const indexFile = readFile(`tools/schematics/${custom}/index.ts`);
updateFile(
`tools/schematics/${custom}/index.ts`,
indexFile.replace(
'name: schema.name',
'name: schema.name, directory: schema.directory'
)
);
const workspace = uniq('workspace');
const dryRunOutput = runCommand(
`npm run workspace-schematic ${custom} ${workspace} -- --no-interactive --directory=dir -d`
);
expect(exists(`libs/dir/${workspace}/src/index.ts`)).toEqual(false);
expect(dryRunOutput).toContain('update angular.json');
expect(dryRunOutput).toContain('update nx.json');
const output = runCommand(
`npm run workspace-schematic ${custom} ${workspace} -- --no-interactive --directory=dir`
);
checkFilesExist(`libs/dir/${workspace}/src/index.ts`);
expect(output).toContain('update angular.json');
expect(output).toContain('update nx.json');
const another = uniq('another');
runCLI(`g workspace-schematic ${another} --no-interactive`);
const listSchematicsOutput = runCommand(
'npm run workspace-schematic -- --list-schematics'
);
expect(listSchematicsOutput).toContain(
'nx workspace-schematic "--list-schematics"'
);
expect(listSchematicsOutput).toContain(custom);
expect(listSchematicsOutput).toContain(another);
const promptOutput = runCommand(
`npm run workspace-schematic ${custom} mylib2 --`
);
expect(promptOutput).toContain(
'In which directory should the library be generated?'
);
}, 1000000);
describe('dep-graph', () => {
beforeAll(() => {
newProject();
runCLI('generate @nrwl/angular:app myapp');
runCLI('generate @nrwl/angular:app myapp2');
runCLI('generate @nrwl/angular:app myapp3');
runCLI('generate @nrwl/angular:lib mylib');
runCLI('generate @nrwl/angular:lib mylib2');
updateFile( updateFile(
'apps/myapp/src/main.ts', `apps/${myapp}/src/app/app.module.ts`,
` `
const y = 1111;
`
);
updateFile(
`apps/${myapp}/src/app/app.component.ts`,
`
const z = 1111;
`
);
updateFile(
`libs/${mylib}/index.ts`,
`
const x = 1111;
`
);
updateFile(
`libs/${mylib}/src/${mylib}.module.ts`,
`
const y = 1111;
`
);
let stdout = runCommand(
`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}/src/${mylib}.module.ts`);
stdout = runCommand(`npm run -s format:check -- --all`);
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.component.ts`);
runCommand(
`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 -- --all');
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.component.ts`);
runCommand('npm run format:write -- --all');
expect(runCommand('npm run -s format:check -- --all')).toEqual('');
});
it('should support workspace-specific schematics', () => {
ensureProject();
const custom = uniq('custom');
runCLI(`g workspace-schematic ${custom} --no-interactive`);
checkFilesExist(
`tools/schematics/${custom}/index.ts`,
`tools/schematics/${custom}/schema.json`
);
const json = readJson(`tools/schematics/${custom}/schema.json`);
json.properties['directory'] = {
type: 'string',
description: 'lib directory'
};
updateFile(
`tools/schematics/${custom}/schema.json`,
JSON.stringify(json)
);
const indexFile = readFile(`tools/schematics/${custom}/index.ts`);
updateFile(
`tools/schematics/${custom}/index.ts`,
indexFile.replace(
'name: schema.name',
'name: schema.name, directory: schema.directory'
)
);
const workspace = uniq('workspace');
const dryRunOutput = runCommand(
`npm run workspace-schematic ${custom} ${workspace} -- --no-interactive --directory=dir -d`
);
expect(exists(`libs/dir/${workspace}/src/index.ts`)).toEqual(false);
expect(dryRunOutput).toContain(`update ${workspaceConfigName()}`);
expect(dryRunOutput).toContain('update nx.json');
const output = runCommand(
`npm run workspace-schematic ${custom} ${workspace} -- --no-interactive --directory=dir`
);
checkFilesExist(`libs/dir/${workspace}/src/index.ts`);
expect(output).toContain(`update ${workspaceConfigName()}`);
expect(output).toContain('update nx.json');
const another = uniq('another');
runCLI(`g workspace-schematic ${another} --no-interactive`);
const listSchematicsOutput = runCommand(
'npm run workspace-schematic -- --list-schematics'
);
expect(listSchematicsOutput).toContain(
'nx workspace-schematic "--list-schematics"'
);
expect(listSchematicsOutput).toContain(custom);
expect(listSchematicsOutput).toContain(another);
const promptOutput = runCommand(
`npm run workspace-schematic ${custom} mylib2 --`
);
expect(promptOutput).toContain(
'In which directory should the library be generated?'
);
}, 1000000);
describe('dep-graph', () => {
beforeAll(() => {
newProject();
runCLI('generate @nrwl/angular:app myapp');
runCLI('generate @nrwl/angular:app myapp2');
runCLI('generate @nrwl/angular:app myapp3');
runCLI('generate @nrwl/angular:lib mylib');
runCLI('generate @nrwl/angular:lib mylib2');
updateFile(
'apps/myapp/src/main.ts',
`
import '@proj/mylib'; import '@proj/mylib';
const s = {loadChildren: '@proj/mylib2'}; const s = {loadChildren: '@proj/mylib2'};
` `
); );
updateFile( updateFile(
'apps/myapp2/src/app/app.component.spec.ts', 'apps/myapp2/src/app/app.component.spec.ts',
`import '@proj/mylib';` `import '@proj/mylib';`
); );
updateFile( updateFile(
'libs/mylib/src/mylib.module.spec.ts', 'libs/mylib/src/mylib.module.spec.ts',
`import '@proj/mylib2';` `import '@proj/mylib2';`
); );
});
it('dep-graph should output json (without critical path) to file', () => {
const file = 'dep-graph.json';
runCommand(`npm run dep-graph -- --file="${file}"`);
expect(() => checkFilesExist(file)).not.toThrow();
const jsonFileContents = readJson(file);
expect(jsonFileContents).toEqual({
deps: {
mylib2: [],
myapp3: [],
'myapp3-e2e': [
{
projectName: 'myapp3',
type: 'implicit'
}
],
myapp2: [
{
projectName: 'mylib',
type: 'es6Import'
}
],
'myapp2-e2e': [
{
projectName: 'myapp2',
type: 'implicit'
}
],
mylib: [
{
projectName: 'mylib2',
type: 'es6Import'
}
],
myapp: [
{
projectName: 'mylib',
type: 'es6Import'
},
{
projectName: 'mylib2',
type: 'loadChildren'
}
],
'myapp-e2e': [
{
projectName: 'myapp',
type: 'implicit'
}
]
},
criticalPath: []
}); });
}, 1000000);
it('dep-graph should output json with critical path to file', () => { it('dep-graph should output json (without critical path) to file', () => {
const file = 'dep-graph.json'; const file = 'dep-graph.json';
runCommand( runCommand(`npm run dep-graph -- --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).toEqual({
expect(jsonFileContents.criticalPath).toContain('myapp2'); deps: {
expect(jsonFileContents.criticalPath).toContain('mylib'); mylib2: [],
expect(jsonFileContents.criticalPath).not.toContain('mylib2'); myapp3: [],
}, 1000000); 'myapp3-e2e': [
{
projectName: 'myapp3',
type: 'implicit'
}
],
myapp2: [
{
projectName: 'mylib',
type: 'es6Import'
}
],
'myapp2-e2e': [
{
projectName: 'myapp2',
type: 'implicit'
}
],
mylib: [
{
projectName: 'mylib2',
type: 'es6Import'
}
],
myapp: [
{
projectName: 'mylib',
type: 'es6Import'
},
{
projectName: 'mylib2',
type: 'loadChildren'
}
],
'myapp-e2e': [
{
projectName: 'myapp',
type: 'implicit'
}
]
},
criticalPath: []
});
}, 1000000);
it('dep-graph should output dot to file', () => { it('dep-graph should output json with critical path to file', () => {
const file = 'dep-graph.dot'; const file = 'dep-graph.json';
runCommand( runCommand(
`npm run dep-graph -- --files="libs/mylib/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 fileContents = readFile(file); const jsonFileContents = readJson(file);
expect(fileContents).toContain('"myapp" -> "mylib"');
expect(fileContents).toContain('"myapp2" -> "mylib"');
expect(fileContents).toContain('"mylib" -> "mylib2"');
}, 1000000);
it('dep-graph should output html to file', () => { expect(jsonFileContents.criticalPath).toContain('myapp');
const file = 'dep-graph.html'; expect(jsonFileContents.criticalPath).toContain('myapp2');
runCommand( expect(jsonFileContents.criticalPath).toContain('mylib');
`npm run dep-graph -- --files="libs/mylib/index.ts" --file="${file}"` expect(jsonFileContents.criticalPath).not.toContain('mylib2');
); }, 1000000);
expect(() => checkFilesExist(file)).not.toThrow(); it('dep-graph should output dot to file', () => {
const file = 'dep-graph.dot';
const fileContents = readFile(file); runCommand(
expect(fileContents).toContain('<html>'); `npm run dep-graph -- --files="libs/mylib/index.ts" --file="${file}"`
expect(fileContents).toContain('<title>myapp&#45;&gt;mylib</title>'); );
expect(fileContents).toContain('<title>myapp&#45;&gt;mylib2</title>');
expect(fileContents).toContain('<title>mylib&#45;&gt;mylib2</title>'); expect(() => checkFilesExist(file)).not.toThrow();
}, 1000000);
const fileContents = readFile(file);
expect(fileContents).toContain('"myapp" -> "mylib"');
expect(fileContents).toContain('"myapp2" -> "mylib"');
expect(fileContents).toContain('"mylib" -> "mylib2"');
}, 1000000);
it('dep-graph should output html to file', () => {
const file = 'dep-graph.html';
runCommand(
`npm run dep-graph -- --files="libs/mylib/index.ts" --file="${file}"`
);
expect(() => checkFilesExist(file)).not.toThrow();
const fileContents = readFile(file);
expect(fileContents).toContain('<html>');
expect(fileContents).toContain('<title>myapp&#45;&gt;mylib</title>');
expect(fileContents).toContain('<title>myapp&#45;&gt;mylib2</title>');
expect(fileContents).toContain('<title>mylib&#45;&gt;mylib2</title>');
}, 1000000);
});
}); });
}); });

View File

@ -6,58 +6,61 @@ import {
readFile, readFile,
ensureProject, ensureProject,
uniq, uniq,
runsInWSL, newProject,
newProject forEachCli,
supportUi
} from './utils'; } from './utils';
describe('Cypress E2E Test runner', () => { forEachCli(() => {
describe('project scaffolding', () => { describe('Cypress E2E Test runner', () => {
it('should generate an app with the Cypress as e2e test runner', () => { describe('project scaffolding', () => {
ensureProject(); it('should generate an app with the Cypress as e2e test runner', () => {
const myapp = uniq('myapp'); ensureProject();
runCLI(`generate @nrwl/angular:app ${myapp} --e2eTestRunner=cypress`);
// Making sure the package.json file contains the Cypress dependency
const packageJson = readJson('package.json');
expect(packageJson.devDependencies['cypress']).toBeTruthy();
// Making sure the cypress folders & files are created
checkFilesExist(`apps/${myapp}-e2e/cypress.json`);
checkFilesExist(`apps/${myapp}-e2e/tsconfig.e2e.json`);
checkFilesExist(`apps/${myapp}-e2e/src/fixtures/example.json`);
checkFilesExist(`apps/${myapp}-e2e/src/integration/app.spec.ts`);
checkFilesExist(`apps/${myapp}-e2e/src/plugins/index.js`);
checkFilesExist(`apps/${myapp}-e2e/src/support/app.po.ts`);
checkFilesExist(`apps/${myapp}-e2e/src/support/index.ts`);
checkFilesExist(`apps/${myapp}-e2e/src/support/commands.ts`);
}, 1000000);
});
if (!runsInWSL()) {
describe('running Cypress', () => {
it('should execute e2e tests using Cypress', () => {
newProject();
const myapp = uniq('myapp'); const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --e2eTestRunner=cypress`); runCLI(`generate @nrwl/angular:app ${myapp} --e2eTestRunner=cypress`);
expect( // Making sure the package.json file contains the Cypress dependency
runCLI(`e2e --project=${myapp}-e2e --headless --watch=false`) const packageJson = readJson('package.json');
).toContain('All specs passed!'); expect(packageJson.devDependencies['cypress']).toBeTruthy();
const originalContents = JSON.parse( // Making sure the cypress folders & files are created
readFile(`apps/${myapp}-e2e/cypress.json`) checkFilesExist(`apps/${myapp}-e2e/cypress.json`);
); checkFilesExist(`apps/${myapp}-e2e/tsconfig.e2e.json`);
delete originalContents.fixturesFolder;
updateFile(
`apps/${myapp}-e2e/cypress.json`,
JSON.stringify(originalContents)
);
expect( checkFilesExist(`apps/${myapp}-e2e/src/fixtures/example.json`);
runCLI(`e2e --project=${myapp}-e2e --headless --watch=false`) checkFilesExist(`apps/${myapp}-e2e/src/integration/app.spec.ts`);
).toContain('All specs passed!'); checkFilesExist(`apps/${myapp}-e2e/src/plugins/index.js`);
checkFilesExist(`apps/${myapp}-e2e/src/support/app.po.ts`);
checkFilesExist(`apps/${myapp}-e2e/src/support/index.ts`);
checkFilesExist(`apps/${myapp}-e2e/src/support/commands.ts`);
}, 1000000); }, 1000000);
}); });
}
if (supportUi()) {
describe('running Cypress', () => {
fit('should execute e2e tests using Cypress', () => {
newProject();
const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --e2eTestRunner=cypress`);
expect(runCLI(`e2e ${myapp}-e2e --headless --no-watch`)).toContain(
'All specs passed!'
);
const originalContents = JSON.parse(
readFile(`apps/${myapp}-e2e/cypress.json`)
);
delete originalContents.fixturesFolder;
updateFile(
`apps/${myapp}-e2e/cypress.json`,
JSON.stringify(originalContents)
);
expect(runCLI(`e2e ${myapp}-e2e --headless --no-watch`)).toContain(
'All specs passed!'
);
}, 1000000);
});
}
});
}); });

View File

@ -1,23 +1,31 @@
import { ensureProject, uniq, runCommand, checkFilesExist } from './utils'; import {
ensureProject,
uniq,
runCommand,
checkFilesExist,
forEachCli
} from './utils';
describe('Delegate to CLI', () => { forEachCli(() => {
it('should delegate to the Angular CLI all non-standard commands', async () => { describe('Delegate to CLI', () => {
ensureProject(); it('should delegate to the cli all non-standard commands', async () => {
ensureProject();
const appName = uniq('app'); const appName = uniq('app');
runCommand(`npm run nx -- g app ${appName}`); runCommand(`npm run nx -- g @nrwl/web:app ${appName}`);
runCommand(`npm run nx -- build ${appName}`); runCommand(`npm run nx -- build ${appName}`);
checkFilesExist( checkFilesExist(
`dist/apps/${appName}/index.html`, `dist/apps/${appName}/index.html`,
`dist/apps/${appName}/polyfills-es2015.js`, `dist/apps/${appName}/polyfills-es2015.js`,
`dist/apps/${appName}/runtime-es2015.js`, `dist/apps/${appName}/runtime-es2015.js`,
`dist/apps/${appName}/main-es2015.js`, `dist/apps/${appName}/main-es2015.js`,
`dist/apps/${appName}/styles-es2015.js`, `dist/apps/${appName}/styles-es2015.js`,
`dist/apps/${appName}/polyfills-es5.js`, `dist/apps/${appName}/polyfills-es5.js`,
`dist/apps/${appName}/runtime-es5.js`, `dist/apps/${appName}/runtime-es5.js`,
`dist/apps/${appName}/main-es5.js`, `dist/apps/${appName}/main-es5.js`,
`dist/apps/${appName}/styles-es5.js` `dist/apps/${appName}/styles-es5.js`
); );
}, 120000); }, 120000);
});
}); });

View File

@ -1,29 +1,33 @@
import { import {
ensureProject, ensureProject,
patchKarmaToWorkOnWSL,
runCLI, runCLI,
uniq, uniq,
updateFile updateFile,
forEachCli,
supportUi
} from './utils'; } from './utils';
describe('DowngradeModule', () => { forEachCli(() => {
it('should generate a downgradeModule setup', async () => { xdescribe('DowngradeModule', () => {
ensureProject(); it('should generate a downgradeModule setup', async () => {
ensureProject();
const myapp = uniq('myapp'); const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --unit-test-runner=karma`); runCLI(`generate @nrwl/angular:app ${myapp} --unit-test-runner=karma`);
patchKarmaToWorkOnWSL();
updateFile( updateFile(
`apps/${myapp}/src/legacy.js`, `apps/${myapp}/src/legacy.js`,
`window.angular.module('legacy', []);` `window.angular.module('legacy', []);`
); );
runCLI( runCLI(
`generate @nrwl/angular:downgrade-module legacy --angularJsImport=./legacy --project=${myapp}` `generate @nrwl/angular:downgrade-module legacy --angularJsImport=./legacy --project=${myapp}`
); );
runCLI(`build ${myapp}`); runCLI(`build ${myapp}`);
expect(runCLI(`test ${myapp} --no-watch`)).toContain('3 SUCCESS'); if (supportUi()) {
}, 1000000); expect(runCLI(`test ${myapp} --no-watch`)).toContain('3 SUCCESS');
}
}, 1000000);
});
}); });

47
e2e/help.test.ts Normal file
View File

@ -0,0 +1,47 @@
import { forEachCli, ensureProject, runCommand, runCLI, cli } from './utils';
forEachCli('nx', () => {
describe('Help', () => {
it('should should help', async () => {
ensureProject();
const mainHelp = runCLI(`--help`);
expect(mainHelp).toContain('Run a target for a project');
expect(mainHelp).toContain('Run task for affected projects');
const genHelp = runCLI(`g @nrwl/web:app --help`);
expect(genHelp).toContain(
'The file extension to be used for style files. (default: css)'
);
const affectedHelp = runCLI(`affected --help`);
expect(affectedHelp).toContain('Run task for affected projects');
const version = runCLI(`--version`);
expect(version).toContain('*'); // stub value
}, 120000);
});
});
forEachCli('angular', () => {
describe('Help', () => {
it('should should help', async () => {
ensureProject();
const mainHelp = runCLI(`--help`);
expect(mainHelp).toContain('Run a target for a project');
expect(mainHelp).toContain('Run task for affected projects');
const genHelp = runCLI(`g @nrwl/web:app --help`);
expect(genHelp).toContain(
'The file extension to be used for style files.'
);
const affectedHelp = runCLI(`affected --help`);
expect(affectedHelp).toContain('Run task for affected projects');
const version = runCLI(`--version`);
expect(version).toContain('*'); // stub value
}, 120000);
});
});

View File

@ -1,23 +1,25 @@
import { runCLIAsync, ensureProject, uniq, runCLI } from './utils'; import { runCLIAsync, ensureProject, uniq, runCLI, forEachCli } from './utils';
describe('Jest', () => { forEachCli(() => {
it('should be able test projects using jest', async done => { describe('Jest', () => {
ensureProject(); it('should be able test projects using jest', async done => {
const mylib = uniq('mylib'); ensureProject();
const myapp = uniq('myapp'); const mylib = uniq('mylib');
runCLI(`generate @nrwl/angular:app ${myapp} --unit-test-runner jest`); const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:lib ${mylib} --unit-test-runner jest`); runCLI(`generate @nrwl/angular:app ${myapp} --unit-test-runner jest`);
runCLI(`generate @nrwl/angular:lib ${mylib} --unit-test-runner jest`);
await Promise.all([ await Promise.all([
runCLIAsync(`generate @nrwl/angular:service test --project ${myapp}`), runCLIAsync(`generate @nrwl/angular:service test --project ${myapp}`),
runCLIAsync(`generate @nrwl/angular:component test --project ${myapp}`), runCLIAsync(`generate @nrwl/angular:component test --project ${myapp}`),
runCLIAsync(`generate @nrwl/angular:service test --project ${mylib}`), runCLIAsync(`generate @nrwl/angular:service test --project ${mylib}`),
runCLIAsync(`generate @nrwl/angular:component test --project ${mylib}`) runCLIAsync(`generate @nrwl/angular:component test --project ${mylib}`)
]); ]);
const appResult = await runCLIAsync(`test ${myapp}`); const appResult = await runCLIAsync(`test ${myapp} --no-watch`);
expect(appResult.stderr).toContain('Test Suites: 3 passed, 3 total'); expect(appResult.stderr).toContain('Test Suites: 3 passed, 3 total');
const libResult = await runCLIAsync(`test ${mylib}`); const libResult = await runCLIAsync(`test ${mylib}`);
expect(libResult.stderr).toContain('Test Suites: 3 passed, 3 total'); expect(libResult.stderr).toContain('Test Suites: 3 passed, 3 total');
done(); done();
}, 45000); }, 45000);
});
}); });

View File

@ -3,37 +3,42 @@ import {
runCLIAsync, runCLIAsync,
ensureProject, ensureProject,
uniq, uniq,
patchKarmaToWorkOnWSL forEachCli,
supportUi
} from './utils'; } from './utils';
describe('Karma', () => { forEachCli(() => {
it('should be able to generate a testable library using karma', async done => { xdescribe('Karma', () => {
ensureProject(); it('should be able to generate a testable library using karma', async done => {
const mylib = uniq('mylib'); ensureProject();
runCLI(`generate @nrwl/angular:lib ${mylib} --unit-test-runner karma`); const mylib = uniq('mylib');
patchKarmaToWorkOnWSL(); runCLI(`generate @nrwl/angular:lib ${mylib} --unit-test-runner karma`);
await Promise.all([ await Promise.all([
runCLIAsync(`generate @nrwl/angular:service test --project ${mylib}`), runCLIAsync(`generate @nrwl/angular:service test --project ${mylib}`),
runCLIAsync(`generate @nrwl/angular:component test --project ${mylib}`) runCLIAsync(`generate @nrwl/angular:component test --project ${mylib}`)
]); ]);
const karmaResult = await runCLIAsync(`test ${mylib}`); if (supportUi()) {
expect(karmaResult.stdout).toContain('3 SUCCESS'); const karmaResult = await runCLIAsync(`test ${mylib}`);
done(); expect(karmaResult.stdout).toContain('3 SUCCESS');
}, 30000); }
done();
}, 30000);
it('should be able to generate a testable application using karma', async done => { it('should be able to generate a testable application using karma', async done => {
ensureProject(); ensureProject();
const myapp = uniq('myapp'); const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --unit-test-runner karma`); runCLI(`generate @nrwl/angular:app ${myapp} --unit-test-runner karma`);
patchKarmaToWorkOnWSL();
await Promise.all([ await Promise.all([
runCLIAsync(`generate @nrwl/angular:service test --project ${myapp}`), runCLIAsync(`generate @nrwl/angular:service test --project ${myapp}`),
runCLIAsync(`generate @nrwl/angular:component test --project ${myapp}`) runCLIAsync(`generate @nrwl/angular:component test --project ${myapp}`)
]); ]);
const karmaResult = await runCLIAsync(`test ${myapp}`); if (supportUi()) {
expect(karmaResult.stdout).toContain('5 SUCCESS'); const karmaResult = await runCLIAsync(`test ${myapp}`);
done(); expect(karmaResult.stdout).toContain('5 SUCCESS');
}, 30000); }
done();
}, 30000);
});
}); });

111
e2e/new.test.ts Normal file
View File

@ -0,0 +1,111 @@
import {
ensureProject,
exists,
expectTestsPass,
getSize,
runCLI,
runCLIAsync,
uniq,
updateFile,
forEachCli,
checkFilesExist,
tmpProjPath,
supportUi
} from './utils';
import { toClassName } from '@nrwl/workspace';
forEachCli(() => {
describe('Create New Workspace', () => {
beforeEach(() => {
ensureProject();
});
it('should work', async () => {
const myapp = uniq('myapp');
const mylib = uniq('mylib');
runCLI(
`generate @nrwl/angular:app ${myapp} --directory=myDir --no-interactive`
);
runCLI(
`generate @nrwl/angular:lib ${mylib} --directory=myDir --no-interactive`
);
updateFile(
`apps/my-dir/${myapp}/src/app/app.module.ts`,
`
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { MyDir${toClassName(
mylib
)}Module } from '@proj/my-dir/${mylib}';
import { AppComponent } from './app.component';
@NgModule({
imports: [BrowserModule, MyDir${toClassName(mylib)}Module],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
`
);
runCLI(`build my-dir-${myapp} --prod --output-hashing none`);
checkFilesExist(
`dist/apps/my-dir/${myapp}/main-es2015.js`,
`dist/apps/my-dir/${myapp}/main-es5.js`
);
// This is a loose requirement because there are a lot of
// influences external from this project that affect this.
const es2015BundleSize = getSize(
tmpProjPath(`dist/apps/my-dir/${myapp}/main-es2015.js`)
);
console.log(
`The current es2015 bundle size is ${es2015BundleSize / 1000} KB`
);
expect(es2015BundleSize).toBeLessThanOrEqual(150000);
const es5BundleSize = getSize(
tmpProjPath(`dist/apps/my-dir/${myapp}/main-es5.js`)
);
console.log(`The current es5 bundle size is ${es5BundleSize / 1000} KB`);
expect(es5BundleSize).toBeLessThanOrEqual(175000);
// running tests for the app
expectTestsPass(await runCLIAsync(`test my-dir-${myapp} --no-watch`));
// running tests for the lib
expectTestsPass(await runCLIAsync(`test my-dir-${mylib} --no-watch`));
if (supportUi()) {
expect(
runCLI(`e2e my-dir-${myapp}-e2e --headless --no-watch`)
).toContain('All specs passed!');
}
}, 1000000);
it('should support router config generation (lazy)', async () => {
const myapp = uniq('myapp');
const mylib = uniq('mylib');
runCLI(`generate @nrwl/angular:app ${myapp} --directory=myDir --routing`);
runCLI(
`generate @nrwl/angular:lib ${mylib} --directory=myDir --routing --lazy --parentModule=apps/my-dir/${myapp}/src/app/app.module.ts`
);
runCLI(`build my-dir-${myapp} --aot`);
expectTestsPass(await runCLIAsync(`test my-dir-${myapp} --no-watch`));
}, 1000000);
it('should support router config generation (eager)', async () => {
const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --directory=myDir --routing`);
const mylib = uniq('mylib');
runCLI(
`generate @nrwl/angular:lib ${mylib} --directory=myDir --routing --parentModule=apps/my-dir/${myapp}/src/app/app.module.ts`
);
runCLI(`build my-dir-${myapp} --aot`);
expectTestsPass(await runCLIAsync(`test my-dir-${myapp} --no-watch`));
}, 1000000);
});
});

View File

@ -6,363 +6,356 @@ import {
runCLI, runCLI,
runCLIAsync, runCLIAsync,
runCommand, runCommand,
runNgNew, runNew,
updateFile updateFile,
forEachCli,
runNgAdd
} from './utils'; } from './utils';
describe('Nrwl Convert to Nx Workspace', () => { forEachCli('angular', () => {
beforeEach(cleanup); describe('Nrwl Convert to Nx Workspace', () => {
afterAll(cleanup); beforeEach(cleanup);
afterAll(cleanup);
it('should generate a workspace', () => { it('should generate a workspace', () => {
runNgNew(); runNew('', false, false);
// update package.json // update package.json
const packageJson = readJson('package.json'); const packageJson = readJson('package.json');
packageJson.description = 'some description'; packageJson.description = 'some description';
updateFile('package.json', JSON.stringify(packageJson, null, 2)); updateFile('package.json', JSON.stringify(packageJson, null, 2));
// confirm that @nrwl and @ngrx dependencies do not exist yet // confirm that @nrwl and @ngrx dependencies do not exist yet
expect(packageJson.devDependencies['@nrwl/workspace']).not.toBeDefined(); expect(packageJson.devDependencies['@nrwl/workspace']).not.toBeDefined();
expect(packageJson.dependencies['@ngrx/store']).not.toBeDefined(); expect(packageJson.dependencies['@ngrx/store']).not.toBeDefined();
expect(packageJson.dependencies['@ngrx/effects']).not.toBeDefined(); expect(packageJson.dependencies['@ngrx/effects']).not.toBeDefined();
expect(packageJson.dependencies['@ngrx/router-store']).not.toBeDefined(); expect(packageJson.dependencies['@ngrx/router-store']).not.toBeDefined();
expect( expect(
packageJson.devDependencies['@ngrx/store-devtools'] packageJson.devDependencies['@ngrx/store-devtools']
).not.toBeDefined(); ).not.toBeDefined();
// update tsconfig.json // update tsconfig.json
const tsconfigJson = readJson('tsconfig.json'); const tsconfigJson = readJson('tsconfig.json');
tsconfigJson.compilerOptions.paths = { a: ['b'] }; tsconfigJson.compilerOptions.paths = { a: ['b'] };
updateFile('tsconfig.json', JSON.stringify(tsconfigJson, null, 2)); updateFile('tsconfig.json', JSON.stringify(tsconfigJson, null, 2));
updateFile('src/scripts.ts', ''); updateFile('src/scripts.ts', '');
// update angular-cli.json // update angular-cli.json
const angularCLIJson = readJson('angular.json'); const angularCLIJson = readJson('angular.json');
angularCLIJson.projects.proj.architect.build.options.scripts = angularCLIJson.projects.proj.architect.test.options.scripts = [ angularCLIJson.projects.proj.architect.build.options.scripts = angularCLIJson.projects.proj.architect.test.options.scripts = [
'src/scripts.ts' 'src/scripts.ts'
]; ];
angularCLIJson.projects.proj.architect.test.options.styles = [ angularCLIJson.projects.proj.architect.test.options.styles = [
'src/styles.css' 'src/styles.css'
]; ];
updateFile('angular.json', JSON.stringify(angularCLIJson, null, 2)); updateFile('angular.json', JSON.stringify(angularCLIJson, null, 2));
// run the command // run the command
runCLI('add @nrwl/workspace --npmScope projscope --skip-install'); runNgAdd('add @nrwl/workspace --npmScope projscope --skip-install');
copyMissingPackages(); copyMissingPackages();
// check that prettier config exits and that files have been moved! // check that prettier config exits and that files have been moved!
checkFilesExist( checkFilesExist(
'.vscode/extensions.json', '.vscode/extensions.json',
'.prettierrc', '.prettierrc',
'apps/proj/src/main.ts', 'apps/proj/src/main.ts',
'apps/proj/src/app/app.module.ts' 'apps/proj/src/app/app.module.ts'
); );
expect(readJson('.vscode/extensions.json').recommendations).toEqual([ expect(readJson('.vscode/extensions.json').recommendations).toEqual([
'nrwl.angular-console', 'nrwl.angular-console',
'angular.ng-template', 'angular.ng-template',
'ms-vscode.vscode-typescript-tslint-plugin', 'ms-vscode.vscode-typescript-tslint-plugin',
'esbenp.prettier-vscode' 'esbenp.prettier-vscode'
]); ]);
// check that package.json got merged // check that package.json got merged
const updatedPackageJson = readJson('package.json'); const updatedPackageJson = readJson('package.json');
expect(updatedPackageJson.description).toEqual('some description'); expect(updatedPackageJson.description).toEqual('some description');
expect(updatedPackageJson.scripts).toEqual({ expect(updatedPackageJson.scripts).toEqual({
ng: 'ng', ng: 'ng',
nx: 'nx', nx: 'nx',
start: 'ng serve', start: 'ng serve',
build: 'ng build', build: 'ng build',
test: 'ng test', test: 'ng test',
lint: 'nx workspace-lint && ng lint', lint: 'nx workspace-lint && ng lint',
e2e: 'ng e2e', e2e: 'ng e2e',
'affected:apps': 'nx affected:apps', 'affected:apps': 'nx affected:apps',
'affected:libs': 'nx affected:libs', 'affected:libs': 'nx affected:libs',
'affected:build': 'nx affected:build', 'affected:build': 'nx affected:build',
'affected:e2e': 'nx affected:e2e', 'affected:e2e': 'nx affected:e2e',
'affected:test': 'nx affected:test', 'affected:test': 'nx affected:test',
'affected:lint': 'nx affected:lint', 'affected:lint': 'nx affected:lint',
'affected:dep-graph': 'nx affected:dep-graph', 'affected:dep-graph': 'nx affected:dep-graph',
affected: 'nx affected', affected: 'nx affected',
format: 'nx format:write', format: 'nx format:write',
'format:write': 'nx format:write', 'format:write': 'nx format:write',
'format:check': 'nx format:check', 'format:check': 'nx format:check',
update: 'ng update @nrwl/workspace', update: 'ng update @nrwl/workspace',
'update:check': 'ng update', 'update:check': 'ng update',
'dep-graph': 'nx dep-graph', 'dep-graph': 'nx dep-graph',
'workspace-schematic': 'nx workspace-schematic', 'workspace-schematic': 'nx workspace-schematic',
help: 'nx help' help: 'nx help'
}); });
expect(updatedPackageJson.devDependencies['@nrwl/workspace']).toBeDefined(); expect(
expect(updatedPackageJson.devDependencies['@angular/cli']).toBeDefined(); updatedPackageJson.devDependencies['@nrwl/workspace']
).toBeDefined();
expect(updatedPackageJson.devDependencies['@angular/cli']).toBeDefined();
const nxJson = readJson('nx.json'); const nxJson = readJson('nx.json');
expect(nxJson).toEqual({ expect(nxJson).toEqual({
npmScope: 'projscope', npmScope: 'projscope',
implicitDependencies: { implicitDependencies: {
'angular.json': '*', 'angular.json': '*',
'package.json': '*', 'package.json': '*',
'tslint.json': '*', 'tslint.json': '*',
'tsconfig.json': '*', 'tsconfig.json': '*',
'nx.json': '*' 'nx.json': '*'
},
projects: {
proj: {
tags: []
}, },
'proj-e2e': { projects: {
tags: [] proj: {
tags: []
},
'proj-e2e': {
tags: []
}
} }
} });
});
// check if angular-cli.json get merged // check if angular-cli.json get merged
const updatedAngularCLIJson = readJson('angular.json'); const updatedAngularCLIJson = readJson('angular.json');
expect(updatedAngularCLIJson.projects.proj.root).toEqual('apps/proj'); expect(updatedAngularCLIJson.projects.proj.root).toEqual('apps/proj');
expect(updatedAngularCLIJson.projects.proj.sourceRoot).toEqual( expect(updatedAngularCLIJson.projects.proj.sourceRoot).toEqual(
'apps/proj/src' 'apps/proj/src'
);
expect(updatedAngularCLIJson.projects.proj.architect.build).toEqual({
builder: '@angular-devkit/build-angular:browser',
options: {
outputPath: 'dist/apps/proj',
index: 'apps/proj/src/index.html',
main: 'apps/proj/src/main.ts',
polyfills: 'apps/proj/src/polyfills.ts',
tsConfig: 'apps/proj/tsconfig.app.json',
assets: ['apps/proj/src/favicon.ico', 'apps/proj/src/assets'],
styles: ['apps/proj/src/styles.css'],
scripts: ['apps/proj/src/scripts.ts']
},
configurations: {
production: {
fileReplacements: [
{
replace: 'apps/proj/src/environments/environment.ts',
with: 'apps/proj/src/environments/environment.prod.ts'
}
],
budgets: [
{
maximumError: '5mb',
maximumWarning: '2mb',
type: 'initial'
}
],
optimization: true,
outputHashing: 'all',
sourceMap: false,
extractCss: true,
namedChunks: false,
aot: true,
extractLicenses: true,
vendorChunk: false,
buildOptimizer: true
}
}
});
expect(updatedAngularCLIJson.projects.proj.architect.serve).toEqual({
builder: '@angular-devkit/build-angular:dev-server',
options: {
browserTarget: 'proj:build'
},
configurations: {
production: {
browserTarget: 'proj:build:production'
}
}
});
expect(updatedAngularCLIJson.projects.proj.architect.test).toEqual({
builder: '@angular-devkit/build-angular:karma',
options: {
main: 'apps/proj/src/test.ts',
polyfills: 'apps/proj/src/polyfills.ts',
tsConfig: 'apps/proj/tsconfig.spec.json',
karmaConfig: 'apps/proj/karma.conf.js',
styles: ['apps/proj/src/styles.css'],
scripts: ['apps/proj/src/scripts.ts'],
assets: ['apps/proj/src/favicon.ico', 'apps/proj/src/assets']
}
});
expect(updatedAngularCLIJson.projects.proj.architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint',
options: {
tsConfig: [
'apps/proj/tsconfig.app.json',
'apps/proj/tsconfig.spec.json'
],
exclude: ['**/node_modules/**']
}
});
expect(updatedAngularCLIJson.projects['proj-e2e'].root).toEqual(
'apps/proj-e2e'
);
expect(updatedAngularCLIJson.projects['proj-e2e'].architect.e2e).toEqual({
builder: '@angular-devkit/build-angular:protractor',
configurations: {
production: {
devServerTarget: 'proj:serve:production'
}
},
options: {
protractorConfig: 'apps/proj-e2e/protractor.conf.js',
devServerTarget: 'proj:serve'
}
});
expect(updatedAngularCLIJson.projects['proj-e2e'].architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint',
options: {
tsConfig: 'apps/proj-e2e/tsconfig.json',
exclude: ['**/node_modules/**']
}
});
const updatedTslint = readJson('tslint.json');
expect(updatedTslint.rules['nx-enforce-module-boundaries']).toEqual([
true,
{
allow: [],
depConstraints: [{ sourceTag: '*', onlyDependOnLibsWithTags: ['*'] }]
}
]);
runCLI('build --prod --outputHashing none');
checkFilesExist('dist/apps/proj/main-es2015.js');
});
it('should generate a workspace and not change dependencies, devDependencies, or vscode extensions if they already exist', () => {
// create a new AngularCLI app
runNgNew();
const nxVersion = '0.0.0';
const schematicsVersion = '0.0.0';
const ngrxVersion = '0.0.0';
// update package.json
const existingPackageJson = readJson('package.json');
existingPackageJson.devDependencies['@nrwl/workspace'] = schematicsVersion;
existingPackageJson.dependencies['@ngrx/store'] = ngrxVersion;
existingPackageJson.dependencies['@ngrx/effects'] = ngrxVersion;
existingPackageJson.dependencies['@ngrx/router-store'] = ngrxVersion;
existingPackageJson.devDependencies['@ngrx/store-devtools'] = ngrxVersion;
updateFile('package.json', JSON.stringify(existingPackageJson, null, 2));
updateFile(
'.vscode/extensions.json',
JSON.stringify({
recommendations: ['eamodio.gitlens', 'angular.ng-template']
})
);
// run the command
runCLI('add @nrwl/workspace --npmScope projscope --skip-install');
// check that dependencies and devDependencies remained the same
const packageJson = readJson('package.json');
expect(packageJson.devDependencies['@nrwl/workspace']).toEqual(
schematicsVersion
);
expect(packageJson.dependencies['@ngrx/store']).toEqual(ngrxVersion);
expect(packageJson.dependencies['@ngrx/effects']).toEqual(ngrxVersion);
expect(packageJson.dependencies['@ngrx/router-store']).toEqual(ngrxVersion);
expect(packageJson.devDependencies['@ngrx/store-devtools']).toEqual(
ngrxVersion
);
expect(readJson('.vscode/extensions.json').recommendations).toEqual([
'eamodio.gitlens',
'angular.ng-template',
'nrwl.angular-console',
'ms-vscode.vscode-typescript-tslint-plugin',
'esbenp.prettier-vscode'
]);
});
it('should convert a project with common libraries in the ecosystem', () => {
// create a new AngularCLI app
runNgNew();
// Add some Angular libraries
runCLI('add @angular/elements');
runCLI('add @angular/material');
runCLI('add @angular/pwa');
runCLI('add @ngrx/store');
runCLI('add @ngrx/effects');
// Add Nx
runCLI('add @nrwl/workspace --skip-install');
});
it('should handle workspaces with no e2e project', async () => {
// create a new AngularCLI app
runNgNew();
// Remove e2e
runCommand('rm -rf e2e');
const existingAngularJson = readJson('angular.json');
delete existingAngularJson.projects['proj'].architect.e2e;
updateFile('angular.json', JSON.stringify(existingAngularJson, null, 2));
// Add @nrwl/workspace
const result = await runCLIAsync(
'add @nrwl/workspace --npmScope projscope --skip-install'
);
checkFilesExist(
'.prettierrc',
'apps/proj/src/main.ts',
'apps/proj/src/app/app.module.ts'
);
expect(result.stderr).toContain(
'No e2e project was migrated because there was none declared in angular.json'
);
});
it('should handle different types of errors', () => {
// create a new AngularCLI app
runNgNew();
// Only remove e2e directory
runCommand('mv e2e e2e-bak');
try {
runCLI('add @nrwl/workspace --npmScope projscope --skip-install');
fail('Did not handle not having a e2e directory');
} catch (e) {
expect(e.stderr.toString()).toContain(
'Your workspace could not be converted into an Nx Workspace because of the above error.'
); );
}
// Put e2e back expect(updatedAngularCLIJson.projects.proj.architect.build).toEqual({
runCommand('mv e2e-bak e2e'); builder: '@angular-devkit/build-angular:browser',
options: {
aot: false,
outputPath: 'dist/apps/proj',
index: 'apps/proj/src/index.html',
main: 'apps/proj/src/main.ts',
polyfills: 'apps/proj/src/polyfills.ts',
tsConfig: 'apps/proj/tsconfig.app.json',
assets: ['apps/proj/src/favicon.ico', 'apps/proj/src/assets'],
styles: ['apps/proj/src/styles.css'],
scripts: ['apps/proj/src/scripts.ts']
},
configurations: {
production: {
fileReplacements: [
{
replace: 'apps/proj/src/environments/environment.ts',
with: 'apps/proj/src/environments/environment.prod.ts'
}
],
budgets: [
{
maximumError: '5mb',
maximumWarning: '2mb',
type: 'initial'
}
],
optimization: true,
outputHashing: 'all',
sourceMap: false,
extractCss: true,
namedChunks: false,
aot: true,
extractLicenses: true,
vendorChunk: false,
buildOptimizer: true
}
}
});
expect(updatedAngularCLIJson.projects.proj.architect.serve).toEqual({
builder: '@angular-devkit/build-angular:dev-server',
options: {
browserTarget: 'proj:build'
},
configurations: {
production: {
browserTarget: 'proj:build:production'
}
}
});
// Remove package.json expect(updatedAngularCLIJson.projects.proj.architect.test).toEqual({
runCommand('mv package.json package.json.bak'); builder: '@angular-devkit/build-angular:karma',
try { options: {
runCLI('add @nrwl/workspace --npmScope projscope --skip-install'); main: 'apps/proj/src/test.ts',
fail('Did not handle not having a package.json'); polyfills: 'apps/proj/src/polyfills.ts',
} catch (e) { tsConfig: 'apps/proj/tsconfig.spec.json',
expect(e.stderr.toString()).toContain( karmaConfig: 'apps/proj/karma.conf.js',
'Your workspace could not be converted into an Nx Workspace because of the above error.' styles: ['apps/proj/src/styles.css'],
scripts: ['apps/proj/src/scripts.ts'],
assets: ['apps/proj/src/favicon.ico', 'apps/proj/src/assets']
}
});
expect(updatedAngularCLIJson.projects.proj.architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint',
options: {
tsConfig: [
'apps/proj/tsconfig.app.json',
'apps/proj/tsconfig.spec.json'
],
exclude: ['**/node_modules/**']
}
});
expect(updatedAngularCLIJson.projects['proj-e2e'].root).toEqual(
'apps/proj-e2e'
);
expect(updatedAngularCLIJson.projects['proj-e2e'].architect.e2e).toEqual({
builder: '@angular-devkit/build-angular:protractor',
configurations: {
production: {
devServerTarget: 'proj:serve:production'
}
},
options: {
protractorConfig: 'apps/proj-e2e/protractor.conf.js',
devServerTarget: 'proj:serve'
}
});
expect(updatedAngularCLIJson.projects['proj-e2e'].architect.lint).toEqual(
{
builder: '@angular-devkit/build-angular:tslint',
options: {
tsConfig: 'apps/proj-e2e/tsconfig.json',
exclude: ['**/node_modules/**']
}
}
); );
}
// Put package.json back const updatedTslint = readJson('tslint.json');
runCommand('mv package.json.bak package.json'); expect(updatedTslint.rules['nx-enforce-module-boundaries']).toEqual([
true,
{
allow: [],
depConstraints: [{ sourceTag: '*', onlyDependOnLibsWithTags: ['*'] }]
}
]);
// Remove src runCLI('build --prod --outputHashing none');
runCommand('mv src src-bak'); checkFilesExist('dist/apps/proj/main-es2015.js');
try { });
runCLI('add @nrwl/workspace --npmScope projscope --skip-install');
fail('Did not handle not having a src directory');
} catch (e) {
expect(e.stderr.toString()).toContain('Path: src does not exist');
}
// Put src back it('should generate a workspace and not change dependencies, devDependencies, or vscode extensions if they already exist', () => {
runCommand('mv src-bak src'); // create a new AngularCLI app
runNew();
const nxVersion = '0.0.0';
const schematicsVersion = '0.0.0';
const ngrxVersion = '0.0.0';
// update package.json
const existingPackageJson = readJson('package.json');
existingPackageJson.devDependencies[
'@nrwl/workspace'
] = schematicsVersion;
existingPackageJson.dependencies['@ngrx/store'] = ngrxVersion;
existingPackageJson.dependencies['@ngrx/effects'] = ngrxVersion;
existingPackageJson.dependencies['@ngrx/router-store'] = ngrxVersion;
existingPackageJson.devDependencies['@ngrx/store-devtools'] = ngrxVersion;
updateFile('package.json', JSON.stringify(existingPackageJson, null, 2));
updateFile(
'.vscode/extensions.json',
JSON.stringify({
recommendations: ['eamodio.gitlens', 'angular.ng-template']
})
);
// run the command
runNgAdd('add @nrwl/workspace --npmScope projscope --skip-install');
// check that dependencies and devDependencies remained the same
const packageJson = readJson('package.json');
expect(packageJson.devDependencies['@nrwl/workspace']).toEqual(
schematicsVersion
);
expect(packageJson.dependencies['@ngrx/store']).toEqual(ngrxVersion);
expect(packageJson.dependencies['@ngrx/effects']).toEqual(ngrxVersion);
expect(packageJson.dependencies['@ngrx/router-store']).toEqual(
ngrxVersion
);
expect(packageJson.devDependencies['@ngrx/store-devtools']).toEqual(
ngrxVersion
);
expect(readJson('.vscode/extensions.json').recommendations).toEqual([
'eamodio.gitlens',
'angular.ng-template',
'nrwl.angular-console',
'ms-vscode.vscode-typescript-tslint-plugin',
'esbenp.prettier-vscode'
]);
});
it('should convert a project with common libraries in the ecosystem', () => {
// create a new AngularCLI app
runNew();
// Add some Angular libraries
runNgAdd('add @angular/elements');
runNgAdd('add @angular/material');
runNgAdd('add @angular/pwa');
runNgAdd('add @ngrx/store');
runNgAdd('add @ngrx/effects');
// Add Nx
runNgAdd('add @nrwl/workspace --skip-install');
});
it('should handle different types of errors', () => {
// create a new AngularCLI app
runNew();
// Only remove e2e directory
runCommand('mv e2e e2e-bak');
try {
runNgAdd('add @nrwl/workspace --npmScope projscope --skip-install');
fail('Did not handle not having a e2e directory');
} catch (e) {
expect(e.stderr.toString()).toContain(
'Your workspace could not be converted into an Nx Workspace because of the above error.'
);
}
// Put e2e back
runCommand('mv e2e-bak e2e');
// Remove package.json
runCommand('mv package.json package.json.bak');
try {
runNgAdd('add @nrwl/workspace --npmScope projscope --skip-install');
fail('Did not handle not having a package.json');
} catch (e) {
expect(e.stderr.toString()).toContain(
'Your workspace could not be converted into an Nx Workspace because of the above error.'
);
}
// Put package.json back
runCommand('mv package.json.bak package.json');
// Remove src
runCommand('mv src src-bak');
try {
runNgAdd('add @nrwl/workspace --npmScope projscope --skip-install');
fail('Did not handle not having a src directory');
} catch (e) {
expect(e.stderr.toString()).toContain('Path: src does not exist');
}
// Put src back
runCommand('mv src-bak src');
});
});
});
forEachCli('nx', () => {
describe('ng-add', () => {
it('is not supported', () => {});
}); });
}); });

View File

@ -1,115 +0,0 @@
import {
ensureProject,
exists,
expectTestsPass,
getSize,
runCLI,
runCLIAsync,
runsInWSL,
uniq,
updateFile
} from './utils';
import { toClassName } from '@nrwl/workspace';
describe('Nrwl Workspace', () => {
beforeEach(() => {
ensureProject();
});
it('should work', async () => {
const myapp = uniq('myapp');
const mylib = uniq('mylib');
runCLI(
`generate @nrwl/angular:app ${myapp} --directory=myDir --no-interactive`
);
runCLI(
`generate @nrwl/angular:lib ${mylib} --directory=myDir --no-interactive`
);
updateFile(
`apps/my-dir/${myapp}/src/app/app.module.ts`,
`
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { MyDir${toClassName(
mylib
)}Module } from '@proj/my-dir/${mylib}';
import { AppComponent } from './app.component';
@NgModule({
imports: [BrowserModule, MyDir${toClassName(mylib)}Module],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
`
);
runCLI(`build --prod --project=my-dir-${myapp} --output-hashing none`);
expect(
exists(`./tmp/proj/dist/apps/my-dir/${myapp}/main-es2015.js`)
).toEqual(true);
expect(exists(`./tmp/proj/dist/apps/my-dir/${myapp}/main-es5.js`)).toEqual(
true
);
// This is a loose requirement because there are a lot of
// influences external from this project that affect this.
const es2015BundleSize = getSize(
`./tmp/proj/dist/apps/my-dir/${myapp}/main-es2015.js`
);
console.log(
`The current es2015 bundle size is ${es2015BundleSize / 1000} KB`
);
expect(es2015BundleSize).toBeLessThanOrEqual(150000);
const es5BundleSize = getSize(
`./tmp/proj/dist/apps/my-dir/${myapp}/main-es5.js`
);
console.log(`The current es5 bundle size is ${es5BundleSize / 1000} KB`);
expect(es5BundleSize).toBeLessThanOrEqual(175000);
// running tests for the app
expectTestsPass(
await runCLIAsync(`test --project=my-dir-${myapp} --no-watch`)
);
// running tests for the lib
expectTestsPass(
await runCLIAsync(`test --project=my-dir-${mylib} --no-watch`)
);
if (!runsInWSL()) {
expect(
runCLI(`e2e --project=my-dir-${myapp}-e2e --headless --watch=false`)
).toContain('All specs passed!');
}
}, 1000000);
it('should support router config generation (lazy)', async () => {
const myapp = uniq('myapp');
const mylib = uniq('mylib');
runCLI(`generate @nrwl/angular:app ${myapp} --directory=myDir --routing`);
runCLI(
`generate @nrwl/angular:lib ${mylib} --directory=myDir --routing --lazy --parentModule=apps/my-dir/${myapp}/src/app/app.module.ts`
);
runCLI(`build --aot --project=my-dir-${myapp}`);
expectTestsPass(
await runCLIAsync(`test --project=my-dir-${myapp} --no-watch`)
);
}, 1000000);
it('should support router config generation (eager)', async () => {
const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --directory=myDir --routing`);
const mylib = uniq('mylib');
runCLI(
`generate @nrwl/angular:lib ${mylib} --directory=myDir --routing --parentModule=apps/my-dir/${myapp}/src/app/app.module.ts`
);
runCLI(`build --aot --project=my-dir-${myapp}`);
expectTestsPass(
await runCLIAsync(`test --project=my-dir-${myapp} --no-watch`)
);
}, 1000000);
});

View File

@ -4,67 +4,74 @@ import {
runCLIAsync, runCLIAsync,
uniq, uniq,
ensureProject, ensureProject,
readJson readJson,
forEachCli
} from './utils'; } from './utils';
describe('ngrx', () => { forEachCli(() => {
it('should work', async () => { describe('ngrx', () => {
ensureProject(); it('should work', async () => {
ensureProject();
const myapp = uniq('myapp'); const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --no-interactive`); runCLI(`generate @nrwl/angular:app ${myapp} --no-interactive`);
// Generate root ngrx state management // Generate root ngrx state management
runCLI( runCLI(
`generate @nrwl/angular:ngrx users --module=apps/${myapp}/src/app/app.module.ts --root` `generate @nrwl/angular:ngrx users --module=apps/${myapp}/src/app/app.module.ts --root`
); );
const packageJson = readJson('package.json'); const packageJson = readJson('package.json');
expect(packageJson.dependencies['@ngrx/store']).toBeDefined(); expect(packageJson.dependencies['@ngrx/store']).toBeDefined();
expect(packageJson.dependencies['@ngrx/effects']).toBeDefined(); expect(packageJson.dependencies['@ngrx/effects']).toBeDefined();
expect(packageJson.dependencies['@ngrx/router-store']).toBeDefined(); expect(packageJson.dependencies['@ngrx/router-store']).toBeDefined();
expect(packageJson.devDependencies['@ngrx/store-devtools']).toBeDefined(); expect(packageJson.devDependencies['@ngrx/store-devtools']).toBeDefined();
const mylib = uniq('mylib'); const mylib = uniq('mylib');
// Generate feature library and ngrx state within that library // Generate feature library and ngrx state within that library
runCLI(`g @nrwl/angular:lib ${mylib} --prefix=fl`); runCLI(`g @nrwl/angular:lib ${mylib} --prefix=fl`);
runCLI( runCLI(
`generate @nrwl/angular:ngrx flights --module=libs/${mylib}/src/lib/${mylib}.module.ts --facade` `generate @nrwl/angular:ngrx flights --module=libs/${mylib}/src/lib/${mylib}.module.ts --facade`
); );
expect(runCLI(`build ${myapp}`)).toContain('chunk {main} main-es2015.js,'); expect(runCLI(`build ${myapp}`)).toContain(
expect(runCLI(`build ${myapp}`)).toContain('chunk {main} main-es5.js,'); 'chunk {main} main-es2015.js,'
expectTestsPass(await runCLIAsync(`test ${myapp} --no-watch`)); );
expectTestsPass(await runCLIAsync(`test ${mylib} --no-watch`)); expect(runCLI(`build ${myapp}`)).toContain('chunk {main} main-es5.js,');
}, 1000000); expectTestsPass(await runCLIAsync(`test ${myapp} --no-watch`));
expectTestsPass(await runCLIAsync(`test ${mylib} --no-watch`));
}, 1000000);
it('should work with creators', async () => { it('should work with creators', async () => {
ensureProject(); ensureProject();
const myapp = uniq('myapp'); const myapp = uniq('myapp');
runCLI(`generate @nrwl/angular:app ${myapp} --routing --no-interactive`); runCLI(`generate @nrwl/angular:app ${myapp} --routing --no-interactive`);
// Generate root ngrx state management // Generate root ngrx state management
runCLI( runCLI(
`generate @nrwl/angular:ngrx users --module=apps/${myapp}/src/app/app.module.ts --root --minimal --syntax=creators` `generate @nrwl/angular:ngrx users --module=apps/${myapp}/src/app/app.module.ts --root --minimal --syntax=creators`
); );
const packageJson = readJson('package.json'); const packageJson = readJson('package.json');
expect(packageJson.dependencies['@ngrx/entity']).toBeDefined(); expect(packageJson.dependencies['@ngrx/entity']).toBeDefined();
expect(packageJson.dependencies['@ngrx/store']).toBeDefined(); expect(packageJson.dependencies['@ngrx/store']).toBeDefined();
expect(packageJson.dependencies['@ngrx/effects']).toBeDefined(); expect(packageJson.dependencies['@ngrx/effects']).toBeDefined();
expect(packageJson.dependencies['@ngrx/router-store']).toBeDefined(); expect(packageJson.dependencies['@ngrx/router-store']).toBeDefined();
expect(packageJson.devDependencies['@ngrx/schematics']).toBeDefined(); expect(packageJson.devDependencies['@ngrx/schematics']).toBeDefined();
expect(packageJson.devDependencies['@ngrx/store-devtools']).toBeDefined(); expect(packageJson.devDependencies['@ngrx/store-devtools']).toBeDefined();
const mylib = uniq('mylib'); const mylib = uniq('mylib');
// Generate feature library and ngrx state within that library // Generate feature library and ngrx state within that library
runCLI(`g @nrwl/angular:lib ${mylib} --prefix=fl`); runCLI(`g @nrwl/angular:lib ${mylib} --prefix=fl`);
runCLI( runCLI(
`generate @nrwl/angular:ngrx flights --module=libs/${mylib}/src/lib/${mylib}.module.ts --facade --syntax=creators` `generate @nrwl/angular:ngrx flights --module=libs/${mylib}/src/lib/${mylib}.module.ts --facade --syntax=creators`
); );
expect(runCLI(`build ${myapp}`)).toContain('chunk {main} main-es2015.js,'); expect(runCLI(`build ${myapp}`)).toContain(
expect(runCLI(`build ${myapp}`)).toContain('chunk {main} main-es5.js,'); 'chunk {main} main-es2015.js,'
expectTestsPass(await runCLIAsync(`test ${myapp} --no-watch`)); );
expectTestsPass(await runCLIAsync(`test ${mylib} --no-watch`)); expect(runCLI(`build ${myapp}`)).toContain('chunk {main} main-es5.js,');
}, 1000000); expectTestsPass(await runCLIAsync(`test ${myapp} --no-watch`));
expectTestsPass(await runCLIAsync(`test ${mylib} --no-watch`));
}, 1000000);
});
}); });

View File

@ -9,7 +9,11 @@ import {
runCLI, runCLI,
runCLIAsync, runCLIAsync,
uniq, uniq,
updateFile updateFile,
forEachCli,
checkFilesExist,
tmpProjPath,
workspaceConfigName
} from './utils'; } from './utils';
function getData(): Promise<any> { function getData(): Promise<any> {
@ -27,168 +31,167 @@ function getData(): Promise<any> {
}); });
} }
describe('Node Applications', () => { forEachCli(() => {
fit('should be able to generate an express application', async done => { describe('Node Applications', () => {
ensureProject(); it('should be able to generate an express application', async done => {
const nodeapp = uniq('nodeapp'); ensureProject();
runCLI(`generate @nrwl/express:app ${nodeapp}`); const nodeapp = uniq('nodeapp');
runCLI(`generate @nrwl/express:app ${nodeapp}`);
updateFile( updateFile(
`apps/${nodeapp}/src/app/test.spec.ts`, `apps/${nodeapp}/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/${nodeapp}/src/assets/file.txt`, ``); updateFile(`apps/${nodeapp}/src/assets/file.txt`, ``);
const jestResult = await runCLIAsync(`test ${nodeapp}`); const jestResult = await runCLIAsync(`test ${nodeapp}`);
expect(jestResult.stderr).toContain('Test Suites: 1 passed, 1 total'); expect(jestResult.stderr).toContain('Test Suites: 1 passed, 1 total');
await runCLIAsync(`build ${nodeapp}`); await runCLIAsync(`build ${nodeapp}`);
expect(exists(`./tmp/proj/dist/apps/${nodeapp}/main.js`)).toBeTruthy(); checkFilesExist(
expect( `dist/apps/${nodeapp}/main.js`,
exists(`./tmp/proj/dist/apps/${nodeapp}/assets/file.txt`) `dist/apps/${nodeapp}/assets/file.txt`,
).toBeTruthy(); `dist/apps/${nodeapp}/main.js.map`
expect(exists(`./tmp/proj/dist/apps/${nodeapp}/main.js.map`)).toBeTruthy(); );
const server = fork(
path.join(__dirname, '../../tmp/proj', `./dist/apps/${nodeapp}/main.js`), const server = fork(`./dist/apps/${nodeapp}/main.js`, [], {
[], cwd: tmpProjPath(),
{
cwd: './tmp/proj',
silent: true 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.message).toEqual(`Welcome to ${nodeapp}!`);
treeKill(server.pid, 'SIGTERM', err => {
expect(err).toBeFalsy();
resolve();
});
}); });
}); expect(server).toBeTruthy();
const config = readJson('angular.json'); await new Promise(resolve => {
config.projects[nodeapp].architect.waitAndPrint = { server.stdout.once('data', async data => {
builder: '@nrwl/workspace:run-commands', expect(data.toString()).toContain(
options: { 'Listening at http://localhost:3333'
commands: [ );
{
command: 'sleep 1 && echo DONE'
}
],
readyWhen: 'DONE'
}
};
config.projects[nodeapp].architect.serve.options.waitUntilTargets = [
`${nodeapp}:waitAndPrint`
];
updateFile('angular.json', JSON.stringify(config));
const process = spawn(
'node',
['./node_modules/.bin/ng', 'serve', nodeapp],
{
cwd: './tmp/proj'
}
);
let collectedOutput = '';
process.stdout.on('data', async (data: Buffer) => {
collectedOutput += data.toString();
if (!data.toString().includes('Listening at http://localhost:3333')) {
return;
}
const result = await getData();
expect(result.message).toEqual(`Welcome to ${nodeapp}!`);
treeKill(process.pid, 'SIGTERM', err => {
expect(collectedOutput.startsWith('DONE')).toBeTruthy();
expect(err).toBeFalsy();
done();
});
});
}, 30000);
it('should be able to generate a nest application', async done => {
ensureProject();
const nestapp = uniq('nestapp');
runCLI(`generate @nrwl/nest:app ${nestapp}`);
updateFile(`apps/${nestapp}/src/assets/file.txt`, ``);
const jestResult = await runCLIAsync(`test ${nestapp}`);
expect(jestResult.stderr).toContain('Test Suites: 2 passed, 2 total');
await runCLIAsync(`build ${nestapp}`);
expect(exists(`./tmp/proj/dist/apps/${nestapp}/main.js`)).toBeTruthy();
expect(
exists(`./tmp/proj/dist/apps/${nestapp}/assets/file.txt`)
).toBeTruthy();
expect(exists(`./tmp/proj/dist/apps/${nestapp}/main.js.map`)).toBeTruthy();
const server = fork(
path.join(__dirname, '../../tmp/proj', `./dist/apps/${nestapp}/main.js`),
[],
{
cwd: './tmp/proj',
silent: true
}
);
expect(server).toBeTruthy();
await new Promise(resolve => {
server.stdout.on('data', async data => {
const message = data.toString();
if (message.includes('Listening at http://localhost:3333')) {
const result = await getData(); const result = await getData();
expect(result.message).toEqual(`Welcome to ${nestapp}!`); expect(result.message).toEqual(`Welcome to ${nodeapp}!`);
treeKill(server.pid, 'SIGTERM', err => { treeKill(server.pid, 'SIGTERM', err => {
expect(err).toBeFalsy(); expect(err).toBeFalsy();
resolve(); resolve();
}); });
});
});
const config = readJson(workspaceConfigName());
config.projects[nodeapp].architect.waitAndPrint = {
builder: '@nrwl/workspace:run-commands',
options: {
commands: [
{
command: 'sleep 1 && echo DONE'
}
],
readyWhen: 'DONE'
} }
};
config.projects[nodeapp].architect.serve.options.waitUntilTargets = [
`${nodeapp}:waitAndPrint`
];
updateFile(workspaceConfigName(), JSON.stringify(config));
const process = spawn(
'node',
['./node_modules/.bin/nx', 'serve', nodeapp],
{
cwd: tmpProjPath()
}
);
let collectedOutput = '';
process.stdout.on('data', async (data: Buffer) => {
collectedOutput += data.toString();
if (!data.toString().includes('Listening at http://localhost:3333')) {
return;
}
const result = await getData();
expect(result.message).toEqual(`Welcome to ${nodeapp}!`);
treeKill(process.pid, 'SIGTERM', err => {
expect(collectedOutput.startsWith('DONE')).toBeTruthy();
expect(err).toBeFalsy();
done();
});
}); });
}); }, 30000);
const process = spawn( it('should be able to generate a nest application', async done => {
'node', ensureProject();
['./node_modules/.bin/ng', 'serve', nestapp], const nestapp = uniq('nestapp');
{ runCLI(`generate @nrwl/nest:app ${nestapp}`);
cwd: './tmp/proj'
}
);
process.stdout.on('data', async (data: Buffer) => { updateFile(`apps/${nestapp}/src/assets/file.txt`, ``);
if (!data.toString().includes('Listening at http://localhost:3333')) { const jestResult = await runCLIAsync(`test ${nestapp}`);
return; expect(jestResult.stderr).toContain('Test Suites: 2 passed, 2 total');
}
const result = await getData(); await runCLIAsync(`build ${nestapp}`);
expect(result.message).toEqual(`Welcome to ${nestapp}!`);
treeKill(process.pid, 'SIGTERM', err => { checkFilesExist(
expect(err).toBeFalsy(); `dist/apps/${nestapp}/main.js`,
done(); `dist/apps/${nestapp}/assets/file.txt`,
`dist/apps/${nestapp}/main.js.map`
);
const server = fork(`./dist/apps/${nestapp}/main.js`, [], {
cwd: tmpProjPath(),
silent: true
}); });
}); expect(server).toBeTruthy();
}, 30000);
it('should be able to generate an empty application', async () => { await new Promise(resolve => {
ensureProject(); server.stdout.on('data', async data => {
const nodeapp = uniq('nodeapp'); const message = data.toString();
if (message.includes('Listening at http://localhost:3333')) {
const result = await getData();
runCLI(`generate @nrwl/node:app ${nodeapp}`); expect(result.message).toEqual(`Welcome to ${nestapp}!`);
updateFile(`apps/${nodeapp}/src/main.ts`, `console.log('Hello World!');`); treeKill(server.pid, 'SIGTERM', err => {
await runCLIAsync(`build ${nodeapp}`); expect(err).toBeFalsy();
expect(exists(`./tmp/proj/dist/apps/${nodeapp}/main.js`)).toBeTruthy(); resolve();
const result = execSync(`node dist/apps/${nodeapp}/main.js`, { });
cwd: './tmp/proj' }
}).toString(); });
expect(result).toContain('Hello World!'); });
}, 30000);
const process = spawn(
'node',
['./node_modules/.bin/nx', 'serve', nestapp],
{
cwd: tmpProjPath()
}
);
process.stdout.on('data', async (data: Buffer) => {
if (!data.toString().includes('Listening at http://localhost:3333')) {
return;
}
const result = await getData();
expect(result.message).toEqual(`Welcome to ${nestapp}!`);
treeKill(process.pid, 'SIGTERM', err => {
expect(err).toBeFalsy();
done();
});
});
}, 30000);
it('should be able to generate an empty application', async () => {
ensureProject();
const nodeapp = uniq('nodeapp');
runCLI(`generate @nrwl/node:app ${nodeapp}`);
updateFile(`apps/${nodeapp}/src/main.ts`, `console.log('Hello World!');`);
await runCLIAsync(`build ${nodeapp}`);
checkFilesExist(`dist/apps/${nodeapp}/main.js`);
const result = execSync(`node dist/apps/${nodeapp}/main.js`, {
cwd: tmpProjPath()
}).toString();
expect(result).toContain('Hello World!');
}, 30000);
});
}); });

View File

@ -7,120 +7,131 @@ import {
runCLIAsync, runCLIAsync,
checkFilesExist, checkFilesExist,
renameFile, renameFile,
readJson readJson,
forEachCli,
supportUi,
workspaceConfigName
} from './utils'; } from './utils';
import { serializeJson } from '@nrwl/workspace'; import { serializeJson } from '@nrwl/workspace';
describe('React Applications', () => { forEachCli(() => {
it('should be able to generate a react app + lib', async () => { describe('React Applications', () => {
ensureProject(); it('should be able to generate a react app + lib', async () => {
const appName = uniq('app'); ensureProject();
const libName = uniq('lib'); const appName = uniq('app');
const libName = uniq('lib');
runCLI(`generate @nrwl/react:app ${appName} --no-interactive`); runCLI(`generate @nrwl/react:app ${appName} --no-interactive`);
runCLI(`generate @nrwl/react:lib ${libName} --no-interactive`); runCLI(`generate @nrwl/react:lib ${libName} --no-interactive`);
const mainPath = `apps/${appName}/src/main.tsx`; const mainPath = `apps/${appName}/src/main.tsx`;
updateFile( updateFile(
`apps/${appName}/src/app/app.css`, `apps/${appName}/src/app/app.css`,
`body { `body {
display: block; display: block;
}` }`
); );
updateFile(mainPath, `import '@proj/${libName}';\n` + readFile(mainPath)); updateFile(mainPath, `import '@proj/${libName}';\n` + readFile(mainPath));
const libTestResults = await runCLIAsync(`test ${libName}`); const libTestResults = await runCLIAsync(`test ${libName}`);
expect(libTestResults.stderr).toContain('Test Suites: 1 passed, 1 total'); expect(libTestResults.stderr).toContain('Test Suites: 1 passed, 1 total');
await testGeneratedApp(appName); await testGeneratedApp(appName);
}, 120000); }, 120000);
it('should generate app with routing', async () => { it('should generate app with routing', async () => {
ensureProject(); ensureProject();
const appName = uniq('app'); const appName = uniq('app');
runCLI(`generate @nrwl/react:app ${appName} --routing --no-interactive`); runCLI(`generate @nrwl/react:app ${appName} --routing --no-interactive`);
await testGeneratedApp(appName); await testGeneratedApp(appName);
}, 120000); }, 120000);
it('should be able to use JSX', async () => { it('should be able to use JSX', async () => {
ensureProject(); ensureProject();
const appName = uniq('app'); const appName = uniq('app');
const libName = uniq('lib'); const libName = uniq('lib');
runCLI(`generate @nrwl/react:app ${appName} --no-interactive`); runCLI(`generate @nrwl/react:app ${appName} --no-interactive`);
runCLI(`generate @nrwl/react:lib ${libName} --no-interactive`); runCLI(`generate @nrwl/react:lib ${libName} --no-interactive`);
renameFile(`apps/${appName}/src/main.tsx`, `apps/${appName}/src/main.jsx`); renameFile(
renameFile( `apps/${appName}/src/main.tsx`,
`apps/${appName}/src/app/app.tsx`, `apps/${appName}/src/main.jsx`
`apps/${appName}/src/app/app.jsx` );
); renameFile(
renameFile( `apps/${appName}/src/app/app.tsx`,
`apps/${appName}/src/app/app.spec.tsx`, `apps/${appName}/src/app/app.jsx`
`apps/${appName}/src/app/app.spec.jsx` );
); renameFile(
renameFile( `apps/${appName}/src/app/app.spec.tsx`,
`apps/${appName}/src/polyfills.ts`, `apps/${appName}/src/app/app.spec.jsx`
`apps/${appName}/src/polyfills.js` );
); renameFile(
const angularJson = readJson('angular.json'); `apps/${appName}/src/polyfills.ts`,
`apps/${appName}/src/polyfills.js`
);
const angularJson = readJson(workspaceConfigName());
angularJson.projects[ angularJson.projects[
appName appName
].architect.build.options.main = `apps/${appName}/src/main.jsx`; ].architect.build.options.main = `apps/${appName}/src/main.jsx`;
angularJson.projects[ angularJson.projects[
appName appName
].architect.build.options.polyfills = `apps/${appName}/src/polyfills.js`; ].architect.build.options.polyfills = `apps/${appName}/src/polyfills.js`;
updateFile('angular.json', serializeJson(angularJson)); updateFile(workspaceConfigName(), serializeJson(angularJson));
const mainPath = `apps/${appName}/src/main.jsx`; const mainPath = `apps/${appName}/src/main.jsx`;
updateFile(mainPath, `import '@proj/${libName}';\n` + readFile(mainPath)); updateFile(mainPath, `import '@proj/${libName}';\n` + readFile(mainPath));
await testGeneratedApp(appName); await testGeneratedApp(appName);
}, 30000); }, 30000);
async function testGeneratedApp(appName) { async function testGeneratedApp(appName) {
const lintResults = runCLI(`lint ${appName}`); const lintResults = runCLI(`lint ${appName}`);
expect(lintResults).toContain('All files pass linting.'); expect(lintResults).toContain('All files pass linting.');
runCLI(`build ${appName}`); runCLI(`build ${appName}`);
checkFilesExist( checkFilesExist(
`dist/apps/${appName}/index.html`, `dist/apps/${appName}/index.html`,
`dist/apps/${appName}/polyfills-es2015.js`, `dist/apps/${appName}/polyfills-es2015.js`,
`dist/apps/${appName}/runtime-es2015.js`, `dist/apps/${appName}/runtime-es2015.js`,
`dist/apps/${appName}/vendor-es2015.js`, `dist/apps/${appName}/vendor-es2015.js`,
`dist/apps/${appName}/main-es2015.js`, `dist/apps/${appName}/main-es2015.js`,
`dist/apps/${appName}/styles-es2015.js`, `dist/apps/${appName}/styles-es2015.js`,
`dist/apps/${appName}/polyfills-es5.js`, `dist/apps/${appName}/polyfills-es5.js`,
`dist/apps/${appName}/runtime-es5.js`, `dist/apps/${appName}/runtime-es5.js`,
`dist/apps/${appName}/vendor-es5.js`, `dist/apps/${appName}/vendor-es5.js`,
`dist/apps/${appName}/main-es5.js`, `dist/apps/${appName}/main-es5.js`,
`dist/apps/${appName}/styles-es5.js` `dist/apps/${appName}/styles-es5.js`
); );
expect(readFile(`dist/apps/${appName}/main-es5.js`)).toContain( expect(readFile(`dist/apps/${appName}/main-es5.js`)).toContain(
'var App = function () {' 'var App = function () {'
); );
expect(readFile(`dist/apps/${appName}/main-es2015.js`)).toContain( expect(readFile(`dist/apps/${appName}/main-es2015.js`)).toContain(
'const App = () => {' 'const App = () => {'
); );
runCLI(`build ${appName} --prod --output-hashing none`); runCLI(`build ${appName} --prod --output-hashing none`);
checkFilesExist( checkFilesExist(
`dist/apps/${appName}/index.html`, `dist/apps/${appName}/index.html`,
`dist/apps/${appName}/polyfills-es2015.js`, `dist/apps/${appName}/polyfills-es2015.js`,
`dist/apps/${appName}/runtime-es2015.js`, `dist/apps/${appName}/runtime-es2015.js`,
`dist/apps/${appName}/main-es2015.js`, `dist/apps/${appName}/main-es2015.js`,
`dist/apps/${appName}/polyfills-es5.js`, `dist/apps/${appName}/polyfills-es5.js`,
`dist/apps/${appName}/runtime-es5.js`, `dist/apps/${appName}/runtime-es5.js`,
`dist/apps/${appName}/main-es5.js`, `dist/apps/${appName}/main-es5.js`,
`dist/apps/${appName}/styles.css` `dist/apps/${appName}/styles.css`
); );
const testResults = await runCLIAsync(`test ${appName}`); const testResults = await runCLIAsync(`test ${appName}`);
expect(testResults.stderr).toContain('Test Suites: 1 passed, 1 total'); expect(testResults.stderr).toContain('Test Suites: 1 passed, 1 total');
const lintE2eResults = runCLI(`lint ${appName}-e2e`); const lintE2eResults = runCLI(`lint ${appName}-e2e`);
expect(lintE2eResults).toContain('All files pass linting.'); expect(lintE2eResults).toContain('All files pass linting.');
const e2eResults = runCLI(`e2e ${appName}-e2e`);
expect(e2eResults).toContain('All specs passed!'); if (supportUi()) {
} const e2eResults = runCLI(`e2e ${appName}-e2e`);
expect(e2eResults).toContain('All specs passed!');
}
}
});
}); });

View File

@ -1,43 +1,47 @@
import { import {
ensureProject, ensureProject,
patchKarmaToWorkOnWSL,
runCLI, runCLI,
uniq, uniq,
updateFile updateFile,
forEachCli,
supportUi
} from './utils'; } from './utils';
describe('Upgrade', () => { forEachCli(() => {
it('should generate an UpgradeModule setup', async () => { xdescribe('Upgrade', () => {
ensureProject(); it('should generate an UpgradeModule setup', async () => {
const myapp = uniq('myapp'); ensureProject();
runCLI(`generate @nrwl/angular:app ${myapp} --unit-test-runner=karma`); const myapp = uniq('myapp');
patchKarmaToWorkOnWSL(); runCLI(`generate @nrwl/angular:app ${myapp} --unit-test-runner=karma`);
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 @nrwl/angular:upgrade-module legacy --angularJsImport=./legacy ' + 'generate @nrwl/angular:upgrade-module legacy --angularJsImport=./legacy ' +
`--angularJsCmpSelector=proj-root-legacy --project=${myapp}` `--angularJsCmpSelector=proj-root-legacy --project=${myapp}`
); );
runCLI(`build ${myapp}`); runCLI(`build ${myapp}`);
expect(runCLI(`test ${myapp} --no-watch`)).toContain('1 SUCCESS'); if (supportUi()) {
}, 1000000); expect(runCLI(`test ${myapp} --no-watch`)).toContain('1 SUCCESS');
}
}, 1000000);
});
}); });

View File

@ -3,57 +3,107 @@ import { readFileSync, statSync, writeFileSync, renameSync } from 'fs';
import { ensureDirSync } from 'fs-extra'; import { ensureDirSync } from 'fs-extra';
import * as path from 'path'; import * as path from 'path';
const projectName: string = 'proj'; export let cli;
export function uniq(prefix: string) { export function uniq(prefix: string) {
return `${prefix}${Math.floor(Math.random() * 10000000)}`; return `${prefix}${Math.floor(Math.random() * 10000000)}`;
} }
function patchPackageJsonDeps() { export function forEachCli(
const p = readFileSync('./tmp/proj/package.json').toString(); selectedCliOrFunction: string | Function,
callback?: Function
) {
let clis;
if (process.env.SELECTED_CLI && selectedCliOrFunction && callback) {
if (selectedCliOrFunction == process.env.SELECTED_CLI) {
clis = [process.env.SELECTED_CLI];
} else {
clis = [];
}
} else if (process.env.SELECTED_CLI) {
clis = [process.env.SELECTED_CLI];
} else {
clis = callback ? [selectedCliOrFunction] : ['nx', 'angular'];
}
const cb: any = callback ? callback : selectedCliOrFunction;
clis.forEach(c => {
describe(`[${c}]`, () => {
beforeEach(() => {
cli = c;
});
cb();
});
});
}
export function workspaceConfigName() {
return cli === 'angular' ? 'angular.json' : 'workspace.json';
}
function patchPackageJsonDeps(addWorkspace = true) {
const p = JSON.parse(readFileSync(tmpProjPath('package.json')).toString());
const workspacePath = path.join(getCwd(), 'build', 'packages', 'workspace'); const workspacePath = path.join(getCwd(), 'build', 'packages', 'workspace');
const angularPath = path.join(getCwd(), 'build', 'packages', 'angular'); const angularPath = path.join(getCwd(), 'build', 'packages', 'angular');
writeFileSync( const reactPath = path.join(getCwd(), 'build', 'packages', 'react');
'./tmp/proj/package.json',
p if (addWorkspace) {
.replace( p.devDependencies['@nrwl/workspace'] = `file:${workspacePath}`;
'"@nrwl/workspace": "*"', }
`"@nrwl/workspace": "file:${workspacePath}"` p.devDependencies['@nrwl/angular'] = `file:${angularPath}`;
) p.devDependencies['@nrwl/react'] = `file:${reactPath}`;
.replace('"@nrwl/angular": "*"', `"@nrwl/angular": "file:${angularPath}"`) writeFileSync(tmpProjPath('package.json'), JSON.stringify(p, null, 2));
);
} }
function runYarnInstall(silent: boolean = true) { function runYarnInstall(silent: boolean = true) {
const install = execSync('yarn install', { const install = execSync('yarn install', {
cwd: './tmp/proj', cwd: tmpProjPath(),
...(silent ? { stdio: ['ignore', 'ignore', 'ignore'] } : {}) ...(silent ? { stdio: ['ignore', 'ignore', 'ignore'] } : {})
}); });
return install ? install.toString() : ''; return install ? install.toString() : '';
} }
export function runNgNew(command?: string, silent?: boolean): string { export function runNew(
const gen = execSync( command?: string,
`../node_modules/.bin/ng new proj --no-interactive --skip-install ${command || silent?: boolean,
''}`, addWorkspace = true
{ ): string {
cwd: `./tmp`, let gen;
...(silent ? { stdio: ['ignore', 'ignore', 'ignore'] } : {}) if (cli === 'angular') {
} gen = execSync(
); `../../node_modules/.bin/ng new proj --no-interactive --skip-install ${command ||
patchPackageJsonDeps(); ''}`,
const install = runYarnInstall(silent); {
cwd: `./tmp/${cli}`,
...(silent ? { stdio: ['ignore', 'ignore', 'ignore'] } : {})
}
);
} else {
gen = execSync(
`node ../../node_modules/@nrwl/tao/index.js new proj --no-interactive --skip-install ${command ||
''}`,
{
cwd: `./tmp/${cli}`,
...(silent && false ? { stdio: ['ignore', 'ignore', 'ignore'] } : {})
}
);
}
patchPackageJsonDeps(addWorkspace);
const install = runYarnInstall(silent && false);
return silent ? null : `${gen ? gen.toString() : ''}${install}`; return silent ? null : `${gen ? gen.toString() : ''}${install}`;
} }
export function newProject(): void { export function newProject(): void {
cleanup(); cleanup();
if (!directoryExists('./tmp/proj_backup')) { if (!directoryExists(tmpBackupProjPath())) {
runNgNew('--collection=@nrwl/workspace --npmScope=proj', true); runNew('--collection=@nrwl/workspace --npmScope=proj', true);
copyMissingPackages(); copyMissingPackages();
writeFileSync( writeFileSync(
'./tmp/proj/node_modules/@angular-devkit/schematics/tasks/node-package/executor.js', tmpProjPath(
'node_modules/@angular-devkit/schematics/tasks/node-package/executor.js'
),
` `
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
@ -67,45 +117,28 @@ function default_1(factoryOptions = {}) {
} }
exports.default = default_1;` exports.default = default_1;`
); );
runCLI('add @nrwl/jest');
runCLI('add @nrwl/cypress'); execSync(`mv ${tmpProjPath()} ${tmpBackupProjPath()}`);
runCLI('add @nrwl/web');
runCLI('add @nrwl/react');
runCLI('add @nrwl/angular');
runCLI('add @nrwl/node');
runCLI('add @nrwl/express');
runCLI('add @nrwl/nest');
execSync('mv ./tmp/proj ./tmp/proj_backup');
} }
execSync('cp -a ./tmp/proj_backup ./tmp/proj'); execSync(`cp -a ${tmpBackupProjPath()} ${tmpProjPath()}`);
} }
export function ensureProject(): void { export function ensureProject(): void {
if (!directoryExists('./tmp/proj')) { if (!directoryExists(tmpProjPath())) {
newProject(); newProject();
} }
} }
export function runsInWSL() { export function supportUi() {
return !!process.env['WINDOWSTMP']; // powershell => wsl => no ui for now
}
export function patchKarmaToWorkOnWSL(): void {
try { try {
const karma = readFile('karma.conf.js'); execSync(`powershell.exe echo 1`, {
if (process.env['WINDOWSTMP']) { stdio: ['ignore', 'ignore', 'ignore']
updateFile( });
'karma.conf.js', return false;
karma.replace( } catch (e) {
`const { constants } = require('karma');`, return true;
` }
const { constants } = require('karma');
process.env['TMPDIR']="${process.env['WINDOWSTMP']}";
`
)
);
}
} catch (e) {}
} }
export function copyMissingPackages(): void { export function copyMissingPackages(): void {
@ -150,7 +183,7 @@ export function copyMissingPackages(): void {
'document-register-element' 'document-register-element'
]; ];
modulesToCopy.forEach(m => copyNodeModule(projectName, m)); modulesToCopy.forEach(m => copyNodeModule(m));
updateFile( updateFile(
'node_modules/@angular-devkit/schematics/tasks/node-package/executor.js', 'node_modules/@angular-devkit/schematics/tasks/node-package/executor.js',
` `
@ -167,17 +200,18 @@ export function copyMissingPackages(): void {
` `
); );
execSync('rm -rf tmp/proj/node_modules/.bin/webpack'); execSync(`rm -rf ${tmpProjPath('node_modules/.bin/webpack')}`);
execSync( execSync(
`cp -a node_modules/.bin/webpack tmp/proj/node_modules/.bin/webpack` `cp -a node_modules/.bin/webpack ${tmpProjPath(
'node_modules/.bin/webpack'
)}`
); );
execSync(`rm -rf ./tmp/proj/node_modules/cypress/node_modules/@types`); execSync(`rm -rf ${tmpProjPath('node_modules/cypress/node_modules/@types')}`);
execSync(`rm -rf ./tmp/proj/@types/sinon-chai/node_modules/@types`);
} }
function copyNodeModule(path: string, name: string) { function copyNodeModule(name: string) {
execSync(`rm -rf tmp/${path}/node_modules/${name}`); execSync(`rm -rf ${tmpProjPath('node_modules/' + name)}`);
execSync(`cp -a node_modules/${name} tmp/${path}/node_modules/${name}`); execSync(`cp -a node_modules/${name} ${tmpProjPath('node_modules/' + name)}`);
} }
export function runCommandAsync( export function runCommandAsync(
@ -190,7 +224,7 @@ export function runCommandAsync(
exec( exec(
command, command,
{ {
cwd: `./tmp/proj` cwd: tmpProjPath()
}, },
(err, stdout, stderr) => { (err, stdout, stderr) => {
if (!opts.silenceError && err) { if (!opts.silenceError && err) {
@ -208,7 +242,35 @@ export function runCLIAsync(
silenceError: false silenceError: false
} }
): Promise<{ stdout: string; stderr: string }> { ): Promise<{ stdout: string; stderr: string }> {
return runCommandAsync(`./node_modules/.bin/ng ${command}`, opts); return runCommandAsync(
`node ./node_modules/@nrwl/cli/bin/nx.js ${command}`,
opts
);
}
export function runNgAdd(
command?: string,
opts = {
silenceError: false
}
): string {
try {
return execSync(`./node_modules/.bin/ng ${command}`, {
cwd: tmpProjPath()
})
.toString()
.replace(
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
''
);
} catch (e) {
if (opts.silenceError) {
return e.stdout.toString();
} else {
console.log(e.stdout.toString(), e.stderr.toString());
throw e;
}
}
} }
export function runCLI( export function runCLI(
@ -218,8 +280,8 @@ export function runCLI(
} }
): string { ): string {
try { try {
return execSync(`./node_modules/.bin/ng ${command}`, { return execSync(`node ./node_modules/@nrwl/cli/bin/nx.js ${command}`, {
cwd: `./tmp/${projectName}` cwd: tmpProjPath()
}) })
.toString() .toString()
.replace( .replace(
@ -244,7 +306,7 @@ export function expectTestsPass(v: { stdout: string; stderr: string }) {
export function runCommand(command: string): string { export function runCommand(command: string): string {
try { try {
return execSync(command, { return execSync(command, {
cwd: `./tmp/${projectName}`, cwd: tmpProjPath(),
stdio: ['pipe', 'pipe', 'pipe'] stdio: ['pipe', 'pipe', 'pipe']
}).toString(); }).toString();
} catch (e) { } catch (e) {
@ -253,23 +315,18 @@ export function runCommand(command: string): string {
} }
export function updateFile(f: string, content: string): void { export function updateFile(f: string, content: string): void {
ensureDirSync(path.dirname(path.join(getCwd(), 'tmp', 'proj', f))); ensureDirSync(path.dirname(tmpProjPath(f)));
writeFileSync(path.join(getCwd(), 'tmp', 'proj', f), content); writeFileSync(tmpProjPath(f), content);
} }
export function renameFile(f: string, newPath: string): void { export function renameFile(f: string, newPath: string): void {
ensureDirSync(path.dirname(path.join(getCwd(), 'tmp', 'proj', newPath))); ensureDirSync(path.dirname(tmpProjPath(newPath)));
renameSync( renameSync(tmpProjPath(f), tmpProjPath(newPath));
path.join(getCwd(), 'tmp', 'proj', f),
path.join(getCwd(), 'tmp', 'proj', newPath)
);
} }
export function checkFilesExist(...expectedFiles: string[]) { export function checkFilesExist(...expectedFiles: string[]) {
expectedFiles.forEach(f => { expectedFiles.forEach(f => {
const ff = f.startsWith('/') const ff = f.startsWith('/') ? f : tmpProjPath(f);
? f
: path.join(getCwd(), 'tmp', projectName, f);
if (!exists(ff)) { if (!exists(ff)) {
throw new Error(`File '${ff}' does not exist`); throw new Error(`File '${ff}' does not exist`);
} }
@ -281,12 +338,12 @@ export function readJson(f: string): any {
} }
export function readFile(f: string) { export function readFile(f: string) {
const ff = f.startsWith('/') ? f : path.join(getCwd(), 'tmp', projectName, f); const ff = f.startsWith('/') ? f : tmpProjPath(f);
return readFileSync(ff).toString(); return readFileSync(ff).toString();
} }
export function cleanup() { export function cleanup() {
execSync('rm -rf ./tmp/proj'); execSync(`rm -rf ${tmpProjPath()}`);
} }
export function getCwd(): string { export function getCwd(): string {
@ -316,3 +373,11 @@ export function exists(filePath: string): boolean {
export function getSize(filePath: string): number { export function getSize(filePath: string): number {
return statSync(filePath).size; return statSync(filePath).size;
} }
export function tmpProjPath(path?: string) {
return path ? `./tmp/${cli}/proj/${path}` : `./tmp/${cli}/proj`;
}
function tmpBackupProjPath(path?: string) {
return path ? `./tmp/${cli}/proj-backup/${path}` : `./tmp/${cli}/proj-backup`;
}

View File

@ -4,53 +4,57 @@ import {
readFile, readFile,
runCLI, runCLI,
runCLIAsync, runCLIAsync,
uniq uniq,
forEachCli,
supportUi
} from './utils'; } from './utils';
describe('Web Components Applications', () => { forEachCli(() => {
it('should be able to generate a web app', async () => { describe('Web Components Applications', () => {
ensureProject(); it('should be able to generate a web app', async () => {
const appName = uniq('app'); ensureProject();
const appName = uniq('app');
runCLI(`generate @nrwl/web:app ${appName} --no-interactive`); runCLI(`generate @nrwl/web:app ${appName} --no-interactive`);
const lintResults = runCLI(`lint ${appName}`); const lintResults = runCLI(`lint ${appName}`);
expect(lintResults).toContain('All files pass linting.'); expect(lintResults).toContain('All files pass linting.');
runCLI(`build ${appName}`); runCLI(`build ${appName}`);
checkFilesExist( checkFilesExist(
`dist/apps/${appName}/index.html`, `dist/apps/${appName}/index.html`,
`dist/apps/${appName}/polyfills-es2015.js`, `dist/apps/${appName}/polyfills-es2015.js`,
`dist/apps/${appName}/runtime-es2015.js`, `dist/apps/${appName}/runtime-es2015.js`,
`dist/apps/${appName}/main-es2015.js`, `dist/apps/${appName}/main-es2015.js`,
`dist/apps/${appName}/styles-es2015.js`, `dist/apps/${appName}/styles-es2015.js`,
`dist/apps/${appName}/polyfills-es5.js`, `dist/apps/${appName}/polyfills-es5.js`,
`dist/apps/${appName}/runtime-es5.js`, `dist/apps/${appName}/runtime-es5.js`,
`dist/apps/${appName}/main-es5.js`, `dist/apps/${appName}/main-es5.js`,
`dist/apps/${appName}/styles-es5.js` `dist/apps/${appName}/styles-es5.js`
); );
expect(readFile(`dist/apps/${appName}/main-es5.js`)).toContain( expect(readFile(`dist/apps/${appName}/main-es5.js`)).toContain(
'var AppElement = /** @class */ (function (_super) {' 'var AppElement = /** @class */ (function (_super) {'
); );
expect(readFile(`dist/apps/${appName}/main-es2015.js`)).toContain( expect(readFile(`dist/apps/${appName}/main-es2015.js`)).toContain(
'class AppElement' 'class AppElement'
); );
runCLI(`build ${appName} --prod --output-hashing none`); runCLI(`build ${appName} --prod --output-hashing none`);
checkFilesExist( checkFilesExist(
`dist/apps/${appName}/index.html`, `dist/apps/${appName}/index.html`,
`dist/apps/${appName}/polyfills-es2015.js`, `dist/apps/${appName}/polyfills-es2015.js`,
`dist/apps/${appName}/runtime-es2015.js`, `dist/apps/${appName}/runtime-es2015.js`,
`dist/apps/${appName}/main-es2015.js`, `dist/apps/${appName}/main-es2015.js`,
`dist/apps/${appName}/polyfills-es5.js`, `dist/apps/${appName}/polyfills-es5.js`,
`dist/apps/${appName}/runtime-es5.js`, `dist/apps/${appName}/runtime-es5.js`,
`dist/apps/${appName}/main-es5.js`, `dist/apps/${appName}/main-es5.js`,
`dist/apps/${appName}/styles.css` `dist/apps/${appName}/styles.css`
); );
const testResults = await runCLIAsync(`test ${appName}`); const testResults = await runCLIAsync(`test ${appName}`);
expect(testResults.stderr).toContain('Test Suites: 1 passed, 1 total'); expect(testResults.stderr).toContain('Test Suites: 1 passed, 1 total');
const lintE2eResults = runCLI(`lint ${appName}-e2e`); const lintE2eResults = runCLI(`lint ${appName}-e2e`);
expect(lintE2eResults).toContain('All files pass linting.'); expect(lintE2eResults).toContain('All files pass linting.');
const e2eResults = runCLI(`e2e ${appName}-e2e`); const e2eResults = runCLI(`e2e ${appName}-e2e`);
expect(e2eResults).toContain('All specs passed!'); expect(e2eResults).toContain('All specs passed!');
}, 120000); }, 120000);
});
}); });

View File

@ -49,7 +49,7 @@
"@ngrx/schematics": "8.1.0", "@ngrx/schematics": "8.1.0",
"@ngrx/store": "8.1.0", "@ngrx/store": "8.1.0",
"@ngrx/store-devtools": "8.1.0", "@ngrx/store-devtools": "8.1.0",
"@schematics/angular": "8.0.0", "@schematics/angular": "8.1.1",
"@testing-library/react": "8.0.5", "@testing-library/react": "8.0.5",
"@types/express": "4.16.0", "@types/express": "4.16.0",
"@types/jasmine": "~2.8.6", "@types/jasmine": "~2.8.6",
@ -94,7 +94,7 @@
"karma-jasmine-html-reporter": "^0.2.2", "karma-jasmine-html-reporter": "^0.2.2",
"karma-webpack": "2.0.4", "karma-webpack": "2.0.4",
"license-webpack-plugin": "^1.4.0", "license-webpack-plugin": "^1.4.0",
"ng-packagr": "5.1.0", "ng-packagr": "5.3.0",
"ngrx-store-freeze": "0.2.4", "ngrx-store-freeze": "0.2.4",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"opn": "^5.3.0", "opn": "^5.3.0",

View File

@ -13,27 +13,27 @@ describe('app', () => {
}); });
describe('not nested', () => { describe('not nested', () => {
it('should update angular.json', async () => { it('should update workspace.json', async () => {
const tree = await runSchematic('app', { name: 'myApp' }, appTree); const tree = await runSchematic('app', { name: 'myApp' }, appTree);
const angularJson = readJsonInTree(tree, '/angular.json'); const workspaceJson = readJsonInTree(tree, '/workspace.json');
expect(angularJson.projects['my-app'].root).toEqual('apps/my-app'); expect(workspaceJson.projects['my-app'].root).toEqual('apps/my-app');
expect(angularJson.projects['my-app-e2e'].root).toEqual( expect(workspaceJson.projects['my-app-e2e'].root).toEqual(
'apps/my-app-e2e' 'apps/my-app-e2e'
); );
expect( expect(
angularJson.projects['my-app'].architect.lint.options.exclude workspaceJson.projects['my-app'].architect.lint.options.exclude
).toEqual(['**/node_modules/**', '!apps/my-app/**']); ).toEqual(['**/node_modules/**', '!apps/my-app/**']);
expect( expect(
angularJson.projects['my-app-e2e'].architect.lint.options.exclude workspaceJson.projects['my-app-e2e'].architect.lint.options.exclude
).toEqual(['**/node_modules/**', '!apps/my-app-e2e/**']); ).toEqual(['**/node_modules/**', '!apps/my-app-e2e/**']);
}); });
it('should remove the e2e target on the application', async () => { it('should remove the e2e target on the application', async () => {
const tree = await runSchematic('app', { name: 'myApp' }, appTree); const tree = await runSchematic('app', { name: 'myApp' }, appTree);
const angularJson = readJsonInTree(tree, '/angular.json'); const workspaceJson = readJsonInTree(tree, '/workspace.json');
expect(angularJson.projects['my-app'].architect.e2e).not.toBeDefined(); expect(workspaceJson.projects['my-app'].architect.e2e).not.toBeDefined();
}); });
it('should update nx.json', async () => { it('should update nx.json', async () => {
@ -107,8 +107,10 @@ describe('app', () => {
let appE2eSpec = noPrefix let appE2eSpec = noPrefix
.read('apps/my-app-e2e/src/app.e2e-spec.ts') .read('apps/my-app-e2e/src/app.e2e-spec.ts')
.toString(); .toString();
let angularJson = JSON.parse(noPrefix.read('angular.json').toString()); let workspaceJson = JSON.parse(
let myAppPrefix = angularJson.projects['my-app'].prefix; noPrefix.read('workspace.json').toString()
);
let myAppPrefix = workspaceJson.projects['my-app'].prefix;
expect(myAppPrefix).toEqual('proj'); expect(myAppPrefix).toEqual('proj');
expect(appE2eSpec).toContain('Welcome to my-app!'); expect(appE2eSpec).toContain('Welcome to my-app!');
@ -118,8 +120,8 @@ describe('app', () => {
appE2eSpec = withPrefix appE2eSpec = withPrefix
.read('apps/my-app-e2e/src/app.e2e-spec.ts') .read('apps/my-app-e2e/src/app.e2e-spec.ts')
.toString(); .toString();
angularJson = JSON.parse(withPrefix.read('angular.json').toString()); workspaceJson = JSON.parse(withPrefix.read('workspace.json').toString());
myAppPrefix = angularJson.projects['my-app'].prefix; myAppPrefix = workspaceJson.projects['my-app'].prefix;
expect(myAppPrefix).toEqual('custom'); expect(myAppPrefix).toEqual('custom');
expect(appE2eSpec).toContain('Welcome to my-app!'); expect(appE2eSpec).toContain('Welcome to my-app!');
@ -127,7 +129,7 @@ describe('app', () => {
xit('should work if the new project root is changed', async () => { xit('should work if the new project root is changed', async () => {
appTree = await callRule( appTree = await callRule(
updateJsonInTree('/angular.json', json => ({ updateJsonInTree('/workspace.json', json => ({
...json, ...json,
newProjectRoot: 'newProjectRoot' newProjectRoot: 'newProjectRoot'
})), })),
@ -141,26 +143,27 @@ describe('app', () => {
}); });
describe('nested', () => { describe('nested', () => {
it('should update angular.json', async () => { it('should update workspace.json', async () => {
const tree = await runSchematic( const tree = await runSchematic(
'app', 'app',
{ name: 'myApp', directory: 'myDir' }, { name: 'myApp', directory: 'myDir' },
appTree appTree
); );
const angularJson = readJsonInTree(tree, '/angular.json'); const workspaceJson = readJsonInTree(tree, '/workspace.json');
expect(angularJson.projects['my-dir-my-app'].root).toEqual( expect(workspaceJson.projects['my-dir-my-app'].root).toEqual(
'apps/my-dir/my-app' 'apps/my-dir/my-app'
); );
expect(angularJson.projects['my-dir-my-app-e2e'].root).toEqual( expect(workspaceJson.projects['my-dir-my-app-e2e'].root).toEqual(
'apps/my-dir/my-app-e2e' 'apps/my-dir/my-app-e2e'
); );
expect( expect(
angularJson.projects['my-dir-my-app'].architect.lint.options.exclude workspaceJson.projects['my-dir-my-app'].architect.lint.options.exclude
).toEqual(['**/node_modules/**', '!apps/my-dir/my-app/**']); ).toEqual(['**/node_modules/**', '!apps/my-dir/my-app/**']);
expect( expect(
angularJson.projects['my-dir-my-app-e2e'].architect.lint.options.exclude workspaceJson.projects['my-dir-my-app-e2e'].architect.lint.options
.exclude
).toEqual(['**/node_modules/**', '!apps/my-dir/my-app-e2e/**']); ).toEqual(['**/node_modules/**', '!apps/my-dir/my-app-e2e/**']);
}); });
@ -314,9 +317,9 @@ describe('app', () => {
{ name: 'myApp', style: 'scss' }, { name: 'myApp', style: 'scss' },
appTree appTree
); );
const angularJson = readJsonInTree(result, 'angular.json'); const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(angularJson.projects['my-app'].schematics).toEqual({ expect(workspaceJson.projects['my-app'].schematics).toEqual({
'@nrwl/workspace:component': { '@nrwl/workspace:component': {
style: 'scss' style: 'scss'
} }
@ -334,12 +337,12 @@ describe('app', () => {
expect(tree.exists('apps/my-app/tsconfig.spec.json')).toBeTruthy(); expect(tree.exists('apps/my-app/tsconfig.spec.json')).toBeTruthy();
expect(tree.exists('apps/my-app/karma.conf.js')).toBeTruthy(); expect(tree.exists('apps/my-app/karma.conf.js')).toBeTruthy();
const angularJson = readJsonInTree(tree, 'angular.json'); const workspaceJson = readJsonInTree(tree, 'workspace.json');
expect(angularJson.projects['my-app'].architect.test.builder).toEqual( expect(workspaceJson.projects['my-app'].architect.test.builder).toEqual(
'@angular-devkit/build-angular:karma' '@angular-devkit/build-angular:karma'
); );
expect( expect(
angularJson.projects['my-app'].architect.lint.options.tsConfig workspaceJson.projects['my-app'].architect.lint.options.tsConfig
).toEqual([ ).toEqual([
'apps/my-app/tsconfig.app.json', 'apps/my-app/tsconfig.app.json',
'apps/my-app/tsconfig.spec.json' 'apps/my-app/tsconfig.spec.json'
@ -367,26 +370,28 @@ describe('app', () => {
expect(tree.exists('apps/my-app/tsconfig.spec.json')).toBeFalsy(); expect(tree.exists('apps/my-app/tsconfig.spec.json')).toBeFalsy();
expect(tree.exists('apps/my-app/jest.config.js')).toBeFalsy(); expect(tree.exists('apps/my-app/jest.config.js')).toBeFalsy();
expect(tree.exists('apps/my-app/karma.config.js')).toBeFalsy(); expect(tree.exists('apps/my-app/karma.config.js')).toBeFalsy();
const angularJson = readJsonInTree(tree, 'angular.json'); const workspaceJson = readJsonInTree(tree, 'workspace.json');
expect(angularJson.projects['my-app'].architect.test).toBeUndefined(); expect(workspaceJson.projects['my-app'].architect.test).toBeUndefined();
expect( expect(
angularJson.projects['my-app'].architect.lint.options.tsConfig workspaceJson.projects['my-app'].architect.lint.options.tsConfig
).toEqual(['apps/my-app/tsconfig.app.json']); ).toEqual(['apps/my-app/tsconfig.app.json']);
}); });
}); });
describe('--e2e-test-runner', () => { describe('--e2e-test-runner', () => {
describe('protractor', () => { describe('protractor', () => {
it('should update angular.json', async () => { it('should update workspace.json', async () => {
const tree = await runSchematic( const tree = await runSchematic(
'app', 'app',
{ name: 'myApp', e2eTestRunner: 'protractor' }, { name: 'myApp', e2eTestRunner: 'protractor' },
appTree appTree
); );
expect(tree.exists('apps/my-app-e2e')).toBeFalsy(); expect(tree.exists('apps/my-app-e2e')).toBeFalsy();
const angularJson = readJsonInTree(tree, 'angular.json'); const workspaceJson = readJsonInTree(tree, 'workspace.json');
expect(angularJson.projects['my-app'].architect.e2e).not.toBeDefined(); expect(
expect(angularJson.projects['my-app-e2e']).toEqual({ workspaceJson.projects['my-app'].architect.e2e
).not.toBeDefined();
expect(workspaceJson.projects['my-app-e2e']).toEqual({
root: 'apps/my-app-e2e', root: 'apps/my-app-e2e',
projectType: 'application', projectType: 'application',
architect: { architect: {
@ -422,30 +427,30 @@ describe('app', () => {
appTree appTree
); );
expect(tree.exists('apps/my-app-e2e')).toBeFalsy(); expect(tree.exists('apps/my-app-e2e')).toBeFalsy();
const angularJson = readJsonInTree(tree, 'angular.json'); const workspaceJson = readJsonInTree(tree, 'workspace.json');
expect(angularJson.projects['my-app-e2e']).toBeUndefined(); expect(workspaceJson.projects['my-app-e2e']).toBeUndefined();
}); });
}); });
}); });
describe('replaceAppNameWithPath', () => { describe('replaceAppNameWithPath', () => {
it('should protect `angular.json` commands and properties', async () => { it('should protect `workspace.json` commands and properties', async () => {
const tree = await runSchematic('app', { name: 'ui' }, appTree); const tree = await runSchematic('app', { name: 'ui' }, appTree);
const angularJson = readJsonInTree(tree, 'angular.json'); const workspaceJson = readJsonInTree(tree, 'workspace.json');
expect(angularJson.projects['ui']).toBeDefined(); expect(workspaceJson.projects['ui']).toBeDefined();
expect( expect(
angularJson.projects['ui']['architect']['build']['builder'] workspaceJson.projects['ui']['architect']['build']['builder']
).toEqual('@angular-devkit/build-angular:browser'); ).toEqual('@angular-devkit/build-angular:browser');
}); });
it('should protect `angular.json` sensible properties value to be renamed', async () => { it('should protect `workspace.json` sensible properties value to be renamed', async () => {
const tree = await runSchematic( const tree = await runSchematic(
'app', 'app',
{ name: 'ui', prefix: 'ui' }, { name: 'ui', prefix: 'ui' },
appTree appTree
); );
const angularJson = readJsonInTree(tree, 'angular.json'); const workspaceJson = readJsonInTree(tree, 'workspace.json');
expect(angularJson.projects['ui'].prefix).toEqual('ui'); expect(workspaceJson.projects['ui'].prefix).toEqual('ui');
}); });
}); });
}); });

View File

@ -15,7 +15,6 @@ import {
import { Schema } from './schema'; import { Schema } from './schema';
import * as ts from 'typescript'; import * as ts from 'typescript';
import { import {
angularSchematicNames,
formatFiles, formatFiles,
getNpmScope, getNpmScope,
getWorkspacePath, getWorkspacePath,
@ -26,7 +25,8 @@ import {
replaceNodeValue, replaceNodeValue,
toFileName, toFileName,
updateJsonInTree, updateJsonInTree,
updateWorkspace updateWorkspace,
addGlobalLint
} from '@nrwl/workspace'; } from '@nrwl/workspace';
import { join, normalize } from '@angular-devkit/core'; import { join, normalize } from '@angular-devkit/core';
import ngAdd from '../ng-add/ng-add'; import ngAdd from '../ng-add/ng-add';
@ -202,6 +202,16 @@ function updateProject(options: NormalizedSchema): Rule {
options.appProjectRoot options.appProjectRoot
); );
const angularSchematicNames = [
'class',
'component',
'directive',
'guard',
'module',
'pipe',
'service'
];
if (fixedProject.schematics) { if (fixedProject.schematics) {
angularSchematicNames.forEach(type => { angularSchematicNames.forEach(type => {
const schematic = `@schematics/angular:${type}`; const schematic = `@schematics/angular:${type}`;
@ -345,13 +355,13 @@ export default function(schema: Schema): Rule {
// Determine the roots where @schematics/angular will place the projects // Determine the roots where @schematics/angular will place the projects
// This is not where the projects actually end up // This is not where the projects actually end up
const angularJson = readJsonInTree(host, getWorkspacePath(host)); const workspaceJson = readJsonInTree(host, getWorkspacePath(host));
const appProjectRoot = angularJson.newProjectRoot const appProjectRoot = workspaceJson.newProjectRoot
? `${angularJson.newProjectRoot}/${options.name}` ? `${workspaceJson.newProjectRoot}/${options.name}`
: options.name; : options.name;
const e2eProjectRoot = angularJson.newProjectRoot const e2eProjectRoot = workspaceJson.newProjectRoot
? `${angularJson.newProjectRoot}/${options.e2eProjectName}` ? `${workspaceJson.newProjectRoot}/${options.e2eProjectName}`
: `${options.name}/e2e`; : `${options.name}/e2e`;
return chain([ return chain([
@ -359,6 +369,7 @@ export default function(schema: Schema): Rule {
...options, ...options,
skipFormat: true skipFormat: true
}), }),
addGlobalLint('tslint'),
externalSchematic('@schematics/angular', 'application', { externalSchematic('@schematics/angular', 'application', {
name: options.name, name: options.name,
inlineStyle: options.inlineStyle, inlineStyle: options.inlineStyle,
@ -373,11 +384,9 @@ export default function(schema: Schema): Rule {
skipPackageJson: false skipPackageJson: false
}), }),
addTsconfigs(options), addTsconfigs(options),
options.e2eTestRunner === 'protractor' options.e2eTestRunner === 'protractor'
? move(e2eProjectRoot, options.e2eProjectRoot) ? move(e2eProjectRoot, options.e2eProjectRoot)
: removeE2e(options, e2eProjectRoot), : removeE2e(options, e2eProjectRoot),
options.e2eTestRunner === 'protractor' options.e2eTestRunner === 'protractor'
? updateE2eProject(options) ? updateE2eProject(options)
: noop(), : noop(),
@ -388,10 +397,8 @@ export default function(schema: Schema): Rule {
project: options.name project: options.name
}) })
: noop(), : noop(),
move(appProjectRoot, options.appProjectRoot), move(appProjectRoot, options.appProjectRoot),
updateProject(options), updateProject(options),
updateComponentTemplate(options), updateComponentTemplate(options),
options.routing ? addRouterRootConfiguration(options) : noop(), options.routing ? addRouterRootConfiguration(options) : noop(),
updateLinting(options), updateLinting(options),

View File

@ -81,7 +81,7 @@ module.exports = function(config) {
}); });
describe('library', () => { describe('library', () => {
it('should alter angular.json', async () => { it('should alter workspace.json', async () => {
const resultTree = await runSchematic( const resultTree = await runSchematic(
'karma-project', 'karma-project',
{ {
@ -89,8 +89,8 @@ module.exports = function(config) {
}, },
appTree appTree
); );
const angularJson = readJsonInTree(resultTree, 'angular.json'); const workspaceJson = readJsonInTree(resultTree, 'workspace.json');
expect(angularJson.projects.lib1.architect.test).toEqual({ expect(workspaceJson.projects.lib1.architect.test).toEqual({
builder: '@angular-devkit/build-angular:karma', builder: '@angular-devkit/build-angular:karma',
options: { options: {
main: 'libs/lib1/src/test.ts', main: 'libs/lib1/src/test.ts',
@ -99,7 +99,7 @@ module.exports = function(config) {
} }
}); });
expect( expect(
angularJson.projects.lib1.architect.lint.options.tsConfig workspaceJson.projects.lib1.architect.lint.options.tsConfig
).toContain('libs/lib1/tsconfig.spec.json'); ).toContain('libs/lib1/tsconfig.spec.json');
}); });
@ -141,7 +141,7 @@ module.exports = function(config) {
}); });
describe('applications', () => { describe('applications', () => {
it('should alter angular.json', async () => { it('should alter workspace.json', async () => {
const resultTree = await runSchematic( const resultTree = await runSchematic(
'karma-project', 'karma-project',
{ {
@ -149,8 +149,8 @@ module.exports = function(config) {
}, },
appTree appTree
); );
const angularJson = readJsonInTree(resultTree, 'angular.json'); const workspaceJson = readJsonInTree(resultTree, 'workspace.json');
expect(angularJson.projects.app1.architect.test).toEqual({ expect(workspaceJson.projects.app1.architect.test).toEqual({
builder: '@angular-devkit/build-angular:karma', builder: '@angular-devkit/build-angular:karma',
options: { options: {
main: 'apps/app1/src/test.ts', main: 'apps/app1/src/test.ts',
@ -163,7 +163,7 @@ module.exports = function(config) {
} }
}); });
expect( expect(
angularJson.projects.app1.architect.lint.options.tsConfig workspaceJson.projects.app1.architect.lint.options.tsConfig
).toContain('apps/app1/tsconfig.spec.json'); ).toContain('apps/app1/tsconfig.spec.json');
}); });

View File

@ -13,7 +13,8 @@ import {
import { import {
readJsonInTree, readJsonInTree,
updateJsonInTree, updateJsonInTree,
offsetFromRoot offsetFromRoot,
updateWorkspaceInTree
} from '@nrwl/workspace'; } from '@nrwl/workspace';
import { join, normalize } from '@angular-devkit/core'; import { join, normalize } from '@angular-devkit/core';
import { getProjectConfig } from '@nrwl/workspace'; import { getProjectConfig } from '@nrwl/workspace';
@ -76,8 +77,8 @@ function updateTsSpecConfig(options: KarmaProjectSchema): Rule {
}; };
} }
function updateAngularJson(options: KarmaProjectSchema): Rule { function updateworkspaceJson(options: KarmaProjectSchema): Rule {
return updateJsonInTree('angular.json', json => { return updateWorkspaceInTree(json => {
const projectConfig = json.projects[options.project]; const projectConfig = json.projects[options.project];
projectConfig.architect.test = { projectConfig.architect.test = {
builder: '@angular-devkit/build-angular:karma', builder: '@angular-devkit/build-angular:karma',
@ -124,6 +125,6 @@ export default function(options: KarmaProjectSchema): Rule {
generateFiles(options), generateFiles(options),
updateTsConfig(options), updateTsConfig(options),
updateTsSpecConfig(options), updateTsSpecConfig(options),
updateAngularJson(options) updateworkspaceJson(options)
]); ]);
} }

View File

@ -5,5 +5,5 @@ This library was generated with [Nx](https://nx.dev).
## Running unit tests ## Running unit tests
Run `ng test <%= name %>` to execute the unit tests. Run `nx test <%= name %>` to execute the unit tests.
<% } %> <% } %>

View File

@ -79,37 +79,39 @@ describe('lib', () => {
expect(packageJson.name).toEqual('@proj/my-lib'); expect(packageJson.name).toEqual('@proj/my-lib');
}); });
it('should update angular.json', async () => { it('should update workspace.json', async () => {
const tree = await runSchematic( const tree = await runSchematic(
'lib', 'lib',
{ name: 'myLib', framework: 'angular', publishable: true }, { name: 'myLib', framework: 'angular', publishable: true },
appTree appTree
); );
const angularJson = readJsonInTree(tree, '/angular.json'); const workspaceJson = readJsonInTree(tree, '/workspace.json');
expect(angularJson.projects['my-lib'].root).toEqual('libs/my-lib'); expect(workspaceJson.projects['my-lib'].root).toEqual('libs/my-lib');
expect(angularJson.projects['my-lib'].architect.build).toBeDefined(); expect(workspaceJson.projects['my-lib'].architect.build).toBeDefined();
expect( expect(
angularJson.projects['my-lib'].architect.lint.options.tsConfig workspaceJson.projects['my-lib'].architect.lint.options.tsConfig
).toEqual([ ).toEqual([
'libs/my-lib/tsconfig.lib.json', 'libs/my-lib/tsconfig.lib.json',
'libs/my-lib/tsconfig.spec.json' 'libs/my-lib/tsconfig.spec.json'
]); ]);
expect( expect(
angularJson.projects['my-lib'].architect.lint.options.exclude workspaceJson.projects['my-lib'].architect.lint.options.exclude
).toEqual(['**/node_modules/**', '!libs/my-lib/**']); ).toEqual(['**/node_modules/**', '!libs/my-lib/**']);
}); });
it('should remove "build" target from angular.json when a library is not publishable', async () => { it('should remove "build" target from workspace.json when a library is not publishable', async () => {
const tree = await runSchematic( const tree = await runSchematic(
'lib', 'lib',
{ name: 'myLib', publishable: false }, { name: 'myLib', publishable: false },
appTree appTree
); );
const angularJson = readJsonInTree(tree, '/angular.json'); const workspaceJson = readJsonInTree(tree, '/workspace.json');
expect(angularJson.projects['my-lib'].root).toEqual('libs/my-lib'); expect(workspaceJson.projects['my-lib'].root).toEqual('libs/my-lib');
expect(angularJson.projects['my-lib'].architect.build).not.toBeDefined(); expect(
workspaceJson.projects['my-lib'].architect.build
).not.toBeDefined();
}); });
it('should update nx.json', async () => { it('should update nx.json', async () => {
@ -216,8 +218,9 @@ describe('lib', () => {
it('should default the prefix to npmScope', async () => { it('should default the prefix to npmScope', async () => {
const noPrefix = await runSchematic('lib', { name: 'myLib' }, appTree); const noPrefix = await runSchematic('lib', { name: 'myLib' }, appTree);
expect( expect(
JSON.parse(noPrefix.read('angular.json').toString()).projects['my-lib'] JSON.parse(noPrefix.read('workspace.json').toString()).projects[
.prefix 'my-lib'
].prefix
).toEqual('proj'); ).toEqual('proj');
const withPrefix = await runSchematic( const withPrefix = await runSchematic(
@ -226,7 +229,7 @@ describe('lib', () => {
appTree appTree
); );
expect( expect(
JSON.parse(withPrefix.read('angular.json').toString()).projects[ JSON.parse(withPrefix.read('workspace.json').toString()).projects[
'my-lib' 'my-lib'
].prefix ].prefix
).toEqual('custom'); ).toEqual('custom');
@ -381,26 +384,26 @@ 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', async () => { it('should update workspace.json', async () => {
const tree = await runSchematic( const tree = await runSchematic(
'lib', 'lib',
{ name: 'myLib', directory: 'myDir' }, { name: 'myLib', directory: 'myDir' },
appTree appTree
); );
const angularJson = readJsonInTree(tree, '/angular.json'); const workspaceJson = readJsonInTree(tree, '/workspace.json');
expect(angularJson.projects['my-dir-my-lib'].root).toEqual( expect(workspaceJson.projects['my-dir-my-lib'].root).toEqual(
'libs/my-dir/my-lib' 'libs/my-dir/my-lib'
); );
expect( expect(
angularJson.projects['my-dir-my-lib'].architect.lint.options.tsConfig workspaceJson.projects['my-dir-my-lib'].architect.lint.options.tsConfig
).toEqual([ ).toEqual([
'libs/my-dir/my-lib/tsconfig.lib.json', 'libs/my-dir/my-lib/tsconfig.lib.json',
'libs/my-dir/my-lib/tsconfig.spec.json' 'libs/my-dir/my-lib/tsconfig.spec.json'
]); ]);
expect( expect(
angularJson.projects['my-dir-my-lib'].architect.lint.options.exclude workspaceJson.projects['my-dir-my-lib'].architect.lint.options.exclude
).toEqual(['**/node_modules/**', '!libs/my-dir/my-lib/**']); ).toEqual(['**/node_modules/**', '!libs/my-dir/my-lib/**']);
}); });
@ -763,9 +766,9 @@ describe('lib', () => {
appTree appTree
); );
const angularJson = readJsonInTree(result, 'angular.json'); const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(angularJson.projects['my-lib'].schematics).toEqual({ expect(workspaceJson.projects['my-lib'].schematics).toEqual({
'@nrwl/angular:component': { '@nrwl/angular:component': {
styleext: 'scss' styleext: 'scss'
} }
@ -785,18 +788,18 @@ describe('lib', () => {
expect(resultTree.exists('libs/my-lib/tsconfig.spec.json')).toBeTruthy(); expect(resultTree.exists('libs/my-lib/tsconfig.spec.json')).toBeTruthy();
expect(resultTree.exists('libs/my-lib/karma.conf.js')).toBeTruthy(); expect(resultTree.exists('libs/my-lib/karma.conf.js')).toBeTruthy();
expect(resultTree.exists('karma.conf.js')).toBeTruthy(); expect(resultTree.exists('karma.conf.js')).toBeTruthy();
const angularJson = readJsonInTree(resultTree, 'angular.json'); const workspaceJson = readJsonInTree(resultTree, 'workspace.json');
expect(angularJson.projects['my-lib'].architect.test.builder).toEqual( expect(workspaceJson.projects['my-lib'].architect.test.builder).toEqual(
'@angular-devkit/build-angular:karma' '@angular-devkit/build-angular:karma'
); );
expect( expect(
angularJson.projects['my-lib'].architect.lint.options.tsConfig workspaceJson.projects['my-lib'].architect.lint.options.tsConfig
).toEqual([ ).toEqual([
'libs/my-lib/tsconfig.lib.json', 'libs/my-lib/tsconfig.lib.json',
'libs/my-lib/tsconfig.spec.json' 'libs/my-lib/tsconfig.spec.json'
]); ]);
expect( expect(
angularJson.projects['my-lib'].architect.lint.options.exclude workspaceJson.projects['my-lib'].architect.lint.options.exclude
).toEqual(['**/node_modules/**', '!libs/my-lib/**']); ).toEqual(['**/node_modules/**', '!libs/my-lib/**']);
}); });
}); });
@ -816,10 +819,10 @@ describe('lib', () => {
expect(resultTree.exists('libs/my-lib/tsconfig.spec.json')).toBeFalsy(); expect(resultTree.exists('libs/my-lib/tsconfig.spec.json')).toBeFalsy();
expect(resultTree.exists('libs/my-lib/jest.config.js')).toBeFalsy(); expect(resultTree.exists('libs/my-lib/jest.config.js')).toBeFalsy();
expect(resultTree.exists('libs/my-lib/karma.conf.js')).toBeFalsy(); expect(resultTree.exists('libs/my-lib/karma.conf.js')).toBeFalsy();
const angularJson = readJsonInTree(resultTree, 'angular.json'); const workspaceJson = readJsonInTree(resultTree, 'workspace.json');
expect(angularJson.projects['my-lib'].architect.test).toBeUndefined(); expect(workspaceJson.projects['my-lib'].architect.test).toBeUndefined();
expect( expect(
angularJson.projects['my-lib'].architect.lint.options.tsConfig workspaceJson.projects['my-lib'].architect.lint.options.tsConfig
).toEqual(['libs/my-lib/tsconfig.lib.json']); ).toEqual(['libs/my-lib/tsconfig.lib.json']);
}); });
}); });

View File

@ -22,7 +22,8 @@ import {
NxJson, NxJson,
updateJsonInTree, updateJsonInTree,
readJsonInTree, readJsonInTree,
offsetFromRoot offsetFromRoot,
addGlobalLint
} from '@nrwl/workspace'; } from '@nrwl/workspace';
import { addGlobal, addIncludeToTsConfig, insert } from '@nrwl/workspace'; import { addGlobal, addIncludeToTsConfig, insert } from '@nrwl/workspace';
import { toClassName, toFileName, toPropertyName } from '@nrwl/workspace'; import { toClassName, toFileName, toPropertyName } from '@nrwl/workspace';
@ -430,6 +431,7 @@ export default function(schema: Schema): Rule {
} }
return chain([ return chain([
addGlobalLint('tslint'),
addUnitTestRunner(options), addUnitTestRunner(options),
externalSchematic('@schematics/angular', 'library', { externalSchematic('@schematics/angular', 'library', {
name: options.name, name: options.name,

View File

@ -74,7 +74,7 @@ describe('ng-add', () => {
}, },
appTree appTree
); );
const { schematics } = readJsonInTree(tree, 'angular.json'); const { schematics } = readJsonInTree(tree, 'workspace.json');
expect(schematics['@nrwl/angular:application'].unitTestRunner).toEqual( expect(schematics['@nrwl/angular:application'].unitTestRunner).toEqual(
'karma' 'karma'
); );
@ -118,7 +118,7 @@ describe('ng-add', () => {
}, },
appTree appTree
); );
const { schematics } = readJsonInTree(tree, 'angular.json'); const { schematics } = readJsonInTree(tree, 'workspace.json');
expect(schematics['@nrwl/angular:application'].unitTestRunner).toEqual( expect(schematics['@nrwl/angular:application'].unitTestRunner).toEqual(
'jest' 'jest'
); );
@ -153,7 +153,7 @@ describe('ng-add', () => {
}, },
appTree appTree
); );
const { schematics } = readJsonInTree(tree, 'angular.json'); const { schematics } = readJsonInTree(tree, 'workspace.json');
expect(schematics['@nrwl/angular:application'].e2eTestRunner).toEqual( expect(schematics['@nrwl/angular:application'].e2eTestRunner).toEqual(
'cypress' 'cypress'
); );
@ -185,7 +185,7 @@ describe('ng-add', () => {
}, },
appTree appTree
); );
const { schematics } = readJsonInTree(tree, 'angular.json'); const { schematics } = readJsonInTree(tree, 'workspace.json');
expect(schematics['@nrwl/angular:application'].e2eTestRunner).toEqual( expect(schematics['@nrwl/angular:application'].e2eTestRunner).toEqual(
'protractor' 'protractor'
); );
@ -196,13 +196,13 @@ describe('ng-add', () => {
describe('defaultCollection', () => { describe('defaultCollection', () => {
it('should be set if none was set before', async () => { it('should be set if none was set before', async () => {
const result = await runSchematic('ng-add', {}, appTree); const result = await runSchematic('ng-add', {}, appTree);
const angularJson = readJsonInTree(result, 'angular.json'); const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(angularJson.cli.defaultCollection).toEqual('@nrwl/angular'); expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/angular');
}); });
it('should be set if @nrwl/workspace was set before', async () => { it('should be set if @nrwl/workspace was set before', async () => {
appTree = await callRule( appTree = await callRule(
updateJsonInTree('angular.json', json => { updateJsonInTree('workspace.json', json => {
json.cli = { json.cli = {
defaultCollection: '@nrwl/workspace' defaultCollection: '@nrwl/workspace'
}; };
@ -212,13 +212,13 @@ describe('ng-add', () => {
appTree appTree
); );
const result = await runSchematic('ng-add', {}, appTree); const result = await runSchematic('ng-add', {}, appTree);
const angularJson = readJsonInTree(result, 'angular.json'); const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(angularJson.cli.defaultCollection).toEqual('@nrwl/angular'); expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/angular');
}); });
it('should not be set if something else was set before', async () => { it('should not be set if something else was set before', async () => {
appTree = await callRule( appTree = await callRule(
updateJsonInTree('angular.json', json => { updateJsonInTree('workspace.json', json => {
json.cli = { json.cli = {
defaultCollection: '@nrwl/react' defaultCollection: '@nrwl/react'
}; };
@ -228,8 +228,8 @@ describe('ng-add', () => {
appTree appTree
); );
const result = await runSchematic('ng-add', {}, appTree); const result = await runSchematic('ng-add', {}, appTree);
const angularJson = readJsonInTree(result, 'angular.json'); const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(angularJson.cli.defaultCollection).toEqual('@nrwl/react'); expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/react');
}); });
}); });
}); });

View File

@ -94,7 +94,7 @@ export function createApp(
}) })
); );
tree.overwrite( tree.overwrite(
'/angular.json', '/workspace.json',
JSON.stringify({ JSON.stringify({
newProjectRoot: '', newProjectRoot: '',
version: 1, version: 1,

View File

@ -1,6 +1,6 @@
export const nxVersion = '*'; export const nxVersion = '*';
export const angularVersion = '^8.0.0'; export const angularVersion = '^8.0.0';
export const angularDevkitVersion = '^0.800.0'; export const angularDevkitVersion = '^0.800.1';
export const angularJsVersion = '1.6.6'; export const angularJsVersion = '1.6.6';
export const ngrxVersion = '8.1.0'; export const ngrxVersion = '8.1.0';
export const rxjsVersion = '~6.4.0'; export const rxjsVersion = '~6.4.0';

View File

@ -29,6 +29,7 @@
"dependencies": { "dependencies": {
"tmp": "0.0.33", "tmp": "0.0.33",
"yargs-parser": "10.0.0", "yargs-parser": "10.0.0",
"yargs": "^11.0.0" "yargs": "^11.0.0",
"@nrwl/tao": "*"
} }
} }

View File

@ -4,130 +4,289 @@
import { output } from '@nrwl/workspace/src/command-line/output'; import { output } from '@nrwl/workspace/src/command-line/output';
import { execSync } from 'child_process'; import { execSync } from 'child_process';
import { writeFileSync } from 'fs'; import { writeFileSync } from 'fs';
import * as inquirer from 'inquirer';
import * as path from 'path'; import * as path from 'path';
import { dirSync } from 'tmp'; import { dirSync } from 'tmp';
import * as yargsParser from 'yargs-parser'; import * as yargsParser from 'yargs-parser';
const presetOptions = [
{
value: 'empty',
name: 'empty [an empty workspace]'
},
{
value: 'angular',
name: 'angular [a workspace with a single Angular application]'
},
{
value: 'react',
name: 'react [a workspace with a single React application]'
},
{
value: 'web-components',
name:
'web components [a workspace with a single app built using web components]'
},
{
value: 'full-stack',
name:
'full-stack [a workspace with a full stack application (NestJS + Angular Ivy)]'
}
];
const tsVersion = 'TYPESCRIPT_VERSION';
const cliVersion = 'NX_VERSION';
const nxVersion = 'NX_VERSION';
const angularCliVersion = 'ANGULAR_CLI_VERSION';
const parsedArgs = yargsParser(process.argv, { const parsedArgs = yargsParser(process.argv, {
string: ['directory'], string: ['cli', 'preset'],
boolean: ['help'] boolean: ['help']
}); });
if (parsedArgs.help) { if (parsedArgs.help) {
console.log(` showHelp();
Usage: create-nx-workspace <directory> [options] [ng new options]
Create a new Nx workspace
Options:
directory path to the workspace root directory
[ng new options] any 'ng new' options
run 'ng new --help' for more information
`);
process.exit(0); process.exit(0);
} }
validateInput(parsedArgs);
const nxTool = { const packageManager = determinePackageManager();
name: 'Schematics', determinePreset(parsedArgs).then(preset => {
packageName: '@nrwl/workspace' return determineCli(preset, parsedArgs).then(cli => {
}; const tmpDir = createSandbox(packageManager, cli);
createApp(tmpDir, cli, parsedArgs, preset);
let packageManager: string; showNxWarning();
try { showCliWarning(preset, parsedArgs);
packageManager = execSync('ng config -g cli.packageManager', {
stdio: ['ignore', 'pipe', 'ignore']
})
.toString()
.trim();
} catch (e) {
packageManager = 'yarn';
}
try {
execSync(`${packageManager} --version`, {
stdio: ['ignore', 'ignore', 'ignore']
}); });
} catch (e) {
packageManager = 'npm';
}
const projectName = parsedArgs._[2];
// check that the workspace name is passed in
if (!projectName) {
output.error({
title: 'A project name is required when creating a new workspace',
bodyLines: [
output.colors.gray('For example:'),
'',
`${output.colors.gray('>')} create-nx-workspace my-new-workspace`
]
});
process.exit(1);
}
// creating the sandbox
output.logSingleLine(`Creating a sandbox...`);
const tmpDir = dirSync().name;
const nxVersion = 'NX_VERSION';
const cliVersion = 'ANGULAR_CLI_VERSION';
const typescriptVersion = 'TYPESCRIPT_VERSION';
writeFileSync(
path.join(tmpDir, 'package.json'),
JSON.stringify({
dependencies: {
[nxTool.packageName]: nxVersion,
'@angular/cli': cliVersion,
typescript: typescriptVersion
},
license: 'MIT'
})
);
execSync(`${packageManager} install --silent`, {
cwd: tmpDir,
stdio: [0, 1, 2]
}); });
// creating the app itself function showHelp() {
const args = process.argv console.log(`
.slice(2) Usage: create-nx-workspace <name> [options] [new workspace options]
.map(a => `"${a}"`)
.join(' ');
output.logSingleLine( Create a new Nx workspace
`${output.colors.gray('Running:')} ng new ${args} --collection=${
nxTool.packageName
}`
);
execSync( Options:
`"${path.join(
tmpDir, name workspace name
'node_modules',
'.bin', preset What to create in a new workspace (options: ${presetOptions
'ng' .map(o => '"' + o.value + '"')
)}" new ${args} --collection=${nxTool.packageName}`, .join(', ')})
{
stdio: [0, 1, 2] cli CLI to power the Nx workspace (options: "nx", "angular")
[new workspace options] any 'new workspace' options
`);
}
function determinePackageManager() {
// If you have Angular CLI installed, read Angular CLI config.
// If it isn't not installed, default to 'yarn'.
let packageManager: string;
try {
packageManager = execSync('ng config -g cli.packageManager', {
stdio: ['ignore', 'pipe', 'ignore'],
timeout: 500
})
.toString()
.trim();
} catch (e) {
packageManager = 'yarn';
} }
); try {
execSync(`${packageManager} --version`, {
stdio: ['ignore', 'ignore', 'ignore']
});
} catch (e) {
packageManager = 'npm';
}
return packageManager;
}
// TODO: vsavkin: reenable for 8.4 function validateInput(parsedArgs: any) {
// try { const projectName = parsedArgs._[2];
// execSync('nx --version');
// } catch (e) { if (!projectName) {
// // no nx found output.error({
// console.log('-----------------------------------------------------------'); title: 'A project name is required when creating a new workspace',
// console.log(`It looks like you don't have the Nx CLI installed globally.`); bodyLines: [
// console.log( output.colors.gray('For example:'),
// `This means that you might have to use "yarn nx" or "npm nx" to execute commands in your workspace.` '',
// ); `${output.colors.gray('>')} create-nx-workspace my-new-workspace`
// console.log( ]
// `If you want to execute the nx command directly, run "yarn global add @nrwl/cli" or "npm install -g @nrwl/cli"` });
// ); process.exit(1);
// console.log('-----------------------------------------------------------'); }
// }
return projectName;
}
function determinePreset(parsedArgs: any): Promise<string> {
if (parsedArgs.preset) {
if (presetOptions.map(o => o.value).indexOf(parsedArgs.preset) === -1) {
console.error(
`Invalid preset. It must be one of the following: ${presetOptions
.map(o => '"' + o.value + '"')
.join(', ')}.`
);
process.exit(1);
} else {
return Promise.resolve(parsedArgs.preset);
}
} else {
return inquirer
.prompt([
{
name: 'Preset',
message: `What to create in the new workspace`,
default: 'empty',
type: 'list',
choices: presetOptions
}
])
.then(a => a.Preset);
}
}
function determineCli(preset: string, parsedArgs: any) {
const angular = {
package: '@angular/cli',
version: angularCliVersion,
command: 'ng'
};
const nx = {
package: '@nrwl/tao',
version: cliVersion,
command: 'tao'
};
if (parsedArgs.cli) {
if (['nx', 'angular'].indexOf(parsedArgs.cli) === -1) {
console.error(
`Invalid cli. It must be one of the following: "nx", "angular".`
);
process.exit(1);
}
return Promise.resolve(parsedArgs.cli === 'angular' ? angular : nx);
}
if (preset == 'angular' || preset == 'full-stack') {
return Promise.resolve(angular);
} else if (preset === 'web-components' || preset === 'react') {
return Promise.resolve(nx);
} else {
return inquirer
.prompt([
{
name: 'CLI',
message: `CLI to power the Nx workspace`,
default: 'nx',
type: 'list',
choices: [
{
value: 'nx',
name:
'Nx [Extensible CLI for JavaScript and TypeScript applications]'
},
{
value: 'angular',
name: 'Angular CLI [Extensible CLI for Angular applications]'
}
]
}
])
.then(a => (a.CLI === 'angular' ? angular : nx));
}
}
function createSandbox(
packageManager: string,
cli: { package: string; version: string }
) {
console.log(`Creating a sandbox with Nx...`);
const tmpDir = dirSync().name;
writeFileSync(
path.join(tmpDir, 'package.json'),
JSON.stringify({
dependencies: {
'@nrwl/workspace': nxVersion,
[cli.package]: cli.version,
typescript: tsVersion
},
license: 'MIT'
})
);
execSync(`${packageManager} install --silent`, {
cwd: tmpDir,
stdio: [0, 1, 2]
});
return tmpDir;
}
function createApp(
tmpDir: string,
cli: { command: string },
parsedArgs: any,
preset: string
) {
// creating the app itself
const args = process.argv
.slice(2)
.filter(a => !a.startsWith('--cli')) // not used by the new command
.map(a => `"${a}"`)
.join(' ');
const presetArg = parsedArgs.preset ? '' : ` --preset=${preset}`;
console.log(`new ${args}${presetArg} --collection=@nrwl/workspace`);
execSync(
`"${path.join(
tmpDir,
'node_modules',
'.bin',
cli.command
)}" new ${args}${presetArg} --collection=@nrwl/workspace`,
{
stdio: [0, 1, 2]
}
);
}
function showNxWarning() {
try {
execSync('nx --version', { stdio: ['ignore', 'ignore', 'ignore'] });
} catch (e) {
// no nx found
console.log('-----------------------------------------------------------');
console.log(`It looks like you don't have the Nx CLI installed globally.`);
console.log(
`This means that you might have to use "yarn nx" or "npm nx" to execute commands in your workspace.`
);
console.log(
`If you want to execute the nx command directly, run "yarn global add @nrwl/cli" or "npm install -g @nrwl/cli"`
);
console.log('-----------------------------------------------------------');
}
}
function showCliWarning(preset: string, parsedArgs: any) {
if (!parsedArgs.cli) {
if (preset == 'angular' || preset == 'full-stack') {
console.log(
'Because you selected an Angular-specific preset, we generated an Nx workspace powered by the Angular CLi.'
);
console.log(
`If you want want to power the workspace using a different CLI, you can pass it using '--cli'. Find out more by running 'create-nx-workspace --help'.`
);
} else if (preset === 'web-components' || preset === 'react') {
console.log('We generated an Nx workspace powered by the Nx CLi.');
console.log(
`If you want want to power the workspace using a different CLI, you can pass it using '--cli'. Find out more by running 'create-nx-workspace --help'.`
);
}
}
}

View File

@ -30,6 +30,7 @@
"@nrwl/workspace": "*", "@nrwl/workspace": "*",
"tmp": "0.0.33", "tmp": "0.0.33",
"yargs-parser": "10.0.0", "yargs-parser": "10.0.0",
"yargs": "^11.0.0" "yargs": "^11.0.0",
"inquirer": "^6.3.1"
} }
} }

View File

@ -37,21 +37,21 @@ 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 update `angular.json` file', async () => { it('should add update `workspace.json` file', async () => {
const tree = await runSchematic( const tree = await runSchematic(
'cypress-project', 'cypress-project',
{ name: 'my-app-e2e', project: 'my-app' }, { name: 'my-app-e2e', project: 'my-app' },
appTree appTree
); );
const angularJson = readJsonInTree(tree, 'angular.json'); const workspaceJson = readJsonInTree(tree, 'workspace.json');
const project = angularJson.projects['my-app-e2e']; const project = workspaceJson.projects['my-app-e2e'];
expect(project.root).toEqual('apps/my-app-e2e'); expect(project.root).toEqual('apps/my-app-e2e');
expect(project.architect.lint).toEqual({ expect(project.architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint', builder: '@angular-devkit/build-angular:tslint',
options: { options: {
tsConfig: 'apps/my-app-e2e/tsconfig.e2e.json', tsConfig: ['apps/my-app-e2e/tsconfig.e2e.json'],
exclude: ['**/node_modules/**', '!apps/my-app-e2e/**'] exclude: ['**/node_modules/**', '!apps/my-app-e2e/**']
} }
}); });
@ -107,13 +107,13 @@ describe('schematic:cypress-project', () => {
}); });
describe('nested', () => { describe('nested', () => {
it('should update angular.json', async () => { it('should update workspace.json', async () => {
const tree = await runSchematic( const tree = await runSchematic(
'cypress-project', 'cypress-project',
{ name: 'my-app-e2e', project: 'my-dir-my-app', directory: 'my-dir' }, { name: 'my-app-e2e', project: 'my-dir-my-app', directory: 'my-dir' },
appTree appTree
); );
const projectConfig = readJsonInTree(tree, 'angular.json').projects[ const projectConfig = readJsonInTree(tree, 'workspace.json').projects[
'my-dir-my-app-e2e' 'my-dir-my-app-e2e'
]; ];
@ -121,7 +121,7 @@ describe('schematic:cypress-project', () => {
expect(projectConfig.architect.lint).toEqual({ expect(projectConfig.architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint', builder: '@angular-devkit/build-angular:tslint',
options: { options: {
tsConfig: 'apps/my-dir/my-app-e2e/tsconfig.e2e.json', tsConfig: ['apps/my-dir/my-app-e2e/tsconfig.e2e.json'],
exclude: ['**/node_modules/**', '!apps/my-dir/my-app-e2e/**'] exclude: ['**/node_modules/**', '!apps/my-dir/my-app-e2e/**']
} }
}); });

View File

@ -9,7 +9,13 @@ import {
} from '@angular-devkit/schematics'; } from '@angular-devkit/schematics';
import { join, normalize } from '@angular-devkit/core'; import { join, normalize } from '@angular-devkit/core';
// app // app
import { updateJsonInTree, NxJson } from '@nrwl/workspace'; import {
updateJsonInTree,
NxJson,
updateWorkspaceInTree,
generateProjectLint,
addGlobalLint
} from '@nrwl/workspace';
import { offsetFromRoot } from '@nrwl/workspace'; import { offsetFromRoot } from '@nrwl/workspace';
import { toFileName } from '@nrwl/workspace'; import { toFileName } from '@nrwl/workspace';
import { Schema } from './schema'; import { Schema } from './schema';
@ -44,8 +50,8 @@ function updateNxJson(options: CypressProjectSchema): Rule {
}); });
} }
function updateAngularJson(options: CypressProjectSchema): Rule { function updateWorkspaceJson(options: CypressProjectSchema): Rule {
return updateJsonInTree('angular.json', json => { return updateWorkspaceInTree(json => {
const architect: any = {}; const architect: any = {};
architect.e2e = { architect.e2e = {
@ -61,16 +67,13 @@ function updateAngularJson(options: CypressProjectSchema): Rule {
} }
} }
}; };
architect.lint = {
builder: '@angular-devkit/build-angular:tslint', architect.lint = generateProjectLint(
options: { normalize(options.projectRoot),
tsConfig: join(normalize(options.projectRoot), 'tsconfig.e2e.json'), join(normalize(options.projectRoot), 'tsconfig.e2e.json'),
exclude: [ options.linter
'**/node_modules/**', );
'!' + join(normalize(options.projectRoot), '**')
]
}
};
json.projects[options.projectName] = { json.projects[options.projectName] = {
root: options.projectRoot, root: options.projectRoot,
sourceRoot: join(normalize(options.projectRoot), 'src'), sourceRoot: join(normalize(options.projectRoot), 'src'),
@ -84,8 +87,9 @@ function updateAngularJson(options: CypressProjectSchema): Rule {
export default function(options: CypressProjectSchema): Rule { export default function(options: CypressProjectSchema): Rule {
options = normalizeOptions(options); options = normalizeOptions(options);
return chain([ return chain([
addGlobalLint(options.linter),
generateFiles(options), generateFiles(options),
updateAngularJson(options), updateWorkspaceJson(options),
updateNxJson(options) updateNxJson(options)
]); ]);
} }

View File

@ -2,4 +2,5 @@ export interface Schema {
project: string; project: string;
name: string; name: string;
directory: string; directory: string;
linter: 'eslint' | 'tslint';
} }

View File

@ -24,6 +24,12 @@
"type": "string", "type": "string",
"description": "A directory where the app is placed", "description": "A directory where the app is placed",
"x-prompt": "In which directory should the library be generated?" "x-prompt": "In which directory should the library be generated?"
},
"linter": {
"description": "The tool to use for running lint checks.",
"type": "string",
"enum": ["eslint", "tslint"],
"default": "tslint"
} }
}, },
"required": ["name"] "required": ["name"]

View File

@ -32,13 +32,13 @@ describe('ng-add', () => {
describe('defaultCollection', () => { describe('defaultCollection', () => {
it('should be set if none was set before', async () => { it('should be set if none was set before', async () => {
const result = await runSchematic('ng-add', {}, tree); const result = await runSchematic('ng-add', {}, tree);
const angularJson = readJsonInTree(result, 'angular.json'); const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(angularJson.cli.defaultCollection).toEqual('@nrwl/express'); expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/express');
}); });
it('should be set if @nrwl/workspace was set before', async () => { it('should be set if @nrwl/workspace was set before', async () => {
tree = await callRule( tree = await callRule(
updateJsonInTree('angular.json', json => { updateJsonInTree('workspace.json', json => {
json.cli = { json.cli = {
defaultCollection: '@nrwl/workspace' defaultCollection: '@nrwl/workspace'
}; };
@ -48,13 +48,13 @@ describe('ng-add', () => {
tree tree
); );
const result = await runSchematic('ng-add', {}, tree); const result = await runSchematic('ng-add', {}, tree);
const angularJson = readJsonInTree(result, 'angular.json'); const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(angularJson.cli.defaultCollection).toEqual('@nrwl/express'); expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/express');
}); });
it('should not be set if something else was set before', async () => { it('should not be set if something else was set before', async () => {
tree = await callRule( tree = await callRule(
updateJsonInTree('angular.json', json => { updateJsonInTree('workspace.json', json => {
json.cli = { json.cli = {
defaultCollection: '@nrwl/angular' defaultCollection: '@nrwl/angular'
}; };
@ -64,8 +64,8 @@ describe('ng-add', () => {
tree tree
); );
const result = await runSchematic('ng-add', {}, tree); const result = await runSchematic('ng-add', {}, tree);
const angularJson = readJsonInTree(result, 'angular.json'); const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(angularJson.cli.defaultCollection).toEqual('@nrwl/angular'); expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/angular');
}); });
}); });
}); });

View File

@ -10,7 +10,7 @@ describe('jestProject', () => {
appTree = Tree.empty(); appTree = Tree.empty();
appTree = createEmptyWorkspace(appTree); appTree = createEmptyWorkspace(appTree);
appTree = await callRule( appTree = await callRule(
updateJsonInTree('angular.json', json => { updateJsonInTree('workspace.json', json => {
json.projects.lib1 = { json.projects.lib1 = {
root: 'libs/lib1', root: 'libs/lib1',
architect: { architect: {
@ -52,7 +52,7 @@ describe('jestProject', () => {
expect(resultTree.exists('/libs/lib1/tsconfig.spec.json')).toBeTruthy(); expect(resultTree.exists('/libs/lib1/tsconfig.spec.json')).toBeTruthy();
}); });
it('should alter angular.json', async () => { it('should alter workspace.json', async () => {
const resultTree = await runSchematic( const resultTree = await runSchematic(
'jest-project', 'jest-project',
{ {
@ -61,8 +61,8 @@ describe('jestProject', () => {
}, },
appTree appTree
); );
const angularJson = readJsonInTree(resultTree, 'angular.json'); const workspaceJson = readJsonInTree(resultTree, 'workspace.json');
expect(angularJson.projects.lib1.architect.test).toEqual({ expect(workspaceJson.projects.lib1.architect.test).toEqual({
builder: '@nrwl/jest:jest', builder: '@nrwl/jest:jest',
options: { options: {
jestConfig: 'libs/lib1/jest.config.js', jestConfig: 'libs/lib1/jest.config.js',
@ -70,9 +70,9 @@ describe('jestProject', () => {
tsConfig: 'libs/lib1/tsconfig.spec.json' tsConfig: 'libs/lib1/tsconfig.spec.json'
} }
}); });
expect(angularJson.projects.lib1.architect.lint.options.tsConfig).toContain( expect(
'libs/lib1/tsconfig.spec.json' workspaceJson.projects.lib1.architect.lint.options.tsConfig
); ).toContain('libs/lib1/tsconfig.spec.json');
}); });
it('should create a jest.config.js', async () => { it('should create a jest.config.js', async () => {
@ -144,7 +144,7 @@ describe('jestProject', () => {
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', async () => { it('should not list the setup file in workspace.json', async () => {
const resultTree = await runSchematic( const resultTree = await runSchematic(
'jest-project', 'jest-project',
{ {
@ -153,9 +153,9 @@ describe('jestProject', () => {
}, },
appTree appTree
); );
const angularJson = readJsonInTree(resultTree, 'angular.json'); const workspaceJson = readJsonInTree(resultTree, 'workspace.json');
expect( expect(
angularJson.projects.lib1.architect.test.options.setupFile workspaceJson.projects.lib1.architect.test.options.setupFile
).toBeUndefined(); ).toBeUndefined();
}); });
@ -189,7 +189,7 @@ describe('jestProject', () => {
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', async () => { it('should not list the setup file in workspace.json', async () => {
const resultTree = await runSchematic( const resultTree = await runSchematic(
'jest-project', 'jest-project',
{ {
@ -198,9 +198,9 @@ describe('jestProject', () => {
}, },
appTree appTree
); );
const angularJson = readJsonInTree(resultTree, 'angular.json'); const workspaceJson = readJsonInTree(resultTree, 'workspace.json');
expect( expect(
angularJson.projects.lib1.architect.test.options.setupFile workspaceJson.projects.lib1.architect.test.options.setupFile
).toBeUndefined(); ).toBeUndefined();
}); });

View File

@ -11,7 +11,11 @@ import {
noop, noop,
filter filter
} from '@angular-devkit/schematics'; } from '@angular-devkit/schematics';
import { readJsonInTree, updateJsonInTree } from '@nrwl/workspace'; import {
readJsonInTree,
updateJsonInTree,
updateWorkspaceInTree
} from '@nrwl/workspace';
import { getProjectConfig, addDepsToPackageJson } from '@nrwl/workspace'; import { getProjectConfig, addDepsToPackageJson } from '@nrwl/workspace';
import { offsetFromRoot } from '@nrwl/workspace'; import { offsetFromRoot } from '@nrwl/workspace';
import { join, normalize } from '@angular-devkit/core'; import { join, normalize } from '@angular-devkit/core';
@ -69,8 +73,8 @@ function updateTsConfig(options: JestProjectSchema): Rule {
}; };
} }
function updateAngularJson(options: JestProjectSchema): Rule { function updateWorkspaceJson(options: JestProjectSchema): Rule {
return updateJsonInTree('angular.json', json => { return updateWorkspaceInTree(json => {
const projectConfig = json.projects[options.project]; const projectConfig = json.projects[options.project];
projectConfig.architect.test = { projectConfig.architect.test = {
builder: '@nrwl/jest:jest', builder: '@nrwl/jest:jest',
@ -106,7 +110,9 @@ function check(options: JestProjectSchema): Rule {
const packageJson = readJsonInTree(host, 'package.json'); const packageJson = readJsonInTree(host, 'package.json');
if (!packageJson.devDependencies.jest) { if (!packageJson.devDependencies.jest) {
context.logger.warn(`"jest" is not installed as a dependency.`); context.logger.warn(`"jest" is not installed as a dependency.`);
context.logger.info(`Add "jest" via "ng add @nrwl/jest"`); context.logger.info(
`Add "jest" via "yarn add --dev @nrwl/jest" or "npm install -D @nrwl/jest"`
);
} }
return host; return host;
}; };
@ -128,6 +134,6 @@ export default function(options: JestProjectSchema): Rule {
check(options), check(options),
generateFiles(options), generateFiles(options),
updateTsConfig(options), updateTsConfig(options),
updateAngularJson(options) updateWorkspaceJson(options)
]); ]);
} }

View File

@ -23,13 +23,13 @@ describe('ng-add', () => {
describe('defaultCollection', () => { describe('defaultCollection', () => {
it('should be set if none was set before', async () => { it('should be set if none was set before', async () => {
const result = await runSchematic('ng-add', {}, tree); const result = await runSchematic('ng-add', {}, tree);
const angularJson = readJsonInTree(result, 'angular.json'); const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(angularJson.cli.defaultCollection).toEqual('@nrwl/nest'); expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/nest');
}); });
it('should be set if @nrwl/workspace was set before', async () => { it('should be set if @nrwl/workspace was set before', async () => {
tree = await callRule( tree = await callRule(
updateJsonInTree('angular.json', json => { updateJsonInTree('workspace.json', json => {
json.cli = { json.cli = {
defaultCollection: '@nrwl/workspace' defaultCollection: '@nrwl/workspace'
}; };
@ -39,13 +39,13 @@ describe('ng-add', () => {
tree tree
); );
const result = await runSchematic('ng-add', {}, tree); const result = await runSchematic('ng-add', {}, tree);
const angularJson = readJsonInTree(result, 'angular.json'); const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(angularJson.cli.defaultCollection).toEqual('@nrwl/nest'); expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/nest');
}); });
it('should not be set if something else was set before', async () => { it('should not be set if something else was set before', async () => {
tree = await callRule( tree = await callRule(
updateJsonInTree('angular.json', json => { updateJsonInTree('workspace.json', json => {
json.cli = { json.cli = {
defaultCollection: '@nrwl/angular' defaultCollection: '@nrwl/angular'
}; };
@ -55,8 +55,8 @@ describe('ng-add', () => {
tree tree
); );
const result = await runSchematic('ng-add', {}, tree); const result = await runSchematic('ng-add', {}, tree);
const angularJson = readJsonInTree(result, 'angular.json'); const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(angularJson.cli.defaultCollection).toEqual('@nrwl/angular'); expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/angular');
}); });
}); });
}); });

View File

@ -14,10 +14,10 @@ describe('app', () => {
}); });
describe('not nested', () => { describe('not nested', () => {
it('should update angular.json', async () => { it('should update workspace.json', async () => {
const tree = await runSchematic('app', { name: 'myNodeApp' }, appTree); const tree = await runSchematic('app', { name: 'myNodeApp' }, appTree);
const angularJson = readJsonInTree(tree, '/angular.json'); const workspaceJson = readJsonInTree(tree, '/workspace.json');
const project = angularJson.projects['my-node-app']; const project = workspaceJson.projects['my-node-app'];
expect(project.root).toEqual('apps/my-node-app'); expect(project.root).toEqual('apps/my-node-app');
expect(project.architect).toEqual( expect(project.architect).toEqual(
jasmine.objectContaining({ jasmine.objectContaining({
@ -52,7 +52,7 @@ describe('app', () => {
} }
}) })
); );
expect(angularJson.projects['my-node-app'].architect.lint).toEqual({ expect(workspaceJson.projects['my-node-app'].architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint', builder: '@angular-devkit/build-angular:tslint',
options: { options: {
tsConfig: [ tsConfig: [
@ -62,8 +62,8 @@ describe('app', () => {
exclude: ['**/node_modules/**', '!apps/my-node-app/**'] exclude: ['**/node_modules/**', '!apps/my-node-app/**']
} }
}); });
expect(angularJson.projects['my-node-app-e2e']).toBeUndefined(); expect(workspaceJson.projects['my-node-app-e2e']).toBeUndefined();
expect(angularJson.defaultProject).toEqual('my-node-app'); expect(workspaceJson.defaultProject).toEqual('my-node-app');
}); });
it('should update nx.json', async () => { it('should update nx.json', async () => {
@ -109,33 +109,33 @@ describe('app', () => {
}); });
describe('nested', () => { describe('nested', () => {
it('should update angular.json', async () => { it('should update workspace.json', async () => {
const tree = await runSchematic( const tree = await runSchematic(
'app', 'app',
{ name: 'myNodeApp', directory: 'myDir' }, { name: 'myNodeApp', directory: 'myDir' },
appTree appTree
); );
const angularJson = readJsonInTree(tree, '/angular.json'); const workspaceJson = readJsonInTree(tree, '/workspace.json');
expect(angularJson.projects['my-dir-my-node-app'].root).toEqual( expect(workspaceJson.projects['my-dir-my-node-app'].root).toEqual(
'apps/my-dir/my-node-app' 'apps/my-dir/my-node-app'
); );
expect(angularJson.projects['my-dir-my-node-app'].architect.lint).toEqual( expect(
{ workspaceJson.projects['my-dir-my-node-app'].architect.lint
builder: '@angular-devkit/build-angular:tslint', ).toEqual({
options: { builder: '@angular-devkit/build-angular:tslint',
tsConfig: [ options: {
'apps/my-dir/my-node-app/tsconfig.app.json', tsConfig: [
'apps/my-dir/my-node-app/tsconfig.spec.json' 'apps/my-dir/my-node-app/tsconfig.app.json',
], 'apps/my-dir/my-node-app/tsconfig.spec.json'
exclude: ['**/node_modules/**', '!apps/my-dir/my-node-app/**'] ],
} exclude: ['**/node_modules/**', '!apps/my-dir/my-node-app/**']
} }
); });
expect(angularJson.projects['my-dir-my-node-app-e2e']).toBeUndefined(); expect(workspaceJson.projects['my-dir-my-node-app-e2e']).toBeUndefined();
expect(angularJson.defaultProject).toEqual('my-dir-my-node-app'); expect(workspaceJson.defaultProject).toEqual('my-dir-my-node-app');
}); });
it('should update nx.json', async () => { it('should update nx.json', async () => {
@ -213,12 +213,12 @@ describe('app', () => {
expect(tree.exists('apps/my-node-app/src/test.ts')).toBeFalsy(); expect(tree.exists('apps/my-node-app/src/test.ts')).toBeFalsy();
expect(tree.exists('apps/my-node-app/tsconfig.spec.json')).toBeFalsy(); expect(tree.exists('apps/my-node-app/tsconfig.spec.json')).toBeFalsy();
expect(tree.exists('apps/my-node-app/jest.config.js')).toBeFalsy(); expect(tree.exists('apps/my-node-app/jest.config.js')).toBeFalsy();
const angularJson = readJsonInTree(tree, 'angular.json'); const workspaceJson = readJsonInTree(tree, 'workspace.json');
expect( expect(
angularJson.projects['my-node-app'].architect.test workspaceJson.projects['my-node-app'].architect.test
).toBeUndefined(); ).toBeUndefined();
expect( expect(
angularJson.projects['my-node-app'].architect.lint.options.tsConfig workspaceJson.projects['my-node-app'].architect.lint.options.tsConfig
).toEqual(['apps/my-node-app/tsconfig.app.json']); ).toEqual(['apps/my-node-app/tsconfig.app.json']);
}); });
}); });
@ -234,7 +234,7 @@ describe('app', () => {
); );
expect(tree.exists('apps/my-frontend/proxy.conf.json')).toBeTruthy(); expect(tree.exists('apps/my-frontend/proxy.conf.json')).toBeTruthy();
const serve = JSON.parse(tree.readContent('angular.json')).projects[ const serve = JSON.parse(tree.readContent('workspace.json')).projects[
'my-frontend' 'my-frontend'
].architect.serve; ].architect.serve;
expect(serve.options.proxyConfig).toEqual( expect(serve.options.proxyConfig).toEqual(
@ -252,7 +252,7 @@ describe('app', () => {
); );
expect(tree.exists('apps/my-frontend/proxy.conf.json')).toBeTruthy(); expect(tree.exists('apps/my-frontend/proxy.conf.json')).toBeTruthy();
const serve = JSON.parse(tree.readContent('angular.json')).projects[ const serve = JSON.parse(tree.readContent('workspace.json')).projects[
'my-frontend' 'my-frontend'
].architect.serve; ].architect.serve;
expect(serve.options.proxyConfig).toEqual( expect(serve.options.proxyConfig).toEqual(

View File

@ -13,7 +13,12 @@ import {
} from '@angular-devkit/schematics'; } from '@angular-devkit/schematics';
import { join, normalize, Path } from '@angular-devkit/core'; import { join, normalize, Path } from '@angular-devkit/core';
import { Schema } from './schema'; import { Schema } from './schema';
import { updateJsonInTree } from '@nrwl/workspace'; import {
updateJsonInTree,
updateWorkspaceInTree,
generateProjectLint,
addGlobalLint
} from '@nrwl/workspace';
import { toFileName } from '@nrwl/workspace'; import { toFileName } from '@nrwl/workspace';
import { getProjectConfig } from '@nrwl/workspace'; import { getProjectConfig } from '@nrwl/workspace';
import { offsetFromRoot } from '@nrwl/workspace'; import { offsetFromRoot } from '@nrwl/workspace';
@ -61,16 +66,6 @@ function getBuildConfig(project: any, options: NormalizedSchema) {
}; };
} }
function getLintConfig(project: any) {
return {
builder: '@angular-devkit/build-angular:tslint',
options: {
tsConfig: [join(project.root, 'tsconfig.app.json')],
exclude: ['**/node_modules/**', '!' + join(project.root, '**')]
}
};
}
function getServeConfig(options: NormalizedSchema) { function getServeConfig(options: NormalizedSchema) {
return { return {
builder: '@nrwl/node:execute', builder: '@nrwl/node:execute',
@ -80,8 +75,8 @@ function getServeConfig(options: NormalizedSchema) {
}; };
} }
function updateAngularJson(options: NormalizedSchema): Rule { function updateWorkspaceJson(options: NormalizedSchema): Rule {
return updateJsonInTree('angular.json', angularJson => { return updateWorkspaceInTree(workspaceJson => {
const project = { const project = {
root: options.appProjectRoot, root: options.appProjectRoot,
sourceRoot: join(options.appProjectRoot, 'src'), sourceRoot: join(options.appProjectRoot, 'src'),
@ -93,12 +88,17 @@ function updateAngularJson(options: NormalizedSchema): Rule {
project.architect.build = getBuildConfig(project, options); project.architect.build = getBuildConfig(project, options);
project.architect.serve = getServeConfig(options); project.architect.serve = getServeConfig(options);
project.architect.lint = getLintConfig(project); project.architect.lint = generateProjectLint(
angularJson.projects[options.name] = project; normalize(project.root),
join(normalize(project.root), 'tsconfig.app.json'),
options.linter
);
angularJson.defaultProject = angularJson.defaultProject || options.name; workspaceJson.projects[options.name] = project;
return angularJson; workspaceJson.defaultProject = workspaceJson.defaultProject || options.name;
return workspaceJson;
}); });
} }
@ -135,7 +135,7 @@ function addProxy(options: NormalizedSchema): Rule {
) )
); );
updateJsonInTree('angular.json', json => { updateWorkspaceInTree(json => {
projectConfig.architect.serve.options.proxyConfig = pathToProxyFile; projectConfig.architect.serve.options.proxyConfig = pathToProxyFile;
json.projects[options.frontendProject] = projectConfig; json.projects[options.frontendProject] = projectConfig;
return json; return json;
@ -151,8 +151,9 @@ export default function(schema: Schema): Rule {
ngAdd({ ngAdd({
skipFormat: true skipFormat: true
}), }),
addGlobalLint(options.linter),
addAppFiles(options), addAppFiles(options),
updateAngularJson(options), updateWorkspaceJson(options),
updateNxJson(options), updateNxJson(options),
options.unitTestRunner === 'jest' options.unitTestRunner === 'jest'
? externalSchematic('@nrwl/jest', 'jest-project', { ? externalSchematic('@nrwl/jest', 'jest-project', {

View File

@ -1,7 +1,3 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = { export const environment = {
production: false production: false
}; };

View File

@ -1,10 +1,10 @@
import { UnitTestRunner } from '../../utils/test-runners';
export interface Schema { export interface Schema {
name: string; name: string;
skipFormat: boolean; skipFormat: boolean;
skipPackageJson: boolean; skipPackageJson: boolean;
directory?: string; directory?: string;
unitTestRunner: UnitTestRunner; unitTestRunner: 'jest' | 'none';
linter: 'eslint' | 'tslint';
tags?: string; tags?: string;
frontendProject?: string; frontendProject?: string;
} }

View File

@ -28,6 +28,12 @@
"default": false, "default": false,
"description": "Do not add dependencies to package.json." "description": "Do not add dependencies to package.json."
}, },
"linter": {
"description": "The tool to use for running lint checks.",
"type": "string",
"enum": ["eslint", "tslint"],
"default": "tslint"
},
"unitTestRunner": { "unitTestRunner": {
"type": "string", "type": "string",
"enum": ["jest", "none"], "enum": ["jest", "none"],

View File

@ -21,13 +21,13 @@ describe('ng-add', () => {
describe('defaultCollection', () => { describe('defaultCollection', () => {
it('should be set if none was set before', async () => { it('should be set if none was set before', async () => {
const result = await runSchematic('ng-add', {}, tree); const result = await runSchematic('ng-add', {}, tree);
const angularJson = readJsonInTree(result, 'angular.json'); const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(angularJson.cli.defaultCollection).toEqual('@nrwl/node'); expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/node');
}); });
it('should be set if @nrwl/workspace was set before', async () => { it('should be set if @nrwl/workspace was set before', async () => {
tree = await callRule( tree = await callRule(
updateJsonInTree('angular.json', json => { updateJsonInTree('workspace.json', json => {
json.cli = { json.cli = {
defaultCollection: '@nrwl/workspace' defaultCollection: '@nrwl/workspace'
}; };
@ -37,13 +37,13 @@ describe('ng-add', () => {
tree tree
); );
const result = await runSchematic('ng-add', {}, tree); const result = await runSchematic('ng-add', {}, tree);
const angularJson = readJsonInTree(result, 'angular.json'); const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(angularJson.cli.defaultCollection).toEqual('@nrwl/node'); expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/node');
}); });
it('should not be set if something else was set before', async () => { it('should not be set if something else was set before', async () => {
tree = await callRule( tree = await callRule(
updateJsonInTree('angular.json', json => { updateJsonInTree('workspace.json', json => {
json.cli = { json.cli = {
defaultCollection: '@nrwl/angular' defaultCollection: '@nrwl/angular'
}; };
@ -53,8 +53,8 @@ describe('ng-add', () => {
tree tree
); );
const result = await runSchematic('ng-add', {}, tree); const result = await runSchematic('ng-add', {}, tree);
const angularJson = readJsonInTree(result, 'angular.json'); const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(angularJson.cli.defaultCollection).toEqual('@nrwl/angular'); expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/angular');
}); });
}); });
}); });

View File

@ -13,15 +13,15 @@ describe('app', () => {
}); });
describe('not nested', () => { describe('not nested', () => {
it('should update angular.json', async () => { it('should update workspace.json', async () => {
const tree = await runSchematic('app', { name: 'myApp' }, appTree); const tree = await runSchematic('app', { name: 'myApp' }, appTree);
const angularJson = readJsonInTree(tree, '/angular.json'); const workspaceJson = readJsonInTree(tree, '/workspace.json');
expect(angularJson.projects['my-app'].root).toEqual('apps/my-app'); expect(workspaceJson.projects['my-app'].root).toEqual('apps/my-app');
expect(angularJson.projects['my-app-e2e'].root).toEqual( expect(workspaceJson.projects['my-app-e2e'].root).toEqual(
'apps/my-app-e2e' 'apps/my-app-e2e'
); );
expect(angularJson.defaultProject).toEqual('my-app'); expect(workspaceJson.defaultProject).toEqual('my-app');
}); });
it('should update nx.json', async () => { it('should update nx.json', async () => {
@ -76,18 +76,18 @@ describe('app', () => {
}); });
describe('nested', () => { describe('nested', () => {
it('should update angular.json', async () => { it('should update workspace.json', async () => {
const tree = await runSchematic( const tree = await runSchematic(
'app', 'app',
{ name: 'myApp', directory: 'myDir' }, { name: 'myApp', directory: 'myDir' },
appTree appTree
); );
const angularJson = readJsonInTree(tree, '/angular.json'); const workspaceJson = readJsonInTree(tree, '/workspace.json');
expect(angularJson.projects['my-dir-my-app'].root).toEqual( expect(workspaceJson.projects['my-dir-my-app'].root).toEqual(
'apps/my-dir/my-app' 'apps/my-dir/my-app'
); );
expect(angularJson.projects['my-dir-my-app-e2e'].root).toEqual( expect(workspaceJson.projects['my-dir-my-app-e2e'].root).toEqual(
'apps/my-dir/my-app-e2e' 'apps/my-dir/my-app-e2e'
); );
}); });
@ -225,8 +225,8 @@ describe('app', () => {
}, },
appTree appTree
); );
const angularJson = readJsonInTree(tree, 'angular.json'); const workspaceJson = readJsonInTree(tree, 'workspace.json');
const architectConfig = angularJson.projects['my-app'].architect; const architectConfig = workspaceJson.projects['my-app'].architect;
expect(architectConfig.build.builder).toEqual('@nrwl/web:build'); expect(architectConfig.build.builder).toEqual('@nrwl/web:build');
expect(architectConfig.build.options).toEqual({ expect(architectConfig.build.options).toEqual({
assets: ['apps/my-app/src/favicon.ico', 'apps/my-app/src/assets'], assets: ['apps/my-app/src/favicon.ico', 'apps/my-app/src/assets'],
@ -270,8 +270,8 @@ describe('app', () => {
}, },
appTree appTree
); );
const angularJson = readJsonInTree(tree, 'angular.json'); const workspaceJson = readJsonInTree(tree, 'workspace.json');
const architectConfig = angularJson.projects['my-app'].architect; const architectConfig = workspaceJson.projects['my-app'].architect;
expect(architectConfig.serve.builder).toEqual('@nrwl/web:dev-server'); expect(architectConfig.serve.builder).toEqual('@nrwl/web:dev-server');
expect(architectConfig.serve.options).toEqual({ expect(architectConfig.serve.options).toEqual({
buildTarget: 'my-app:build' buildTarget: 'my-app:build'
@ -289,8 +289,8 @@ describe('app', () => {
}, },
appTree appTree
); );
const angularJson = readJsonInTree(tree, 'angular.json'); const workspaceJson = readJsonInTree(tree, 'workspace.json');
expect(angularJson.projects['my-app'].architect.lint).toEqual({ expect(workspaceJson.projects['my-app'].architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint', builder: '@angular-devkit/build-angular:tslint',
options: { options: {
exclude: ['**/node_modules/**', '!apps/my-app/**'], exclude: ['**/node_modules/**', '!apps/my-app/**'],
@ -312,10 +312,10 @@ describe('app', () => {
expect(tree.exists('apps/my-app/src/app/app.spec.tsx')).toBeFalsy(); expect(tree.exists('apps/my-app/src/app/app.spec.tsx')).toBeFalsy();
expect(tree.exists('apps/my-app/tsconfig.spec.json')).toBeFalsy(); expect(tree.exists('apps/my-app/tsconfig.spec.json')).toBeFalsy();
expect(tree.exists('apps/my-app/jest.config.js')).toBeFalsy(); expect(tree.exists('apps/my-app/jest.config.js')).toBeFalsy();
const angularJson = readJsonInTree(tree, 'angular.json'); const workspaceJson = readJsonInTree(tree, 'workspace.json');
expect(angularJson.projects['my-app'].architect.test).toBeUndefined(); expect(workspaceJson.projects['my-app'].architect.test).toBeUndefined();
expect( expect(
angularJson.projects['my-app'].architect.lint.options.tsConfig workspaceJson.projects['my-app'].architect.lint.options.tsConfig
).toEqual(['apps/my-app/tsconfig.app.json']); ).toEqual(['apps/my-app/tsconfig.app.json']);
}); });
}); });
@ -328,8 +328,8 @@ describe('app', () => {
appTree appTree
); );
expect(tree.exists('apps/my-app-e2e')).toBeFalsy(); expect(tree.exists('apps/my-app-e2e')).toBeFalsy();
const angularJson = readJsonInTree(tree, 'angular.json'); const workspaceJson = readJsonInTree(tree, 'workspace.json');
expect(angularJson.projects['my-app-e2e']).toBeUndefined(); expect(workspaceJson.projects['my-app-e2e']).toBeUndefined();
}); });
}); });
@ -417,17 +417,17 @@ describe('app', () => {
expect(content).toContain('<StyledApp>'); expect(content).toContain('<StyledApp>');
}); });
it('should exclude styles from angular.json', async () => { it('should exclude styles from workspace.json', async () => {
const tree = await runSchematic( const tree = await runSchematic(
'app', 'app',
{ name: 'myApp', style: '@emotion/styled' }, { name: 'myApp', style: '@emotion/styled' },
appTree appTree
); );
const angularJSON = readJsonInTree(tree, 'angular.json'); const workspaceJson = readJsonInTree(tree, 'workspace.json');
expect( expect(
angularJSON.projects['my-app'].architect.build.options.styles workspaceJson.projects['my-app'].architect.build.options.styles
).toEqual([]); ).toEqual([]);
}); });

View File

@ -19,9 +19,14 @@ import {
NxJson, NxJson,
offsetFromRoot, offsetFromRoot,
toFileName, toFileName,
updateJsonInTree updateJsonInTree,
generateProjectLint,
addGlobalLint
} from '@nrwl/workspace'; } from '@nrwl/workspace';
import { addDepsToPackageJson } from '@nrwl/workspace/src/utils/ast-utils'; import {
addDepsToPackageJson,
updateWorkspaceInTree
} from '@nrwl/workspace/src/utils/ast-utils';
import ngAdd from '../ng-add/ng-add'; import ngAdd from '../ng-add/ng-add';
import * as ts from 'typescript'; import * as ts from 'typescript';
@ -48,6 +53,7 @@ export default function(schema: Schema): Rule {
ngAdd({ ngAdd({
skipFormat: true skipFormat: true
}), }),
addGlobalLint(options.linter),
createApplicationFiles(options), createApplicationFiles(options),
updateNxJson(options), updateNxJson(options),
addProject(options), addProject(options),
@ -102,7 +108,7 @@ function updateNxJson(options: NormalizedSchema): Rule {
} }
function addProject(options: NormalizedSchema): Rule { function addProject(options: NormalizedSchema): Rule {
return updateJsonInTree('angular.json', json => { return updateWorkspaceInTree(json => {
const architect: { [key: string]: any } = {}; const architect: { [key: string]: any } = {};
architect.build = { architect.build = {
@ -166,16 +172,11 @@ function addProject(options: NormalizedSchema): Rule {
} }
}; };
architect.lint = { architect.lint = generateProjectLint(
builder: '@angular-devkit/build-angular:tslint', normalize(options.appProjectRoot),
options: { join(normalize(options.appProjectRoot), 'tsconfig.app.json'),
tsConfig: [join(options.appProjectRoot, 'tsconfig.app.json')], options.linter
exclude: [ );
'**/node_modules/**',
'!' + join(options.appProjectRoot, '**')
]
}
};
json.projects[options.projectName] = { json.projects[options.projectName] = {
root: options.appProjectRoot, root: options.appProjectRoot,

View File

@ -1,13 +1,12 @@
import { E2eTestRunner, UnitTestRunner } from '../../utils/test-runners';
export interface Schema { export interface Schema {
name: string; name: string;
style?: string; style?: string;
skipFormat: boolean; skipFormat: boolean;
directory?: string; directory?: string;
tags?: string; tags?: string;
unitTestRunner: UnitTestRunner; unitTestRunner: 'jest' | 'none';
e2eTestRunner: E2eTestRunner; e2eTestRunner: 'cypress' | 'none';
linter: 'eslint' | 'tslint';
pascalCaseFiles?: boolean; pascalCaseFiles?: boolean;
classComponent?: boolean; classComponent?: boolean;
routing?: boolean; routing?: boolean;

View File

@ -50,6 +50,12 @@
] ]
} }
}, },
"linter": {
"description": "The tool to use for running lint checks.",
"type": "string",
"enum": ["eslint", "tslint"],
"default": "tslint"
},
"routing": { "routing": {
"type": "boolean", "type": "boolean",
"description": "Generate application with routes", "description": "Generate application with routes",

View File

@ -6,7 +6,7 @@
"properties": { "properties": {
"project": { "project": {
"type": "string", "type": "string",
"description": "The name of the project (as specified in angular.json).", "description": "The name of the project.",
"$default": { "$default": {
"$source": "projectName" "$source": "projectName"
}, },

View File

@ -4,4 +4,4 @@ This library was generated with [Nx](https://nx.dev).
## Running unit tests ## Running unit tests
Run `ng test <%= name %>` to execute the unit tests via [Jest](https://jestjs.io). Run `yarn test <%= name %>` to execute the unit tests via [Jest](https://jestjs.io).

View File

@ -13,13 +13,12 @@ describe('lib', () => {
}); });
describe('not nested', () => { describe('not nested', () => {
it('should update angular.json', async () => { it('should update workspace.json', async () => {
const tree = await runSchematic('lib', { name: 'myLib' }, appTree); const tree = await runSchematic('lib', { name: 'myLib' }, appTree);
const angularJson = readJsonInTree(tree, '/angular.json'); const workspaceJson = readJsonInTree(tree, '/workspace.json');
expect(workspaceJson.projects['my-lib'].root).toEqual('libs/my-lib');
expect(angularJson.projects['my-lib'].root).toEqual('libs/my-lib'); expect(workspaceJson.projects['my-lib'].architect.build).toBeUndefined();
expect(angularJson.projects['my-lib'].architect.build).toBeUndefined(); expect(workspaceJson.projects['my-lib'].architect.lint).toEqual({
expect(angularJson.projects['my-lib'].architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint', builder: '@angular-devkit/build-angular:tslint',
options: { options: {
exclude: ['**/node_modules/**', '!libs/my-lib/**'], exclude: ['**/node_modules/**', '!libs/my-lib/**'],
@ -172,18 +171,18 @@ describe('lib', () => {
).toBeTruthy(); ).toBeTruthy();
}); });
it('should update angular.json', async () => { it('should update workspace.json', async () => {
const tree = await runSchematic( const tree = await runSchematic(
'lib', 'lib',
{ name: 'myLib', directory: 'myDir' }, { name: 'myLib', directory: 'myDir' },
appTree appTree
); );
const angularJson = readJsonInTree(tree, '/angular.json'); const workspaceJson = readJsonInTree(tree, '/workspace.json');
expect(angularJson.projects['my-dir-my-lib'].root).toEqual( expect(workspaceJson.projects['my-dir-my-lib'].root).toEqual(
'libs/my-dir/my-lib' 'libs/my-dir/my-lib'
); );
expect(angularJson.projects['my-dir-my-lib'].architect.lint).toEqual({ expect(workspaceJson.projects['my-dir-my-lib'].architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint', builder: '@angular-devkit/build-angular:tslint',
options: { options: {
exclude: ['**/node_modules/**', '!libs/my-dir/my-lib/**'], exclude: ['**/node_modules/**', '!libs/my-dir/my-lib/**'],
@ -256,10 +255,10 @@ describe('lib', () => {
); );
expect(resultTree.exists('libs/my-lib/tsconfig.spec.json')).toBeFalsy(); expect(resultTree.exists('libs/my-lib/tsconfig.spec.json')).toBeFalsy();
expect(resultTree.exists('libs/my-lib/jest.config.js')).toBeFalsy(); expect(resultTree.exists('libs/my-lib/jest.config.js')).toBeFalsy();
const angularJson = readJsonInTree(resultTree, 'angular.json'); const workspaceJson = readJsonInTree(resultTree, 'workspace.json');
expect(angularJson.projects['my-lib'].architect.test).toBeUndefined(); expect(workspaceJson.projects['my-lib'].architect.test).toBeUndefined();
expect( expect(
angularJson.projects['my-lib'].architect.lint.options.tsConfig workspaceJson.projects['my-lib'].architect.lint.options.tsConfig
).toEqual(['libs/my-lib/tsconfig.lib.json']); ).toEqual(['libs/my-lib/tsconfig.lib.json']);
}); });
}); });

View File

@ -22,7 +22,10 @@ import {
readJsonInTree, readJsonInTree,
toClassName, toClassName,
toFileName, toFileName,
updateJsonInTree updateJsonInTree,
updateWorkspaceInTree,
addGlobalLint,
generateProjectLint
} from '@nrwl/workspace'; } from '@nrwl/workspace';
import { join, normalize, Path } from '@angular-devkit/core'; import { join, normalize, Path } from '@angular-devkit/core';
import * as ts from 'typescript'; import * as ts from 'typescript';
@ -44,6 +47,7 @@ export default function(schema: Schema): Rule {
const options = normalizeOptions(schema); const options = normalizeOptions(schema);
return chain([ return chain([
addGlobalLint(options.linter),
createFiles(options), createFiles(options),
!options.skipTsConfig ? updateTsConfig(options) : noop(), !options.skipTsConfig ? updateTsConfig(options) : noop(),
addProject(options), addProject(options),
@ -71,19 +75,14 @@ export default function(schema: Schema): Rule {
} }
function addProject(options: NormalizedSchema): Rule { function addProject(options: NormalizedSchema): Rule {
return updateJsonInTree('angular.json', json => { return updateWorkspaceInTree(json => {
const architect: { [key: string]: any } = {}; const architect: { [key: string]: any } = {};
architect.lint = { architect.lint = generateProjectLint(
builder: '@angular-devkit/build-angular:tslint', normalize(options.projectRoot),
options: { join(normalize(options.projectRoot), 'tsconfig.lib.json'),
tsConfig: [join(normalize(options.projectRoot), 'tsconfig.lib.json')], options.linter
exclude: [ );
'**/node_modules/**',
'!' + join(normalize(options.projectRoot), '**')
]
}
};
json.projects[options.name] = { json.projects[options.name] = {
root: options.projectRoot, root: options.projectRoot,

View File

@ -1,5 +1,3 @@
import { UnitTestRunner } from '../../utils/test-runners';
export interface Schema { export interface Schema {
name: string; name: string;
directory?: string; directory?: string;
@ -11,5 +9,6 @@ export interface Schema {
pascalCaseFiles?: boolean; pascalCaseFiles?: boolean;
routing?: boolean; routing?: boolean;
parentRoute?: string; parentRoute?: string;
unitTestRunner: UnitTestRunner; unitTestRunner: 'jest' | 'none';
linter: 'eslint' | 'tslint';
} }

View File

@ -50,6 +50,12 @@
] ]
} }
}, },
"linter": {
"description": "The tool to use for running lint checks.",
"type": "string",
"enum": ["eslint", "tslint"],
"default": "tslint"
},
"unitTestRunner": { "unitTestRunner": {
"type": "string", "type": "string",
"enum": ["jest", "none"], "enum": ["jest", "none"],

View File

@ -27,13 +27,13 @@ describe('ng-add', () => {
describe('defaultCollection', () => { describe('defaultCollection', () => {
it('should be set if none was set before', async () => { it('should be set if none was set before', async () => {
const result = await runSchematic('ng-add', {}, tree); const result = await runSchematic('ng-add', {}, tree);
const angularJson = readJsonInTree(result, 'angular.json'); const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(angularJson.cli.defaultCollection).toEqual('@nrwl/react'); expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/react');
}); });
it('should be set if @nrwl/workspace was set before', async () => { it('should be set if @nrwl/workspace was set before', async () => {
tree = await callRule( tree = await callRule(
updateJsonInTree('angular.json', json => { updateJsonInTree('workspace.json', json => {
json.cli = { json.cli = {
defaultCollection: '@nrwl/workspace' defaultCollection: '@nrwl/workspace'
}; };
@ -43,13 +43,13 @@ describe('ng-add', () => {
tree tree
); );
const result = await runSchematic('ng-add', {}, tree); const result = await runSchematic('ng-add', {}, tree);
const angularJson = readJsonInTree(result, 'angular.json'); const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(angularJson.cli.defaultCollection).toEqual('@nrwl/react'); expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/react');
}); });
it('should not be set if something else was set before', async () => { it('should not be set if something else was set before', async () => {
tree = await callRule( tree = await callRule(
updateJsonInTree('angular.json', json => { updateJsonInTree('workspace.json', json => {
json.cli = { json.cli = {
defaultCollection: '@nrwl/angular' defaultCollection: '@nrwl/angular'
}; };
@ -59,8 +59,8 @@ describe('ng-add', () => {
tree tree
); );
const result = await runSchematic('ng-add', {}, tree); const result = await runSchematic('ng-add', {}, tree);
const angularJson = readJsonInTree(result, 'angular.json'); const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(angularJson.cli.defaultCollection).toEqual('@nrwl/angular'); expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/angular');
}); });
}); });
}); });

View File

@ -1,4 +1,4 @@
import { readCliConfigFile, updateJsonFile } from '@nrwl/workspace'; import { readWorkspaceConfigPath, updateJsonFile } from '@nrwl/workspace';
import { writeFileSync, unlinkSync } from 'fs'; import { writeFileSync, unlinkSync } from 'fs';
import { offsetFromRoot } from '@nrwl/workspace'; import { offsetFromRoot } from '@nrwl/workspace';
import * as path from 'path'; import * as path from 'path';
@ -6,7 +6,7 @@ import * as path from 'path';
export default { export default {
description: 'Create tsconfig.app.json for every app', description: 'Create tsconfig.app.json for every app',
run: () => { run: () => {
const config = readCliConfigFile(); const config = readWorkspaceConfigPath();
config.apps.forEach(app => { config.apps.forEach(app => {
if (!app.root.startsWith('apps/')) return; if (!app.root.startsWith('apps/')) return;
const offset = offsetFromRoot(app.root); const offset = offsetFromRoot(app.root);

View File

@ -5,7 +5,7 @@ import { stripIndents } from '@angular-devkit/core/src/utils/literals';
export default { export default {
description: `Create nx.json before migrating to Angular CLI 6.`, description: `Create nx.json before migrating to Angular CLI 6.`,
run: () => { run: () => {
if (!existsSync('.angular-cli.json') && existsSync('angular.json')) { if (!existsSync('.angular-cli.json') && existsSync('workspace.json')) {
console.warn(stripIndents` console.warn(stripIndents`
You have already upgraded to Angular CLI 6. You have already upgraded to Angular CLI 6.
We will not be able to recover information about your project's tags for you. We will not be able to recover information about your project's tags for you.
@ -13,8 +13,8 @@ export default {
return; return;
} }
const angularJson = readJsonFile('.angular-cli.json'); const workspaceJson = readJsonFile('.angular-cli.json');
const projects = angularJson.apps.reduce((projects, app) => { const projects = workspaceJson.apps.reduce((projects, app) => {
if (app.name === '$workspaceRoot') { if (app.name === '$workspaceRoot') {
return projects; return projects;
} }
@ -33,7 +33,7 @@ export default {
writeFileSync( writeFileSync(
'nx.json', 'nx.json',
serializeJson({ serializeJson({
npmScope: angularJson.project.npmScope, npmScope: workspaceJson.project.npmScope,
projects: projects projects: projects
}) })
); );

View File

@ -7,7 +7,7 @@ import { join } from 'path';
export default { export default {
description: `Switch to Nx 6.0`, description: `Switch to Nx 6.0`,
run: () => { run: () => {
if (!existsSync('.angular-cli.json') && existsSync('angular.json')) { if (!existsSync('.angular-cli.json') && existsSync('workspace.json')) {
console.warn(stripIndents` console.warn(stripIndents`
You have already upgraded to Angular CLI 6. You have already upgraded to Angular CLI 6.
We will not be able to recover information about your project's tags for you. We will not be able to recover information about your project's tags for you.

View File

@ -10,7 +10,8 @@ import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
import { import {
createOrUpdate, createOrUpdate,
readJsonInTree, readJsonInTree,
updateJsonInTree updateJsonInTree,
updateWorkspaceInTree
} from '@nrwl/workspace'; } from '@nrwl/workspace';
import { serializeJson, renameSync } from '@nrwl/workspace'; import { serializeJson, renameSync } from '@nrwl/workspace';
import { parseTarget, serializeTarget } from '@nrwl/workspace'; import { parseTarget, serializeTarget } from '@nrwl/workspace';
@ -256,8 +257,8 @@ function createTsconfigLibJson(host: Tree, project: any) {
} }
function createAdditionalFiles(host: Tree) { function createAdditionalFiles(host: Tree) {
const angularJson = readJsonInTree(host, 'angular.json'); const workspaceJson = readJsonInTree(host, 'workspace.json');
Object.entries<any>(angularJson.projects).forEach(([key, project]) => { Object.entries<any>(workspaceJson.projects).forEach(([key, project]) => {
if (project.architect.test) { if (project.architect.test) {
createTsconfigSpecJson(host, project); createTsconfigSpecJson(host, project);
createKarma(host, project); createKarma(host, project);
@ -282,9 +283,9 @@ function createAdditionalFiles(host: Tree) {
} }
function moveE2eTests(host: Tree, context: SchematicContext) { function moveE2eTests(host: Tree, context: SchematicContext) {
const angularJson = readJsonInTree(host, 'angular.json'); const workspaceJson = readJsonInTree(host, 'workspace.json');
Object.entries<any>(angularJson.projects).forEach(([key, p]) => { Object.entries<any>(workspaceJson.projects).forEach(([key, p]) => {
if (p.projectType === 'application' && !p.architect.e2e) { if (p.projectType === 'application' && !p.architect.e2e) {
renameSync(`${p.root}/e2e`, `${p.root}-e2e/src`, err => { renameSync(`${p.root}/e2e`, `${p.root}-e2e/src`, err => {
if (!err) { if (!err) {
@ -320,9 +321,9 @@ function deleteUnneededFiles(host: Tree) {
} }
function patchLibIndexFiles(host: Tree, context: SchematicContext) { function patchLibIndexFiles(host: Tree, context: SchematicContext) {
const angularJson = readJsonInTree(host, 'angular.json'); const workspaceJson = readJsonInTree(host, 'workspace.json');
Object.entries<any>(angularJson.projects).forEach(([key, p]) => { Object.entries<any>(workspaceJson.projects).forEach(([key, p]) => {
if (p.projectType === 'library') { if (p.projectType === 'library') {
try { try {
// TODO: incorporate this into fileutils.renameSync // TODO: incorporate this into fileutils.renameSync
@ -489,8 +490,8 @@ function createDefaultE2eTsConfig(host: Tree, project: any) {
} }
function updateTsConfigs(host: Tree) { function updateTsConfigs(host: Tree) {
const angularJson = readJsonInTree(host, 'angular.json'); const workspaceJson = readJsonInTree(host, 'workspace.json');
Object.entries<any>(angularJson.projects).forEach(([key, project]) => { Object.entries<any>(workspaceJson.projects).forEach(([key, project]) => {
if ( if (
project.architect.build && project.architect.build &&
project.architect.build.options.main.startsWith('apps') project.architect.build.options.main.startsWith('apps')
@ -562,7 +563,7 @@ function updateTsConfigs(host: Tree) {
return host; return host;
} }
const updateAngularJson = updateJsonInTree('angular.json', json => { const updateworkspaceJson = updateWorkspaceInTree(json => {
json.newProjectRoot = ''; json.newProjectRoot = '';
json.cli = { json.cli = {
...json.cli, ...json.cli,
@ -681,7 +682,7 @@ function addInstallTask(host: Tree, context: SchematicContext) {
} }
function checkCli6Upgraded(host: Tree) { function checkCli6Upgraded(host: Tree) {
if (!host.exists('angular.json') && host.exists('.angular-cli.json')) { if (!host.exists('workspace.json') && host.exists('.angular-cli.json')) {
throw new Error( throw new Error(
'Please install the latest version and run ng update @angular/cli first' 'Please install the latest version and run ng update @angular/cli first'
); );
@ -701,7 +702,7 @@ export default function(): Rule {
return chain([ return chain([
checkCli6Upgraded, checkCli6Upgraded,
updatePackageJson, updatePackageJson,
updateAngularJson, updateworkspaceJson,
moveE2eTests, moveE2eTests,
updateTsConfigs, updateTsConfigs,
createAdditionalFiles, createAdditionalFiles,

View File

@ -25,7 +25,7 @@ const addImplicitDependencies = updateJsonInTree<NxJson>('nx.json', nxJson => {
return { return {
...nxJson, ...nxJson,
implicitDependencies: { implicitDependencies: {
'angular.json': '*', 'workspace.json': '*',
'package.json': '*', 'package.json': '*',
'tsconfig.json': '*', 'tsconfig.json': '*',
'tslint.json': '*', 'tslint.json': '*',

View File

@ -15,7 +15,7 @@ describe('Update 7.2.0', () => {
scripts: {} scripts: {}
}); });
createJson('tsconfig.json', {}); createJson('tsconfig.json', {});
createJson('angular.json', { createJson('workspace.json', {
projects: { projects: {
app1: { app1: {
root: 'apps/app1', root: 'apps/app1',
@ -321,7 +321,7 @@ describe('Update 7.2.0', () => {
it('should fix cypress lint configs', async () => { it('should fix cypress lint configs', async () => {
initialTree = await schematicRunner initialTree = await schematicRunner
.callRule( .callRule(
updateJsonInTree('angular.json', json => { updateJsonInTree('workspace.json', json => {
json.projects['app2-e2e'].architect.lint.options.tsConfig = json.projects['app2-e2e'].architect.lint.options.tsConfig =
'e2e/tsconfig.e2e.json'; 'e2e/tsconfig.e2e.json';
return json; return json;
@ -333,8 +333,8 @@ describe('Update 7.2.0', () => {
.runSchematicAsync('update-7.2.0', {}, initialTree) .runSchematicAsync('update-7.2.0', {}, initialTree)
.toPromise(); .toPromise();
expect( expect(
readJsonInTree(result, 'angular.json').projects['app2-e2e'].architect.lint readJsonInTree(result, 'workspace.json').projects['app2-e2e'].architect
.options.tsConfig .lint.options.tsConfig
).toEqual('apps/app2-e2e/tsconfig.e2e.json'); ).toEqual('apps/app2-e2e/tsconfig.e2e.json');
[ [
'/apps/app1/tsconfig.app.json', '/apps/app1/tsconfig.app.json',
@ -358,7 +358,7 @@ describe('Update 7.2.0', () => {
it('should not fail for non-existing tsconfigs', async () => { it('should not fail for non-existing tsconfigs', async () => {
initialTree = await schematicRunner initialTree = await schematicRunner
.callRule( .callRule(
updateJsonInTree('angular.json', json => { updateJsonInTree('workspace.json', json => {
json.projects['app2'].architect.lint.options.tsConfig = json.projects['app2'].architect.lint.options.tsConfig =
'apps/nonexistent/tsconfig.app.json'; 'apps/nonexistent/tsconfig.app.json';
return json; return json;

View File

@ -10,7 +10,11 @@ import { normalize, join, Path, dirname } from '@angular-devkit/core';
import { relative } from 'path'; import { relative } from 'path';
import { updateJsonInTree, readJsonInTree } from '@nrwl/workspace'; import {
updateJsonInTree,
readJsonInTree,
updateWorkspaceInTree
} from '@nrwl/workspace';
import { getWorkspacePath } from '@nrwl/workspace'; import { getWorkspacePath } from '@nrwl/workspace';
import { offsetFromRoot, addUpdateTask } from '@nrwl/workspace'; import { offsetFromRoot, addUpdateTask } from '@nrwl/workspace';
import { stripIndents } from '@angular-devkit/core/src/utils/literals'; import { stripIndents } from '@angular-devkit/core/src/utils/literals';
@ -159,9 +163,9 @@ function updateTsConfigs(project: any): Rule {
} }
function fixCypressConfigs(host: Tree, context: SchematicContext): Rule { function fixCypressConfigs(host: Tree, context: SchematicContext): Rule {
const angularJson = readJsonInTree(host, 'angular.json'); const workspaceJson = readJsonInTree(host, 'workspace.json');
return chain( return chain(
Object.entries<any>(angularJson.projects) Object.entries<any>(workspaceJson.projects)
.filter( .filter(
([key, project]) => ([key, project]) =>
project.architect.e2e && project.architect.e2e &&
@ -175,12 +179,12 @@ function fixCypressConfigs(host: Tree, context: SchematicContext): Rule {
} }
function fixCypressConfig(project: any, projectKey: string): Rule { function fixCypressConfig(project: any, projectKey: string): Rule {
return updateJsonInTree('angular.json', angularJson => { return updateWorkspaceInTree(workspaceJson => {
angularJson.projects[projectKey].architect.lint.options.tsConfig = join( workspaceJson.projects[projectKey].architect.lint.options.tsConfig = join(
project.root, project.root,
'tsconfig.e2e.json' 'tsconfig.e2e.json'
); );
return angularJson; return workspaceJson;
}); });
} }

View File

@ -5,15 +5,16 @@ import * as path from 'path';
import { serializeJson } from '@nrwl/workspace'; import { serializeJson } from '@nrwl/workspace';
import { readJsonInTree } from '@nrwl/workspace'; import { readJsonInTree } from '@nrwl/workspace';
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
describe('Update 7.5.0', () => { describe('Update 7.5.0', () => {
let initialTree: Tree; let initialTree: Tree;
let schematicRunner: SchematicTestRunner; let schematicRunner: SchematicTestRunner;
beforeEach(() => { beforeEach(() => {
initialTree = Tree.empty(); initialTree = createEmptyWorkspace(Tree.empty());
initialTree.create( initialTree.overwrite(
'package.json', 'package.json',
serializeJson({ serializeJson({
devDependencies: { devDependencies: {

View File

@ -9,6 +9,7 @@ import { join } from 'path';
import { serializeJson } from '@nrwl/workspace'; import { serializeJson } from '@nrwl/workspace';
import { readJsonInTree, updateJsonInTree } from '@nrwl/workspace'; import { readJsonInTree, updateJsonInTree } from '@nrwl/workspace';
import { stripIndents } from '@angular-devkit/core/src/utils/literals'; import { stripIndents } from '@angular-devkit/core/src/utils/literals';
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
const effectContents = ` const effectContents = `
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
@ -77,9 +78,9 @@ describe('Update 7.6.0', () => {
let schematicRunner: SchematicTestRunner; let schematicRunner: SchematicTestRunner;
beforeEach(() => { beforeEach(() => {
initialTree = new UnitTestTree(Tree.empty()); initialTree = createEmptyWorkspace(Tree.empty());
initialTree.create( initialTree.overwrite(
'package.json', 'package.json',
serializeJson({ serializeJson({
dependencies: { dependencies: {
@ -159,22 +160,22 @@ describe('Update 7.6.0', () => {
.toPromise(); .toPromise();
expect( expect(
readJsonInTree(result, 'angular.json').schematics[ readJsonInTree(result, 'workspace.json').schematics[
'@nrwl/schematics:library' '@nrwl/schematics:library'
].unitTestRunner ].unitTestRunner
).toEqual('karma'); ).toEqual('karma');
expect( expect(
readJsonInTree(result, 'angular.json').schematics[ readJsonInTree(result, 'workspace.json').schematics[
'@nrwl/schematics:application' '@nrwl/schematics:application'
].unitTestRunner ].unitTestRunner
).toEqual('karma'); ).toEqual('karma');
expect( expect(
readJsonInTree(result, 'angular.json').schematics[ readJsonInTree(result, 'workspace.json').schematics[
'@nrwl/schematics:application' '@nrwl/schematics:application'
].e2eTestRunner ].e2eTestRunner
).toEqual('protractor'); ).toEqual('protractor');
expect( expect(
readJsonInTree(result, 'angular.json').schematics[ readJsonInTree(result, 'workspace.json').schematics[
'@nrwl/schematics:node-application' '@nrwl/schematics:node-application'
].framework ].framework
).toEqual('express'); ).toEqual('express');

View File

@ -8,7 +8,8 @@ import {
formatFiles, formatFiles,
insert, insert,
readJsonInTree, readJsonInTree,
updateJsonInTree updateJsonInTree,
updateWorkspaceInTree
} from '@nrwl/workspace'; } from '@nrwl/workspace';
import { import {
getSourceNodes, getSourceNodes,
@ -371,7 +372,7 @@ const addDotEnv = updateJsonInTree('package.json', json => {
return json; return json;
}); });
const setDefaults = updateJsonInTree('angular.json', json => { const setDefaults = updateWorkspaceInTree(json => {
if (!json.schematics) { if (!json.schematics) {
json.schematics = {}; json.schematics = {};
} }

View File

@ -7,13 +7,14 @@ import {
import { join } from 'path'; import { join } from 'path';
import { readJsonInTree } from '@nrwl/workspace'; import { readJsonInTree } from '@nrwl/workspace';
import { serializeJson } from '@nrwl/workspace'; import { serializeJson } from '@nrwl/workspace';
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
describe('Update 7.7.0', () => { describe('Update 7.7.0', () => {
let initialTree: Tree; let initialTree: Tree;
let schematicRunner: SchematicTestRunner; let schematicRunner: SchematicTestRunner;
beforeEach(() => { beforeEach(() => {
initialTree = new UnitTestTree(Tree.empty()); initialTree = createEmptyWorkspace(Tree.empty());
schematicRunner = new SchematicTestRunner( schematicRunner = new SchematicTestRunner(
'@nrwl/schematics', '@nrwl/schematics',
@ -28,7 +29,7 @@ describe('Update 7.7.0', () => {
.toPromise(); .toPromise();
expect( expect(
readJsonInTree(result, 'angular.json').schematics[ readJsonInTree(result, 'workspace.json').schematics[
'@nrwl/schematics:library' '@nrwl/schematics:library'
].framework ].framework
).toEqual('angular'); ).toEqual('angular');
@ -37,7 +38,7 @@ describe('Update 7.7.0', () => {
describe('jest update', () => { describe('jest update', () => {
beforeEach(() => { beforeEach(() => {
initialTree.create( initialTree.overwrite(
'package.json', 'package.json',
serializeJson({ serializeJson({
devDependencies: { devDependencies: {

View File

@ -1,7 +1,7 @@
import { chain, Rule, Tree } from '@angular-devkit/schematics'; import { chain, Rule, Tree } from '@angular-devkit/schematics';
import { updateJsonInTree, insert } from '@nrwl/workspace'; import { updateJsonInTree, insert } from '@nrwl/workspace';
import { formatFiles } from '@nrwl/workspace'; import { formatFiles, updateWorkspaceInTree } from '@nrwl/workspace';
import * as ts from 'typescript'; import * as ts from 'typescript';
import { import {
@ -9,7 +9,7 @@ import {
ReplaceChange ReplaceChange
} from '@nrwl/workspace/src/utils/ast-utils'; } from '@nrwl/workspace/src/utils/ast-utils';
const setDefaults = updateJsonInTree('angular.json', json => { const setDefaults = updateWorkspaceInTree(json => {
if (!json.schematics) { if (!json.schematics) {
json.schematics = {}; json.schematics = {};
} }

View File

@ -3,14 +3,15 @@ import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
import { serializeJson } from '@nrwl/workspace'; import { serializeJson } from '@nrwl/workspace';
import * as path from 'path'; import * as path from 'path';
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
describe('Update 7.8.1', () => { describe('Update 7.8.1', () => {
let initialTree: Tree; let initialTree: Tree;
let schematicRunner: SchematicTestRunner; let schematicRunner: SchematicTestRunner;
beforeEach(() => { beforeEach(() => {
initialTree = Tree.empty(); initialTree = createEmptyWorkspace(Tree.empty());
initialTree.create( initialTree.overwrite(
'package.json', 'package.json',
serializeJson({ serializeJson({
scripts: {} scripts: {}

View File

@ -3,13 +3,14 @@ import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
import { updateJsonInTree, readJsonInTree } from '@nrwl/workspace'; import { updateJsonInTree, readJsonInTree } from '@nrwl/workspace';
import * as path from 'path'; import * as path from 'path';
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
describe('Update 8-0-0', () => { describe('Update 8-0-0', () => {
let initialTree: Tree; let initialTree: Tree;
let schematicRunner: SchematicTestRunner; let schematicRunner: SchematicTestRunner;
beforeEach(async () => { beforeEach(async () => {
initialTree = Tree.empty(); initialTree = createEmptyWorkspace(Tree.empty());
schematicRunner = new SchematicTestRunner( schematicRunner = new SchematicTestRunner(
'@nrwl/schematics', '@nrwl/schematics',
path.join(__dirname, '../migrations.json') path.join(__dirname, '../migrations.json')
@ -56,7 +57,7 @@ describe('Update 8-0-0', () => {
.toPromise(); .toPromise();
initialTree = await schematicRunner initialTree = await schematicRunner
.callRule( .callRule(
updateJsonInTree('angular.json', json => ({ updateJsonInTree('workspace.json', json => ({
projects: { projects: {
'my-app': { 'my-app': {
architect: { architect: {
@ -158,7 +159,7 @@ describe('Update 8-0-0', () => {
const tree = await schematicRunner const tree = await schematicRunner
.runSchematicAsync('update-8.0.0', {}, initialTree) .runSchematicAsync('update-8.0.0', {}, initialTree)
.toPromise(); .toPromise();
const { projects } = readJsonInTree(tree, 'angular.json'); const { projects } = readJsonInTree(tree, 'workspace.json');
const { architect } = projects['my-app']; const { architect } = projects['my-app'];
expect(architect.cypress.builder).toEqual('@nrwl/cypress:cypress'); expect(architect.cypress.builder).toEqual('@nrwl/cypress:cypress');
expect(architect.jest.builder).toEqual('@nrwl/jest:jest'); expect(architect.jest.builder).toEqual('@nrwl/jest:jest');
@ -281,7 +282,7 @@ describe('Update 8-0-0', () => {
.runSchematicAsync('update-8.0.0', {}, initialTree) .runSchematicAsync('update-8.0.0', {}, initialTree)
.toPromise(); .toPromise();
const defaultCollection = readJsonInTree(tree, 'angular.json').cli const defaultCollection = readJsonInTree(tree, 'workspace.json').cli
.defaultCollection; .defaultCollection;
expect(defaultCollection).toEqual('@nrwl/angular'); expect(defaultCollection).toEqual('@nrwl/angular');
}); });
@ -304,7 +305,7 @@ describe('Update 8-0-0', () => {
.runSchematicAsync('update-8.0.0', {}, initialTree) .runSchematicAsync('update-8.0.0', {}, initialTree)
.toPromise(); .toPromise();
const defaultCollection = readJsonInTree(tree, 'angular.json').cli const defaultCollection = readJsonInTree(tree, 'workspace.json').cli
.defaultCollection; .defaultCollection;
expect(defaultCollection).toEqual('@nrwl/react'); expect(defaultCollection).toEqual('@nrwl/react');
}); });
@ -326,7 +327,7 @@ describe('Update 8-0-0', () => {
.runSchematicAsync('update-8.0.0', {}, initialTree) .runSchematicAsync('update-8.0.0', {}, initialTree)
.toPromise(); .toPromise();
const defaultCollection = readJsonInTree(tree, 'angular.json').cli const defaultCollection = readJsonInTree(tree, 'workspace.json').cli
.defaultCollection; .defaultCollection;
expect(defaultCollection).toEqual('@nrwl/nest'); expect(defaultCollection).toEqual('@nrwl/nest');
}); });
@ -347,7 +348,7 @@ describe('Update 8-0-0', () => {
.runSchematicAsync('update-8.0.0', {}, initialTree) .runSchematicAsync('update-8.0.0', {}, initialTree)
.toPromise(); .toPromise();
const defaultCollection = readJsonInTree(tree, 'angular.json').cli const defaultCollection = readJsonInTree(tree, 'workspace.json').cli
.defaultCollection; .defaultCollection;
expect(defaultCollection).toEqual('@nrwl/express'); expect(defaultCollection).toEqual('@nrwl/express');
}); });
@ -368,7 +369,7 @@ describe('Update 8-0-0', () => {
.runSchematicAsync('update-8.0.0', {}, initialTree) .runSchematicAsync('update-8.0.0', {}, initialTree)
.toPromise(); .toPromise();
const defaultCollection = readJsonInTree(tree, 'angular.json').cli const defaultCollection = readJsonInTree(tree, 'workspace.json').cli
.defaultCollection; .defaultCollection;
expect(defaultCollection).toEqual('@nrwl/express'); expect(defaultCollection).toEqual('@nrwl/express');
}); });
@ -385,7 +386,7 @@ describe('Update 8-0-0', () => {
.toPromise(); .toPromise();
initialTree = await schematicRunner initialTree = await schematicRunner
.callRule( .callRule(
updateJsonInTree('angular.json', json => ({ updateJsonInTree('workspace.json', json => ({
...json, ...json,
projects: {} projects: {}
})), })),
@ -396,7 +397,7 @@ describe('Update 8-0-0', () => {
.runSchematicAsync('update-8.0.0', {}, initialTree) .runSchematicAsync('update-8.0.0', {}, initialTree)
.toPromise(); .toPromise();
const defaultCollection = readJsonInTree(tree, 'angular.json').cli const defaultCollection = readJsonInTree(tree, 'workspace.json').cli
.defaultCollection; .defaultCollection;
expect(defaultCollection).toEqual('@nrwl/workspace'); expect(defaultCollection).toEqual('@nrwl/workspace');
}); });

View File

@ -11,7 +11,8 @@ import {
insert, insert,
readJsonInTree, readJsonInTree,
updateJsonInTree, updateJsonInTree,
addUpdateTask addUpdateTask,
updateWorkspaceInTree
} from '@nrwl/workspace'; } from '@nrwl/workspace';
import { import {
createSourceFile, createSourceFile,
@ -31,7 +32,7 @@ function addDependencies() {
return (host: Tree, context: SchematicContext) => { return (host: Tree, context: SchematicContext) => {
const dependencies = readJsonInTree(host, 'package.json').dependencies; const dependencies = readJsonInTree(host, 'package.json').dependencies;
const builders = new Set<string>(); const builders = new Set<string>();
const projects = readJsonInTree(host, 'angular.json').projects; const projects = readJsonInTree(host, 'workspace.json').projects;
Object.values<any>(projects) Object.values<any>(projects)
.filter( .filter(
project => project =>
@ -110,7 +111,7 @@ const updateUpdateScript = updateJsonInTree('package.json', json => {
return json; return json;
}); });
const updateBuilders = updateJsonInTree('angular.json', json => { const updateBuilders = updateWorkspaceInTree(json => {
if (!json.projects) { if (!json.projects) {
return json; return json;
} }
@ -285,7 +286,7 @@ const updateDefaultCollection = (host: Tree, context: SchematicContext) => {
'package.json' 'package.json'
); );
return updateJsonInTree('angular.json', json => { return updateWorkspaceInTree(json => {
json.cli = json.cli || {}; json.cli = json.cli || {};
if (dependencies['@nrwl/angular']) { if (dependencies['@nrwl/angular']) {
json.cli.defaultCollection = '@nrwl/angular'; json.cli.defaultCollection = '@nrwl/angular';

View File

@ -1,7 +1,7 @@
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import { updateJsonFile, readCliConfigFile } from '@nrwl/workspace'; import { updateJsonFile, readWorkspaceConfigPath } from '@nrwl/workspace';
type Migration = { description: string; run(): void }; type Migration = { description: string; run(): void };
type MigrationName = { name: string; migration: Migration }; type MigrationName = { name: string; migration: Migration };
@ -32,7 +32,7 @@ updateLatestMigration();
console.log('All migrations run successfully'); console.log('All migrations run successfully');
function readLatestMigration(): string { function readLatestMigration(): string {
const angularCli = readCliConfigFile(); const angularCli = readWorkspaceConfigPath();
return angularCli.project.latestMigration; return angularCli.project.latestMigration;
} }

44
packages/tao/index.ts Normal file
View File

@ -0,0 +1,44 @@
#!/usr/bin/env node
import './src/compat/angular-cli-compat';
export async function invokeCommand(
command: string,
root: string,
commandArgs: string[]
) {
if (command === undefined) {
command = 'help';
}
switch (command) {
case 'new':
return (await import('./src/commands/generate')).taoNew(
root,
commandArgs
);
case 'generate':
case 'g':
return (await import('./src/commands/generate')).generate(
root,
commandArgs
);
case 'run':
case 'r':
return (await import('./src/commands/run')).run(root, commandArgs);
case 'help':
case '--help':
return (await import('./src/commands/help')).printHelp();
default:
// this is to make `tao test mylib` same as `tao run mylib:test`
return (await import('./src/commands/run')).run(root, [
`${commandArgs[0]}:${command}`,
...commandArgs.slice(1)
]);
}
}
export async function invokeCli(root: string, args: string[]) {
const [command, ...commandArgs] = args;
process.exit(await invokeCommand(command, root, commandArgs));
}
invokeCli(process.cwd(), process.argv.slice(2));

41
packages/tao/package.json Normal file
View File

@ -0,0 +1,41 @@
{
"name": "@nrwl/tao",
"version": "0.0.1",
"description": "CLI for generating code and running commands",
"repository": {
"type": "git",
"url": "git+https://github.com/nrwl/nx.git"
},
"keywords": [
"Monorepo",
"Angular",
"React",
"Web",
"Node",
"Nest",
"Jest",
"Cypress",
"CLI"
],
"main": "index.js",
"types": "index.d.ts",
"author": "Victor Savkin",
"license": "MIT",
"bugs": {
"url": "https://github.com/nrwl/nx/issues"
},
"bin": {
"tao": "./index.js"
},
"homepage": "https://nx.dev",
"peerDependencies": {
"@nrwl/workspace": "*"
},
"dependencies": {
"@angular-devkit/schematics": "8.1.1",
"@angular-devkit/core": "8.1.1",
"@angular-devkit/architect": "0.801.1",
"inquirer": "^6.3.1",
"minimist": "^1.2.0"
}
}

View File

@ -0,0 +1,354 @@
import {
convertToCamelCase,
handleErrors,
Schema,
coerceTypes
} from '../shared/params';
import {
JsonObject,
logging,
normalize,
schema,
tags,
terminal,
virtualFs,
experimental
} from '@angular-devkit/core';
import { DryRunEvent, HostTree, Schematic } from '@angular-devkit/schematics';
import { NodeJsSyncHost } from '@angular-devkit/core/node';
import { NodeWorkflow } from '@angular-devkit/schematics/tools';
import * as inquirer from 'inquirer';
import { logger } from '../shared/logger';
import { printHelp, commandName } from '../shared/print-help';
import * as fs from 'fs';
import minimist = require('minimist');
interface GenerateOptions {
collectionName: string;
schematicName: string;
schematicOptions: { [k: string]: string };
help: boolean;
debug: boolean;
dryRun: boolean;
force: boolean;
interactive: boolean;
defaults: boolean;
}
function throwInvalidInvocation() {
throw new Error(
`Specify the schematic name (e.g., ${commandName} generate collection-name:schematic-name)`
);
}
function parseGenerateOpts(
args: string[],
mode: 'generate' | 'new',
defaultCollection: string | null
): GenerateOptions {
const schematicOptions = convertToCamelCase(
minimist(args, {
boolean: ['help', 'dryRun', 'debug', 'force', 'interactive'],
alias: {
dryRun: 'dry-run'
},
default: {
debug: false,
dryRun: false,
interactive: true
}
})
);
let collectionName = null;
let schematicName = null;
if (mode === 'generate') {
if (!schematicOptions['_'] || schematicOptions['_'].length === 0) {
throwInvalidInvocation();
}
[collectionName, schematicName] = schematicOptions['_'].shift()!.split(':');
if (!schematicName) {
schematicName = collectionName;
collectionName = defaultCollection;
}
} else {
collectionName = schematicOptions.collection;
schematicName = '';
}
if (!collectionName) {
throwInvalidInvocation();
}
const res = {
collectionName,
schematicName,
schematicOptions,
help: schematicOptions.help,
debug: schematicOptions.debug,
dryRun: schematicOptions.dryRun,
force: schematicOptions.force,
interactive: schematicOptions.interactive,
defaults: schematicOptions.defaults
};
delete schematicOptions.debug;
delete schematicOptions.dryRun;
delete schematicOptions.force;
delete schematicOptions.interactive;
delete schematicOptions.defaults;
delete schematicOptions.help;
delete schematicOptions['--'];
return res;
}
function createRecorder(record: any, logger: logging.Logger) {
return (event: DryRunEvent) => {
const eventPath = event.path.startsWith('/')
? event.path.substr(1)
: event.path;
if (event.kind === 'error') {
record.error = true;
logger.warn(
`ERROR! ${eventPath} ${
event.description == 'alreadyExist'
? 'already exists'
: 'does not exist.'
}.`
);
} else if (event.kind === 'update') {
record.loggingQueue.push(
tags.oneLine`${terminal.white('UPDATE')} ${eventPath} (${
event.content.length
} bytes)`
);
} else if (event.kind === 'create') {
record.loggingQueue.push(
tags.oneLine`${terminal.green('CREATE')} ${eventPath} (${
event.content.length
} bytes)`
);
} else if (event.kind === 'delete') {
record.loggingQueue.push(`${terminal.yellow('DELETE')} ${eventPath}`);
} else if (event.kind === 'rename') {
record.loggingQueue.push(
`${terminal.blue('RENAME')} ${eventPath} => ${event.to}`
);
}
};
}
function createWorkflow(
fsHost: virtualFs.Host<fs.Stats>,
root: string,
opts: GenerateOptions
) {
const workflow = new NodeWorkflow(fsHost, {
force: opts.force,
dryRun: opts.dryRun,
packageManager: 'yarn',
root: normalize(root)
});
const _params = opts.schematicOptions._;
delete opts.schematicOptions._;
workflow.registry.addSmartDefaultProvider('argv', (schema: JsonObject) => {
if ('index' in schema) {
return _params[Number(schema['index'])];
} else {
return _params;
}
});
if (opts.interactive !== false && isTTY()) {
workflow.registry.usePromptProvider(
(definitions: Array<schema.PromptDefinition>) => {
const questions: inquirer.Questions = definitions.map(definition => {
const question = {
name: definition.id,
message: definition.message,
default: definition.default as any
} as any;
const validator = definition.validator;
if (validator) {
question.validate = (input: any) => validator(input);
}
switch (definition.type) {
case 'confirmation':
question.type = 'confirm';
break;
case 'list':
question.type = !!definition.multiselect ? 'checkbox' : 'list';
question.choices =
definition.items &&
definition.items.map(item => {
if (typeof item == 'string') {
return item;
} else {
return {
name: item.label,
value: item.value
};
}
});
break;
default:
question.type = definition.type;
break;
}
return question;
});
return inquirer.prompt(questions);
}
);
}
return workflow;
}
function getCollection(workflow: NodeWorkflow, name: string) {
const collection = workflow.engine.createCollection(name);
if (!collection) throw new Error(`Cannot find collection '${name}'`);
return collection;
}
function printGenHelp(opts: GenerateOptions, schema: Schema) {
printHelp(
`${commandName} generate ${opts.collectionName}:${opts.schematicName}`,
schema
);
}
async function getSchematicDefaults(
root: string,
collection: string,
schematic: string
) {
const workspace = await new experimental.workspace.Workspace(
normalize(root) as any,
new NodeJsSyncHost()
)
.loadWorkspaceFromHost('workspace.json' as any)
.toPromise();
let result = {};
if (workspace.getSchematics()) {
const schematicObject = workspace.getSchematics()[
`${collection}:${schematic}`
];
if (schematicObject) {
result = { ...result, ...(schematicObject as {}) };
}
const collectionObject = workspace.getSchematics()[collection];
if (
typeof collectionObject == 'object' &&
!Array.isArray(collectionObject)
) {
result = { ...result, ...(collectionObject[schematic] as {}) };
}
}
return result;
}
async function runSchematic(
root: string,
workflow: NodeWorkflow,
logger: logging.Logger,
opts: GenerateOptions,
schematic: Schematic<any, any>
): Promise<number> {
const flattenedSchema = await workflow.registry
.flatten(schematic.description.schemaJson!)
.toPromise();
if (opts.help) {
printGenHelp(opts, flattenedSchema as any);
} else {
const defaults =
opts.schematicName === 'tao-new'
? {}
: await getSchematicDefaults(
root,
opts.collectionName,
opts.schematicName
);
const record = { loggingQueue: [] as string[], error: false };
workflow.reporter.subscribe(createRecorder(record, logger));
const schematicOptions = coerceTypes(
opts.schematicOptions,
flattenedSchema as any
);
await workflow
.execute({
collection: opts.collectionName,
schematic: opts.schematicName,
options: { ...defaults, ...schematicOptions },
debug: opts.debug,
logger
})
.toPromise();
if (!record.error) {
record.loggingQueue.forEach(log => logger.info(log));
}
if (opts.dryRun) {
logger.warn(`\nNOTE: The "dryRun" flag means no changes were made.`);
}
}
return 0;
}
export async function generate(root: string, args: string[]) {
return handleErrors(logger, async () => {
const fsHost = new virtualFs.ScopedHost(
new NodeJsSyncHost(),
normalize(root)
);
const opts = parseGenerateOpts(
args,
'generate',
await readDefaultCollection(fsHost)
);
const workflow = createWorkflow(fsHost, root, opts);
const collection = getCollection(workflow, opts.collectionName);
const schematic = collection.createSchematic(opts.schematicName, true);
return runSchematic(
root,
workflow,
logger,
{ ...opts, schematicName: schematic.description.name },
schematic
);
});
}
async function readDefaultCollection(host: virtualFs.Host<any>) {
const workspaceJson = JSON.parse(
new HostTree(host).read('workspace.json')!.toString()
);
return workspaceJson.cli ? workspaceJson.cli.defaultCollection : null;
}
export async function taoNew(root: string, args: string[]) {
return handleErrors(logger, async () => {
const fsHost = new virtualFs.ScopedHost(
new NodeJsSyncHost(),
normalize(root)
);
const opts = parseGenerateOpts(args, 'new', null);
const workflow = createWorkflow(fsHost, root, opts);
const collection = getCollection(workflow, opts.collectionName);
const schematic = collection.createSchematic('tao-new', true);
return runSchematic(
root,
workflow,
logger,
{ ...opts, schematicName: schematic.description.name },
schematic
);
});
}
function isTTY(): boolean {
return !!process.stdout.isTTY && !!process.env['CI'];
}

View File

@ -0,0 +1,35 @@
import { tags } from '@angular-devkit/core';
import { logger } from '../shared/logger';
import { toolDescription, commandName } from '../shared/print-help';
import { terminal } from '@angular-devkit/core';
export function printHelp() {
logger.info(tags.stripIndent`
${terminal.bold(toolDescription)}
${terminal.bold('Create a new project.')}
${commandName} new ${terminal.grey(
'[project-name] [--collection=schematic-collection] [options, ...]'
)}
${terminal.bold('Generate code.')}
${commandName} generate ${terminal.grey(
'[schematic-collection:][schematic] [options, ...]'
)}
${commandName} g ${terminal.grey(
'[schematic-collection:][schematic] [options, ...]'
)}
${terminal.bold('Run target.')}
${commandName} run ${terminal.grey(
'[project][:target][:configuration] [options, ...]'
)}
${commandName} r ${terminal.grey(
'[project][:target][:configuration] [options, ...]'
)}
You can also use the infix notation to run a target:
${commandName} [target] [project] [options, ...]
`);
return 0;
}

View File

@ -0,0 +1,123 @@
import {
convertToCamelCase,
handleErrors,
Schema,
coerceTypes
} from '../shared/params';
import {
experimental,
json,
normalize,
schema,
tags
} from '@angular-devkit/core';
import { NodeJsSyncHost } from '@angular-devkit/core/node';
import { WorkspaceNodeModulesArchitectHost } from '@angular-devkit/architect/node';
import { Architect } from '@angular-devkit/architect';
import { logger } from '../shared/logger';
import minimist = require('minimist');
import { printHelp, commandName } from '../shared/print-help';
export interface RunOptions {
project: string;
target: string;
configuration: string;
help: boolean;
runOptions: { [k: string]: any };
}
function throwInvalidInvocation() {
throw new Error(
`Specify the project name and the target (e.g., ${commandName} run proj:build)`
);
}
function parseRunOpts(
args: string[],
defaultProjectName: string | null
): RunOptions {
const runOptions = convertToCamelCase(
minimist(args, {
boolean: ['help', 'prod'],
string: ['configuration', 'project']
})
);
const help = runOptions.help;
if (!runOptions._ || !runOptions._[0]) {
throwInvalidInvocation();
}
let [project, target, configuration] = runOptions._[0].split(':');
if (!project && defaultProjectName) project = defaultProjectName;
if (!project || !target) {
throwInvalidInvocation();
}
if (runOptions.configuration) {
configuration = runOptions.configuration;
}
if (runOptions.prod) {
configuration = 'production';
}
if (runOptions.project) {
project = runOptions.project;
}
const res = { project, target, configuration, help, runOptions };
delete runOptions['help'];
delete runOptions['_'];
delete runOptions['configuration'];
delete runOptions['prod'];
delete runOptions['project'];
return res;
}
function printRunHelp(opts: RunOptions, schema: Schema) {
printHelp(`${commandName} run ${opts.project}:${opts.target}`, schema);
}
export async function run(root: string, args: string[]) {
return handleErrors(logger, async () => {
const fsHost = new NodeJsSyncHost();
const workspace = await new experimental.workspace.Workspace(
normalize(root) as any,
fsHost
)
.loadWorkspaceFromHost('workspace.json' as any)
.toPromise();
const opts = parseRunOpts(args, workspace.getDefaultProjectName());
const registry = new json.schema.CoreSchemaRegistry();
registry.addPostTransform(schema.transforms.addUndefinedDefaults);
const architectHost = new WorkspaceNodeModulesArchitectHost(
workspace,
root
);
const architect = new Architect(architectHost, registry);
const builderConf = await architectHost.getBuilderNameForTarget({
project: opts.project,
target: opts.target
});
const builderDesc = await architectHost.resolveBuilder(builderConf);
const flattenedSchema = await registry
.flatten(builderDesc.optionSchema! as json.JsonObject)
.toPromise();
if (opts.help) {
printRunHelp(opts, flattenedSchema as any);
return 0;
} else {
const runOptions = coerceTypes(opts.runOptions, flattenedSchema as any);
const run = await architect.scheduleTarget(
{
project: opts.project,
target: opts.target,
configuration: opts.configuration
},
runOptions,
{ logger }
);
const result = await run.output.toPromise();
await run.stop();
return result.success ? 0 : 1;
}
});
}

View File

@ -0,0 +1,24 @@
const Module = require('module');
const originalRequire = Module.prototype.require;
Module.prototype.require = function() {
const result = originalRequire.apply(this, arguments);
if (arguments[0].startsWith('@angular-devkit/core')) {
const Workspace = originalRequire.apply(this, [
`@angular-devkit/core/src/experimental/workspace`
]).Workspace;
Workspace._workspaceFileNames = [
'workspace.json',
...Workspace._workspaceFileNames
];
const core = originalRequire.apply(this, [
`@angular-devkit/core/src/workspace/core`
]);
core._test_addWorkspaceFile('workspace.json', core.WorkspaceFormat.JSON);
}
return result;
};
try {
require('@angular-devkit/build-angular/src/utils/version').Version.assertCompatibleAngularVersion = () => {};
} catch (e) {}

View File

@ -0,0 +1,13 @@
import { createConsoleLogger } from '@angular-devkit/core/node';
import { terminal } from '@angular-devkit/core';
export const logger = createConsoleLogger(
false,
process.stdout,
process.stderr,
{
warn: s => terminal.bold(terminal.yellow(s)),
error: s => terminal.bold(terminal.red(s)),
fatal: s => terminal.bold(terminal.red(s))
}
);

View File

@ -0,0 +1,35 @@
import { convertToCamelCase } from './params';
describe('params', () => {
describe('convertToCamelCase', () => {
it('should convert dash case to camel case', () => {
expect(
convertToCamelCase({
'one-two': 1
})
).toEqual({
oneTwo: 1
});
});
it('should not convert camel case', () => {
expect(
convertToCamelCase({
oneTwo: 1
})
).toEqual({
oneTwo: 1
});
});
it('should handle mixed case', () => {
expect(
convertToCamelCase({
'one-Two': 1
})
).toEqual({
oneTwo: 1
});
});
});
});

View File

@ -0,0 +1,50 @@
import { logging } from '@angular-devkit/core';
import { UnsuccessfulWorkflowExecution } from '@angular-devkit/schematics';
export type Schema = {
properties: { [p: string]: any };
required: string[];
description: string;
};
export async function handleErrors(logger: logging.Logger, fn: Function) {
try {
return await fn();
} catch (err) {
if (err instanceof UnsuccessfulWorkflowExecution) {
logger.fatal('The Schematic workflow failed. See above.');
} else {
logger.fatal(err.message);
}
return 1;
}
}
export function convertToCamelCase(parsed: {
[k: string]: any;
}): { [k: string]: any } {
return Object.keys(parsed).reduce(
(m, c) => ({ ...m, [camelCase(c)]: parsed[c] }),
{}
);
}
function camelCase(input: string): string {
if (input.indexOf('-') > 1) {
return input
.toLowerCase()
.replace(/-(.)/g, (match, group1) => group1.toUpperCase());
} else {
return input;
}
}
export function coerceTypes(opts: { [k: string]: any }, schema: Schema) {
Object.keys(opts).forEach(k => {
if (schema.properties[k] && schema.properties[k].type == 'boolean') {
opts[k] = opts[k] === true || opts[k] === 'true';
} else if (schema.properties[k] && schema.properties[k].type == 'number') {
opts[k] = Number(opts[k]);
}
});
return opts;
}

View File

@ -0,0 +1,44 @@
import { Schema } from './params';
import { logger } from './logger';
import { tags } from '@angular-devkit/core';
import { terminal } from '@angular-devkit/core';
export function printHelp(header: string, schema: Schema) {
const allPositional = Object.keys(schema.properties).filter(key => {
const p = schema.properties[key];
return p['$default'] && p['$default']['$source'] === 'argv';
});
const positional = allPositional.length > 0 ? ` [${allPositional[0]}]` : '';
const args = Object.keys(schema.properties)
.map(name => {
const d = schema.properties[name];
const def = d.default ? ` (default: ${d.default})` : '';
return formatOption(name, `${d.description}${def}`);
})
.join('\n');
logger.info(tags.stripIndent`
${terminal.bold(header + positional + ' [options,...]')}
${terminal.bold('Options')}:
${args}
${formatOption('help', 'Show available options for project target.')}
`);
}
function formatOption(name: string, description: string) {
return ` --${(name + ' ').substr(0, 22)}${terminal.grey(
description
)}`;
}
export let commandName = 'nx';
export let toolDescription = 'Nx - Extensible Dev Tools for Monorepos.';
export function setCommandNameAndDescription(
name: string,
description: string
) {
commandName = name;
toolDescription = description;
}

View File

@ -13,15 +13,15 @@ describe('app', () => {
}); });
describe('not nested', () => { describe('not nested', () => {
it('should update angular.json', async () => { it('should update workspace.json', async () => {
const tree = await runSchematic('app', { name: 'myApp' }, appTree); const tree = await runSchematic('app', { name: 'myApp' }, appTree);
const angularJson = readJsonInTree(tree, '/angular.json'); const workspaceJson = readJsonInTree(tree, '/workspace.json');
expect(angularJson.projects['my-app'].root).toEqual('apps/my-app'); expect(workspaceJson.projects['my-app'].root).toEqual('apps/my-app');
expect(angularJson.projects['my-app-e2e'].root).toEqual( expect(workspaceJson.projects['my-app-e2e'].root).toEqual(
'apps/my-app-e2e' 'apps/my-app-e2e'
); );
expect(angularJson.defaultProject).toEqual('my-app'); expect(workspaceJson.defaultProject).toEqual('my-app');
}); });
it('should update nx.json', async () => { it('should update nx.json', async () => {
@ -78,18 +78,18 @@ describe('app', () => {
}); });
describe('nested', () => { describe('nested', () => {
it('should update angular.json', async () => { it('should update workspace.json', async () => {
const tree = await runSchematic( const tree = await runSchematic(
'app', 'app',
{ name: 'myApp', directory: 'myDir' }, { name: 'myApp', directory: 'myDir' },
appTree appTree
); );
const angularJson = readJsonInTree(tree, '/angular.json'); const workspaceJson = readJsonInTree(tree, '/workspace.json');
expect(angularJson.projects['my-dir-my-app'].root).toEqual( expect(workspaceJson.projects['my-dir-my-app'].root).toEqual(
'apps/my-dir/my-app' 'apps/my-dir/my-app'
); );
expect(angularJson.projects['my-dir-my-app-e2e'].root).toEqual( expect(workspaceJson.projects['my-dir-my-app-e2e'].root).toEqual(
'apps/my-dir/my-app-e2e' 'apps/my-dir/my-app-e2e'
); );
}); });
@ -217,8 +217,8 @@ describe('app', () => {
}, },
appTree appTree
); );
const angularJson = readJsonInTree(tree, 'angular.json'); const workspaceJson = readJsonInTree(tree, 'workspace.json');
const architectConfig = angularJson.projects['my-app'].architect; const architectConfig = workspaceJson.projects['my-app'].architect;
expect(architectConfig.build.builder).toEqual('@nrwl/web:build'); expect(architectConfig.build.builder).toEqual('@nrwl/web:build');
expect(architectConfig.build.options).toEqual({ expect(architectConfig.build.options).toEqual({
assets: ['apps/my-app/src/favicon.ico', 'apps/my-app/src/assets'], assets: ['apps/my-app/src/favicon.ico', 'apps/my-app/src/assets'],
@ -262,8 +262,8 @@ describe('app', () => {
}, },
appTree appTree
); );
const angularJson = readJsonInTree(tree, 'angular.json'); const workspaceJson = readJsonInTree(tree, 'workspace.json');
const architectConfig = angularJson.projects['my-app'].architect; const architectConfig = workspaceJson.projects['my-app'].architect;
expect(architectConfig.serve.builder).toEqual('@nrwl/web:dev-server'); expect(architectConfig.serve.builder).toEqual('@nrwl/web:dev-server');
expect(architectConfig.serve.options).toEqual({ expect(architectConfig.serve.options).toEqual({
buildTarget: 'my-app:build' buildTarget: 'my-app:build'
@ -281,11 +281,12 @@ describe('app', () => {
}, },
appTree appTree
); );
const angularJson = readJsonInTree(tree, 'angular.json'); const workspaceJson = readJsonInTree(tree, 'workspace.json');
expect(angularJson.projects['my-app'].architect.lint).toEqual({
expect(workspaceJson.projects['my-app'].architect.lint).toEqual({
builder: '@angular-devkit/build-angular:tslint', builder: '@angular-devkit/build-angular:tslint',
options: { options: {
exclude: ['**/node_modules/**'], exclude: ['**/node_modules/**', '!apps/my-app/**'],
tsConfig: [ tsConfig: [
'apps/my-app/tsconfig.app.json', 'apps/my-app/tsconfig.app.json',
'apps/my-app/tsconfig.spec.json' 'apps/my-app/tsconfig.spec.json'
@ -318,10 +319,10 @@ describe('app', () => {
expect(tree.exists('apps/my-app/src/app/app.spec.ts')).toBeFalsy(); expect(tree.exists('apps/my-app/src/app/app.spec.ts')).toBeFalsy();
expect(tree.exists('apps/my-app/tsconfig.spec.json')).toBeFalsy(); expect(tree.exists('apps/my-app/tsconfig.spec.json')).toBeFalsy();
expect(tree.exists('apps/my-app/jest.config.js')).toBeFalsy(); expect(tree.exists('apps/my-app/jest.config.js')).toBeFalsy();
const angularJson = readJsonInTree(tree, 'angular.json'); const workspaceJson = readJsonInTree(tree, 'workspace.json');
expect(angularJson.projects['my-app'].architect.test).toBeUndefined(); expect(workspaceJson.projects['my-app'].architect.test).toBeUndefined();
expect( expect(
angularJson.projects['my-app'].architect.lint.options.tsConfig workspaceJson.projects['my-app'].architect.lint.options.tsConfig
).toEqual(['apps/my-app/tsconfig.app.json']); ).toEqual(['apps/my-app/tsconfig.app.json']);
}); });
}); });
@ -334,8 +335,8 @@ describe('app', () => {
appTree appTree
); );
expect(tree.exists('apps/my-app-e2e')).toBeFalsy(); expect(tree.exists('apps/my-app-e2e')).toBeFalsy();
const angularJson = readJsonInTree(tree, 'angular.json'); const workspaceJson = readJsonInTree(tree, 'workspace.json');
expect(angularJson.projects['my-app-e2e']).toBeUndefined(); expect(workspaceJson.projects['my-app-e2e']).toBeUndefined();
}); });
}); });
}); });

View File

@ -21,7 +21,10 @@ import {
names, names,
offsetFromRoot, offsetFromRoot,
getNpmScope, getNpmScope,
formatFiles formatFiles,
updateWorkspaceInTree,
generateProjectLint,
addGlobalLint
} from '@nrwl/workspace'; } from '@nrwl/workspace';
import ngAdd from '../ng-add/ng-add'; import ngAdd from '../ng-add/ng-add';
@ -58,7 +61,7 @@ function updateNxJson(options: NormalizedSchema): Rule {
} }
function addProject(options: NormalizedSchema): Rule { function addProject(options: NormalizedSchema): Rule {
return updateJsonInTree('angular.json', json => { return updateWorkspaceInTree(json => {
const architect: { [key: string]: any } = {}; const architect: { [key: string]: any } = {};
architect.build = { architect.build = {
@ -122,15 +125,11 @@ function addProject(options: NormalizedSchema): Rule {
} }
}; };
architect.lint = { architect.lint = generateProjectLint(
builder: '@angular-devkit/build-angular:tslint', normalize(options.appProjectRoot),
options: { join(normalize(options.appProjectRoot), 'tsconfig.app.json'),
tsConfig: [ options.linter
join(normalize(options.appProjectRoot), 'tsconfig.app.json') );
],
exclude: ['**/node_modules/**']
}
};
json.projects[options.projectName] = { json.projects[options.projectName] = {
root: options.appProjectRoot, root: options.appProjectRoot,
@ -154,6 +153,7 @@ export default function(schema: Schema): Rule {
ngAdd({ ngAdd({
skipFormat: true skipFormat: true
}), }),
addGlobalLint(options.linter),
createApplicationFiles(options), createApplicationFiles(options),
updateNxJson(options), updateNxJson(options),
addProject(options), addProject(options),

View File

@ -1,5 +1,3 @@
import { E2eTestRunner, UnitTestRunner } from '../../utils/test-runners';
export interface Schema { export interface Schema {
name: string; name: string;
prefix?: string; prefix?: string;
@ -7,6 +5,7 @@ export interface Schema {
skipFormat: boolean; skipFormat: boolean;
directory?: string; directory?: string;
tags?: string; tags?: string;
unitTestRunner: UnitTestRunner; unitTestRunner: 'jest' | 'none';
e2eTestRunner: E2eTestRunner; e2eTestRunner: 'cypress' | 'none';
linter: 'eslint' | 'tslint';
} }

View File

@ -42,6 +42,12 @@
] ]
} }
}, },
"linter": {
"description": "The tool to use for running lint checks.",
"type": "string",
"enum": ["eslint", "tslint"],
"default": "tslint"
},
"skipFormat": { "skipFormat": {
"description": "Skip formatting files", "description": "Skip formatting files",
"type": "boolean", "type": "boolean",

View File

@ -23,13 +23,13 @@ describe('ng-add', () => {
describe('defaultCollection', () => { describe('defaultCollection', () => {
it('should be set if none was set before', async () => { it('should be set if none was set before', async () => {
const result = await runSchematic('ng-add', {}, tree); const result = await runSchematic('ng-add', {}, tree);
const angularJson = readJsonInTree(result, 'angular.json'); const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(angularJson.cli.defaultCollection).toEqual('@nrwl/web'); expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/web');
}); });
it('should be set if @nrwl/workspace was set before', async () => { it('should be set if @nrwl/workspace was set before', async () => {
tree = await callRule( tree = await callRule(
updateJsonInTree('angular.json', json => { updateJsonInTree('workspace.json', json => {
json.cli = { json.cli = {
defaultCollection: '@nrwl/workspace' defaultCollection: '@nrwl/workspace'
}; };
@ -39,13 +39,13 @@ describe('ng-add', () => {
tree tree
); );
const result = await runSchematic('ng-add', {}, tree); const result = await runSchematic('ng-add', {}, tree);
const angularJson = readJsonInTree(result, 'angular.json'); const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(angularJson.cli.defaultCollection).toEqual('@nrwl/web'); expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/web');
}); });
it('should not be set if something else was set before', async () => { it('should not be set if something else was set before', async () => {
tree = await callRule( tree = await callRule(
updateJsonInTree('angular.json', json => { updateJsonInTree('workspace.json', json => {
json.cli = { json.cli = {
defaultCollection: '@nrwl/angular' defaultCollection: '@nrwl/angular'
}; };
@ -55,8 +55,8 @@ describe('ng-add', () => {
tree tree
); );
const result = await runSchematic('ng-add', {}, tree); const result = await runSchematic('ng-add', {}, tree);
const angularJson = readJsonInTree(result, 'angular.json'); const workspaceJson = readJsonInTree(result, 'workspace.json');
expect(angularJson.cli.defaultCollection).toEqual('@nrwl/angular'); expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/angular');
}); });
}); });
}); });

View File

@ -26,12 +26,12 @@ export function _findDefaultServePath(
/^(\w+:)?\/\//.test(baseHref || '') || /^(\w+:)?\/\//.test(baseHref || '') ||
/^(\w+:)?\/\//.test(deployUrl || '') /^(\w+:)?\/\//.test(deployUrl || '')
) { ) {
// If baseHref or deployUrl is absolute, unsupported by ng serve // If baseHref or deployUrl is absolute, unsupported by nx serve
return null; return null;
} }
// normalize baseHref // normalize baseHref
// for ng serve the starting base is always `/` so a relative // for nx serve the starting base is always `/` so a relative
// and root relative value are identical // and root relative value are identical
const baseHrefParts = (baseHref || '').split('/').filter(part => part !== ''); const baseHrefParts = (baseHref || '').split('/').filter(part => part !== '');
if (baseHref && !baseHref.endsWith('/')) { if (baseHref && !baseHref.endsWith('/')) {
@ -42,7 +42,7 @@ export function _findDefaultServePath(
if (deployUrl && deployUrl[0] === '/') { if (deployUrl && deployUrl[0] === '/') {
if (baseHref && baseHref[0] === '/' && normalizedBaseHref !== deployUrl) { if (baseHref && baseHref[0] === '/' && normalizedBaseHref !== deployUrl) {
// If baseHref and deployUrl are root relative and not equivalent, unsupported by ng serve // If baseHref and deployUrl are root relative and not equivalent, unsupported by nx serve
return null; return null;
} }

View File

@ -30,6 +30,13 @@
"hidden": true "hidden": true
}, },
"tao-new": {
"factory": "./src/schematics/tao-new/tao-new",
"schema": "./src/schematics/tao-new/schema.json",
"description": "Create a workspace",
"hidden": true
},
"library": { "library": {
"factory": "./src/schematics/library/library", "factory": "./src/schematics/library/library",
"schema": "./src/schematics/library/schema.json", "schema": "./src/schematics/library/schema.json",

Some files were not shown because too many files have changed in this diff Show More