diff --git a/.nvmrc b/.nvmrc index 832d385064..603606bc91 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -16.14.0 +18.17.0 diff --git a/schematics/ng-add/index.ts b/schematics/ng-add/index.ts index 3eec010760..7e832e6463 100644 --- a/schematics/ng-add/index.ts +++ b/schematics/ng-add/index.ts @@ -3,33 +3,51 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ -import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; +import { getProjectFromWorkspace } from '@angular/cdk/schematics'; + +import { chain, noop, Rule, schematic, SchematicContext, Tree } from '@angular-devkit/schematics'; import { NodePackageInstallTask, RunSchematicTask } from '@angular-devkit/schematics/tasks'; +import { getWorkspace } from '@schematics/angular/utility/workspace'; import { addPackageToPackageJson } from '../utils/package-config'; +import { getProjectStyle } from '../utils/project-style'; +// generated by scripts/schematics/set-version.ts // @ts-ignore import { hammerjsVersion, zorroVersion } from '../utils/version-names'; import { Schema } from './schema'; export default function (options: Schema): Rule { - return (host: Tree, context: SchematicContext) => { - // The CLI inserts `ng-zorro-antd` into the `package.json` before this schematic runs. - // This means that we do not need to insert Angular Material into `package.json` files again. - // In some cases though, it could happen that this schematic runs outside of the CLI `ng add` - // command, or Material is only listed a dev dependency. If that is the case, we insert a - // version based on the current build version (substituted version placeholder). - if (!options.skipPackageJson) { - addPackageToPackageJson(host, 'ng-zorro-antd', zorroVersion); - if (options.gestures) { - addPackageToPackageJson(host, 'hammerjs', hammerjsVersion); + return chain([ + (host: Tree, context: SchematicContext) => { + // The CLI inserts `ng-zorro-antd` into the `package.json` before this schematic runs. + // This means that we do not need to insert Angular Material into `package.json` files again. + // In some cases though, it could happen that this schematic runs outside of the CLI `ng add` + // command, or Material is only listed a dev dependency. If that is the case, we insert a + // version based on the current build version (substituted version placeholder). + if (!options.skipPackageJson) { + addPackageToPackageJson(host, 'ng-zorro-antd', zorroVersion); + if (options.gestures) { + addPackageToPackageJson(host, 'hammerjs', hammerjsVersion); + } } - } - // Since the Angular Material schematics depend on the schematic utility functions from the - // CDK, we need to install the CDK before loading the schematic files that import from the CDK. - if (!options.skipInstall) { - const installTaskId = context.addTask(new NodePackageInstallTask()); - context.addTask(new RunSchematicTask('ng-add-setup-project', options), [installTaskId]); - } - }; + // Since the Angular Material schematics depend on the schematic utility functions from the + // CDK, we need to install the CDK before loading the schematic files that import from the CDK. + if (!options.skipInstall) { + const installTaskId = context.addTask(new NodePackageInstallTask()); + context.addTask(new RunSchematicTask('ng-add-setup-project', options), [installTaskId]); + } + }, + options.template ? applyTemplate(options) : noop(), + ]); } + +function applyTemplate(options: Schema): Rule { + return async (host: Tree) => { + const workspace = await getWorkspace(host); + const project = getProjectFromWorkspace(workspace, options.project); + const style = getProjectStyle(project); + + return schematic(options.template, {...options, style}); + } +} \ No newline at end of file diff --git a/schematics/ng-add/setup-project/theming.ts b/schematics/ng-add/setup-project/theming.ts index 42bde93738..64fcd8c411 100644 --- a/schematics/ng-add/setup-project/theming.ts +++ b/schematics/ng-add/setup-project/theming.ts @@ -22,8 +22,11 @@ const defaultCustomThemeFilename = 'theme.less'; /** Object that maps a CLI target to its default builder name. */ const defaultTargetBuilders = { - build: '@angular-devkit/build-angular:application', - test: '@angular-devkit/build-angular:karma' + build: [ + '@angular-devkit/build-angular:application', // for esbuild + '@angular-devkit/build-angular:browser' // for webpack + ], + test: ['@angular-devkit/build-angular:karma'] }; /** Add pre-built styles to the main project style file. */ @@ -144,7 +147,7 @@ function validateDefaultTargetBuilder( ): boolean { const defaultBuilder = defaultTargetBuilders[targetName]; const targetConfig = project.targets && project.targets.get(targetName); - const isDefaultBuilder = targetConfig && targetConfig.builder === defaultBuilder; + const isDefaultBuilder = targetConfig && defaultBuilder.includes(targetConfig.builder); if (!isDefaultBuilder && targetName === 'build') { throw new SchematicsException( diff --git a/schematics/ng-add/standalone.spec.ts b/schematics/ng-add/standalone.spec.ts index e6732f65fe..48baa491e1 100644 --- a/schematics/ng-add/standalone.spec.ts +++ b/schematics/ng-add/standalone.spec.ts @@ -31,7 +31,7 @@ describe('[standalone] ng-add schematic', () => { beforeEach(async () => { runner = new SchematicTestRunner('schematics', require.resolve('../collection.json')); - appTree = await createTestApp(runner, { standalone: true }); + appTree = await createTestApp(runner); }); it('should update package.json', async () => { diff --git a/schematics/ng-component/index.spec.ts b/schematics/ng-component/index.spec.ts index f189037490..c1ad8b71db 100644 --- a/schematics/ng-component/index.spec.ts +++ b/schematics/ng-component/index.spec.ts @@ -3,7 +3,6 @@ import { SchematicTestRunner } from '@angular-devkit/schematics/testing'; import { ChangeDetection, Style } from '@schematics/angular/component/schema'; import { createTestApp } from '../testing/test-app'; -import {getFileContent} from "../utils/get-file-content"; const appOptions = { name: 'ng-zorro', @@ -34,7 +33,7 @@ describe('ng-component schematic', () => { beforeEach(async () => { runner = new SchematicTestRunner('schematics', require.resolve('../collection.json')); - appTree = await createTestApp(runner, { ...appOptions, standalone: true }); + appTree = await createTestApp(runner, { ...appOptions }); }); it('should create a component', async () => { diff --git a/schematics/ng-generate/blank/index.ts b/schematics/ng-generate/blank/index.ts index 57e2f7a4fd..858d70c063 100644 --- a/schematics/ng-generate/blank/index.ts +++ b/schematics/ng-generate/blank/index.ts @@ -27,7 +27,6 @@ export default function(options: Schema): Rule { const buffer = host.read(appHTMLFile); if (!buffer) { - context.logger.error( `Could not find the project ${appHTMLFile} file inside of the ` + `workspace config` ); diff --git a/schematics/ng-generate/side-menu/index.spec.ts b/schematics/ng-generate/side-menu/index.spec.ts index 70f6287063..25128e6f75 100644 --- a/schematics/ng-generate/side-menu/index.spec.ts +++ b/schematics/ng-generate/side-menu/index.spec.ts @@ -16,20 +16,20 @@ describe('side-menu schematic', () => { beforeEach(async () => { runner = new SchematicTestRunner('schematics', require.resolve('../../collection.json')); - appTree = await createTestApp(runner, { standalone: true }); + appTree = await createTestApp(runner, { standalone: false }); }); it('should create side-menu files', async () => { const options = { ...defaultOptions }; const tree = await runner.runSchematic('sidemenu', options, appTree); - const files = tree.files; - expect(files).toEqual( + + expect(tree.files).toEqual( jasmine.arrayContaining([ '/projects/ng-zorro/src/app/app.component.html', '/projects/ng-zorro/src/app/app.component.css', '/projects/ng-zorro/src/app/app.component.ts', - '/projects/ng-zorro/src/app/app.routes.ts', - '/projects/ng-zorro/src/app/pages/welcome/welcome.routes.ts', + '/projects/ng-zorro/src/app/app-routing.module.ts', + '/projects/ng-zorro/src/app/pages/welcome/welcome-routing.module.ts', '/projects/ng-zorro/src/app/pages/welcome/welcome.component.ts', '/projects/ng-zorro/src/app/pages/welcome/welcome.component.css', '/projects/ng-zorro/src/app/pages/welcome/welcome.component.html' @@ -37,50 +37,35 @@ describe('side-menu schematic', () => { ); }); - it('should set the style preprocessor correctly', async () => { - const options = { ...defaultOptions, style: Style.Less }; - const tree = await runner.runSchematic('sidemenu', options, appTree); - const files = tree.files; - const appContent = getFileContent(tree, '/projects/ng-zorro/src/app/app.component.ts'); - const welcomeContent = getFileContent(tree, '/projects/ng-zorro/src/app/pages/welcome/welcome.component.ts'); - expect(appContent).toContain('app.component.less'); - expect(welcomeContent).toContain('welcome.component.less'); - - expect(files).toEqual( - jasmine.arrayContaining([ - '/projects/ng-zorro/src/app/app.component.less', - '/projects/ng-zorro/src/app/pages/welcome/welcome.component.less' - ]) - ); - }); - - xit('should fall back to the @schematics/angular:component option value', async () => { - const options = { ...defaultOptions, template: 'sidemenu' }; - appTree = await createTestApp(runner, { style: Style.Less }); - const tree = await runner.runSchematic('ng-add', options, appTree); - - expect(tree.files).toEqual( - jasmine.arrayContaining([ - '/projects/ng-zorro/src/app/app.component.less', - '/projects/ng-zorro/src/app/pages/welcome/welcome.component.less' - ]) - ); - }); - - xit('should fall back to the @schematics/angular:component option value', async () => { - const options = { ...defaultOptions, template: 'sidemenu' }; - appTree = await createTestApp(runner, { inlineStyle: true }); - const tree = await runner.runSchematic('ng-add', options, appTree); - - expect(tree.files).not.toEqual('/projects/ng-zorro/src/app/pages/welcome/welcome.component.css'); - }); - - xit('should fall back to the @schematics/angular:component option value', async () => { - const options = { ...defaultOptions, template: 'sidemenu' }; - appTree = await createTestApp(runner, { inlineTemplate: true }); - const tree = await runner.runSchematic('ng-add', options, appTree); - - expect(tree.files).not.toEqual('/projects/ng-zorro/src/app/pages/welcome/welcome.component.html'); + describe('style option', () => { + it('should set the style preprocessor correctly', async () => { + const options = { ...defaultOptions, style: Style.Less }; + const tree = await runner.runSchematic('sidemenu', options, appTree); + const appContent = getFileContent(tree, '/projects/ng-zorro/src/app/app.component.ts'); + const welcomeContent = getFileContent(tree, '/projects/ng-zorro/src/app/pages/welcome/welcome.component.ts'); + expect(appContent).toContain('app.component.less'); + expect(welcomeContent).toContain('welcome.component.less'); + + expect(tree.files).toEqual( + jasmine.arrayContaining([ + '/projects/ng-zorro/src/app/app.component.less', + '/projects/ng-zorro/src/app/pages/welcome/welcome.component.less' + ]) + ); + }); + + it('should fall back to the @schematics/angular:component option value', async () => { + const options = { ...defaultOptions, template: 'sidemenu' }; + appTree = await createTestApp(runner, { style: Style.Less, standalone: false }); + const tree = await runner.runSchematic('ng-add', options, appTree); + + expect(tree.files).toEqual( + jasmine.arrayContaining([ + '/projects/ng-zorro/src/app/app.component.less', + '/projects/ng-zorro/src/app/pages/welcome/welcome.component.less' + ]) + ); + }); }); it('should set the prefix correctly', async () => { diff --git a/schematics/ng-generate/side-menu/index.ts b/schematics/ng-generate/side-menu/index.ts index c4c2cc9b84..2324fa4d98 100644 --- a/schematics/ng-generate/side-menu/index.ts +++ b/schematics/ng-generate/side-menu/index.ts @@ -32,35 +32,12 @@ export default function (options: Schema): Rule { const project = getProjectFromWorkspace(workspace, options.project); const mainFile = getProjectMainFile(project); const prefix = options.prefix || project.prefix; - - if (isStandaloneApp(host, mainFile)) { - return chain([ - mergeWith( - apply(url('./standalone/src'), [ - applyTemplates({ - ...strings, - ...options, - prefix - }), - move(project.sourceRoot as string), - forEach((fileEntry: FileEntry) => { - if (host.exists(fileEntry.path)) { - host.overwrite(fileEntry.path, fileEntry.content); - } - return fileEntry; - }) - ]), - MergeStrategy.Overwrite - ), - addRootProvider(options.project, ({ code, external }) => { - return code`${external('provideNzIcons', './icons-provider')}()`; - }) - ]); - } + const isStandalone = isStandaloneApp(host, mainFile); + const templateSourcePath = isStandalone ? './standalone' : './files'; return chain([ mergeWith( - apply(url('./files/src'), [ + apply(url(`${templateSourcePath}/src`), [ applyTemplates({ ...strings, ...options, @@ -76,10 +53,17 @@ export default function (options: Schema): Rule { ]), MergeStrategy.Overwrite ), - addModule('AppRoutingModule', './app-routing.module', options.project), - addModule('IconsProviderModule', './icons-provider.module', options.project), - addModule('NzLayoutModule', 'ng-zorro-antd/layout', options.project), - addModule('NzMenuModule', 'ng-zorro-antd/menu', options.project) + isStandalone ? + addRootProvider(options.project, ({ code, external }) => { + return code`${external('provideNzIcons', './icons-provider')}()`; + }) + : + chain([ + addModule('AppRoutingModule', './app-routing.module', options.project), + addModule('IconsProviderModule', './icons-provider.module', options.project), + addModule('NzLayoutModule', 'ng-zorro-antd/layout', options.project), + addModule('NzMenuModule', 'ng-zorro-antd/menu', options.project) + ]) ]); }; } diff --git a/schematics/ng-generate/side-menu/standalone.spec.ts b/schematics/ng-generate/side-menu/standalone.spec.ts index 6e4abddde2..9c5af74d0c 100644 --- a/schematics/ng-generate/side-menu/standalone.spec.ts +++ b/schematics/ng-generate/side-menu/standalone.spec.ts @@ -16,7 +16,7 @@ describe('[standalone] side-menu schematic', () => { beforeEach(async () => { runner = new SchematicTestRunner('schematics', require.resolve('../../collection.json')); - appTree = await createTestApp(runner, { standalone: true }); + appTree = await createTestApp(runner); }); it('should create side-menu files', async () => { @@ -38,61 +38,48 @@ describe('[standalone] side-menu schematic', () => { ); }); - it('should mark components as standalone', async () => { + it('should infer the standalone option from the project structure', async () => { const options = { ...defaultOptions }; const tree = await runner.runSchematic('sidemenu', options, appTree); const appContent = getFileContent(tree, '/projects/ng-zorro/src/app/app.component.ts'); - const welcomeContent = getFileContent(tree, '/projects/ng-zorro/src/app/pages/welcome/welcome.component.ts'); + + expect(tree.exists('/projects/material/src/app/app.module.ts')).toBe(false); expect(appContent).toContain('standalone: true'); - expect(welcomeContent).toContain('standalone: true'); + expect(appContent).toContain('imports: ['); }); - it('should set the style preprocessor correctly', async () => { - const options = { ...defaultOptions, style: Style.Less }; - const tree = await runner.runSchematic('sidemenu', options, appTree); - const files = tree.files; - const appContent = getFileContent(tree, '/projects/ng-zorro/src/app/app.component.ts'); - const welcomeContent = getFileContent(tree, '/projects/ng-zorro/src/app/pages/welcome/welcome.component.ts'); - - expect(appContent).toContain('app.component.less'); - expect(welcomeContent).toContain('welcome.component.less'); - - expect(files).toEqual( - jasmine.arrayContaining([ - '/projects/ng-zorro/src/app/app.component.less', - '/projects/ng-zorro/src/app/pages/welcome/welcome.component.less' - ]) - ); - }); - - xit('should fall back to the @schematics/angular:component option value', async () => { - const options = { ...defaultOptions, template: 'sidemenu' }; - appTree = await createTestApp(runner, { style: Style.Less, standalone: true }); - const tree = await runner.runSchematic('ng-add', options, appTree); - - expect(tree.files).toEqual( - jasmine.arrayContaining([ - '/projects/ng-zorro/src/app/app.component.less', - '/projects/ng-zorro/src/app/pages/welcome/welcome.component.less' - ]) - ); - }); - - it('should fall back to the @schematics/angular:component option value', async () => { - const options = { ...defaultOptions, template: 'sidemenu' }; - appTree = await createTestApp(runner, { inlineStyle: true, standalone: true }); - const tree = await runner.runSchematic('ng-add', options, appTree); - - expect(tree.files).not.toEqual('/projects/ng-zorro/src/app/pages/welcome/welcome.component.css'); - }); - - it('should fall back to the @schematics/angular:component option value', async () => { - const options = { ...defaultOptions, template: 'sidemenu' }; - appTree = await createTestApp(runner, { inlineTemplate: true, standalone: true }); - const tree = await runner.runSchematic('ng-add', options, appTree); - - expect(tree.files).not.toEqual('/projects/ng-zorro/src/app/pages/welcome/welcome.component.html'); + describe('style option', () => { + it('should set the style preprocessor correctly', async () => { + const options = { ...defaultOptions, style: Style.Less }; + const tree = await runner.runSchematic('sidemenu', options, appTree); + const files = tree.files; + const appContent = getFileContent(tree, '/projects/ng-zorro/src/app/app.component.ts'); + const welcomeContent = getFileContent(tree, '/projects/ng-zorro/src/app/pages/welcome/welcome.component.ts'); + + expect(appContent).toContain('app.component.less'); + expect(welcomeContent).toContain('welcome.component.less'); + + expect(files).toEqual( + jasmine.arrayContaining([ + '/projects/ng-zorro/src/app/app.component.less', + '/projects/ng-zorro/src/app/pages/welcome/welcome.component.less' + ]) + ); + }); + + it('should fall back to the @schematics/angular:component option value', async () => { + const options = { ...defaultOptions, template: 'sidemenu' }; + appTree = await createTestApp(runner, { style: Style.Less, standalone: true }); + const tree = await runner.runSchematic('ng-add', options, appTree); + + expect(tree.files).toEqual( + jasmine.arrayContaining([ + '/projects/ng-zorro/src/app/app.component.less', + '/projects/ng-zorro/src/app/pages/welcome/welcome.component.less' + ]) + ); + }); }); it('should set the prefix correctly', async () => { diff --git a/schematics/ng-generate/topnav/index.spec.ts b/schematics/ng-generate/topnav/index.spec.ts index 4ea497c911..9d53ef4666 100644 --- a/schematics/ng-generate/topnav/index.spec.ts +++ b/schematics/ng-generate/topnav/index.spec.ts @@ -16,7 +16,7 @@ describe('top-nav schematic', () => { beforeEach(async () => { runner = new SchematicTestRunner('schematics', require.resolve('../../collection.json')); - appTree = await createTestApp(runner, {name: 'ng-zorro-top-nav'}); + appTree = await createTestApp(runner, {name: 'ng-zorro-top-nav', standalone: false}); }); it('should create top-nav files', async () => { @@ -29,8 +29,8 @@ describe('top-nav schematic', () => { '/projects/ng-zorro-top-nav/src/app/app.component.html', '/projects/ng-zorro-top-nav/src/app/app.component.css', '/projects/ng-zorro-top-nav/src/app/app.component.ts', - '/projects/ng-zorro-top-nav/src/app/app.routes.ts', - '/projects/ng-zorro-top-nav/src/app/pages/welcome/welcome.routes.ts', + '/projects/ng-zorro-top-nav/src/app/app-routing.module.ts', + '/projects/ng-zorro-top-nav/src/app/pages/welcome/welcome-routing.module.ts', '/projects/ng-zorro-top-nav/src/app/pages/welcome/welcome.component.ts', '/projects/ng-zorro-top-nav/src/app/pages/welcome/welcome.component.css', '/projects/ng-zorro-top-nav/src/app/pages/welcome/welcome.component.html' @@ -66,5 +66,4 @@ describe('top-nav schematic', () => { expect(appContent).toContain(`selector: 'nz-root'`); expect(welcomeContent).toContain(`selector: 'nz-welcome'`); }); - }); diff --git a/schematics/ng-generate/topnav/standalone.spec.ts b/schematics/ng-generate/topnav/standalone.spec.ts index 959517a0c5..611e037046 100644 --- a/schematics/ng-generate/topnav/standalone.spec.ts +++ b/schematics/ng-generate/topnav/standalone.spec.ts @@ -16,7 +16,7 @@ describe('[standalone] top-nav schematic', () => { beforeEach(async () => { runner = new SchematicTestRunner('schematics', require.resolve('../../collection.json')); - appTree = await createTestApp(runner, {name: 'ng-zorro-top-nav', standalone: true}); + appTree = await createTestApp(runner, {name: 'ng-zorro-top-nav'}); }); it('should create top-nav files', async () => { @@ -38,14 +38,15 @@ describe('[standalone] top-nav schematic', () => { ); }); - it('should mark components as standalone', async () => { + it('should fall back to the @schematics/angular:component option value', async () => { const options = {...defaultOptions}; const tree = await runner.runSchematic('topnav', options, appTree); const appContent = getFileContent(tree, '/projects/ng-zorro-top-nav/src/app/app.component.ts'); - const welcomeContent = getFileContent(tree, '/projects/ng-zorro-top-nav/src/app/pages/welcome/welcome.component.ts'); + + expect(tree.exists('/projects/material/src/app/app.module.ts')).toBe(false); expect(appContent).toContain('standalone: true'); - expect(welcomeContent).toContain('standalone: true'); + expect(appContent).toContain('imports: ['); }); it('should set the style preprocessor correctly', async () => { @@ -76,5 +77,4 @@ describe('[standalone] top-nav schematic', () => { expect(appContent).toContain(`selector: 'nz-root'`); expect(welcomeContent).toContain(`selector: 'nz-welcome'`); }); - });