/
githubmirror
/
angular-cli
Обзор
Документация
Войти
/
githubmirror
/
angular-cli
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
packages/schematics/angular/server/index_spec.ts
364 строки
15 KB
Alan Agius
refactor(@schematics/angular): remove third-party typescript copy
28 апр 2026, 14:35
28 апр 2026, 14:35
c6dd57a
Код
Авторство
О чём код?
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing'; import { parse as parseJson } from 'jsonc-parser'; import { CompilerOptions } from 'typescript'; import { Schema as ApplicationOptions, Style } from '../application/schema'; import { NodeDependencyType, addPackageJsonDependency } from '../utility/dependencies'; import { Builders } from '../utility/workspace-models'; import { Schema as WorkspaceOptions } from '../workspace/schema'; import { Schema as ServerOptions } from './schema'; describe('Server Schematic', () => { const schematicRunner = new SchematicTestRunner( '@schematics/angular', require.resolve('../collection.json'), ); const defaultOptions: ServerOptions = { project: 'bar', }; const workspaceOptions: WorkspaceOptions = { name: 'workspace', newProjectRoot: 'projects', version: '6.0.0', }; const appOptions: ApplicationOptions = { name: 'bar', inlineStyle: false, inlineTemplate: false, routing: false, style: Style.Css, skipTests: false, skipPackageJson: false, }; let appTree: UnitTestTree; beforeEach(async () => { appTree = await schematicRunner.runSchematic('workspace', workspaceOptions); }); describe('non standalone application', () => { beforeEach(async () => { appTree = await schematicRunner.runSchematic( 'application', { ...appOptions, standalone: false }, appTree, ); }); it('should create a root module file', async () => { const tree = await schematicRunner.runSchematic('server', defaultOptions, appTree); const filePath = '/projects/bar/src/app/app.module.server.ts'; expect(tree.exists(filePath)).toBeTrue(); }); it('should create a main file', async () => { const tree = await schematicRunner.runSchematic('server', defaultOptions, appTree); const filePath = '/projects/bar/src/main.server.ts'; expect(tree.exists(filePath)).toBeTrue(); const contents = tree.readContent(filePath); expect(contents).toContain( `export { AppServerModule as default } from './app/app.module.server';`, ); }); it('should account for renamed app component and module', async () => { appTree.create( '/projects/bar/src/app/my-custom-module.ts', ` import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { MyCustomApp } from './foo/bar/baz/app.foo'; @NgModule({ declarations: [MyCustomApp], imports: [BrowserModule], bootstrap: [MyCustomApp] }) export class MyCustomModule {} `, ); appTree.overwrite( '/projects/bar/src/main.ts', ` import { platformBrowser } from '@angular/platform-browser'; import { MyCustomModule } from './app/my-custom-module'; platformBrowser().bootstrapModule(MyCustomModule) .catch(err => console.error(err)); `, ); const tree = await schematicRunner.runSchematic('server', defaultOptions, appTree); const filePath = '/projects/bar/src/app/app.module.server.ts'; expect(tree.exists(filePath)).toBeTrue(); const contents = tree.readContent(filePath); expect(contents).toContain(`import { MyCustomApp } from './foo/bar/baz/app.foo';`); expect(contents).toContain(`import { MyCustomModule } from './my-custom-module';`); expect(contents).toContain(`imports: [MyCustomModule],`); expect(contents).toContain(`bootstrap: [MyCustomApp],`); }); it('should account for renamed app component and module that have been aliased', async () => { appTree.create( '/projects/bar/src/app/my-custom-module.ts', ` import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { MyCustomApp as MyAliasedApp } from './foo/bar/baz/app.foo'; @NgModule({ declarations: [MyAliasedApp], imports: [BrowserModule], bootstrap: [MyAliasedApp] }) export class MyCustomModule {} `, ); appTree.overwrite( '/projects/bar/src/main.ts', ` import { platformBrowser } from '@angular/platform-browser'; import { MyCustomModule as MyAliasedModule } from './app/my-custom-module'; platformBrowser().bootstrapModule(MyAliasedModule) .catch(err => console.error(err)); `, ); const tree = await schematicRunner.runSchematic('server', defaultOptions, appTree); const filePath = '/projects/bar/src/app/app.module.server.ts'; expect(tree.exists(filePath)).toBeTrue(); const contents = tree.readContent(filePath); expect(contents).toContain(`import { MyCustomApp } from './foo/bar/baz/app.foo';`); expect(contents).toContain(`import { MyCustomModule } from './my-custom-module';`); expect(contents).toContain(`imports: [MyCustomModule],`); expect(contents).toContain(`bootstrap: [MyCustomApp],`); }); it('should add dependency: @angular/platform-server', async () => { const tree = await schematicRunner.runSchematic('server', defaultOptions, appTree); const filePath = '/package.json'; const contents = tree.readContent(filePath); expect(contents).toMatch(/"@angular\/platform-server": "/); }); it('should install npm dependencies', async () => { await schematicRunner.runSchematic('server', defaultOptions, appTree); expect(schematicRunner.tasks.length).toBe(1); expect(schematicRunner.tasks[0].name).toBe('node-package'); expect((schematicRunner.tasks[0].options as { command: string }).command).toBe('install'); }); it('should update tsconfig.app.json', async () => { const tree = await schematicRunner.runSchematic('server', defaultOptions, appTree); const filePath = '/projects/bar/tsconfig.app.json'; const contents = parseJson(tree.readContent(filePath).toString()); expect(contents.compilerOptions.types).toEqual(['node']); }); it(`should add 'provideClientHydration' to the providers list`, async () => { const tree = await schematicRunner.runSchematic('server', defaultOptions, appTree); const contents = tree.readContent('/projects/bar/src/app/app-module.ts'); expect(contents).toContain(`provideClientHydration()`); }); }); describe('standalone application', () => { beforeEach(async () => { appTree = await schematicRunner.runSchematic('application', appOptions, appTree); }); it('should create not root module file', async () => { const tree = await schematicRunner.runSchematic('server', defaultOptions, appTree); const filePath = '/projects/bar/src/app/app.module.server.ts'; expect(tree.exists(filePath)).toEqual(false); }); it('should update workspace and add the server option', async () => { const tree = await schematicRunner.runSchematic('server', defaultOptions, appTree); const filePath = '/angular.json'; const contents = tree.readContent(filePath); const config = JSON.parse(contents.toString()); const targets = config.projects.bar.architect; expect(targets.build.options.server).toEqual('projects/bar/src/main.server.ts'); }); it('should create a main file', async () => { const tree = await schematicRunner.runSchematic('server', defaultOptions, appTree); const filePath = '/projects/bar/src/main.server.ts'; expect(tree.exists(filePath)).toBeTrue(); const contents = tree.readContent(filePath); expect(contents).toContain(`bootstrapApplication(App, config, context)`); }); it('should account for renamed app component', async () => { appTree.overwrite( '/projects/bar/src/main.ts', ` import { bootstrapApplication } from '@angular/platform-browser'; import { appConfig } from './app/app.config'; import { MyCustomApp } from './foo/bar/baz/app.foo'; bootstrapApplication(MyCustomApp, appConfig, context) .catch((err) => console.error(err)); `, ); const tree = await schematicRunner.runSchematic('server', defaultOptions, appTree); const filePath = '/projects/bar/src/main.server.ts'; expect(tree.exists(filePath)).toBeTrue(); const contents = tree.readContent(filePath); expect(contents).toContain(`import { MyCustomApp } from './foo/bar/baz/app.foo';`); expect(contents).toContain(`bootstrapApplication(MyCustomApp, config, context)`); }); it('should account for renamed app component that is aliased within the main file', async () => { appTree.overwrite( '/projects/bar/src/main.ts', ` import { bootstrapApplication } from '@angular/platform-browser'; import { appConfig } from './app/app.config'; import { MyCustomApp as MyCustomAlias } from './foo/bar/baz/app.foo'; bootstrapApplication(MyCustomAlias, appConfig) .catch((err) => console.error(err)); `, ); const tree = await schematicRunner.runSchematic('server', defaultOptions, appTree); const filePath = '/projects/bar/src/main.server.ts'; expect(tree.exists(filePath)).toBeTrue(); const contents = tree.readContent(filePath); expect(contents).toContain(`import { MyCustomApp } from './foo/bar/baz/app.foo';`); expect(contents).toContain(`bootstrapApplication(MyCustomApp, config, context)`); }); it('should create server app config file', async () => { const tree = await schematicRunner.runSchematic('server', defaultOptions, appTree); const filePath = '/projects/bar/src/app/app.config.server.ts'; expect(tree.exists(filePath)).toBeTrue(); const contents = tree.readContent(filePath); expect(contents).toContain(`const serverConfig: ApplicationConfig = {`); }); it(`should add 'provideClientHydration' to the providers list`, async () => { const tree = await schematicRunner.runSchematic('server', defaultOptions, appTree); const contents = tree.readContent('/projects/bar/src/app/app.config.ts'); expect(contents).toContain(`provideClientHydration()`); }); }); describe('Legacy browser builder', () => { function convertBuilderToLegacyBrowser(): void { const config = JSON.parse(appTree.readContent('/angular.json')); const build = config.projects.bar.architect.build; build.builder = Builders.Browser; build.options = { ...build.options, main: 'projects/bar/src/main.ts', browser: undefined, }; build.configurations.development = { ...build.configurations.development, vendorChunk: true, namedChunks: true, buildOptimizer: false, }; appTree.overwrite('/angular.json', JSON.stringify(config, undefined, 2)); } beforeEach(async () => { appTree = await schematicRunner.runSchematic('application', appOptions, appTree); convertBuilderToLegacyBrowser(); }); it(`should not add import to '@angular/localize' as type in 'tsconfig.server.json' when it's not a dependency`, async () => { const tree = await schematicRunner.runSchematic('server', defaultOptions, appTree); const { compilerOptions } = tree.readJson('/projects/bar/tsconfig.server.json') as { compilerOptions: CompilerOptions; }; expect(compilerOptions.types).not.toContain('@angular/localize'); }); it(`should add import to '@angular/localize' as type in 'tsconfig.server.json' when it's a dependency`, async () => { addPackageJsonDependency(appTree, { name: '@angular/localize', type: NodeDependencyType.Default, version: 'latest', }); const tree = await schematicRunner.runSchematic('server', defaultOptions, appTree); const { compilerOptions } = tree.readJson('/projects/bar/tsconfig.server.json') as { compilerOptions: CompilerOptions; }; expect(compilerOptions.types).toContain('@angular/localize'); }); it('should update workspace with a server target', async () => { const tree = await schematicRunner.runSchematic('server', defaultOptions, appTree); const filePath = '/angular.json'; const contents = tree.readContent(filePath); const config = JSON.parse(contents.toString()); const targets = config.projects.bar.architect; expect(targets.server).toBeDefined(); expect(targets.server.builder).toBeDefined(); const opts = targets.server.options; expect(opts.outputPath).toEqual('dist/bar/server'); expect(opts.main).toEqual('projects/bar/src/main.server.ts'); expect(opts.tsConfig).toEqual('projects/bar/tsconfig.server.json'); }); it('should update workspace with a build target outputPath', async () => { const tree = await schematicRunner.runSchematic('server', defaultOptions, appTree); const filePath = '/angular.json'; const contents = tree.readContent(filePath); const config = JSON.parse(contents.toString()); const targets = config.projects.bar.architect; expect(targets.build.options.outputPath).toEqual('dist/bar/browser'); }); it(`should work when 'tsconfig.app.json' has comments`, async () => { const appTsConfigPath = '/projects/bar/tsconfig.app.json'; const appTsConfigContent = appTree.readContent(appTsConfigPath); appTree.overwrite(appTsConfigPath, '// comment in json file\n' + appTsConfigContent); const tree = await schematicRunner.runSchematic('server', defaultOptions, appTree); const filePath = '/projects/bar/tsconfig.server.json'; expect(tree.exists(filePath)).toBeTrue(); }); it('should create a tsconfig file for a generated application', async () => { const tree = await schematicRunner.runSchematic('server', defaultOptions, appTree); const filePath = '/projects/bar/tsconfig.server.json'; expect(tree.exists(filePath)).toBeTrue(); const contents = parseJson(tree.readContent(filePath).toString()); expect(contents).toEqual({ extends: './tsconfig.app.json', compilerOptions: { outDir: '../../out-tsc/server', types: ['node'], }, files: ['src/main.server.ts'], }); const angularConfig = JSON.parse(tree.readContent('angular.json')); expect(angularConfig.projects.bar.architect.server.options.tsConfig).toEqual( 'projects/bar/tsconfig.server.json', ); }); }); });