diff --git a/.prettierignore b/.prettierignore index 274f5e1d335d5..02fb37a9ad485 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,13 +7,14 @@ packages/workspace/src/generators/**/files/**/*.json packages/workspace/src/core/dep-graph/vendor.js packages/angular/src/schematics/**/files/**/*.json packages/angular/src/migrations/**/files/**/*.json -packages/web/src/schematics/**/files/**/*.json +packages/web/src/generators/**/files/**/*.json packages/node/src/schematics/**/files/**/*.json packages/express/src/schematics/**/files/**/*.json packages/nest/src/schematics/**/files/**/*.json packages/react/src/schematics/**/files/**/*.json packages/jest/src/schematics/**/files/**/*.json packages/**/schematics/**/files/**/*.html +packages/**/generators/**/files/**/*.html /.vscode /.idea /.github diff --git a/packages/cypress/collection.json b/packages/cypress/collection.json index 22c61bb180be0..b6c1f9a81f2a7 100644 --- a/packages/cypress/collection.json +++ b/packages/cypress/collection.json @@ -3,7 +3,7 @@ "version": "0.1", "schematics": { "init": { - "factory": "./src/generators/init/init#initSchematic", + "factory": "./src/generators/init/init#cypressInitSchematic", "schema": "./src/generators/init/schema.json", "description": "Initialize the @nrwl/cypress plugin", "aliases": ["ng-add"], @@ -18,7 +18,7 @@ }, "generators": { "init": { - "factory": "./src/generators/init/init#initGenerator", + "factory": "./src/generators/init/init#cypressInitGenerator", "schema": "./src/generators/init/schema.json", "description": "Initialize the @nrwl/cypress plugin", "aliases": ["ng-add"], diff --git a/packages/cypress/index.ts b/packages/cypress/index.ts index c4ab940d0d664..1a6175679a08e 100644 --- a/packages/cypress/index.ts +++ b/packages/cypress/index.ts @@ -1 +1,2 @@ export { cypressProjectGenerator } from './src/generators/cypress-project/cypress-project'; +export { cypressInitGenerator } from './src/generators/init/init'; diff --git a/packages/cypress/src/generators/cypress-project/cypress-project.ts b/packages/cypress/src/generators/cypress-project/cypress-project.ts index d70a13e0c4a1a..f64f9ad628a25 100644 --- a/packages/cypress/src/generators/cypress-project/cypress-project.ts +++ b/packages/cypress/src/generators/cypress-project/cypress-project.ts @@ -76,7 +76,7 @@ async function addLinter(host: Tree, options: CypressProjectSchema) { ], }); - if (options.linter !== Linter.EsLint) { + if (!options.linter || options.linter !== Linter.EsLint) { return installTask; } @@ -127,6 +127,8 @@ function normalizeOptions(host: Tree, options: Schema): CypressProjectSchema { options.name ) : joinPathFragments(appsDir, options.name); + + options.linter = options.linter || Linter.EsLint; return { ...options, projectName, diff --git a/packages/cypress/src/generators/cypress-project/schema.d.ts b/packages/cypress/src/generators/cypress-project/schema.d.ts index 3c358be911f0a..8ed33c44570f6 100644 --- a/packages/cypress/src/generators/cypress-project/schema.d.ts +++ b/packages/cypress/src/generators/cypress-project/schema.d.ts @@ -4,7 +4,7 @@ export interface Schema { project?: string; name: string; directory?: string; - linter: Linter; + linter?: Linter; js?: boolean; skipFormat?: boolean; } diff --git a/packages/cypress/src/generators/init/init.spec.ts b/packages/cypress/src/generators/init/init.spec.ts index f3378a02035f0..1ee6be5542dfd 100644 --- a/packages/cypress/src/generators/init/init.spec.ts +++ b/packages/cypress/src/generators/init/init.spec.ts @@ -2,7 +2,7 @@ import { readJson, Tree, updateJson } from '@nrwl/devkit'; import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; import { cypressVersion } from '../../utils/versions'; -import initGenerator from './init'; +import { cypressInitGenerator } from './init'; describe('init', () => { let tree: Tree; @@ -21,7 +21,7 @@ describe('init', () => { json.devDependencies[existing] = existingVersion; return json; }); - initGenerator(tree); + cypressInitGenerator(tree); const packageJson = readJson(tree, 'package.json'); expect(packageJson.devDependencies.cypress).toBeDefined(); diff --git a/packages/cypress/src/generators/init/init.ts b/packages/cypress/src/generators/init/init.ts index 6aec86a071297..52cfb33566e47 100644 --- a/packages/cypress/src/generators/init/init.ts +++ b/packages/cypress/src/generators/init/init.ts @@ -24,9 +24,9 @@ function updateDependencies(host: Tree) { ); } -export function initGenerator(host: Tree) { +export function cypressInitGenerator(host: Tree) { return updateDependencies(host); } -export default initGenerator; -export const initSchematic = convertNxGenerator(initGenerator); +export default cypressInitGenerator; +export const cypressInitSchematic = convertNxGenerator(cypressInitGenerator); diff --git a/packages/jest/index.ts b/packages/jest/index.ts index 98202012775e1..173f624287912 100644 --- a/packages/jest/index.ts +++ b/packages/jest/index.ts @@ -4,3 +4,4 @@ export { } from './src/utils/config/update-config'; export { jestConfigObjectAst } from './src/utils/config/functions'; export { jestProjectGenerator } from './src/generators/jest-project/jest-project'; +export { jestInitGenerator } from './src/generators/init/init'; diff --git a/packages/jest/src/generators/jest-project/jest-project.ts b/packages/jest/src/generators/jest-project/jest-project.ts index 0c29489536b5b..11d92afa57c1a 100644 --- a/packages/jest/src/generators/jest-project/jest-project.ts +++ b/packages/jest/src/generators/jest-project/jest-project.ts @@ -23,6 +23,10 @@ function normalizeOptions(options: JestProjectSchema) { options.testEnvironment = ''; } + if (!options.hasOwnProperty('supportTsx')) { + options.supportTsx = false; + } + // if we support TSX or babelJest we don't support angular(html templates) if (options.supportTsx || options.babelJest) { options.skipSerializers = true; diff --git a/packages/jest/src/generators/jest-project/schema.d.ts b/packages/jest/src/generators/jest-project/schema.d.ts index afae1c2a6adef..af5cc3955755b 100644 --- a/packages/jest/src/generators/jest-project/schema.d.ts +++ b/packages/jest/src/generators/jest-project/schema.d.ts @@ -1,13 +1,13 @@ export interface JestProjectSchema { project: string; - supportTsx: boolean; + supportTsx?: boolean; /** * @deprecated */ - skipSetupFile: boolean; - setupFile: 'angular' | 'web-components' | 'none'; - skipSerializers: boolean; + skipSetupFile?: boolean; + setupFile?: 'angular' | 'web-components' | 'none'; + skipSerializers?: boolean; testEnvironment?: 'node' | 'jsdom' | ''; - babelJest: boolean; - skipFormat: boolean; + babelJest?: boolean; + skipFormat?: boolean; } diff --git a/packages/linter/src/generators/init/init.ts b/packages/linter/src/generators/init/init.ts index 142e4cfb9ac12..b64716cc1fec2 100644 --- a/packages/linter/src/generators/init/init.ts +++ b/packages/linter/src/generators/init/init.ts @@ -15,7 +15,7 @@ import { import { Linter } from '../utils/linter'; export interface LinterInitOptions { - linter: Linter; + linter?: Linter; } const globalTsLintConfiguration = { @@ -197,9 +197,9 @@ function initEsLint(tree: Tree) { } export function lintInitGenerator(tree: Tree, options: LinterInitOptions) { - if (options.linter === Linter.TsLint) { - return initTsLint(tree); - } else if (options.linter === Linter.EsLint) { + if (!options.linter || options.linter === Linter.EsLint) { return initEsLint(tree); + } else if (options.linter === Linter.TsLint) { + return initTsLint(tree); } } diff --git a/packages/linter/src/generators/lint-project/lint-project.ts b/packages/linter/src/generators/lint-project/lint-project.ts index bacfd1428e1c9..9829ef36132cf 100644 --- a/packages/linter/src/generators/lint-project/lint-project.ts +++ b/packages/linter/src/generators/lint-project/lint-project.ts @@ -13,7 +13,7 @@ import { lintInitGenerator } from '../init/init'; interface LintProjectOptions { project: string; - linter: Linter; + linter?: Linter; eslintFilePatterns?: string[]; tsConfigPaths?: string[]; skipFormat: boolean; diff --git a/packages/react/src/schematics/library/library.ts b/packages/react/src/schematics/library/library.ts index 822558a1643cf..f8c1962609043 100644 --- a/packages/react/src/schematics/library/library.ts +++ b/packages/react/src/schematics/library/library.ts @@ -46,10 +46,10 @@ import { } from '../../utils/versions'; import { Schema } from './schema'; import { libsDir } from '@nrwl/workspace/src/utils/ast-utils'; -import { initRootBabelConfig } from '@nrwl/web/src/utils/rules'; import { updateBabelJestConfig } from '../../rules/update-babel-jest-config'; import { names, offsetFromRoot } from '@nrwl/devkit'; import { wrapAngularDevkitSchematic } from '@nrwl/devkit/ngcli-adapter'; +import init from '../init/init'; export interface NormalizedSchema extends Schema { name: string; @@ -76,6 +76,11 @@ export default function (schema: Schema): Rule { options.style = 'none'; } return chain([ + init({ + ...options, + e2eTestRunner: 'none', + skipFormat: true, + }), addLintFiles(options.projectRoot, options.linter, { localConfig: reactEslintJson, extraPackageDeps: extraEslintDependencies, @@ -125,7 +130,6 @@ export default function (schema: Schema): Rule { {} ), updateAppRoutes(options, context), - initRootBabelConfig(), formatFiles(options), ])(host, context); }; diff --git a/packages/tao/src/shared/workspace.ts b/packages/tao/src/shared/workspace.ts index e1edf4ed8d50e..8ae327bc87885 100644 --- a/packages/tao/src/shared/workspace.ts +++ b/packages/tao/src/shared/workspace.ts @@ -44,7 +44,10 @@ export interface WorkspaceConfiguration { /** * Default generator collection. It is used when no collection is provided. */ - cli?: { defaultCollection: string }; + cli?: { + packageManager?: 'npm' | 'yarn' | 'pnpm'; + defaultCollection?: string; + }; } /** diff --git a/packages/web/collection.json b/packages/web/collection.json index 1b188871f2dee..0afa5e429d07c 100644 --- a/packages/web/collection.json +++ b/packages/web/collection.json @@ -4,15 +4,30 @@ "extends": ["@nrwl/workspace"], "schematics": { "init": { - "factory": "./src/schematics/init/init", - "schema": "./src/schematics/init/schema.json", + "factory": "./src/generators/init/init#webInitSchematic", + "schema": "./src/generators/init/schema.json", "description": "Add @nrwl/web to a project", "hidden": true }, "application": { - "factory": "./src/schematics/application/application", - "schema": "./src/schematics/application/schema.json", + "factory": "./src/generators/application/application#applicationSchematic", + "schema": "./src/generators/application/schema.json", + "aliases": ["app"], + "description": "Create an application" + } + }, + "generators": { + "init": { + "factory": "./src/generators/init/init#webInitGenerator", + "schema": "./src/generators/init/schema.json", + "description": "Add @nrwl/web to a project", + "hidden": true + }, + + "application": { + "factory": "./src/generators/application/application#applicationGenerator", + "schema": "./src/generators/application/schema.json", "aliases": ["app"], "description": "Create an application" } diff --git a/packages/web/index.ts b/packages/web/index.ts index 47d712ab8ead5..69032cd12bf52 100644 --- a/packages/web/index.ts +++ b/packages/web/index.ts @@ -1 +1 @@ -export { applicationGenerator } from './src/schematics/application/application'; +export { applicationGenerator } from './src/generators/application/application'; diff --git a/packages/web/src/builders/build/build.impl.ts b/packages/web/src/builders/build/build.impl.ts index 8893392918830..fbf9677f7b111 100644 --- a/packages/web/src/builders/build/build.impl.ts +++ b/packages/web/src/builders/build/build.impl.ts @@ -33,9 +33,9 @@ import { ExtraEntryPoint } from '../../utils/third-party/browser/schema'; export interface WebBuildBuilderOptions extends BuildBuilderOptions { index: string; - budgets: any[]; - baseHref: string; - deployUrl: string; + budgets?: any[]; + baseHref?: string; + deployUrl?: string; extractCss?: boolean; crossOrigin?: CrossOriginValue; @@ -49,6 +49,8 @@ export interface WebBuildBuilderOptions extends BuildBuilderOptions { vendorChunk?: boolean; commonChunk?: boolean; + namedChunks?: boolean; + stylePreprocessingOptions?: any; subresourceIntegrity?: boolean; diff --git a/packages/web/src/schematics/application/application.spec.ts b/packages/web/src/generators/application/application.spec.ts similarity index 63% rename from packages/web/src/schematics/application/application.spec.ts rename to packages/web/src/generators/application/application.spec.ts index cd1b53ff9df85..c1f763e1a4439 100644 --- a/packages/web/src/schematics/application/application.spec.ts +++ b/packages/web/src/generators/application/application.spec.ts @@ -1,23 +1,21 @@ -import { Tree } from '@angular-devkit/schematics'; -import { createEmptyWorkspace } from '@nrwl/workspace/testing'; -import * as stripJsonComments from 'strip-json-comments'; -import { readJsonInTree, NxJson } from '@nrwl/workspace'; -import { runSchematic } from '../../utils/testing'; +import { readJson, Tree } from '@nrwl/devkit'; +import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; +import { NxJson } from '@nrwl/workspace/src/core/shared-interfaces'; +import { applicationGenerator } from './application'; import { Schema } from './schema'; describe('app', () => { - let appTree: Tree; + let tree: Tree; beforeEach(() => { - appTree = Tree.empty(); - appTree = createEmptyWorkspace(appTree); + tree = createTreeWithEmptyWorkspace(); }); describe('not nested', () => { it('should update workspace.json', async () => { - const tree = await runSchematic('app', { name: 'myApp' }, appTree); - const workspaceJson = readJsonInTree(tree, '/workspace.json'); + await applicationGenerator(tree, { name: 'myApp' }); + const workspaceJson = readJson(tree, '/workspace.json'); expect(workspaceJson.projects['my-app'].root).toEqual('apps/my-app'); expect(workspaceJson.projects['my-app-e2e'].root).toEqual( @@ -27,12 +25,8 @@ describe('app', () => { }); it('should update nx.json', async () => { - const tree = await runSchematic( - 'app', - { name: 'myApp', tags: 'one,two' }, - appTree - ); - const nxJson = readJsonInTree(tree, '/nx.json'); + await applicationGenerator(tree, { name: 'myApp', tags: 'one,two' }); + const nxJson = readJson(tree, '/nx.json'); expect(nxJson.projects).toEqual({ 'my-app': { tags: ['one', 'two'], @@ -45,7 +39,7 @@ describe('app', () => { }); it('should generate files', async () => { - const tree = await runSchematic('app', { name: 'myApp' }, appTree); + await applicationGenerator(tree, { name: 'myApp' }); expect(tree.exists('apps/my-app/src/main.ts')).toBeTruthy(); expect(tree.exists('apps/my-app/src/app/app.element.ts')).toBeTruthy(); expect( @@ -53,7 +47,7 @@ describe('app', () => { ).toBeTruthy(); expect(tree.exists('apps/my-app/src/app/app.element.css')).toBeTruthy(); - const tsconfig = readJsonInTree(tree, 'apps/my-app/tsconfig.json'); + const tsconfig = readJson(tree, 'apps/my-app/tsconfig.json'); expect(tsconfig.references).toEqual([ { path: './tsconfig.app.json', @@ -63,21 +57,15 @@ describe('app', () => { }, ]); - const tsconfigApp = JSON.parse( - stripJsonComments(tree.readContent('apps/my-app/tsconfig.app.json')) - ); + const tsconfigApp = readJson(tree, 'apps/my-app/tsconfig.app.json'); expect(tsconfigApp.compilerOptions.outDir).toEqual('../../dist/out-tsc'); expect(tsconfigApp.extends).toEqual('./tsconfig.json'); - const linter = JSON.parse( - stripJsonComments(tree.readContent('apps/my-app/.eslintrc.json')) - ); - expect(linter.extends).toEqual('../../.eslintrc.json'); + const linter = readJson(tree, 'apps/my-app/.eslintrc.json'); + expect(linter.extends).toEqual(['../../.eslintrc.json']); expect(tree.exists('apps/my-app-e2e/cypress.json')).toBeTruthy(); - const tsconfigE2E = JSON.parse( - stripJsonComments(tree.readContent('apps/my-app-e2e/tsconfig.e2e.json')) - ); + const tsconfigE2E = readJson(tree, 'apps/my-app-e2e/tsconfig.e2e.json'); expect(tsconfigE2E.compilerOptions.outDir).toEqual('../../dist/out-tsc'); expect(tsconfigE2E.extends).toEqual('./tsconfig.json'); }); @@ -85,12 +73,11 @@ describe('app', () => { describe('nested', () => { it('should update workspace.json', async () => { - const tree = await runSchematic( - 'app', - { name: 'myApp', directory: 'myDir' }, - appTree - ); - const workspaceJson = readJsonInTree(tree, '/workspace.json'); + await applicationGenerator(tree, { + name: 'myApp', + directory: 'myDir', + }); + const workspaceJson = readJson(tree, '/workspace.json'); expect(workspaceJson.projects['my-dir-my-app'].root).toEqual( 'apps/my-dir/my-app' @@ -101,12 +88,12 @@ describe('app', () => { }); it('should update nx.json', async () => { - const tree = await runSchematic( - 'app', - { name: 'myApp', directory: 'myDir', tags: 'one,two' }, - appTree - ); - const nxJson = readJsonInTree(tree, '/nx.json'); + await applicationGenerator(tree, { + name: 'myApp', + directory: 'myDir', + tags: 'one,two', + }); + const nxJson = readJson(tree, '/nx.json'); expect(nxJson.projects).toEqual({ 'my-dir-my-app': { tags: ['one', 'two'], @@ -120,16 +107,14 @@ describe('app', () => { it('should generate files', async () => { const hasJsonValue = ({ path, expectedValue, lookupFn }) => { - const content = tree.readContent(path); - const config = JSON.parse(stripJsonComments(content)); + const config = readJson(tree, path); expect(lookupFn(config)).toEqual(expectedValue); }; - const tree = await runSchematic( - 'app', - { name: 'myApp', directory: 'myDir' }, - appTree - ); + await applicationGenerator(tree, { + name: 'myApp', + directory: 'myDir', + }); // Make sure these exist [ @@ -156,62 +141,50 @@ describe('app', () => { { path: 'apps/my-dir/my-app/.eslintrc.json', lookupFn: (json) => json.extends, - expectedValue: '../../../.eslintrc.json', + expectedValue: ['../../../.eslintrc.json'], }, ].forEach(hasJsonValue); }); it('should create Nx specific template', async () => { - const tree = await runSchematic( - 'app', - { name: 'myApp', directory: 'myDir' }, - appTree - ); + await applicationGenerator(tree, { + name: 'myApp', + directory: 'myDir', + }); expect( - tree.readContent('apps/my-dir/my-app/src/app/app.element.ts') + tree.read('apps/my-dir/my-app/src/app/app.element.ts').toString() ).toBeTruthy(); expect( - tree.readContent('apps/my-dir/my-app/src/app/app.element.ts') + tree.read('apps/my-dir/my-app/src/app/app.element.ts').toString() ).toContain('Thank you for using and showing some ♥ for Nx.'); }); }); describe('--style scss', () => { it('should generate scss styles', async () => { - const result = await runSchematic( - 'app', - { name: 'myApp', style: 'scss' }, - appTree - ); - expect(result.exists('apps/my-app/src/app/app.element.scss')).toEqual( - true - ); + await applicationGenerator(tree, { + name: 'myApp', + style: 'scss', + }); + expect(tree.exists('apps/my-app/src/app/app.element.scss')).toEqual(true); }); }); it('should setup jest without serializers', async () => { - const tree = await runSchematic( - 'app', - { - name: 'my-App', - }, - appTree - ); + await applicationGenerator(tree, { + name: 'my-App', + }); - expect(tree.readContent('apps/my-app/jest.config.js')).not.toContain( + expect(tree.read('apps/my-app/jest.config.js').toString()).not.toContain( `'jest-preset-angular/build/AngularSnapshotSerializer.js',` ); }); it('should setup the nrwl web build builder', async () => { - const tree = await runSchematic( - 'app', - { - name: 'my-App', - }, - appTree - ); - const workspaceJson = readJsonInTree(tree, 'workspace.json'); + await applicationGenerator(tree, { + name: 'my-App', + }); + const workspaceJson = readJson(tree, 'workspace.json'); const architectConfig = workspaceJson.projects['my-app'].architect; expect(architectConfig.build.builder).toEqual('@nrwl/web:build'); expect(architectConfig.build.outputs).toEqual(['{options.outputPath}']); @@ -250,14 +223,10 @@ describe('app', () => { }); it('should setup the nrwl web dev server builder', async () => { - const tree = await runSchematic( - 'app', - { - name: 'my-App', - }, - appTree - ); - const workspaceJson = readJsonInTree(tree, 'workspace.json'); + await applicationGenerator(tree, { + name: 'my-App', + }); + const workspaceJson = readJson(tree, 'workspace.json'); const architectConfig = workspaceJson.projects['my-app'].architect; expect(architectConfig.serve.builder).toEqual('@nrwl/web:dev-server'); expect(architectConfig.serve.options).toEqual({ @@ -269,14 +238,10 @@ describe('app', () => { }); it('should setup the eslint builder', async () => { - const tree = await runSchematic( - 'app', - { - name: 'my-App', - }, - appTree - ); - const workspaceJson = readJsonInTree(tree, 'workspace.json'); + await applicationGenerator(tree, { + name: 'my-App', + }); + const workspaceJson = readJson(tree, 'workspace.json'); expect(workspaceJson.projects['my-app'].architect.lint).toEqual({ builder: '@nrwl/linter:eslint', @@ -288,13 +253,9 @@ describe('app', () => { describe('--prefix', () => { it('should use the prefix in the index.html', async () => { - const tree = await runSchematic( - 'app', - { name: 'myApp', prefix: 'prefix' }, - appTree - ); + await applicationGenerator(tree, { name: 'myApp', prefix: 'prefix' }); - expect(tree.readContent('apps/my-app/src/index.html')).toContain( + expect(tree.read('apps/my-app/src/index.html').toString()).toContain( '' ); }); @@ -302,16 +263,17 @@ describe('app', () => { describe('--unit-test-runner none', () => { it('should not generate test configuration', async () => { - const tree = await runSchematic( - 'app', - { name: 'myApp', unitTestRunner: 'none' }, - appTree - ); + await applicationGenerator(tree, { + name: 'myApp', + unitTestRunner: 'none', + }); expect(tree.exists('jest.config.js')).toBeFalsy(); - expect(tree.exists('apps/my-app/src/app/app.spec.ts')).toBeFalsy(); + expect( + tree.exists('apps/my-app/src/app/app.element.spec.ts') + ).toBeFalsy(); expect(tree.exists('apps/my-app/tsconfig.spec.json')).toBeFalsy(); expect(tree.exists('apps/my-app/jest.config.js')).toBeFalsy(); - const workspaceJson = readJsonInTree(tree, 'workspace.json'); + const workspaceJson = readJson(tree, 'workspace.json'); expect(workspaceJson.projects['my-app'].architect.test).toBeUndefined(); expect(workspaceJson.projects['my-app'].architect.lint) .toMatchInlineSnapshot(` @@ -329,44 +291,40 @@ describe('app', () => { describe('--e2e-test-runner none', () => { it('should not generate test configuration', async () => { - const tree = await runSchematic( - 'app', - { name: 'myApp', e2eTestRunner: 'none' }, - appTree - ); + await applicationGenerator(tree, { + name: 'myApp', + e2eTestRunner: 'none', + }); expect(tree.exists('apps/my-app-e2e')).toBeFalsy(); - const workspaceJson = readJsonInTree(tree, 'workspace.json'); + const workspaceJson = readJson(tree, 'workspace.json'); expect(workspaceJson.projects['my-app-e2e']).toBeUndefined(); }); }); describe('--babelJest', () => { it('should use babel for jest', async () => { - const tree = await runSchematic( - 'app', - { name: 'myApp', babelJest: true } as Schema, - appTree - ); + await applicationGenerator(tree, { + name: 'myApp', + babelJest: true, + } as Schema); - expect(tree.readContent(`apps/my-app/jest.config.js`)) + expect(tree.read(`apps/my-app/jest.config.js`).toString()) .toMatchInlineSnapshot(` "module.exports = { displayName: 'my-app', preset: '../../jest.preset.js', setupFilesAfterEnv: ['/src/test-setup.ts'], transform: { - '^.+\\\\\\\\.[tj]s$': [ - 'babel-jest', - { cwd: __dirname, configFile: './babel-jest.config.json' }, - ], + '^.+\\\\\\\\.[tj]s$': [ 'babel-jest', + { cwd: __dirname, configFile: './babel-jest.config.json' }] }, - moduleFileExtensions: ['ts', 'js', 'html'], - coverageDirectory: '../../coverage/apps/my-app', + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../coverage/apps/my-app' }; " `); - expect(readJsonInTree(tree, 'apps/my-app/babel-jest.config.json')) + expect(readJson(tree, 'apps/my-app/babel-jest.config.json')) .toMatchInlineSnapshot(` Object { "presets": Array [ diff --git a/packages/web/src/generators/application/application.ts b/packages/web/src/generators/application/application.ts new file mode 100644 index 0000000000000..0efa5bea9180e --- /dev/null +++ b/packages/web/src/generators/application/application.ts @@ -0,0 +1,243 @@ +import { + addProjectConfiguration, + convertNxGenerator, + formatFiles, + generateFiles, + getWorkspaceLayout, + joinPathFragments, + names, + NxJsonProjectConfiguration, + offsetFromRoot, + ProjectConfiguration, + readWorkspaceConfiguration, + TargetConfiguration, + Tree, + updateWorkspaceConfiguration, +} from '@nrwl/devkit'; + +import { join } from 'path'; + +import { webInitGenerator } from '../init/init'; +import { cypressProjectGenerator } from '@nrwl/cypress'; +import { Linter, lintProjectGenerator } from '@nrwl/linter'; +import { jestProjectGenerator } from '@nrwl/jest'; + +import { WebBuildBuilderOptions } from '../../builders/build/build.impl'; +import { Schema } from './schema'; + +interface NormalizedSchema extends Schema { + projectName: string; + appProjectRoot: string; + e2eProjectName: string; + e2eProjectRoot: string; + parsedTags: string[]; +} + +function createApplicationFiles(tree: Tree, options: NormalizedSchema) { + generateFiles(tree, join(__dirname, './files/app'), options.appProjectRoot, { + ...options, + ...names(options.name), + tmpl: '', + offsetFromRoot: offsetFromRoot(options.appProjectRoot), + }); + if (options.unitTestRunner === 'none') { + tree.delete(join(options.appProjectRoot, './src/app/app.element.spec.ts')); + } +} + +function addBuildTarget( + project: ProjectConfiguration, + options: NormalizedSchema +): ProjectConfiguration { + const buildOptions: WebBuildBuilderOptions = { + outputPath: joinPathFragments('dist', options.appProjectRoot), + index: joinPathFragments(options.appProjectRoot, 'src/index.html'), + main: joinPathFragments(options.appProjectRoot, 'src/main.ts'), + polyfills: joinPathFragments(options.appProjectRoot, 'src/polyfills.ts'), + tsConfig: joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'), + assets: [ + joinPathFragments(options.appProjectRoot, 'src/favicon.ico'), + joinPathFragments(options.appProjectRoot, 'src/assets'), + ], + styles: [ + joinPathFragments(options.appProjectRoot, `src/styles.${options.style}`), + ], + scripts: [], + }; + const productionBuildOptions: Partial = { + fileReplacements: [ + { + replace: joinPathFragments( + options.appProjectRoot, + `src/environments/environment.ts` + ), + with: joinPathFragments( + options.appProjectRoot, + `src/environments/environment.prod.ts` + ), + }, + ], + optimization: true, + outputHashing: 'all', + sourceMap: false, + extractCss: true, + namedChunks: false, + extractLicenses: true, + vendorChunk: false, + budgets: [ + { + type: 'initial', + maximumWarning: '2mb', + maximumError: '5mb', + }, + ], + }; + + return { + ...project, + targets: { + ...project.targets, + build: { + executor: '@nrwl/web:build', + outputs: ['{options.outputPath}'], + options: buildOptions, + configurations: { + production: productionBuildOptions, + }, + }, + }, + }; +} + +function addServeTarget( + project: ProjectConfiguration, + options: NormalizedSchema +) { + const serveTarget: TargetConfiguration = { + executor: '@nrwl/web:dev-server', + options: { + buildTarget: `${options.projectName}:build`, + }, + configurations: { + production: { + buildTarget: `${options.projectName}:build:production`, + }, + }, + }; + + return { + ...project, + targets: { + ...project.targets, + serve: serveTarget, + }, + }; +} + +function addProject(tree: Tree, options: NormalizedSchema) { + const targets: Record = {}; + let project: ProjectConfiguration & NxJsonProjectConfiguration = { + projectType: 'application', + root: options.appProjectRoot, + sourceRoot: joinPathFragments(options.appProjectRoot, 'src'), + tags: options.parsedTags, + targets, + }; + + project = addBuildTarget(project, options); + project = addServeTarget(project, options); + + addProjectConfiguration(tree, options.projectName, project); + + const workspace = readWorkspaceConfiguration(tree); + + if (!workspace.defaultProject) { + workspace.defaultProject = options.projectName; + + updateWorkspaceConfiguration(tree, workspace); + } +} + +export async function applicationGenerator(host: Tree, schema: Schema) { + const options = normalizeOptions(host, schema); + + let installTask = await webInitGenerator(host, { + ...options, + skipFormat: true, + }); + + createApplicationFiles(host, options); + addProject(host, options); + + const lintInstallTask = await lintProjectGenerator(host, { + linter: options.linter, + project: options.projectName, + tsConfigPaths: [ + joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'), + ], + eslintFilePatterns: [`${options.appProjectRoot}/**/*.ts`], + skipFormat: true, + }); + installTask = lintInstallTask || installTask; + + if (options.e2eTestRunner === 'cypress') { + const cypressInstallTask = await cypressProjectGenerator(host, { + ...options, + name: options.name + '-e2e', + directory: options.directory, + project: options.projectName, + }); + installTask = cypressInstallTask || installTask; + } + if (options.unitTestRunner === 'jest') { + const jestInstallTask = await jestProjectGenerator(host, { + project: options.projectName, + skipSerializers: true, + setupFile: 'web-components', + babelJest: options.babelJest, + }); + installTask = jestInstallTask || installTask; + } + + if (!schema.skipFormat) { + await formatFiles(host); + } + return installTask; +} + +function normalizeOptions(host: Tree, options: Schema): NormalizedSchema { + const appDirectory = options.directory + ? `${names(options.directory).fileName}/${names(options.name).fileName}` + : names(options.name).fileName; + + const { appsDir, npmScope: defaultPrefix } = getWorkspaceLayout(host); + + const appProjectName = appDirectory.replace(new RegExp('/', 'g'), '-'); + const e2eProjectName = `${appProjectName}-e2e`; + + const appProjectRoot = `${appsDir}/${appDirectory}`; + const e2eProjectRoot = `${appsDir}/${appDirectory}-e2e`; + + const parsedTags = options.tags + ? options.tags.split(',').map((s) => s.trim()) + : []; + + options.style = options.style || 'css'; + options.linter = options.linter || Linter.EsLint; + options.unitTestRunner = options.unitTestRunner || 'jest'; + options.e2eTestRunner = options.e2eTestRunner || 'cypress'; + + return { + ...options, + prefix: options.prefix ? options.prefix : defaultPrefix, + name: names(options.name).fileName, + projectName: appProjectName, + appProjectRoot, + e2eProjectRoot, + e2eProjectName, + parsedTags, + }; +} + +export default applicationGenerator; +export const applicationSchematic = convertNxGenerator(applicationGenerator); diff --git a/packages/web/src/schematics/application/files/app/.babelrc__tmpl__ b/packages/web/src/generators/application/files/app/.babelrc__tmpl__ similarity index 100% rename from packages/web/src/schematics/application/files/app/.babelrc__tmpl__ rename to packages/web/src/generators/application/files/app/.babelrc__tmpl__ diff --git a/packages/web/src/schematics/application/files/app/browserslist b/packages/web/src/generators/application/files/app/browserslist similarity index 100% rename from packages/web/src/schematics/application/files/app/browserslist rename to packages/web/src/generators/application/files/app/browserslist diff --git a/packages/web/src/schematics/application/files/app/src/app/app.element.__style__ b/packages/web/src/generators/application/files/app/src/app/app.element.__style__ similarity index 100% rename from packages/web/src/schematics/application/files/app/src/app/app.element.__style__ rename to packages/web/src/generators/application/files/app/src/app/app.element.__style__ diff --git a/packages/web/src/schematics/application/files/app/src/app/app.element.spec.ts__tmpl__ b/packages/web/src/generators/application/files/app/src/app/app.element.spec.ts__tmpl__ similarity index 100% rename from packages/web/src/schematics/application/files/app/src/app/app.element.spec.ts__tmpl__ rename to packages/web/src/generators/application/files/app/src/app/app.element.spec.ts__tmpl__ diff --git a/packages/web/src/schematics/application/files/app/src/app/app.element.ts__tmpl__ b/packages/web/src/generators/application/files/app/src/app/app.element.ts__tmpl__ similarity index 100% rename from packages/web/src/schematics/application/files/app/src/app/app.element.ts__tmpl__ rename to packages/web/src/generators/application/files/app/src/app/app.element.ts__tmpl__ diff --git a/packages/web/src/schematics/application/files/app/src/assets/.gitkeep b/packages/web/src/generators/application/files/app/src/assets/.gitkeep similarity index 100% rename from packages/web/src/schematics/application/files/app/src/assets/.gitkeep rename to packages/web/src/generators/application/files/app/src/assets/.gitkeep diff --git a/packages/web/src/schematics/application/files/app/src/environments/environment.prod.ts__tmpl__ b/packages/web/src/generators/application/files/app/src/environments/environment.prod.ts__tmpl__ similarity index 100% rename from packages/web/src/schematics/application/files/app/src/environments/environment.prod.ts__tmpl__ rename to packages/web/src/generators/application/files/app/src/environments/environment.prod.ts__tmpl__ diff --git a/packages/web/src/schematics/application/files/app/src/environments/environment.ts__tmpl__ b/packages/web/src/generators/application/files/app/src/environments/environment.ts__tmpl__ similarity index 100% rename from packages/web/src/schematics/application/files/app/src/environments/environment.ts__tmpl__ rename to packages/web/src/generators/application/files/app/src/environments/environment.ts__tmpl__ diff --git a/packages/web/src/schematics/application/files/app/src/favicon.ico b/packages/web/src/generators/application/files/app/src/favicon.ico similarity index 100% rename from packages/web/src/schematics/application/files/app/src/favicon.ico rename to packages/web/src/generators/application/files/app/src/favicon.ico diff --git a/packages/web/src/schematics/application/files/app/src/index.html b/packages/web/src/generators/application/files/app/src/index.html similarity index 100% rename from packages/web/src/schematics/application/files/app/src/index.html rename to packages/web/src/generators/application/files/app/src/index.html diff --git a/packages/web/src/schematics/application/files/app/src/main.ts__tmpl__ b/packages/web/src/generators/application/files/app/src/main.ts__tmpl__ similarity index 100% rename from packages/web/src/schematics/application/files/app/src/main.ts__tmpl__ rename to packages/web/src/generators/application/files/app/src/main.ts__tmpl__ diff --git a/packages/web/src/schematics/application/files/app/src/polyfills.ts__tmpl__ b/packages/web/src/generators/application/files/app/src/polyfills.ts__tmpl__ similarity index 100% rename from packages/web/src/schematics/application/files/app/src/polyfills.ts__tmpl__ rename to packages/web/src/generators/application/files/app/src/polyfills.ts__tmpl__ diff --git a/packages/web/src/schematics/application/files/app/src/styles.__style__ b/packages/web/src/generators/application/files/app/src/styles.__style__ similarity index 100% rename from packages/web/src/schematics/application/files/app/src/styles.__style__ rename to packages/web/src/generators/application/files/app/src/styles.__style__ diff --git a/packages/web/src/schematics/application/files/app/tsconfig.app.json b/packages/web/src/generators/application/files/app/tsconfig.app.json similarity index 100% rename from packages/web/src/schematics/application/files/app/tsconfig.app.json rename to packages/web/src/generators/application/files/app/tsconfig.app.json diff --git a/packages/web/src/schematics/application/files/app/tsconfig.json b/packages/web/src/generators/application/files/app/tsconfig.json similarity index 100% rename from packages/web/src/schematics/application/files/app/tsconfig.json rename to packages/web/src/generators/application/files/app/tsconfig.json diff --git a/packages/web/src/generators/application/schema.d.ts b/packages/web/src/generators/application/schema.d.ts new file mode 100644 index 0000000000000..2e6e69d38ddfa --- /dev/null +++ b/packages/web/src/generators/application/schema.d.ts @@ -0,0 +1,14 @@ +import { Linter } from '@nrwl/linter'; + +export interface Schema { + name: string; + prefix?: string; + style?: string; + skipFormat?: boolean; + directory?: string; + tags?: string; + unitTestRunner?: 'jest' | 'none'; + e2eTestRunner?: 'cypress' | 'none'; + linter?: Linter; + babelJest?: boolean; +} diff --git a/packages/web/src/schematics/application/schema.json b/packages/web/src/generators/application/schema.json similarity index 100% rename from packages/web/src/schematics/application/schema.json rename to packages/web/src/generators/application/schema.json diff --git a/packages/web/src/generators/init/init.spec.ts b/packages/web/src/generators/init/init.spec.ts new file mode 100644 index 0000000000000..cab4a7e743c19 --- /dev/null +++ b/packages/web/src/generators/init/init.spec.ts @@ -0,0 +1,79 @@ +import { addDependenciesToPackageJson, readJson, Tree } from '@nrwl/devkit'; +import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; + +import { nxVersion } from '../../utils/versions'; + +import webInitGenerator from './init'; + +describe('init', () => { + let tree: Tree; + + beforeEach(() => { + tree = createTreeWithEmptyWorkspace(); + }); + + it('should add web dependencies', async () => { + const existing = 'existing'; + const existingVersion = '1.0.0'; + addDependenciesToPackageJson( + tree, + { + '@nrwl/web': nxVersion, + [existing]: existingVersion, + }, + { + [existing]: existingVersion, + } + ); + await webInitGenerator(tree, {}); + const packageJson = readJson(tree, 'package.json'); + expect(packageJson.devDependencies['@nrwl/web']).toBeDefined(); + expect(packageJson.devDependencies[existing]).toBeDefined(); + expect(packageJson.dependencies['@nrwl/web']).toBeUndefined(); + expect(packageJson.dependencies['document-register-element']).toBeDefined(); + expect(packageJson.dependencies[existing]).toBeDefined(); + }); + + describe('defaultCollection', () => { + it('should be set if none was set before', async () => { + await webInitGenerator(tree, {}); + const workspaceJson = readJson(tree, 'workspace.json'); + expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/web'); + }); + }); + + it('should not add jest config if unitTestRunner is none', async () => { + await webInitGenerator(tree, { + unitTestRunner: 'none', + }); + expect(tree.exists('jest.config.js')).toBe(false); + }); + + describe('babel config', () => { + it('should create babel config if not present', async () => { + await webInitGenerator(tree, { + unitTestRunner: 'none', + }); + expect(tree.exists('babel.config.json')).toBe(true); + }); + + it('should not overwrite existing babel config', async () => { + tree.write('babel.config.json', '{ "preset": ["preset-awesome"] }'); + + await webInitGenerator(tree, { + unitTestRunner: 'none', + }); + + const existing = readJson(tree, 'babel.config.json'); + expect(existing).toEqual({ preset: ['preset-awesome'] }); + }); + + it('should not overwrite existing babel config (.js)', async () => { + tree.write('/babel.config.js', 'module.exports = () => {};'); + await webInitGenerator(tree, { + unitTestRunner: 'none', + }); + expect(tree.exists('babel.config.json')).toBe(false); + }); + }); +}); diff --git a/packages/web/src/generators/init/init.ts b/packages/web/src/generators/init/init.ts new file mode 100644 index 0000000000000..0a2eccf47ba70 --- /dev/null +++ b/packages/web/src/generators/init/init.ts @@ -0,0 +1,82 @@ +import { + addDependenciesToPackageJson, + convertNxGenerator, + formatFiles, + GeneratorCallback, + readWorkspaceConfiguration, + Tree, + updateJson, + updateWorkspaceConfiguration, + writeJson, +} from '@nrwl/devkit'; +import { Schema } from './schema'; +import { + documentRegisterElementVersion, + nxVersion, +} from '../../utils/versions'; +import { cypressInitGenerator } from '@nrwl/cypress'; +import { jestInitGenerator } from '@nrwl/jest'; + +function updateDependencies(tree: Tree) { + updateJson(tree, 'package.json', (json) => { + delete json.dependencies['@nrwl/web']; + return json; + }); + + return addDependenciesToPackageJson( + tree, + { + 'core-js': '^3.6.5', + 'document-register-element': documentRegisterElementVersion, + tslib: '^2.0.0', + }, + { + '@nrwl/web': nxVersion, + } + ); +} + +function setDefaultCollection(tree: Tree) { + const workspace = readWorkspaceConfiguration(tree); + workspace.cli = workspace.cli || {}; + + const defaultCollection = workspace.cli.defaultCollection; + + if (!defaultCollection || defaultCollection === '@nrwl/workspace') { + workspace.cli.defaultCollection = '@nrwl/web'; + } + + updateWorkspaceConfiguration(tree, workspace); +} + +function initRootBabelConfig(tree: Tree) { + if (tree.exists('/babel.config.json') || tree.exists('/babel.config.js')) { + return; + } + + writeJson(tree, '/babel.config.json', { + presets: ['@nrwl/web/babel'], + babelrcRoots: ['*'], // Make sure .babelrc files other than root can be loaded in a monorepo + }); +} + +export async function webInitGenerator(tree: Tree, schema: Schema) { + let installTask: GeneratorCallback; + + setDefaultCollection(tree); + if (!schema.unitTestRunner || schema.unitTestRunner === 'jest') { + installTask = jestInitGenerator(tree, {}); + } + if (!schema.e2eTestRunner || schema.e2eTestRunner === 'cypress') { + installTask = cypressInitGenerator(tree) || installTask; + } + installTask = updateDependencies(tree) || installTask; + initRootBabelConfig(tree); + if (!schema.skipFormat) { + await formatFiles(tree); + } + return installTask; +} + +export default webInitGenerator; +export const webInitSchematic = convertNxGenerator(webInitGenerator); diff --git a/packages/web/src/generators/init/schema.d.ts b/packages/web/src/generators/init/schema.d.ts new file mode 100644 index 0000000000000..7ab6757712763 --- /dev/null +++ b/packages/web/src/generators/init/schema.d.ts @@ -0,0 +1,5 @@ +export interface Schema { + unitTestRunner?: 'jest' | 'none'; + e2eTestRunner?: 'cypress' | 'none'; + skipFormat?: boolean; +} diff --git a/packages/web/src/schematics/init/schema.json b/packages/web/src/generators/init/schema.json similarity index 97% rename from packages/web/src/schematics/init/schema.json rename to packages/web/src/generators/init/schema.json index b24a2257cdec0..b30764c266ff6 100644 --- a/packages/web/src/schematics/init/schema.json +++ b/packages/web/src/generators/init/schema.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/schema", "id": "NxWebInit", + "cli": "nx", "title": "Init Web Plugin", "type": "object", "properties": { diff --git a/packages/web/src/schematics/application/application.ts b/packages/web/src/schematics/application/application.ts deleted file mode 100644 index dcb66c235c762..0000000000000 --- a/packages/web/src/schematics/application/application.ts +++ /dev/null @@ -1,217 +0,0 @@ -import { join, normalize } from '@angular-devkit/core'; -import { - chain, - Rule, - Tree, - SchematicContext, - mergeWith, - apply, - template, - move, - url, - externalSchematic, - noop, - filter, -} from '@angular-devkit/schematics'; -import { Schema } from './schema'; -import { - updateJsonInTree, - NxJson, - getNpmScope, - formatFiles, - updateWorkspaceInTree, - generateProjectLint, - addLintFiles, -} from '@nrwl/workspace'; -import init from '../init/init'; -import { appsDir } from '@nrwl/workspace/src/utils/ast-utils'; -import { names, offsetFromRoot } from '@nrwl/devkit'; -import { wrapAngularDevkitSchematic } from '@nrwl/devkit/ngcli-adapter'; - -interface NormalizedSchema extends Schema { - projectName: string; - appProjectRoot: string; - e2eProjectName: string; - e2eProjectRoot: string; - parsedTags: string[]; -} - -function createApplicationFiles(options: NormalizedSchema): Rule { - return mergeWith( - apply(url(`./files/app`), [ - template({ - ...options, - ...names(options.name), - tmpl: '', - offsetFromRoot: offsetFromRoot(options.appProjectRoot), - }), - options.unitTestRunner === 'none' - ? filter((file) => file !== '/src/app/app.spec.ts') - : noop(), - move(options.appProjectRoot), - ]) - ); -} - -function updateNxJson(options: NormalizedSchema): Rule { - return updateJsonInTree('nx.json', (json) => { - json.projects[options.projectName] = { tags: options.parsedTags }; - return json; - }); -} - -function addProject(options: NormalizedSchema): Rule { - return updateWorkspaceInTree((json) => { - const architect: { [key: string]: any } = {}; - - architect.build = { - builder: '@nrwl/web:build', - outputs: ['{options.outputPath}'], - options: { - outputPath: join(normalize('dist'), options.appProjectRoot), - index: join(normalize(options.appProjectRoot), 'src/index.html'), - main: join(normalize(options.appProjectRoot), 'src/main.ts'), - polyfills: join(normalize(options.appProjectRoot), 'src/polyfills.ts'), - tsConfig: join(normalize(options.appProjectRoot), 'tsconfig.app.json'), - assets: [ - join(normalize(options.appProjectRoot), 'src/favicon.ico'), - join(normalize(options.appProjectRoot), 'src/assets'), - ], - styles: [ - join( - normalize(options.appProjectRoot), - `src/styles.${options.style}` - ), - ], - scripts: [], - }, - configurations: { - production: { - fileReplacements: [ - { - replace: join( - normalize(options.appProjectRoot), - `src/environments/environment.ts` - ), - with: join( - normalize(options.appProjectRoot), - `src/environments/environment.prod.ts` - ), - }, - ], - optimization: true, - outputHashing: 'all', - sourceMap: false, - extractCss: true, - namedChunks: false, - extractLicenses: true, - vendorChunk: false, - budgets: [ - { - type: 'initial', - maximumWarning: '2mb', - maximumError: '5mb', - }, - ], - }, - }, - }; - - architect.serve = { - builder: '@nrwl/web:dev-server', - options: { - buildTarget: `${options.projectName}:build`, - }, - configurations: { - production: { - buildTarget: `${options.projectName}:build:production`, - }, - }, - }; - - architect.lint = generateProjectLint( - normalize(options.appProjectRoot), - join(normalize(options.appProjectRoot), 'tsconfig.app.json'), - options.linter, - [`${options.appProjectRoot}/**/*.ts`] - ); - - json.projects[options.projectName] = { - root: options.appProjectRoot, - sourceRoot: join(normalize(options.appProjectRoot), 'src'), - projectType: 'application', - architect, - }; - - json.defaultProject = json.defaultProject || options.projectName; - - return json; - }); -} - -export default function (schema: Schema): Rule { - return (host: Tree, context: SchematicContext) => { - const options = normalizeOptions(host, schema); - - return chain([ - init({ - ...options, - skipFormat: true, - }), - addLintFiles(options.appProjectRoot, options.linter), - createApplicationFiles(options), - updateNxJson(options), - addProject(options), - options.e2eTestRunner === 'cypress' - ? externalSchematic('@nrwl/cypress', 'cypress-project', { - ...options, - name: options.name + '-e2e', - directory: options.directory, - project: options.projectName, - }) - : noop(), - options.unitTestRunner === 'jest' - ? externalSchematic('@nrwl/jest', 'jest-project', { - project: options.projectName, - skipSerializers: true, - setupFile: 'web-components', - babelJest: options.babelJest, - }) - : noop(), - formatFiles(options), - ])(host, context); - }; -} - -function normalizeOptions(host: Tree, options: Schema): NormalizedSchema { - const appDirectory = options.directory - ? `${names(options.directory).fileName}/${names(options.name).fileName}` - : names(options.name).fileName; - - const appProjectName = appDirectory.replace(new RegExp('/', 'g'), '-'); - const e2eProjectName = `${appProjectName}-e2e`; - - const appProjectRoot = `${appsDir(host)}/${appDirectory}`; - const e2eProjectRoot = `${appsDir(host)}/${appDirectory}-e2e`; - - const parsedTags = options.tags - ? options.tags.split(',').map((s) => s.trim()) - : []; - - const defaultPrefix = getNpmScope(host); - return { - ...options, - prefix: options.prefix ? options.prefix : defaultPrefix, - name: names(options.name).fileName, - projectName: appProjectName, - appProjectRoot, - e2eProjectRoot, - e2eProjectName, - parsedTags, - }; -} - -export const applicationGenerator = wrapAngularDevkitSchematic( - '@nrwl/web', - 'application' -); diff --git a/packages/web/src/schematics/application/schema.d.ts b/packages/web/src/schematics/application/schema.d.ts deleted file mode 100644 index a8471460e6848..0000000000000 --- a/packages/web/src/schematics/application/schema.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -export interface Schema { - name: string; - prefix?: string; - style?: string; - skipFormat: boolean; - directory?: string; - tags?: string; - unitTestRunner: 'jest' | 'none'; - e2eTestRunner: 'cypress' | 'none'; - linter: Linter; - babelJest?: boolean; -} diff --git a/packages/web/src/schematics/init/init.spec.ts b/packages/web/src/schematics/init/init.spec.ts deleted file mode 100644 index c5145b371deaf..0000000000000 --- a/packages/web/src/schematics/init/init.spec.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { Tree } from '@angular-devkit/schematics'; -import { createEmptyWorkspace } from '@nrwl/workspace/testing'; -import { addDepsToPackageJson, readJsonInTree } from '@nrwl/workspace'; -import { callRule, runSchematic } from '../../utils/testing'; -import { nxVersion } from '../../utils/versions'; - -describe('init', () => { - let tree: Tree; - - beforeEach(() => { - tree = Tree.empty(); - tree = createEmptyWorkspace(tree); - }); - - it('should add web dependencies', async () => { - const existing = 'existing'; - const existingVersion = '1.0.0'; - await callRule( - addDepsToPackageJson( - { '@nrwl/web': nxVersion, [existing]: existingVersion }, - { [existing]: existingVersion }, - false - ), - tree - ); - const result = await runSchematic('init', {}, tree); - const packageJson = readJsonInTree(result, 'package.json'); - expect(packageJson.devDependencies['@nrwl/web']).toBeDefined(); - expect(packageJson.devDependencies[existing]).toBeDefined(); - expect(packageJson.dependencies['@nrwl/web']).toBeUndefined(); - expect(packageJson.dependencies['document-register-element']).toBeDefined(); - expect(packageJson.dependencies[existing]).toBeDefined(); - }); - - describe('defaultCollection', () => { - it('should be set if none was set before', async () => { - const result = await runSchematic('init', {}, tree); - const workspaceJson = readJsonInTree(result, 'workspace.json'); - expect(workspaceJson.cli.defaultCollection).toEqual('@nrwl/web'); - }); - }); - - it('should not add jest config if unitTestRunner is none', async () => { - const result = await runSchematic( - 'init', - { - unitTestRunner: 'none', - }, - tree - ); - expect(result.exists('jest.config.js')).toBe(false); - }); - - describe('babel config', () => { - it('should create babel config if not present', async () => { - const result = await runSchematic( - 'init', - { - unitTestRunner: 'none', - }, - tree - ); - expect(result.exists('babel.config.json')).toBe(true); - }); - - it('should not overwrite existing babel config', async () => { - tree.create('babel.config.json', '{ "preset": ["preset-awesome"] }'); - - const result = await runSchematic( - 'init', - { - unitTestRunner: 'none', - }, - tree - ); - - const existing = result.read('babel.config.json').toString(); - expect(existing).toMatch('{ "preset": ["preset-awesome"] }'); - }); - - it('should not overwrite existing babel config (.js)', async () => { - tree.create('/babel.config.js', 'module.exports = () => {};'); - const result = await runSchematic( - 'init', - { - unitTestRunner: 'none', - }, - tree - ); - expect(result.exists('babel.config.json')).toBe(false); - }); - }); -}); diff --git a/packages/web/src/schematics/init/init.ts b/packages/web/src/schematics/init/init.ts deleted file mode 100644 index 47b1508106c16..0000000000000 --- a/packages/web/src/schematics/init/init.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { chain, noop, Rule, Tree } from '@angular-devkit/schematics'; -import { - addPackageWithInit, - formatFiles, - setDefaultCollection, - updateJsonInTree, -} from '@nrwl/workspace'; -import { Schema } from './schema'; -import { - documentRegisterElementVersion, - nxVersion, -} from '../../utils/versions'; -import { initRootBabelConfig } from '../../utils/rules'; - -function updateDependencies(): Rule { - return updateJsonInTree('package.json', (json) => { - delete json.dependencies['@nrwl/web']; - json.dependencies = { - ...json.dependencies, - 'core-js': '^3.6.5', - 'document-register-element': documentRegisterElementVersion, - tslib: '^2.0.0', - }; - json.devDependencies = { - ...json.devDependencies, - '@nrwl/web': nxVersion, - }; - return json; - }); -} - -export default function (schema: Schema) { - return chain([ - setDefaultCollection('@nrwl/web'), - schema.unitTestRunner === 'jest' - ? addPackageWithInit('@nrwl/jest') - : noop(), - schema.e2eTestRunner === 'cypress' - ? addPackageWithInit('@nrwl/cypress') - : noop(), - updateDependencies(), - initRootBabelConfig(), - formatFiles(schema), - ]); -} diff --git a/packages/web/src/schematics/init/schema.d.ts b/packages/web/src/schematics/init/schema.d.ts deleted file mode 100644 index 9f7995c018ef1..0000000000000 --- a/packages/web/src/schematics/init/schema.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface Schema { - unitTestRunner: 'jest' | 'none'; - e2eTestRunner: 'cypress' | 'none'; - skipFormat: boolean; -} diff --git a/packages/web/src/utils/types.ts b/packages/web/src/utils/types.ts index 6197ccaf57268..395f6f5351950 100644 --- a/packages/web/src/utils/types.ts +++ b/packages/web/src/utils/types.ts @@ -24,7 +24,7 @@ export interface BuildBuilderOptions { maxWorkers?: number; poll?: number; - fileReplacements: FileReplacement[]; + fileReplacements?: FileReplacement[]; assets?: any[]; progress?: boolean; diff --git a/packages/workspace/src/generators/new/new.spec.ts b/packages/workspace/src/generators/new/new.spec.ts index 6d56f2afa7bc6..5438b81760200 100644 --- a/packages/workspace/src/generators/new/new.spec.ts +++ b/packages/workspace/src/generators/new/new.spec.ts @@ -62,22 +62,25 @@ describe('new', () => { }); describe('--packageManager', () => { - describe.each([['npm'], ['yarn'], ['pnpm']])('%s', (packageManager) => { - it('should set the packageManager in workspace.json', async () => { - await newGenerator(tree, { - ...defaultOptions, - name: 'my-workspace', - directory: 'my-workspace', - npmScope: 'npmScope', - appName: 'app', - cli: 'angular', - packageManager, - }); + describe.each([['npm'], ['yarn'], ['pnpm']])( + '%s', + (packageManager: 'npm' | 'yarn' | 'pnpm') => { + it('should set the packageManager in workspace.json', async () => { + await newGenerator(tree, { + ...defaultOptions, + name: 'my-workspace', + directory: 'my-workspace', + npmScope: 'npmScope', + appName: 'app', + cli: 'angular', + packageManager, + }); - const workspaceJson = readJson(tree, 'my-workspace/angular.json'); - expect(workspaceJson.cli.packageManager).toEqual(packageManager); - }); - }); + const workspaceJson = readJson(tree, 'my-workspace/angular.json'); + expect(workspaceJson.cli.packageManager).toEqual(packageManager); + }); + } + ); }); it('should not modify any existing files', async () => { diff --git a/packages/workspace/src/generators/new/new.ts b/packages/workspace/src/generators/new/new.ts index 2499f26205acf..1630847f9a37b 100644 --- a/packages/workspace/src/generators/new/new.ts +++ b/packages/workspace/src/generators/new/new.ts @@ -8,6 +8,9 @@ import { convertNxGenerator, names, getPackageManagerCommand, + readWorkspaceConfiguration, + updateWorkspaceConfiguration, + WorkspaceConfiguration, } from '@nrwl/devkit'; import { join } from 'path'; @@ -45,7 +48,7 @@ export interface Schema { commit?: { name: string; email: string; message?: string }; defaultBase: string; linter: 'tslint' | 'eslint'; - packageManager?: string; + packageManager?: 'npm' | 'yarn' | 'pnpm'; } function generatePreset(host: Tree, opts: Schema) { @@ -344,13 +347,17 @@ function setDefaultPackageManager(host: Tree, options: Schema) { return; } - updateJson(host, getWorkspacePath(host, options), (json) => { - if (!json.cli) { - json.cli = {}; + updateJson( + host, + getWorkspacePath(host, options), + (json) => { + if (!json.cli) { + json.cli = {}; + } + json.cli.packageManager = options.packageManager; + return json; } - json.cli['packageManager'] = options.packageManager; - return json; - }); + ); } function setDefault( diff --git a/packages/workspace/src/generators/preset/preset.spec.ts b/packages/workspace/src/generators/preset/preset.spec.ts index 4297672ccef4b..c9032d2463362 100644 --- a/packages/workspace/src/generators/preset/preset.spec.ts +++ b/packages/workspace/src/generators/preset/preset.spec.ts @@ -1,4 +1,4 @@ -import { Tree } from '@nrwl/devkit'; +import { readJson, Tree } from '@nrwl/devkit'; import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; import { presetGenerator } from './preset'; @@ -9,9 +9,9 @@ describe('preset', () => { tree = createTreeWithEmptyWorkspace(); }); - // TODO: reenable. This doesn't work because wrapAngularDevkit uses the fs - xdescribe('--preset', () => { - describe('angular', () => { + describe('--preset', () => { + // TODO: reenable. This doesn't work because wrapAngularDevkit uses the fs + xdescribe('angular', () => { it('should create files (preset = angular)', async () => { await presetGenerator(tree, { name: 'proj', @@ -29,6 +29,20 @@ describe('preset', () => { ).toBe('@nrwl/angular'); }); }); + + describe('web-components', () => { + it('should create files (preset = web-components)', async () => { + await presetGenerator(tree, { + name: 'proj', + preset: 'web-components', + cli: 'nx', + }); + expect(tree.exists('/apps/proj/src/main.ts')).toBe(true); + expect(readJson(tree, '/workspace.json').cli.defaultCollection).toBe( + '@nrwl/web' + ); + }); + }); }); // it('should create files (preset = react)', async () => { @@ -43,17 +57,6 @@ describe('preset', () => { // ).toBe('@nrwl/react'); // }); // - // it('should create files (preset = web-components)', async () => { - // const tree = await runSchematic( - // 'preset', - // { name: 'proj', preset: 'web-components' }, - // tree - // ); - // expect(tree.exists('/apps/proj/src/main.ts')).toBe(true); - // expect( - // JSON.parse(tree.readContent('/workspace.json')).cli.defaultCollection - // ).toBe('@nrwl/web'); - // }); // // it('should create files (preset = next)', async () => { // const tree = await runSchematic( diff --git a/tsconfig.base.json b/tsconfig.base.json index 19f6acfecaa48..9b35894107069 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -24,6 +24,7 @@ "@nrwl/workspace/*": ["./packages/workspace/*"], "@nrwl/cli": ["./packages/cli"], "@nrwl/cli/*": ["./packages/cli/*"], + "@nrwl/cypress": ["./packages/cypress"], "@nrwl/express": ["./packages/express"], "@nrwl/nest": ["./packages/nest"], "@nrwl/next": ["./packages/next"],