From 921f1c18266ead602c2e2c627a171608507807d4 Mon Sep 17 00:00:00 2001 From: HyperLifelll9 Date: Tue, 20 Feb 2024 23:56:14 +0800 Subject: [PATCH] feat: improve schematics (#8411) --- schematics/ng-add/index.spec.ts | 49 ++------ .../setup-project/add-animations-module.ts | 107 ------------------ .../setup-project/add-required-modules.ts | 5 +- .../setup-project/add-required-providers.ts | 34 ++++++ schematics/ng-add/setup-project/index.ts | 4 +- schematics/ng-add/standalone.spec.ts | 54 +-------- 6 files changed, 51 insertions(+), 202 deletions(-) delete mode 100644 schematics/ng-add/setup-project/add-animations-module.ts create mode 100644 schematics/ng-add/setup-project/add-required-providers.ts diff --git a/schematics/ng-add/index.spec.ts b/schematics/ng-add/index.spec.ts index 566fb3e1a5..f3ad2b6a83 100644 --- a/schematics/ng-add/index.spec.ts +++ b/schematics/ng-add/index.spec.ts @@ -1,9 +1,4 @@ -import { - addModuleImportToRootModule, - getProjectFromWorkspace, - getProjectTargetOptions, - addImportToModule -} from '@angular/cdk/schematics'; +import { getProjectFromWorkspace, getProjectTargetOptions } from '@angular/cdk/schematics'; import { normalize } from '@angular-devkit/core'; import { WorkspaceDefinition } from '@angular-devkit/core/src/workspace'; @@ -129,58 +124,28 @@ describe('ng-add schematic', () => { expect(assetsString).toContain(iconPathSegment); }); - it('should required modules', async () => { + it('should required modules and providers', async () => { const options = { ...defaultOptions }; const tree = await runner.runSchematic('ng-add-setup-project', options, appTree); const fileContent = getFileContent(tree, '/projects/ng-zorro/src/app/app.module.ts'); + expect(fileContent).toContain('provideHttpClient()'); expect(fileContent).toContain('FormsModule'); - expect(fileContent).toContain('HttpClientModule'); }); - it('should add browserAnimationsModuleName if animations is enable', async () => { + it('should add provideAnimationsAsync() function call if animations is enable', async () => { const options = { ...defaultOptions, animations: true }; const tree = await runner.runSchematic('ng-add-setup-project', options, appTree); const fileContent = getFileContent(tree, '/projects/ng-zorro/src/app/app.module.ts'); - expect(fileContent).toContain('BrowserAnimationsModule'); + expect(fileContent).toContain('provideAnimationsAsync()'); }); - it('should add noopAnimationsModuleName if animations is disable', async () => { + it(`should add provideAnimationsAsync('noop') function call if animations is disable`, async () => { const options = { ...defaultOptions, animations: false }; const tree = await runner.runSchematic('ng-add-setup-project', options, appTree); const fileContent = getFileContent(tree, '/projects/ng-zorro/src/app/app.module.ts'); - expect(fileContent).toContain('NoopAnimationsModule'); - }); - - it('should not add BrowserAnimationsModule if NoopAnimationsModule is set up', async () => { - const options = { ...defaultOptions, animations: true }; - - const workspace = await getWorkspace(appTree); - const project = getProjectFromWorkspace(workspace, defaultOptions.project); - - addModuleImportToRootModule(appTree, 'NoopAnimationsModule', '@angular/platform-browser/animations', project); - - const tree = await runner.runSchematic('ng-add-setup-project', options, appTree); - const fileContent = getFileContent(tree, '/projects/ng-zorro/src/app/app.module.ts'); - - expect(fileContent).toContain('NoopAnimationsModule'); - expect(fileContent).not.toContain('BrowserAnimationsModule'); - }); - - it('should not add NoopAnimationsModule if BrowserAnimationsModule is set up', async () => { - const options = { ...defaultOptions, animations: false }; - - const workspace = await getWorkspace(appTree); - const project = getProjectFromWorkspace(workspace as unknown as WorkspaceDefinition, defaultOptions.project); - - addModuleImportToRootModule(appTree, 'BrowserAnimationsModule', '@angular/platform-browser/animations', project); - - const tree = await runner.runSchematic('ng-add-setup-project', options, appTree); - const fileContent = getFileContent(tree, '/projects/ng-zorro/src/app/app.module.ts'); - - expect(fileContent).not.toContain('NoopAnimationsModule'); - expect(fileContent).toContain('BrowserAnimationsModule'); + expect(fileContent).toContain(`provideAnimationsAsync('noop')`); }); it('should register default locale id', async () => { diff --git a/schematics/ng-add/setup-project/add-animations-module.ts b/schematics/ng-add/setup-project/add-animations-module.ts deleted file mode 100644 index 74af5cc9ed..0000000000 --- a/schematics/ng-add/setup-project/add-animations-module.ts +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE - */ - -import { - addModuleImportToRootModule, - getAppModulePath, - getProjectFromWorkspace, - getProjectMainFile, - hasNgModuleImport, - isStandaloneApp -} from '@angular/cdk/schematics'; - -import { ProjectDefinition } from '@angular-devkit/core/src/workspace'; -import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; -import { - addFunctionalProvidersToStandaloneBootstrap, - callsProvidersFunction, - importsProvidersFrom -} from '@schematics/angular/private/standalone'; -import { getWorkspace } from '@schematics/angular/utility/workspace'; - -import { Schema } from '../schema'; - -export function addAnimationsModule(options: Schema): Rule { - return async (host: Tree, context: SchematicContext) => { - const workspace = await getWorkspace(host); - const project = getProjectFromWorkspace(workspace, options.project); - const mainFilePath = getProjectMainFile(project); - if (isStandaloneApp(host, mainFilePath)) { - addAnimationsToStandaloneApp(host, mainFilePath, context, options); - } else { - addAnimationsToNonStandaloneApp(host, project, mainFilePath, context, options); - } - }; -} - -/** Adds the animations module to an app that is bootstrap using the standalone component APIs. */ -function addAnimationsToStandaloneApp(host: Tree, mainFile: string, context: SchematicContext, options: Schema): void { - const animationsFunction = 'provideAnimations'; - const noopAnimationsFunction = 'provideNoopAnimations'; - - if (options.animations) { - // In case the project explicitly uses provideNoopAnimations, we should print a warning - // message that makes the user aware of the fact that we won't automatically set up - // animations. If we would add provideAnimations while provideNoopAnimations - // is already configured, we would cause unexpected behavior and runtime exceptions. - if (callsProvidersFunction(host, mainFile, noopAnimationsFunction)) { - context.logger.error( - `Could not add "${animationsFunction}" ` + `because "${noopAnimationsFunction}" is already provided.` - ); - context.logger.info(`Please manually set up browser animations.`); - } else { - addFunctionalProvidersToStandaloneBootstrap( - host, - mainFile, - animationsFunction, - '@angular/platform-browser/animations' - ); - } - } else if (!options.animations && !importsProvidersFrom(host, mainFile, animationsFunction)) { - // Do not add the provideNoopAnimations if the project already explicitly uses - // the provideAnimations. - addFunctionalProvidersToStandaloneBootstrap( - host, - mainFile, - noopAnimationsFunction, - '@angular/platform-browser/animations' - ); - } -} - -/** - * Adds the animations module to an app that is bootstrap - * using the non-standalone component APIs. - */ -function addAnimationsToNonStandaloneApp( - host: Tree, - project: ProjectDefinition, - mainFile: string, - context: SchematicContext, - options: Schema -): void { - const browserAnimationsModuleName = 'BrowserAnimationsModule'; - const noopAnimationsModuleName = 'NoopAnimationsModule'; - const appModulePath = getAppModulePath(host, mainFile); - if (options.animations) { - // In case the project explicitly uses the NoopAnimationsModule, we should print a warning - // message that makes the user aware of the fact that we won't automatically set up - // animations. If we would add the BrowserAnimationsModule while the NoopAnimationsModule - // is already configured, we would cause unexpected behavior and runtime exceptions. - if (hasNgModuleImport(host, appModulePath, noopAnimationsModuleName)) { - context.logger.error( - `Could not set up "${browserAnimationsModuleName}" ` + - `because "${noopAnimationsModuleName}" is already imported.` - ); - context.logger.info(`Please manually set up browser animations.`); - } else { - addModuleImportToRootModule(host, browserAnimationsModuleName, '@angular/platform-browser/animations', project); - } - } else if (!options.animations && !hasNgModuleImport(host, appModulePath, browserAnimationsModuleName)) { - // Do not add the NoopAnimationsModule module if the project already explicitly uses - // the BrowserAnimationsModule. - addModuleImportToRootModule(host, noopAnimationsModuleName, '@angular/platform-browser/animations', project); - } -} diff --git a/schematics/ng-add/setup-project/add-required-modules.ts b/schematics/ng-add/setup-project/add-required-modules.ts index ce3c6db389..fb1bfc0f92 100644 --- a/schematics/ng-add/setup-project/add-required-modules.ts +++ b/schematics/ng-add/setup-project/add-required-modules.ts @@ -9,13 +9,12 @@ import { addRootImport } from '@schematics/angular/utility'; import { Schema } from '../schema'; const modulesMap = { - FormsModule: '@angular/forms', - HttpClientModule: '@angular/common/http' + FormsModule: '@angular/forms' }; export function addRequiredModules(options: Schema): Rule { const rules = Object.entries(modulesMap).map(([symbolName, moduleName]) => { - return addRootImport(options.project, ({code, external}) => { + return addRootImport(options.project, ({ code, external }) => { return code`${external(symbolName, moduleName)}`; }); }); diff --git a/schematics/ng-add/setup-project/add-required-providers.ts b/schematics/ng-add/setup-project/add-required-providers.ts new file mode 100644 index 0000000000..68551c7a80 --- /dev/null +++ b/schematics/ng-add/setup-project/add-required-providers.ts @@ -0,0 +1,34 @@ +/** + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE + */ + +import { Rule, chain } from '@angular-devkit/schematics'; +import { addRootProvider } from '@schematics/angular/utility'; + +import { Schema } from '../schema'; + +export function addRequiredProviders(options: Schema): Rule { + return chain([ + addAnimations(options), + addHttpClient(options) + ]); +} + +function addAnimations(options: Schema): Rule { + return addRootProvider(options.project, ({ code, external }) => { + return code`${external( + 'provideAnimationsAsync', + '@angular/platform-browser/animations/async', + )}(${options.animations ? '' : `'noop'`})`; + }); +} + +function addHttpClient(options: Schema): Rule { + return addRootProvider(options.project, ({ code, external }) => { + return code`${external( + 'provideHttpClient', + '@angular/common/http', + )}()`; + }); +} diff --git a/schematics/ng-add/setup-project/index.ts b/schematics/ng-add/setup-project/index.ts index f9452a65c5..0db2d6dbb1 100644 --- a/schematics/ng-add/setup-project/index.ts +++ b/schematics/ng-add/setup-project/index.ts @@ -6,9 +6,9 @@ import { chain, noop, Rule } from '@angular-devkit/schematics'; import { Schema } from '../schema'; -import { addAnimationsModule } from './add-animations-module'; import { addIconToAssets } from './add-icon-assets'; import { addRequiredModules } from './add-required-modules'; +import { addRequiredProviders } from './add-required-providers'; import { hammerjsImport } from './hammerjs-import'; import { registerLocale } from './register-locale'; import { addThemeToAppStyles } from './theming'; @@ -17,7 +17,7 @@ export default function (options: Schema): Rule { return chain([ registerLocale(options), addRequiredModules(options), - addAnimationsModule(options), + addRequiredProviders(options), addThemeToAppStyles(options), options.dynamicIcon ? addIconToAssets(options) : noop(), options.gestures ? hammerjsImport(options) : noop() diff --git a/schematics/ng-add/standalone.spec.ts b/schematics/ng-add/standalone.spec.ts index 48baa491e1..d6c1a5f140 100644 --- a/schematics/ng-add/standalone.spec.ts +++ b/schematics/ng-add/standalone.spec.ts @@ -4,14 +4,12 @@ */ import { getProjectFromWorkspace, getProjectTargetOptions } from '@angular/cdk/schematics'; -import { firstValueFrom } from 'rxjs'; import { normalize } from '@angular-devkit/core'; import { WorkspaceDefinition } from '@angular-devkit/core/src/workspace'; import { Tree } from '@angular-devkit/schematics'; import { NodePackageName } from '@angular-devkit/schematics/tasks/package-manager/options'; import { SchematicTestRunner } from '@angular-devkit/schematics/testing'; -import { addRootImport } from '@schematics/angular/utility'; import { getWorkspace } from '@schematics/angular/utility/workspace'; import { join } from 'path'; @@ -132,69 +130,29 @@ describe('[standalone] ng-add schematic', () => { expect(assetsString).toContain(iconPathSegment); }); - it('should required modules', async () => { + it('should required modules and providers', async () => { const options = { ...defaultOptions }; const tree = await runner.runSchematic('ng-add-setup-project', options, appTree); const fileContent = getFileContent(tree, '/projects/ng-zorro/src/app/app.config.ts'); + expect(fileContent).toContain('provideHttpClient()'); expect(fileContent).toContain('FormsModule'); - expect(fileContent).toContain('HttpClientModule'); }); - it('should add browserAnimationsModuleName if animations is enable', async () => { + it('should add provideAnimationsAsync() call function if animations is enable', async () => { const options = { ...defaultOptions, animations: true }; const tree = await runner.runSchematic('ng-add-setup-project', options, appTree); const fileContent = getFileContent(tree, '/projects/ng-zorro/src/app/app.config.ts'); - expect(fileContent).toContain('provideAnimations'); + expect(fileContent).toContain('provideAnimationsAsync()'); }); - it('should add noopAnimationsModuleName if animations is disable', async () => { + it(`should add provideAnimationsAsync('noop') function call if animations is disable`, async () => { const options = { ...defaultOptions, animations: false }; const tree = await runner.runSchematic('ng-add-setup-project', options, appTree); const fileContent = getFileContent(tree, '/projects/ng-zorro/src/app/app.config.ts'); - expect(fileContent).toContain('provideNoopAnimations'); - }); - - it('should not add BrowserAnimationsModule if NoopAnimationsModule is set up', async () => { - const options = { ...defaultOptions, animations: true }; - - // Add NoopAnimationsModule - await firstValueFrom( - runner.callRule( - addRootImport(options.project, ({ code, external }) => { - return code`${external('NoopAnimationsModule', '@angular/platform-browser/animations')}`; - }), - appTree - ) - ); - - const tree = await runner.runSchematic('ng-add-setup-project', options, appTree); - const fileContent = getFileContent(tree, '/projects/ng-zorro/src/app/app.config.ts'); - - expect(fileContent).toContain('NoopAnimationsModule'); - expect(fileContent).not.toContain('BrowserAnimationsModule'); - }); - - it('should not add NoopAnimationsModule if BrowserAnimationsModule is set up', async () => { - const options = { ...defaultOptions, animations: false }; - - // import BrowserAnimationsModule - await firstValueFrom( - runner.callRule( - addRootImport(options.project, ({ code, external }) => { - return code`${external('BrowserAnimationsModule', '@angular/platform-browser/animations')}`; - }), - appTree - ) - ); - - const tree = await runner.runSchematic('ng-add-setup-project', options, appTree); - const fileContent = getFileContent(tree, '/projects/ng-zorro/src/app/app.config.ts'); - - expect(fileContent).not.toContain('NoopAnimationsModule'); - expect(fileContent).toContain('BrowserAnimationsModule'); + expect(fileContent).toContain(`provideAnimationsAsync('noop')`); }); it('should register default locale id', async () => {