diff --git a/packages/schematics/angular/app-shell/index_spec.ts b/packages/schematics/angular/app-shell/index_spec.ts
index 46c6bd1caad6..86c033fc8e96 100644
--- a/packages/schematics/angular/app-shell/index_spec.ts
+++ b/packages/schematics/angular/app-shell/index_spec.ts
@@ -40,63 +40,71 @@ describe('App Shell Schematic', () => {
beforeEach(async () => {
appTree = await schematicRunner.runSchematic('workspace', workspaceOptions);
- appTree = await schematicRunner.runSchematic('application', appOptions, appTree);
});
- it('should add app shell configuration', async () => {
- const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
- const filePath = '/angular.json';
- const content = tree.readContent(filePath);
- const workspace = JSON.parse(content);
- const target = workspace.projects.bar.architect['build'];
- expect(target.configurations.production.appShell).toBeTrue();
- });
+ describe('non standalone application', () => {
+ beforeEach(async () => {
+ appTree = await schematicRunner.runSchematic(
+ 'application',
+ { ...appOptions, standalone: false },
+ appTree,
+ );
+ });
- it('should ensure the client app has a router-outlet', async () => {
- appTree = await schematicRunner.runSchematic('workspace', workspaceOptions);
- appTree = await schematicRunner.runSchematic(
- 'application',
- { ...appOptions, routing: false },
- appTree,
- );
- await expectAsync(
- schematicRunner.runSchematic('app-shell', defaultOptions, appTree),
- ).toBeRejected();
- });
+ it('should add app shell configuration', async () => {
+ const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
+ const filePath = '/angular.json';
+ const content = tree.readContent(filePath);
+ const workspace = JSON.parse(content);
+ const target = workspace.projects.bar.architect['build'];
+ expect(target.configurations.production.appShell).toBeTrue();
+ });
- it('should add a server app', async () => {
- const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
- const filePath = '/projects/bar/src/app/app.module.server.ts';
- expect(tree.exists(filePath)).toEqual(true);
- });
+ it('should ensure the client app has a router-outlet', async () => {
+ appTree = await schematicRunner.runSchematic('workspace', workspaceOptions);
+ appTree = await schematicRunner.runSchematic(
+ 'application',
+ { ...appOptions, routing: false },
+ appTree,
+ );
+ await expectAsync(
+ schematicRunner.runSchematic('app-shell', defaultOptions, appTree),
+ ).toBeRejected();
+ });
- it('should add router module to client app module', async () => {
- const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
- const filePath = '/projects/bar/src/app/app.module.ts';
- const content = tree.readContent(filePath);
- expect(content).toMatch(/import { RouterModule } from '@angular\/router';/);
- });
+ it('should add a server app', async () => {
+ const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
+ const filePath = '/projects/bar/src/app/app.module.server.ts';
+ expect(tree.exists(filePath)).toEqual(true);
+ });
- it('should not fail when AppModule have imported RouterModule already', async () => {
- const updateRecorder = appTree.beginUpdate('/projects/bar/src/app/app.module.ts');
- updateRecorder.insertLeft(0, "import { RouterModule } from '@angular/router';");
- appTree.commitUpdate(updateRecorder);
+ it('should add router module to client app module', async () => {
+ const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
+ const filePath = '/projects/bar/src/app/app.module.ts';
+ const content = tree.readContent(filePath);
+ expect(content).toMatch(/import { RouterModule } from '@angular\/router';/);
+ });
- const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
- const filePath = '/projects/bar/src/app/app.module.ts';
- const content = tree.readContent(filePath);
- expect(content).toMatch(/import { RouterModule } from '@angular\/router';/);
- });
+ it('should not fail when AppModule have imported RouterModule already', async () => {
+ const updateRecorder = appTree.beginUpdate('/projects/bar/src/app/app.module.ts');
+ updateRecorder.insertLeft(0, "import { RouterModule } from '@angular/router';");
+ appTree.commitUpdate(updateRecorder);
- describe('Add router-outlet', () => {
- function makeInlineTemplate(tree: UnitTestTree, template?: string): void {
- template =
- template ||
- `
+ const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
+ const filePath = '/projects/bar/src/app/app.module.ts';
+ const content = tree.readContent(filePath);
+ expect(content).toMatch(/import { RouterModule } from '@angular\/router';/);
+ });
+
+ describe('Add router-outlet', () => {
+ function makeInlineTemplate(tree: UnitTestTree, template?: string): void {
+ template =
+ template ||
+ `
App works!
`;
- const newText = `
+ const newText = `
import { Component } from '@angular/core';
@Component({
@@ -109,112 +117,100 @@ describe('App Shell Schematic', () => {
export class AppComponent { }
`;
- tree.overwrite('/projects/bar/src/app/app.component.ts', newText);
- tree.delete('/projects/bar/src/app/app.component.html');
- }
-
- it('should not re-add the router outlet (external template)', async () => {
- const htmlPath = '/projects/bar/src/app/app.component.html';
- appTree.overwrite(htmlPath, '');
- const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
- const content = tree.readContent(htmlPath);
- const matches = content.match(/<\/router-outlet>/g);
- const numMatches = matches ? matches.length : 0;
- expect(numMatches).toEqual(1);
+ tree.overwrite('/projects/bar/src/app/app.component.ts', newText);
+ tree.delete('/projects/bar/src/app/app.component.html');
+ }
+
+ it('should not re-add the router outlet (external template)', async () => {
+ const htmlPath = '/projects/bar/src/app/app.component.html';
+ appTree.overwrite(htmlPath, '');
+ const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
+ const content = tree.readContent(htmlPath);
+ const matches = content.match(/<\/router-outlet>/g);
+ const numMatches = matches ? matches.length : 0;
+ expect(numMatches).toEqual(1);
+ });
+
+ it('should not re-add the router outlet (inline template)', async () => {
+ makeInlineTemplate(appTree, '');
+ const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
+ const content = tree.readContent('/projects/bar/src/app/app.component.ts');
+ const matches = content.match(/<\/router-outlet>/g);
+ const numMatches = matches ? matches.length : 0;
+ expect(numMatches).toEqual(1);
+ });
});
- it('should not re-add the router outlet (inline template)', async () => {
- makeInlineTemplate(appTree, '');
+ it('should add router imports to server module', async () => {
const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
- const content = tree.readContent('/projects/bar/src/app/app.component.ts');
- const matches = content.match(/<\/router-outlet>/g);
- const numMatches = matches ? matches.length : 0;
- expect(numMatches).toEqual(1);
+ const filePath = '/projects/bar/src/app/app.module.server.ts';
+ const content = tree.readContent(filePath);
+ expect(content).toMatch(/import { Routes, RouterModule } from '@angular\/router';/);
});
- });
-
- it('should add router imports to server module', async () => {
- const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
- const filePath = '/projects/bar/src/app/app.module.server.ts';
- const content = tree.readContent(filePath);
- expect(content).toMatch(/import { Routes, RouterModule } from '@angular\/router';/);
- });
- it('should work if server config was added prior to running the app-shell schematic', async () => {
- let tree = await schematicRunner.runSchematic('server', defaultOptions, appTree);
- tree = await schematicRunner.runSchematic('app-shell', defaultOptions, tree);
- expect(tree.exists('/projects/bar/src/app/app-shell/app-shell.component.ts')).toBe(true);
- });
+ it('should work if server config was added prior to running the app-shell schematic', async () => {
+ let tree = await schematicRunner.runSchematic('server', defaultOptions, appTree);
+ tree = await schematicRunner.runSchematic('app-shell', defaultOptions, tree);
+ expect(tree.exists('/projects/bar/src/app/app-shell/app-shell.component.ts')).toBe(true);
+ });
- it('should define a server route', async () => {
- const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
- const filePath = '/projects/bar/src/app/app.module.server.ts';
- const content = tree.readContent(filePath);
- expect(content).toMatch(/const routes: Routes = \[/);
- });
+ it('should define a server route', async () => {
+ const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
+ const filePath = '/projects/bar/src/app/app.module.server.ts';
+ const content = tree.readContent(filePath);
+ expect(content).toMatch(/const routes: Routes = \[/);
+ });
- it('should import RouterModule with forRoot', async () => {
- const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
- const filePath = '/projects/bar/src/app/app.module.server.ts';
- const content = tree.readContent(filePath);
- expect(content).toMatch(
- /const routes: Routes = \[ { path: 'shell', component: AppShellComponent }\];/,
- );
- expect(content).toMatch(/ServerModule,\r?\n\s*RouterModule\.forRoot\(routes\),/);
- });
+ it('should import RouterModule with forRoot', async () => {
+ const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
+ const filePath = '/projects/bar/src/app/app.module.server.ts';
+ const content = tree.readContent(filePath);
+ expect(content).toMatch(
+ /const routes: Routes = \[ { path: 'shell', component: AppShellComponent }\];/,
+ );
+ expect(content).toMatch(/ServerModule,\r?\n\s*RouterModule\.forRoot\(routes\),/);
+ });
- it('should create the shell component', async () => {
- const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
- expect(tree.exists('/projects/bar/src/app/app-shell/app-shell.component.ts')).toBe(true);
- const content = tree.readContent('/projects/bar/src/app/app.module.server.ts');
- expect(content).toMatch(/app-shell\.component/);
+ it('should create the shell component', async () => {
+ const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
+ expect(tree.exists('/projects/bar/src/app/app-shell/app-shell.component.ts')).toBe(true);
+ const content = tree.readContent('/projects/bar/src/app/app.module.server.ts');
+ expect(content).toMatch(/app-shell\.component/);
+ });
});
describe('standalone application', () => {
- const standaloneAppName = 'baz';
- const standaloneAppOptions: ApplicationOptions = {
- ...appOptions,
- name: standaloneAppName,
- standalone: true,
- };
- const defaultStandaloneOptions: AppShellOptions = {
- project: standaloneAppName,
- };
-
beforeEach(async () => {
- appTree = await schematicRunner.runSchematic('application', standaloneAppOptions, appTree);
+ appTree = await schematicRunner.runSchematic('application', appOptions, appTree);
});
it('should ensure the client app has a router-outlet', async () => {
- appTree = await schematicRunner.runSchematic('workspace', workspaceOptions);
+ const appName = 'baz';
appTree = await schematicRunner.runSchematic(
'application',
- { ...standaloneAppOptions, routing: false },
+ {
+ ...appOptions,
+ name: appName,
+ routing: false,
+ },
appTree,
);
+
await expectAsync(
- schematicRunner.runSchematic('app-shell', defaultStandaloneOptions, appTree),
+ schematicRunner.runSchematic('app-shell', { ...defaultOptions, project: appName }, appTree),
).toBeRejected();
});
it('should create the shell component', async () => {
- const tree = await schematicRunner.runSchematic(
- 'app-shell',
- defaultStandaloneOptions,
- appTree,
- );
- expect(tree.exists('/projects/baz/src/app/app-shell/app-shell.component.ts')).toBe(true);
- const content = tree.readContent('/projects/baz/src/app/app.config.server.ts');
+ const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
+ expect(tree.exists('/projects/bar/src/app/app-shell/app-shell.component.ts')).toBe(true);
+ const content = tree.readContent('/projects/bar/src/app/app.config.server.ts');
expect(content).toMatch(/app-shell\.component/);
});
it('should define a server route', async () => {
- const tree = await schematicRunner.runSchematic(
- 'app-shell',
- defaultStandaloneOptions,
- appTree,
- );
- const filePath = '/projects/baz/src/app/app.config.server.ts';
+ const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
+ const filePath = '/projects/bar/src/app/app.config.server.ts';
const content = tree.readContent(filePath);
expect(tags.oneLine`${content}`).toContain(tags.oneLine`{
provide: ROUTES,
@@ -229,23 +225,15 @@ describe('App Shell Schematic', () => {
});
it(`should add import to 'ROUTES' token from '@angular/router'`, async () => {
- const tree = await schematicRunner.runSchematic(
- 'app-shell',
- defaultStandaloneOptions,
- appTree,
- );
- const filePath = '/projects/baz/src/app/app.config.server.ts';
+ const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
+ const filePath = '/projects/bar/src/app/app.config.server.ts';
const content = tree.readContent(filePath);
expect(content).toContain(`import { ROUTES } from '@angular/router';`);
});
it(`should add import to 'AppShellComponent'`, async () => {
- const tree = await schematicRunner.runSchematic(
- 'app-shell',
- defaultStandaloneOptions,
- appTree,
- );
- const filePath = '/projects/baz/src/app/app.config.server.ts';
+ const tree = await schematicRunner.runSchematic('app-shell', defaultOptions, appTree);
+ const filePath = '/projects/bar/src/app/app.config.server.ts';
const content = tree.readContent(filePath);
expect(content).toContain(
`import { AppShellComponent } from './app-shell/app-shell.component';`,
@@ -275,7 +263,8 @@ describe('App Shell Schematic', () => {
appTree.overwrite('/angular.json', JSON.stringify(config, undefined, 2));
}
- beforeEach(() => {
+ beforeEach(async () => {
+ appTree = await schematicRunner.runSchematic('application', appOptions, appTree);
convertBuilderToLegacyBrowser();
});
diff --git a/packages/schematics/angular/application/index.ts b/packages/schematics/angular/application/index.ts
index 83e906143a19..5967c138fdd0 100644
--- a/packages/schematics/angular/application/index.ts
+++ b/packages/schematics/angular/application/index.ts
@@ -37,13 +37,6 @@ export default function (options: ApplicationOptions): Rule {
const { appDir, appRootSelector, componentOptions, folderName, sourceDir } =
await getAppOptions(host, options);
- if (options.standalone) {
- context.logger.warn(
- 'Standalone application structure is new and not yet supported by many existing' +
- ` 'ng add' and 'ng update' integrations with community libraries.`,
- );
- }
-
return chain([
addAppToWorkspaceFile(options, appDir, folderName),
options.standalone
@@ -183,20 +176,14 @@ function addAppToWorkspaceFile(
];
schematicsWithTests.forEach((type) => {
- if (!(`@schematics/angular:${type}` in schematics)) {
- schematics[`@schematics/angular:${type}`] = {};
- }
- (schematics[`@schematics/angular:${type}`] as JsonObject).skipTests = true;
+ ((schematics[`@schematics/angular:${type}`] ??= {}) as JsonObject).skipTests = true;
});
}
- if (options.standalone) {
+ if (!options.standalone) {
const schematicsWithStandalone = ['component', 'directive', 'pipe'];
schematicsWithStandalone.forEach((type) => {
- if (!(`@schematics/angular:${type}` in schematics)) {
- schematics[`@schematics/angular:${type}`] = {};
- }
- (schematics[`@schematics/angular:${type}`] as JsonObject).standalone = true;
+ ((schematics[`@schematics/angular:${type}`] ??= {}) as JsonObject).standalone = false;
});
}
diff --git a/packages/schematics/angular/application/index_spec.ts b/packages/schematics/angular/application/index_spec.ts
index d6d97b964089..7ab935992295 100644
--- a/packages/schematics/angular/application/index_spec.ts
+++ b/packages/schematics/angular/application/index_spec.ts
@@ -40,12 +40,13 @@ describe('Application Schematic', () => {
});
it('should create all files of an application', async () => {
- const options = { ...defaultOptions };
-
- const tree = await schematicRunner.runSchematic('application', options, workspaceTree);
+ const tree = await schematicRunner.runSchematic(
+ 'application',
+ { ...defaultOptions, standalone: false },
+ workspaceTree,
+ );
- const files = tree.files;
- expect(files).toEqual(
+ expect(tree.files).toEqual(
jasmine.arrayContaining([
'/projects/foo/tsconfig.app.json',
'/projects/foo/tsconfig.spec.json',
@@ -89,52 +90,6 @@ describe('Application Schematic', () => {
expect(workspace.projects.foo.prefix).toEqual('pre');
});
- it('should handle the routing flag', async () => {
- const options = { ...defaultOptions, routing: true };
-
- const tree = await schematicRunner.runSchematic('application', options, workspaceTree);
-
- const files = tree.files;
- expect(files).toContain('/projects/foo/src/app/app.module.ts');
- expect(files).toContain('/projects/foo/src/app/app-routing.module.ts');
- const moduleContent = tree.readContent('/projects/foo/src/app/app.module.ts');
- expect(moduleContent).toMatch(/import { AppRoutingModule } from '.\/app-routing.module'/);
- const routingModuleContent = tree.readContent('/projects/foo/src/app/app-routing.module.ts');
- expect(routingModuleContent).toMatch(/RouterModule.forRoot\(routes\)/);
- });
-
- it('should import BrowserModule in the app module', async () => {
- const tree = await schematicRunner.runSchematic('application', defaultOptions, workspaceTree);
-
- const path = '/projects/foo/src/app/app.module.ts';
- const content = tree.readContent(path);
- expect(content).toMatch(/import { BrowserModule } from '@angular\/platform-browser';/);
- });
-
- it('should declare app component in the app module', async () => {
- const tree = await schematicRunner.runSchematic('application', defaultOptions, workspaceTree);
-
- const path = '/projects/foo/src/app/app.module.ts';
- const content = tree.readContent(path);
- expect(content).toMatch(/import { AppComponent } from '\.\/app\.component';/);
- });
-
- it(`should set 'defaultEncapsulation' in main.ts when 'ViewEncapsulation' is provided`, async () => {
- const tree = await schematicRunner.runSchematic(
- 'application',
- {
- ...defaultOptions,
- viewEncapsulation: ViewEncapsulation.ShadowDom,
- },
- workspaceTree,
- );
-
- const path = '/projects/foo/src/main.ts';
- const content = tree.readContent(path);
- expect(content).toContain('defaultEncapsulation: ViewEncapsulation.ShadowDom');
- expect(content).toContain(`import { ViewEncapsulation } from '@angular/core'`);
- });
-
it('should set the right paths in the tsconfig.app.json', async () => {
const tree = await schematicRunner.runSchematic('application', defaultOptions, workspaceTree);
@@ -214,85 +169,6 @@ describe('Application Schematic', () => {
});
});
- it('should create correct files when using minimal', async () => {
- const options = { ...defaultOptions, minimal: true };
- const tree = await schematicRunner.runSchematic('application', options, workspaceTree);
-
- const files = tree.files;
- [
- '/projects/foo/tsconfig.spec.json',
- '/projects/foo/src/app/app.component.css',
- '/projects/foo/src/app/app.component.html',
- '/projects/foo/src/app/app.component.spec.ts',
- ].forEach((x) => expect(files).not.toContain(x));
-
- expect(files).toEqual(
- jasmine.arrayContaining([
- '/projects/foo/tsconfig.app.json',
- '/projects/foo/src/favicon.ico',
- '/projects/foo/src/index.html',
- '/projects/foo/src/main.ts',
- '/projects/foo/src/styles.css',
- '/projects/foo/src/app/app.module.ts',
- '/projects/foo/src/app/app.component.ts',
- ]),
- );
- });
-
- it('should create correct files when using minimal and inlineStyle=false', async () => {
- const options = { ...defaultOptions, minimal: true, inlineStyle: false };
- const tree = await schematicRunner.runSchematic('application', options, workspaceTree);
-
- const files = tree.files;
- [
- '/projects/foo/tsconfig.spec.json',
- '/projects/foo/karma.conf.js',
- '/projects/foo/src/test.ts',
- '/projects/foo/src/app/app.component.html',
- '/projects/foo/src/app/app.component.spec.ts',
- ].forEach((x) => expect(files).not.toContain(x));
-
- expect(files).toEqual(
- jasmine.arrayContaining([
- '/projects/foo/tsconfig.app.json',
- '/projects/foo/src/favicon.ico',
- '/projects/foo/src/index.html',
- '/projects/foo/src/main.ts',
- '/projects/foo/src/styles.css',
- '/projects/foo/src/app/app.module.ts',
- '/projects/foo/src/app/app.component.css',
- '/projects/foo/src/app/app.component.ts',
- ]),
- );
- });
-
- it('should create correct files when using minimal and inlineTemplate=false', async () => {
- const options = { ...defaultOptions, minimal: true, inlineTemplate: false };
- const tree = await schematicRunner.runSchematic('application', options, workspaceTree);
-
- const files = tree.files;
- [
- '/projects/foo/tsconfig.spec.json',
- '/projects/foo/karma.conf.js',
- '/projects/foo/src/test.ts',
- '/projects/foo/src/app/app.component.css',
- '/projects/foo/src/app/app.component.spec.ts',
- ].forEach((x) => expect(files).not.toContain(x));
-
- expect(files).toEqual(
- jasmine.arrayContaining([
- '/projects/foo/tsconfig.app.json',
- '/projects/foo/src/favicon.ico',
- '/projects/foo/src/index.html',
- '/projects/foo/src/main.ts',
- '/projects/foo/src/styles.css',
- '/projects/foo/src/app/app.module.ts',
- '/projects/foo/src/app/app.component.html',
- '/projects/foo/src/app/app.component.ts',
- ]),
- );
- });
-
describe(`update package.json`, () => {
it(`should add build-angular to devDependencies`, async () => {
const tree = await schematicRunner.runSchematic('application', defaultOptions, workspaceTree);
@@ -357,7 +233,6 @@ describe('Application Schematic', () => {
'/src/index.html',
'/src/main.ts',
'/src/styles.css',
- '/src/app/app.module.ts',
'/src/app/app.component.css',
'/src/app/app.component.html',
'/src/app/app.component.spec.ts',
@@ -524,82 +399,238 @@ describe('Application Schematic', () => {
expect(cfg.projects['@myscope/myapp']).toBeDefined();
});
- describe('standalone', () => {
- it('should create all files of a standalone application', async () => {
- const options = { ...defaultOptions, standalone: true };
+ it('should create correct files when using minimal', async () => {
+ const options = { ...defaultOptions, minimal: true };
+ const tree = await schematicRunner.runSchematic('application', options, workspaceTree);
- const tree = await schematicRunner.runSchematic('application', options, workspaceTree);
+ const files = tree.files;
+ [
+ '/projects/foo/tsconfig.spec.json',
+ '/projects/foo/src/app/app.component.css',
+ '/projects/foo/src/app/app.component.html',
+ '/projects/foo/src/app/app.component.spec.ts',
+ ].forEach((x) => expect(files).not.toContain(x));
- const files = tree.files;
- expect(files).toEqual(
- jasmine.arrayContaining([
- '/projects/foo/tsconfig.app.json',
- '/projects/foo/tsconfig.spec.json',
- '/projects/foo/src/favicon.ico',
- '/projects/foo/src/index.html',
- '/projects/foo/src/main.ts',
- '/projects/foo/src/styles.css',
- '/projects/foo/src/app/app.config.ts',
- '/projects/foo/src/app/app.component.css',
- '/projects/foo/src/app/app.component.html',
- '/projects/foo/src/app/app.component.spec.ts',
- '/projects/foo/src/app/app.component.ts',
- ]),
- );
- });
+ expect(files).toEqual(
+ jasmine.arrayContaining([
+ '/projects/foo/tsconfig.app.json',
+ '/projects/foo/src/favicon.ico',
+ '/projects/foo/src/index.html',
+ '/projects/foo/src/main.ts',
+ '/projects/foo/src/styles.css',
+ '/projects/foo/src/app/app.component.ts',
+ ]),
+ );
+ });
- it('should not create any module files', async () => {
- const options = { ...defaultOptions, standalone: true };
+ it('should create correct files when using minimal and inlineStyle=false', async () => {
+ const options = { ...defaultOptions, minimal: true, inlineStyle: false };
+ const tree = await schematicRunner.runSchematic('application', options, workspaceTree);
- const tree = await schematicRunner.runSchematic('application', options, workspaceTree);
- const moduleFiles = tree.files.filter((file) => file.endsWith('.module.ts'));
- expect(moduleFiles.length).toEqual(0);
- });
+ const files = tree.files;
+ [
+ '/projects/foo/tsconfig.spec.json',
+ '/projects/foo/karma.conf.js',
+ '/projects/foo/src/test.ts',
+ '/projects/foo/src/app/app.component.html',
+ '/projects/foo/src/app/app.component.spec.ts',
+ ].forEach((x) => expect(files).not.toContain(x));
- it('should create a standalone component', async () => {
- const options = { ...defaultOptions, standalone: true };
+ expect(files).toEqual(
+ jasmine.arrayContaining([
+ '/projects/foo/tsconfig.app.json',
+ '/projects/foo/src/favicon.ico',
+ '/projects/foo/src/index.html',
+ '/projects/foo/src/main.ts',
+ '/projects/foo/src/styles.css',
+ '/projects/foo/src/app/app.component.css',
+ '/projects/foo/src/app/app.component.ts',
+ ]),
+ );
+ });
- const tree = await schematicRunner.runSchematic('application', options, workspaceTree);
+ it('should create correct files when using minimal and inlineTemplate=false', async () => {
+ const options = { ...defaultOptions, minimal: true, inlineTemplate: false };
+ const tree = await schematicRunner.runSchematic('application', options, workspaceTree);
+
+ const files = tree.files;
+ [
+ '/projects/foo/tsconfig.spec.json',
+ '/projects/foo/karma.conf.js',
+ '/projects/foo/src/test.ts',
+ '/projects/foo/src/app/app.component.css',
+ '/projects/foo/src/app/app.component.spec.ts',
+ ].forEach((x) => expect(files).not.toContain(x));
+
+ expect(files).toEqual(
+ jasmine.arrayContaining([
+ '/projects/foo/tsconfig.app.json',
+ '/projects/foo/src/favicon.ico',
+ '/projects/foo/src/index.html',
+ '/projects/foo/src/main.ts',
+ '/projects/foo/src/styles.css',
+ '/projects/foo/src/app/app.component.html',
+ '/projects/foo/src/app/app.component.ts',
+ ]),
+ );
+ });
+
+ it('should create all files of a standalone application', async () => {
+ const options = { ...defaultOptions, standalone: true };
+
+ const tree = await schematicRunner.runSchematic('application', options, workspaceTree);
- const component = tree.readContent('/projects/foo/src/app/app.component.ts');
- expect(component).toMatch(/standalone: true/);
+ const files = tree.files;
+ expect(files).toEqual(
+ jasmine.arrayContaining([
+ '/projects/foo/tsconfig.app.json',
+ '/projects/foo/tsconfig.spec.json',
+ '/projects/foo/src/favicon.ico',
+ '/projects/foo/src/index.html',
+ '/projects/foo/src/main.ts',
+ '/projects/foo/src/styles.css',
+ '/projects/foo/src/app/app.config.ts',
+ '/projects/foo/src/app/app.component.css',
+ '/projects/foo/src/app/app.component.html',
+ '/projects/foo/src/app/app.component.spec.ts',
+ '/projects/foo/src/app/app.component.ts',
+ ]),
+ );
+ });
+
+ it('should not create any module files', async () => {
+ const options = { ...defaultOptions, standalone: true };
+
+ const tree = await schematicRunner.runSchematic('application', options, workspaceTree);
+ const moduleFiles = tree.files.filter((file) => file.endsWith('.module.ts'));
+ expect(moduleFiles.length).toEqual(0);
+ });
+
+ it('should create a standalone component', async () => {
+ const options = { ...defaultOptions, standalone: true };
+
+ const tree = await schematicRunner.runSchematic('application', options, workspaceTree);
+
+ const component = tree.readContent('/projects/foo/src/app/app.component.ts');
+ expect(component).toMatch(/standalone: true/);
+ });
+
+ it('should create routing information by default', async () => {
+ const options = { ...defaultOptions, standalone: true };
+
+ const tree = await schematicRunner.runSchematic('application', options, workspaceTree);
+
+ expect(tree.files).toContain('/projects/foo/src/app/app.routes.ts');
+
+ const component = tree.readContent('/projects/foo/src/app/app.component.ts');
+ expect(component).toContain(`import { RouterOutlet } from '@angular/router';`);
+ expect(component).toContain(`imports: [CommonModule, RouterOutlet]`);
+
+ const config = tree.readContent('/projects/foo/src/app/app.config.ts');
+ expect(config).toContain(`import { provideRouter } from '@angular/router';`);
+ expect(config).toContain(`import { routes } from './app.routes';`);
+ expect(config).toContain('provideRouter(routes)');
+ });
+
+ it('should create a main.ts', async () => {
+ const options = { ...defaultOptions, standalone: true };
+ const tree = await schematicRunner.runSchematic('application', options, workspaceTree);
+
+ const main = tree.readContent('/projects/foo/src/main.ts');
+ expect(main).toContain('bootstrapApplication');
+ });
+
+ describe('standalone=false', () => {
+ it(`should set 'defaultEncapsulation' in main.ts when 'ViewEncapsulation' is provided`, async () => {
+ const tree = await schematicRunner.runSchematic(
+ 'application',
+ {
+ ...defaultOptions,
+ standalone: false,
+ viewEncapsulation: ViewEncapsulation.ShadowDom,
+ },
+ workspaceTree,
+ );
+
+ const path = '/projects/foo/src/main.ts';
+ const content = tree.readContent(path);
+ expect(content).toContain('defaultEncapsulation: ViewEncapsulation.ShadowDom');
+ expect(content).toContain(`import { ViewEncapsulation } from '@angular/core'`);
});
- it('should create routing information by default', async () => {
- const options = { ...defaultOptions, standalone: true };
+ it('should handle the routing flag', async () => {
+ const options = { ...defaultOptions, routing: true, standalone: false };
const tree = await schematicRunner.runSchematic('application', options, workspaceTree);
- expect(tree.files).toContain('/projects/foo/src/app/app.routes.ts');
+ const files = tree.files;
+ expect(files).toContain('/projects/foo/src/app/app.module.ts');
+ expect(files).toContain('/projects/foo/src/app/app-routing.module.ts');
+ const moduleContent = tree.readContent('/projects/foo/src/app/app.module.ts');
+ expect(moduleContent).toMatch(/import { AppRoutingModule } from '.\/app-routing.module'/);
+ const routingModuleContent = tree.readContent('/projects/foo/src/app/app-routing.module.ts');
+ expect(routingModuleContent).toMatch(/RouterModule.forRoot\(routes\)/);
+ });
- const component = tree.readContent('/projects/foo/src/app/app.component.ts');
- expect(component).toContain(`import { RouterOutlet } from '@angular/router';`);
- expect(component).toContain(`imports: [CommonModule, RouterOutlet]`);
+ it('should import BrowserModule in the app module', async () => {
+ const tree = await schematicRunner.runSchematic(
+ 'application',
+ { ...defaultOptions, standalone: false },
+ workspaceTree,
+ );
- const config = tree.readContent('/projects/foo/src/app/app.config.ts');
- expect(config).toContain(`import { provideRouter } from '@angular/router';`);
- expect(config).toContain(`import { routes } from './app.routes';`);
- expect(config).toContain('provideRouter(routes)');
+ const path = '/projects/foo/src/app/app.module.ts';
+ const content = tree.readContent(path);
+ expect(content).toMatch(/import { BrowserModule } from '@angular\/platform-browser';/);
});
- it('should create a main.ts', async () => {
- const options = { ...defaultOptions, standalone: true };
- const tree = await schematicRunner.runSchematic('application', options, workspaceTree);
+ it('should declare app component in the app module', async () => {
+ const tree = await schematicRunner.runSchematic(
+ 'application',
+ { ...defaultOptions, standalone: false },
+ workspaceTree,
+ );
- const main = tree.readContent('/projects/foo/src/main.ts');
- expect(main).toContain('bootstrapApplication');
+ const path = '/projects/foo/src/app/app.module.ts';
+ const content = tree.readContent(path);
+ expect(content).toMatch(/import { AppComponent } from '\.\/app\.component';/);
});
- it('should set the default schematic options to be standalone', async () => {
- const options = { ...defaultOptions, standalone: true };
+ it('should create all files of an application', async () => {
+ const options = { ...defaultOptions, standalone: false };
+
const tree = await schematicRunner.runSchematic('application', options, workspaceTree);
+ const files = tree.files;
+ expect(files).toEqual(
+ jasmine.arrayContaining([
+ '/projects/foo/tsconfig.app.json',
+ '/projects/foo/tsconfig.spec.json',
+ '/projects/foo/src/main.ts',
+ '/projects/foo/src/styles.css',
+ '/projects/foo/src/app/app-routing.module.ts',
+ '/projects/foo/src/app/app.module.ts',
+ '/projects/foo/src/app/app.component.css',
+ '/projects/foo/src/app/app.component.html',
+ '/projects/foo/src/app/app.component.spec.ts',
+ '/projects/foo/src/app/app.component.ts',
+ ]),
+ );
+ });
+
+ it('should set the default schematic options to be standalone=false', async () => {
+ const tree = await schematicRunner.runSchematic(
+ 'application',
+ { ...defaultOptions, standalone: false },
+ workspaceTree,
+ );
+
const workspace = JSON.parse(tree.readContent('/angular.json'));
expect(workspace.projects.foo.schematics).toEqual(
jasmine.objectContaining({
- '@schematics/angular:component': { standalone: true },
- '@schematics/angular:directive': { standalone: true },
- '@schematics/angular:pipe': { standalone: true },
+ '@schematics/angular:component': { standalone: false },
+ '@schematics/angular:directive': { standalone: false },
+ '@schematics/angular:pipe': { standalone: false },
}),
);
});
diff --git a/packages/schematics/angular/application/schema.json b/packages/schematics/angular/application/schema.json
index 95936b544b45..a71c7b358ddd 100644
--- a/packages/schematics/angular/application/schema.json
+++ b/packages/schematics/angular/application/schema.json
@@ -105,7 +105,7 @@
"standalone": {
"description": "Creates an application based upon the standalone API, without NgModules.",
"type": "boolean",
- "default": false,
+ "default": true,
"x-user-analytics": "ep.ng_standalone"
}
},
diff --git a/packages/schematics/angular/component/index_spec.ts b/packages/schematics/angular/component/index_spec.ts
index 627f503dea3c..c45c8fde106a 100644
--- a/packages/schematics/angular/component/index_spec.ts
+++ b/packages/schematics/angular/component/index_spec.ts
@@ -47,29 +47,14 @@ describe('Component Schematic', () => {
skipTests: false,
skipPackageJson: false,
};
+
let appTree: UnitTestTree;
+
beforeEach(async () => {
appTree = await schematicRunner.runSchematic('workspace', workspaceOptions);
appTree = await schematicRunner.runSchematic('application', appOptions, appTree);
});
- it('should create a component', async () => {
- const options = { ...defaultOptions };
- const tree = await schematicRunner.runSchematic('component', options, appTree);
- const files = tree.files;
- expect(files).toEqual(
- jasmine.arrayContaining([
- '/projects/bar/src/app/foo/foo.component.css',
- '/projects/bar/src/app/foo/foo.component.html',
- '/projects/bar/src/app/foo/foo.component.spec.ts',
- '/projects/bar/src/app/foo/foo.component.ts',
- ]),
- );
- const moduleContent = tree.readContent('/projects/bar/src/app/app.module.ts');
- expect(moduleContent).toMatch(/import.*Foo.*from '.\/foo\/foo.component'/);
- expect(moduleContent).toMatch(/declarations:\s*\[[^\]]+?,\r?\n\s+FooComponent\r?\n/m);
- });
-
it('should set change detection to OnPush', async () => {
const options = { ...defaultOptions, changeDetection: 'OnPush' };
@@ -117,50 +102,6 @@ describe('Component Schematic', () => {
);
});
- it('should find the closest module', async () => {
- const options = { ...defaultOptions };
- const fooModule = '/projects/bar/src/app/foo/foo.module.ts';
- appTree.create(
- fooModule,
- `
- import { NgModule } from '@angular/core';
-
- @NgModule({
- imports: [],
- declarations: []
- })
- export class FooModule { }
- `,
- );
-
- const tree = await schematicRunner.runSchematic('component', options, appTree);
- const fooModuleContent = tree.readContent(fooModule);
- expect(fooModuleContent).toMatch(/import { FooComponent } from '.\/foo.component'/);
- });
-
- it('should export the component', async () => {
- const options = { ...defaultOptions, export: true };
-
- const tree = await schematicRunner.runSchematic('component', options, appTree);
- const appModuleContent = tree.readContent('/projects/bar/src/app/app.module.ts');
- expect(appModuleContent).toMatch(/exports: \[\n(\s*) {2}FooComponent\n\1\]/);
- });
-
- it('should import into a specified module', async () => {
- const options = { ...defaultOptions, module: 'app.module.ts' };
-
- const tree = await schematicRunner.runSchematic('component', options, appTree);
- const appModule = tree.readContent('/projects/bar/src/app/app.module.ts');
-
- expect(appModule).toMatch(/import { FooComponent } from '.\/foo\/foo.component'/);
- });
-
- it('should fail if specified module does not exist', async () => {
- const options = { ...defaultOptions, module: '/projects/bar/src/app.moduleXXX.ts' };
-
- await expectAsync(schematicRunner.runSchematic('component', options, appTree)).toBeRejected();
- });
-
it('should handle upper case paths', async () => {
const pathOption = 'projects/bar/src/app/SOME/UPPER/DIR';
const options = { ...defaultOptions, path: pathOption };
@@ -310,42 +251,6 @@ describe('Component Schematic', () => {
expect(tree.files).toContain('/projects/bar/src/app/foo/foo.html');
});
- it('should use the module flag even if the module is a routing module', async () => {
- const routingFileName = 'app-routing.module.ts';
- const routingModulePath = `/projects/bar/src/app/${routingFileName}`;
- const newTree = createAppModule(appTree, routingModulePath);
- const options = { ...defaultOptions, module: routingFileName };
- const tree = await schematicRunner.runSchematic('component', options, newTree);
- const content = tree.readContent(routingModulePath);
- expect(content).toMatch(/import { FooComponent } from '.\/foo\/foo.component/);
- });
-
- it('should handle a path in the name option', async () => {
- const options = { ...defaultOptions, name: 'dir/test-component' };
-
- const tree = await schematicRunner.runSchematic('component', options, appTree);
- const content = tree.readContent('/projects/bar/src/app/app.module.ts');
- expect(content).toMatch(
- /import { TestComponentComponent } from '\.\/dir\/test-component\/test-component.component'/,
- );
- });
-
- it('should handle a path in the name and module options', async () => {
- appTree = await schematicRunner.runSchematic(
- 'module',
- { name: 'admin/module', project: 'bar' },
- appTree,
- );
-
- const options = { ...defaultOptions, name: 'other/test-component', module: 'admin/module' };
- appTree = await schematicRunner.runSchematic('component', options, appTree);
-
- const content = appTree.readContent('/projects/bar/src/app/admin/module/module.module.ts');
- expect(content).toMatch(
- /import { TestComponentComponent } from '..\/..\/other\/test-component\/test-component.component'/,
- );
- });
-
it('should create the right selector with a path in the name', async () => {
const options = { ...defaultOptions, name: 'sub/test' };
appTree = await schematicRunner.runSchematic('component', options, appTree);
@@ -360,23 +265,6 @@ describe('Component Schematic', () => {
expect(content).not.toMatch(/selector: 'app-test'/);
});
- it('should respect the sourceRoot value', async () => {
- const config = JSON.parse(appTree.readContent('/angular.json'));
- config.projects.bar.sourceRoot = 'projects/bar/custom';
- appTree.overwrite('/angular.json', JSON.stringify(config, null, 2));
-
- // should fail without a module in that dir
- await expectAsync(
- schematicRunner.runSchematic('component', defaultOptions, appTree),
- ).toBeRejected();
-
- // move the module
- appTree.rename('/projects/bar/src/app/app.module.ts', '/projects/bar/custom/app/app.module.ts');
- appTree = await schematicRunner.runSchematic('component', defaultOptions, appTree);
-
- expect(appTree.files).toContain('/projects/bar/custom/app/foo/foo.component.ts');
- });
-
it('should respect the skipTests option', async () => {
const options = { ...defaultOptions, skipTests: true };
const tree = await schematicRunner.runSchematic('component', options, appTree);
@@ -434,4 +322,148 @@ describe('Component Schematic', () => {
expect(testContent).toContain('imports: [FooComponent]');
expect(testContent).not.toContain('declarations');
});
+
+ describe('standalone=false', () => {
+ const defaultNonStandaloneOptions: ComponentOptions = {
+ ...defaultOptions,
+ standalone: false,
+ project: 'baz',
+ };
+
+ beforeEach(async () => {
+ appTree = await schematicRunner.runSchematic(
+ 'application',
+ { ...appOptions, standalone: false, name: 'baz' },
+ appTree,
+ );
+ });
+
+ it('should create a component', async () => {
+ const options = { ...defaultNonStandaloneOptions };
+ const tree = await schematicRunner.runSchematic('component', options, appTree);
+ const files = tree.files;
+ expect(files).toEqual(
+ jasmine.arrayContaining([
+ '/projects/baz/src/app/foo/foo.component.css',
+ '/projects/baz/src/app/foo/foo.component.html',
+ '/projects/baz/src/app/foo/foo.component.spec.ts',
+ '/projects/baz/src/app/foo/foo.component.ts',
+ ]),
+ );
+ const moduleContent = tree.readContent('/projects/baz/src/app/app.module.ts');
+ expect(moduleContent).toMatch(/import.*Foo.*from '.\/foo\/foo.component'/);
+ expect(moduleContent).toMatch(/declarations:\s*\[[^\]]+?,\r?\n\s+FooComponent\r?\n/m);
+ });
+
+ it('should use the module flag even if the module is a routing module', async () => {
+ const routingFileName = 'app-routing.module.ts';
+ const routingModulePath = `/projects/baz/src/app/${routingFileName}`;
+ const newTree = createAppModule(appTree, routingModulePath);
+ const options = { ...defaultNonStandaloneOptions, module: routingFileName };
+ const tree = await schematicRunner.runSchematic('component', options, newTree);
+ const content = tree.readContent(routingModulePath);
+ expect(content).toMatch(/import { FooComponent } from '.\/foo\/foo.component/);
+ });
+
+ it('should handle a path in the name option', async () => {
+ const options = { ...defaultNonStandaloneOptions, name: 'dir/test-component' };
+
+ const tree = await schematicRunner.runSchematic('component', options, appTree);
+ const content = tree.readContent('/projects/baz/src/app/app.module.ts');
+ expect(content).toMatch(
+ /import { TestComponentComponent } from '\.\/dir\/test-component\/test-component.component'/,
+ );
+ });
+
+ it('should handle a path in the name and module options', async () => {
+ appTree = await schematicRunner.runSchematic(
+ 'module',
+ { name: 'admin/module', project: 'baz' },
+ appTree,
+ );
+
+ const options = {
+ ...defaultNonStandaloneOptions,
+ name: 'other/test-component',
+ module: 'admin/module',
+ };
+ appTree = await schematicRunner.runSchematic('component', options, appTree);
+
+ const content = appTree.readContent('/projects/baz/src/app/admin/module/module.module.ts');
+ expect(content).toMatch(
+ /import { TestComponentComponent } from '..\/..\/other\/test-component\/test-component.component'/,
+ );
+ });
+
+ it('should find the closest module', async () => {
+ const options = { ...defaultNonStandaloneOptions };
+ const fooModule = '/projects/baz/src/app/foo/foo.module.ts';
+ appTree.create(
+ fooModule,
+ `
+ import { NgModule } from '@angular/core';
+
+ @NgModule({
+ imports: [],
+ declarations: []
+ })
+ export class FooModule { }
+ `,
+ );
+
+ const tree = await schematicRunner.runSchematic('component', options, appTree);
+ const fooModuleContent = tree.readContent(fooModule);
+ expect(fooModuleContent).toMatch(/import { FooComponent } from '.\/foo.component'/);
+ });
+
+ it('should export the component', async () => {
+ const options = { ...defaultNonStandaloneOptions, export: true };
+
+ const tree = await schematicRunner.runSchematic('component', options, appTree);
+ const appModuleContent = tree.readContent('/projects/baz/src/app/app.module.ts');
+ expect(appModuleContent).toMatch(/exports: \[\n(\s*) {2}FooComponent\n\1\]/);
+ });
+
+ it('should import into a specified module', async () => {
+ const options = { ...defaultNonStandaloneOptions, module: 'app.module.ts' };
+
+ const tree = await schematicRunner.runSchematic('component', options, appTree);
+ const appModule = tree.readContent('/projects/baz/src/app/app.module.ts');
+
+ expect(appModule).toMatch(/import { FooComponent } from '.\/foo\/foo.component'/);
+ });
+
+ it('should respect the sourceRoot value', async () => {
+ const config = JSON.parse(appTree.readContent('/angular.json'));
+ config.projects.baz.sourceRoot = 'projects/baz/custom';
+ appTree.overwrite('/angular.json', JSON.stringify(config, null, 2));
+
+ // should fail without a module in that dir
+ await expectAsync(
+ schematicRunner.runSchematic('component', defaultNonStandaloneOptions, appTree),
+ ).toBeRejected();
+
+ // move the module
+ appTree.rename(
+ '/projects/baz/src/app/app.module.ts',
+ '/projects/baz/custom/app/app.module.ts',
+ );
+ appTree = await schematicRunner.runSchematic(
+ 'component',
+ defaultNonStandaloneOptions,
+ appTree,
+ );
+
+ expect(appTree.files).toContain('/projects/baz/custom/app/foo/foo.component.ts');
+ });
+
+ it('should fail if specified module does not exist', async () => {
+ const options = {
+ ...defaultNonStandaloneOptions,
+ module: '/projects/baz/src/app.moduleXXX.ts',
+ };
+
+ await expectAsync(schematicRunner.runSchematic('component', options, appTree)).toBeRejected();
+ });
+ });
});
diff --git a/packages/schematics/angular/component/schema.json b/packages/schematics/angular/component/schema.json
index b7c3f952791e..e2e3914b41b9 100644
--- a/packages/schematics/angular/component/schema.json
+++ b/packages/schematics/angular/component/schema.json
@@ -54,7 +54,7 @@
"standalone": {
"description": "Whether the generated component is standalone.",
"type": "boolean",
- "default": false,
+ "default": true,
"x-user-analytics": "ep.ng_standalone"
},
"viewEncapsulation": {
diff --git a/packages/schematics/angular/config/index_spec.ts b/packages/schematics/angular/config/index_spec.ts
index 9b549271f09a..a180b8f2c147 100644
--- a/packages/schematics/angular/config/index_spec.ts
+++ b/packages/schematics/angular/config/index_spec.ts
@@ -11,7 +11,7 @@ import { Schema as ApplicationOptions } from '../application/schema';
import { Schema as WorkspaceOptions } from '../workspace/schema';
import { Schema as ConfigOptions, Type as ConfigType } from './schema';
-describe('Application Schematic', () => {
+describe('Config Schematic', () => {
const schematicRunner = new SchematicTestRunner(
'@schematics/angular',
require.resolve('../collection.json'),
diff --git a/packages/schematics/angular/directive/index_spec.ts b/packages/schematics/angular/directive/index_spec.ts
index 6cdf847ee005..8ca7b42d8eb6 100644
--- a/packages/schematics/angular/directive/index_spec.ts
+++ b/packages/schematics/angular/directive/index_spec.ts
@@ -45,18 +45,6 @@ describe('Directive Schematic', () => {
appTree = await schematicRunner.runSchematic('application', appOptions, appTree);
});
- it('should create a directive', async () => {
- const options = { ...defaultOptions };
-
- const tree = await schematicRunner.runSchematic('directive', options, appTree);
- const files = tree.files;
- expect(files).toContain('/projects/bar/src/app/foo.directive.spec.ts');
- expect(files).toContain('/projects/bar/src/app/foo.directive.ts');
- const moduleContent = tree.readContent('/projects/bar/src/app/app.module.ts');
- expect(moduleContent).toMatch(/import.*Foo.*from '.\/foo.directive'/);
- expect(moduleContent).toMatch(/declarations:\s*\[[^\]]+?,\r?\n\s+FooDirective\r?\n/m);
- });
-
it('should create respect the flat flag', async () => {
const options = { ...defaultOptions, flat: false };
@@ -66,50 +54,6 @@ describe('Directive Schematic', () => {
expect(files).toContain('/projects/bar/src/app/foo/foo.directive.ts');
});
- it('should find the closest module', async () => {
- const options = { ...defaultOptions, flat: false };
- const fooModule = '/projects/bar/src/app/foo/foo.module.ts';
- appTree.create(
- fooModule,
- `
- import { NgModule } from '@angular/core';
-
- @NgModule({
- imports: [],
- declarations: []
- })
- export class FooModule { }
- `,
- );
-
- const tree = await schematicRunner.runSchematic('directive', options, appTree);
- const fooModuleContent = tree.readContent(fooModule);
- expect(fooModuleContent).toMatch(/import { FooDirective } from '.\/foo.directive'/);
- });
-
- it('should export the directive', async () => {
- const options = { ...defaultOptions, export: true };
-
- const tree = await schematicRunner.runSchematic('directive', options, appTree);
- const appModuleContent = tree.readContent('/projects/bar/src/app/app.module.ts');
- expect(appModuleContent).toMatch(/exports: \[\n(\s*) {2}FooDirective\n\1\]/);
- });
-
- it('should import into a specified module', async () => {
- const options = { ...defaultOptions, module: 'app.module.ts' };
-
- const tree = await schematicRunner.runSchematic('directive', options, appTree);
- const appModule = tree.readContent('/projects/bar/src/app/app.module.ts');
-
- expect(appModule).toMatch(/import { FooDirective } from '.\/foo.directive'/);
- });
-
- it('should fail if specified module does not exist', async () => {
- const options = { ...defaultOptions, module: '/projects/bar/src/app/app.moduleXXX.ts' };
-
- await expectAsync(schematicRunner.runSchematic('directive', options, appTree)).toBeRejected();
- });
-
it('should converts dash-cased-name to a camelCasedSelector', async () => {
const options = { ...defaultOptions, name: 'my-dir' };
@@ -150,23 +94,6 @@ describe('Directive Schematic', () => {
expect(content).toMatch(/selector: '\[foo\]'/);
});
- it('should respect the sourceRoot value', async () => {
- const config = JSON.parse(appTree.readContent('/angular.json'));
- config.projects.bar.sourceRoot = 'projects/bar/custom';
- appTree.overwrite('/angular.json', JSON.stringify(config, null, 2));
-
- // should fail without a module in that dir
- await expectAsync(
- schematicRunner.runSchematic('directive', defaultOptions, appTree),
- ).toBeRejected();
-
- // move the module
- appTree.rename('/projects/bar/src/app/app.module.ts', '/projects/bar/custom/app/app.module.ts');
- appTree = await schematicRunner.runSchematic('directive', defaultOptions, appTree);
-
- expect(appTree.files).toContain('/projects/bar/custom/app/foo.directive.ts');
- });
-
it('should respect skipTests flag', async () => {
const options = { ...defaultOptions, skipTests: true };
@@ -179,10 +106,107 @@ describe('Directive Schematic', () => {
it('should create a standalone directive', async () => {
const options = { ...defaultOptions, standalone: true };
const tree = await schematicRunner.runSchematic('directive', options, appTree);
- const moduleContent = tree.readContent('/projects/bar/src/app/app.module.ts');
const directiveContent = tree.readContent('/projects/bar/src/app/foo.directive.ts');
expect(directiveContent).toContain('standalone: true');
expect(directiveContent).toContain('class FooDirective');
- expect(moduleContent).not.toContain('FooDirective');
+ });
+
+ describe('standalone=false', () => {
+ const defaultNonStandaloneOptions: DirectiveOptions = {
+ ...defaultOptions,
+ standalone: false,
+ project: 'baz',
+ };
+
+ beforeEach(async () => {
+ appTree = await schematicRunner.runSchematic(
+ 'application',
+ { ...appOptions, standalone: false, name: 'baz' },
+ appTree,
+ );
+ });
+
+ it('should create a directive', async () => {
+ const options = { ...defaultNonStandaloneOptions };
+
+ const tree = await schematicRunner.runSchematic('directive', options, appTree);
+ const files = tree.files;
+ expect(files).toContain('/projects/baz/src/app/foo.directive.spec.ts');
+ expect(files).toContain('/projects/baz/src/app/foo.directive.ts');
+ const moduleContent = tree.readContent('/projects/baz/src/app/app.module.ts');
+ expect(moduleContent).toMatch(/import.*Foo.*from '.\/foo.directive'/);
+ expect(moduleContent).toMatch(/declarations:\s*\[[^\]]+?,\r?\n\s+FooDirective\r?\n/m);
+ });
+
+ it('should respect the sourceRoot value', async () => {
+ const config = JSON.parse(appTree.readContent('/angular.json'));
+ config.projects.baz.sourceRoot = 'projects/baz/custom';
+ appTree.overwrite('/angular.json', JSON.stringify(config, null, 2));
+
+ // should fail without a module in that dir
+ await expectAsync(
+ schematicRunner.runSchematic('directive', defaultNonStandaloneOptions, appTree),
+ ).toBeRejected();
+
+ // move the module
+ appTree.rename(
+ '/projects/baz/src/app/app.module.ts',
+ '/projects/baz/custom/app/app.module.ts',
+ );
+ appTree = await schematicRunner.runSchematic(
+ 'directive',
+ defaultNonStandaloneOptions,
+ appTree,
+ );
+
+ expect(appTree.files).toContain('/projects/baz/custom/app/foo.directive.ts');
+ });
+
+ it('should find the closest module', async () => {
+ const options = { ...defaultNonStandaloneOptions, flat: false };
+ const fooModule = '/projects/baz/src/app/foo/foo.module.ts';
+ appTree.create(
+ fooModule,
+ `
+ import { NgModule } from '@angular/core';
+
+ @NgModule({
+ imports: [],
+ declarations: []
+ })
+ export class FooModule { }
+ `,
+ );
+
+ const tree = await schematicRunner.runSchematic('directive', options, appTree);
+ const fooModuleContent = tree.readContent(fooModule);
+ expect(fooModuleContent).toMatch(/import { FooDirective } from '.\/foo.directive'/);
+ });
+
+ it('should export the directive', async () => {
+ const options = { ...defaultNonStandaloneOptions, export: true };
+
+ const tree = await schematicRunner.runSchematic('directive', options, appTree);
+ const appModuleContent = tree.readContent('/projects/baz/src/app/app.module.ts');
+ expect(appModuleContent).toMatch(/exports: \[\n(\s*) {2}FooDirective\n\1\]/);
+ });
+
+ it('should import into a specified module', async () => {
+ const options = { ...defaultNonStandaloneOptions, module: 'app.module.ts' };
+
+ const tree = await schematicRunner.runSchematic('directive', options, appTree);
+ const appModule = tree.readContent('/projects/baz/src/app/app.module.ts');
+
+ expect(appModule).toMatch(/import { FooDirective } from '.\/foo.directive'/);
+ });
+
+ it('should fail if specified module does not exist', async () => {
+ const options = {
+ ...defaultNonStandaloneOptions,
+ module: '/projects/baz/src/app/app.moduleXXX.ts',
+ };
+
+ await expectAsync(schematicRunner.runSchematic('directive', options, appTree)).toBeRejected();
+ });
});
});
diff --git a/packages/schematics/angular/directive/schema.json b/packages/schematics/angular/directive/schema.json
index 71a5656f3605..a5664d79c1ca 100644
--- a/packages/schematics/angular/directive/schema.json
+++ b/packages/schematics/angular/directive/schema.json
@@ -63,7 +63,7 @@
"standalone": {
"description": "Whether the generated directive is standalone.",
"type": "boolean",
- "default": false,
+ "default": true,
"x-user-analytics": "ep.ng_standalone"
},
"flat": {
diff --git a/packages/schematics/angular/e2e/index.ts b/packages/schematics/angular/e2e/index.ts
index 36d383710e12..83731a8f02f0 100644
--- a/packages/schematics/angular/e2e/index.ts
+++ b/packages/schematics/angular/e2e/index.ts
@@ -22,6 +22,7 @@ import {
DependencyType,
ExistingBehavior,
addDependency,
+ addRootProvider,
updateWorkspace,
} from '@schematics/angular/utility';
import { posix as path } from 'path';
@@ -92,6 +93,11 @@ export default function (options: E2eOptions): Rule {
move(e2eRootPath),
]),
),
+ addRootProvider(
+ relatedAppName,
+ ({ code, external }) =>
+ code`${external('provideProtractorTestingSupport', '@angular/platform-browser')}()`,
+ ),
...E2E_DEV_DEPENDENCIES.map((name) =>
addDependency(name, latestVersions[name], {
type: DependencyType.Dev,
diff --git a/packages/schematics/angular/library/index_spec.ts b/packages/schematics/angular/library/index_spec.ts
index dcfa251de67b..a7e44a803683 100644
--- a/packages/schematics/angular/library/index_spec.ts
+++ b/packages/schematics/angular/library/index_spec.ts
@@ -43,7 +43,7 @@ describe('Library Schematic', () => {
workspaceTree = await schematicRunner.runSchematic('workspace', workspaceOptions);
});
- it('should create files', async () => {
+ it('should create correct files', async () => {
const tree = await schematicRunner.runSchematic('library', defaultOptions, workspaceTree);
const files = tree.files;
@@ -55,7 +55,6 @@ describe('Library Schematic', () => {
'/projects/foo/tsconfig.lib.json',
'/projects/foo/tsconfig.lib.prod.json',
'/projects/foo/src/my-index.ts',
- '/projects/foo/src/lib/foo.module.ts',
'/projects/foo/src/lib/foo.component.spec.ts',
'/projects/foo/src/lib/foo.component.ts',
'/projects/foo/src/lib/foo.service.spec.ts',
@@ -64,6 +63,17 @@ describe('Library Schematic', () => {
);
});
+ it('should not add reference to module file in entry-file', async () => {
+ const tree = await schematicRunner.runSchematic('library', defaultOptions, workspaceTree);
+ expect(tree.readContent('/projects/foo/src/my-index.ts')).not.toContain('foo.module');
+ });
+
+ it('should create a standalone component', async () => {
+ const tree = await schematicRunner.runSchematic('library', defaultOptions, workspaceTree);
+ const componentContent = tree.readContent('/projects/foo/src/lib/foo.component.ts');
+ expect(componentContent).toContain('standalone: true');
+ });
+
describe('custom projectRoot', () => {
const customProjectRootOptions: GenerateLibrarySchema = {
name: 'foo',
@@ -90,7 +100,6 @@ describe('Library Schematic', () => {
'/some/other/directory/bar/tsconfig.lib.json',
'/some/other/directory/bar/tsconfig.lib.prod.json',
'/some/other/directory/bar/src/my-index.ts',
- '/some/other/directory/bar/src/lib/foo.module.ts',
'/some/other/directory/bar/src/lib/foo.component.spec.ts',
'/some/other/directory/bar/src/lib/foo.component.ts',
'/some/other/directory/bar/src/lib/foo.service.spec.ts',
@@ -204,13 +213,6 @@ describe('Library Schematic', () => {
expect(svcContent).toMatch(/providedIn: 'root'/);
});
- it('should export the component in the NgModule', async () => {
- const tree = await schematicRunner.runSchematic('library', defaultOptions, workspaceTree);
-
- const fileContent = getFileContent(tree, '/projects/foo/src/lib/foo.module.ts');
- expect(fileContent).toMatch(/exports: \[\n(\s*) {2}FooComponent\n\1\]/);
- });
-
describe(`update package.json`, () => {
it(`should add ng-packagr to devDependencies`, async () => {
const tree = await schematicRunner.runSchematic('library', defaultOptions, workspaceTree);
@@ -318,7 +320,7 @@ describe('Library Schematic', () => {
const pkgJsonPath = '/projects/myscope/mylib/package.json';
expect(tree.files).toContain(pkgJsonPath);
- expect(tree.files).toContain('/projects/myscope/mylib/src/lib/mylib.module.ts');
+ expect(tree.files).toContain('/projects/myscope/mylib/src/lib/mylib.service.ts');
expect(tree.files).toContain('/projects/myscope/mylib/src/lib/mylib.component.ts');
const pkgJson = JSON.parse(tree.readContent(pkgJsonPath));
@@ -391,13 +393,24 @@ describe('Library Schematic', () => {
);
});
- describe('standalone', () => {
- const defaultStandaloneOptions = { ...defaultOptions, standalone: true };
+ describe('standalone=false', () => {
+ const defaultNonStandaloneOptions = { ...defaultOptions, standalone: false };
- it('should create correct files', async () => {
+ it('should export the component in the NgModule', async () => {
const tree = await schematicRunner.runSchematic(
'library',
- defaultStandaloneOptions,
+ defaultNonStandaloneOptions,
+ workspaceTree,
+ );
+
+ const fileContent = getFileContent(tree, '/projects/foo/src/lib/foo.module.ts');
+ expect(fileContent).toMatch(/exports: \[\n(\s*) {2}FooComponent\n\1\]/);
+ });
+
+ it('should create files', async () => {
+ const tree = await schematicRunner.runSchematic(
+ 'library',
+ defaultNonStandaloneOptions,
workspaceTree,
);
@@ -410,6 +423,7 @@ describe('Library Schematic', () => {
'/projects/foo/tsconfig.lib.json',
'/projects/foo/tsconfig.lib.prod.json',
'/projects/foo/src/my-index.ts',
+ '/projects/foo/src/lib/foo.module.ts',
'/projects/foo/src/lib/foo.component.spec.ts',
'/projects/foo/src/lib/foo.component.ts',
'/projects/foo/src/lib/foo.service.spec.ts',
@@ -417,24 +431,5 @@ describe('Library Schematic', () => {
]),
);
});
-
- it('should not add reference to module file in entry-file', async () => {
- const tree = await schematicRunner.runSchematic(
- 'library',
- defaultStandaloneOptions,
- workspaceTree,
- );
- expect(tree.readContent('/projects/foo/src/my-index.ts')).not.toContain('foo.module');
- });
-
- it('should create a standalone component', async () => {
- const tree = await schematicRunner.runSchematic(
- 'library',
- defaultStandaloneOptions,
- workspaceTree,
- );
- const componentContent = tree.readContent('/projects/foo/src/lib/foo.component.ts');
- expect(componentContent).toContain('standalone: true');
- });
});
});
diff --git a/packages/schematics/angular/library/schema.json b/packages/schematics/angular/library/schema.json
index 4d6638f04937..d2b1ff037279 100644
--- a/packages/schematics/angular/library/schema.json
+++ b/packages/schematics/angular/library/schema.json
@@ -51,7 +51,7 @@
"standalone": {
"description": "Creates a library based upon the standalone API, without NgModules.",
"type": "boolean",
- "default": false,
+ "default": true,
"x-user-analytics": "ep.ng_standalone"
}
},
diff --git a/packages/schematics/angular/module/index_spec.ts b/packages/schematics/angular/module/index_spec.ts
index 2bd31e16afca..21aa8f9ddcc6 100644
--- a/packages/schematics/angular/module/index_spec.ts
+++ b/packages/schematics/angular/module/index_spec.ts
@@ -33,6 +33,7 @@ describe('Module Schematic', () => {
name: 'bar',
inlineStyle: false,
inlineTemplate: false,
+ standalone: false,
routing: true,
skipTests: false,
skipPackageJson: false,
diff --git a/packages/schematics/angular/ng-new/index_spec.ts b/packages/schematics/angular/ng-new/index_spec.ts
index a53b8de6f5c6..95c4cc77e4be 100644
--- a/packages/schematics/angular/ng-new/index_spec.ts
+++ b/packages/schematics/angular/ng-new/index_spec.ts
@@ -37,13 +37,15 @@ describe('Ng New Schematic', () => {
jasmine.arrayContaining([
'/bar/tsconfig.app.json',
'/bar/src/main.ts',
- '/bar/src/app/app.module.ts',
+ '/bar/src/app/app.config.ts',
]),
);
+
+ expect(files).not.toEqual(jasmine.arrayContaining(['/bar/src/app/app.module.ts']));
});
- it('should create files of a standalone application', async () => {
- const options = { ...defaultOptions, standalone: true };
+ it('should create module files of a standalone=false application', async () => {
+ const options = { ...defaultOptions, standalone: false };
const tree = await schematicRunner.runSchematic('ng-new', options);
const files = tree.files;
@@ -51,11 +53,9 @@ describe('Ng New Schematic', () => {
jasmine.arrayContaining([
'/bar/tsconfig.app.json',
'/bar/src/main.ts',
- '/bar/src/app/app.routes.ts',
- '/bar/src/app/app.config.ts',
+ '/bar/src/app/app.module.ts',
]),
);
- expect(files).not.toEqual(jasmine.arrayContaining(['/bar/src/app/app.module.ts']));
});
it('should should set the prefix in angular.json and in app.component.ts', async () => {
@@ -66,10 +66,11 @@ describe('Ng New Schematic', () => {
expect(content).toMatch(/"prefix": "pre"/);
});
- it('should set up the app module', async () => {
+ it('should set up the app module when standalone=false', async () => {
const options: NgNewOptions = {
name: 'foo',
version: '6.0.0',
+ standalone: false,
};
const tree = await schematicRunner.runSchematic('ng-new', options);
diff --git a/packages/schematics/angular/ng-new/schema.json b/packages/schematics/angular/ng-new/schema.json
index e827f696c0b7..321c4ef90dda 100644
--- a/packages/schematics/angular/ng-new/schema.json
+++ b/packages/schematics/angular/ng-new/schema.json
@@ -137,7 +137,7 @@
"standalone": {
"description": "Creates an application based upon the standalone API, without NgModules.",
"type": "boolean",
- "default": false,
+ "default": true,
"x-user-analytics": "ep.ng_standalone"
}
},
diff --git a/packages/schematics/angular/pipe/index_spec.ts b/packages/schematics/angular/pipe/index_spec.ts
index 6c4822f06a3e..9116e1f2544d 100644
--- a/packages/schematics/angular/pipe/index_spec.ts
+++ b/packages/schematics/angular/pipe/index_spec.ts
@@ -40,122 +40,142 @@ describe('Pipe Schematic', () => {
skipPackageJson: false,
};
let appTree: UnitTestTree;
- beforeEach(async () => {
- appTree = await schematicRunner.runSchematic('workspace', workspaceOptions);
- appTree = await schematicRunner.runSchematic('application', appOptions, appTree);
- });
-
- it('should create a pipe', async () => {
- const options = { ...defaultOptions };
-
- const tree = await schematicRunner.runSchematic('pipe', options, appTree);
- const files = tree.files;
- expect(files).toContain('/projects/bar/src/app/foo.pipe.spec.ts');
- expect(files).toContain('/projects/bar/src/app/foo.pipe.ts');
- const moduleContent = getFileContent(tree, '/projects/bar/src/app/app.module.ts');
- expect(moduleContent).toMatch(/import.*Foo.*from '.\/foo.pipe'/);
- expect(moduleContent).toMatch(/declarations:\s*\[[^\]]+?,\r?\n\s+FooPipe\r?\n/m);
- const fileContent = tree.readContent('/projects/bar/src/app/foo.pipe.ts');
- expect(fileContent).toContain('transform(value: unknown, ...args: unknown[])');
- });
-
- it('should import into a specified module', async () => {
- const options = { ...defaultOptions, module: 'app.module.ts' };
-
- const tree = await schematicRunner.runSchematic('pipe', options, appTree);
- const appModule = getFileContent(tree, '/projects/bar/src/app/app.module.ts');
-
- expect(appModule).toMatch(/import { FooPipe } from '.\/foo.pipe'/);
- });
-
- it('should fail if specified module does not exist', async () => {
- const options = { ...defaultOptions, module: '/projects/bar/src/app/app.moduleXXX.ts' };
-
- await expectAsync(schematicRunner.runSchematic('pipe', options, appTree)).toBeRejected();
- });
-
- it('should handle a path in the name and module options', async () => {
- appTree = await schematicRunner.runSchematic(
- 'module',
- { name: 'admin/module', project: 'bar' },
- appTree,
- );
-
- const options = { ...defaultOptions, module: 'admin/module' };
- appTree = await schematicRunner.runSchematic('pipe', options, appTree);
-
- const content = appTree.readContent('/projects/bar/src/app/admin/module/module.module.ts');
- expect(content).toMatch(/import { FooPipe } from '\.\.\/\.\.\/foo.pipe'/);
- });
-
- it('should export the pipe', async () => {
- const options = { ...defaultOptions, export: true };
- const tree = await schematicRunner.runSchematic('pipe', options, appTree);
- const appModuleContent = getFileContent(tree, '/projects/bar/src/app/app.module.ts');
- expect(appModuleContent).toMatch(/exports: \[\n(\s*) {2}FooPipe\n\1\]/);
+ const defaultNonStandaloneOptions: PipeOptions = { ...defaultOptions, standalone: false };
+
+ describe('standalone=false', () => {
+ beforeEach(async () => {
+ appTree = await schematicRunner.runSchematic('workspace', workspaceOptions);
+ appTree = await schematicRunner.runSchematic(
+ 'application',
+ { ...appOptions, standalone: false },
+ appTree,
+ );
+ });
+
+ it('should create a pipe', async () => {
+ const tree = await schematicRunner.runSchematic('pipe', defaultNonStandaloneOptions, appTree);
+ const files = tree.files;
+ expect(files).toContain('/projects/bar/src/app/foo.pipe.spec.ts');
+ expect(files).toContain('/projects/bar/src/app/foo.pipe.ts');
+ const moduleContent = getFileContent(tree, '/projects/bar/src/app/app.module.ts');
+ expect(moduleContent).toMatch(/import.*Foo.*from '.\/foo.pipe'/);
+ expect(moduleContent).toMatch(/declarations:\s*\[[^\]]+?,\r?\n\s+FooPipe\r?\n/m);
+ const fileContent = tree.readContent('/projects/bar/src/app/foo.pipe.ts');
+ expect(fileContent).toContain('transform(value: unknown, ...args: unknown[])');
+ });
+
+ it('should import into a specified module', async () => {
+ const options = { ...defaultNonStandaloneOptions, module: 'app.module.ts' };
+
+ const tree = await schematicRunner.runSchematic('pipe', options, appTree);
+ const appModule = getFileContent(tree, '/projects/bar/src/app/app.module.ts');
+
+ expect(appModule).toMatch(/import { FooPipe } from '.\/foo.pipe'/);
+ });
+
+ it('should fail if specified module does not exist', async () => {
+ const options = {
+ ...defaultNonStandaloneOptions,
+ module: '/projects/bar/src/app/app.moduleXXX.ts',
+ };
+
+ await expectAsync(schematicRunner.runSchematic('pipe', options, appTree)).toBeRejected();
+ });
+
+ it('should handle a path in the name and module options', async () => {
+ appTree = await schematicRunner.runSchematic(
+ 'module',
+ { name: 'admin/module', project: 'bar' },
+ appTree,
+ );
+
+ const options = { ...defaultNonStandaloneOptions, module: 'admin/module' };
+ appTree = await schematicRunner.runSchematic('pipe', options, appTree);
+
+ const content = appTree.readContent('/projects/bar/src/app/admin/module/module.module.ts');
+ expect(content).toMatch(/import { FooPipe } from '\.\.\/\.\.\/foo.pipe'/);
+ });
+
+ it('should export the pipe', async () => {
+ const options = { ...defaultNonStandaloneOptions, export: true };
+
+ const tree = await schematicRunner.runSchematic('pipe', options, appTree);
+ const appModuleContent = getFileContent(tree, '/projects/bar/src/app/app.module.ts');
+ expect(appModuleContent).toMatch(/exports: \[\n(\s*) {2}FooPipe\n\1\]/);
+ });
+
+ it('should respect the flat flag', async () => {
+ const options = { ...defaultNonStandaloneOptions, flat: false };
+
+ const tree = await schematicRunner.runSchematic('pipe', options, appTree);
+ const files = tree.files;
+ expect(files).toContain('/projects/bar/src/app/foo/foo.pipe.spec.ts');
+ expect(files).toContain('/projects/bar/src/app/foo/foo.pipe.ts');
+ const moduleContent = getFileContent(tree, '/projects/bar/src/app/app.module.ts');
+ expect(moduleContent).toMatch(/import.*Foo.*from '.\/foo\/foo.pipe'/);
+ expect(moduleContent).toMatch(/declarations:\s*\[[^\]]+?,\r?\n\s+FooPipe\r?\n/m);
+ });
+
+ it('should use the module flag even if the module is a routing module', async () => {
+ const routingFileName = 'app-routing.module.ts';
+ const routingModulePath = `/projects/bar/src/app/${routingFileName}`;
+ const newTree = createAppModule(appTree, routingModulePath);
+ const options = { ...defaultNonStandaloneOptions, module: routingFileName };
+ const tree = await schematicRunner.runSchematic('pipe', options, newTree);
+ const content = getFileContent(tree, routingModulePath);
+ expect(content).toMatch(/import { FooPipe } from '.\/foo.pipe/);
+ });
+
+ it('should respect the sourceRoot value', async () => {
+ const config = JSON.parse(appTree.readContent('/angular.json'));
+ config.projects.bar.sourceRoot = 'projects/bar/custom';
+ appTree.overwrite('/angular.json', JSON.stringify(config, null, 2));
+
+ // should fail without a module in that dir
+ await expectAsync(
+ schematicRunner.runSchematic('pipe', defaultNonStandaloneOptions, appTree),
+ ).toBeRejected();
+
+ // move the module
+ appTree.rename(
+ '/projects/bar/src/app/app.module.ts',
+ '/projects/bar/custom/app/app.module.ts',
+ );
+ appTree = await schematicRunner.runSchematic('pipe', defaultNonStandaloneOptions, appTree);
+ expect(appTree.files).toContain('/projects/bar/custom/app/foo.pipe.ts');
+ });
});
- it('should respect the flat flag', async () => {
- const options = { ...defaultOptions, flat: false };
-
- const tree = await schematicRunner.runSchematic('pipe', options, appTree);
- const files = tree.files;
- expect(files).toContain('/projects/bar/src/app/foo/foo.pipe.spec.ts');
- expect(files).toContain('/projects/bar/src/app/foo/foo.pipe.ts');
- const moduleContent = getFileContent(tree, '/projects/bar/src/app/app.module.ts');
- expect(moduleContent).toMatch(/import.*Foo.*from '.\/foo\/foo.pipe'/);
- expect(moduleContent).toMatch(/declarations:\s*\[[^\]]+?,\r?\n\s+FooPipe\r?\n/m);
- });
-
- it('should use the module flag even if the module is a routing module', async () => {
- const routingFileName = 'app-routing.module.ts';
- const routingModulePath = `/projects/bar/src/app/${routingFileName}`;
- const newTree = createAppModule(appTree, routingModulePath);
- const options = { ...defaultOptions, module: routingFileName };
- const tree = await schematicRunner.runSchematic('pipe', options, newTree);
- const content = getFileContent(tree, routingModulePath);
- expect(content).toMatch(/import { FooPipe } from '.\/foo.pipe/);
- });
-
- it('should respect the sourceRoot value', async () => {
- const config = JSON.parse(appTree.readContent('/angular.json'));
- config.projects.bar.sourceRoot = 'projects/bar/custom';
- appTree.overwrite('/angular.json', JSON.stringify(config, null, 2));
-
- // should fail without a module in that dir
- await expectAsync(schematicRunner.runSchematic('pipe', defaultOptions, appTree)).toBeRejected();
-
- // move the module
- appTree.rename('/projects/bar/src/app/app.module.ts', '/projects/bar/custom/app/app.module.ts');
- appTree = await schematicRunner.runSchematic('pipe', defaultOptions, appTree);
- expect(appTree.files).toContain('/projects/bar/custom/app/foo.pipe.ts');
- });
-
- it('should respect the skipTests flag', async () => {
- const options = { ...defaultOptions, skipTests: true };
-
- const tree = await schematicRunner.runSchematic('pipe', options, appTree);
- const files = tree.files;
- expect(files).not.toContain('/projects/bar/src/app/foo.pipe.spec.ts');
- expect(files).toContain('/projects/bar/src/app/foo.pipe.ts');
- });
-
- it('should create a standalone pipe', async () => {
- const options = { ...defaultOptions, standalone: true };
- const tree = await schematicRunner.runSchematic('pipe', options, appTree);
- const moduleContent = tree.readContent('/projects/bar/src/app/app.module.ts');
- const pipeContent = tree.readContent('/projects/bar/src/app/foo.pipe.ts');
- expect(pipeContent).toContain('standalone: true');
- expect(pipeContent).toContain('class FooPipe');
- expect(moduleContent).not.toContain('FooPipe');
- });
-
- it('should error when class name contains invalid characters', async () => {
- const options = { ...defaultOptions, name: '1Clazz' };
-
- await expectAsync(schematicRunner.runSchematic('pipe', options, appTree)).toBeRejectedWithError(
- 'Class name "1Clazz" is invalid.',
- );
+ describe('standalone=true', () => {
+ beforeEach(async () => {
+ appTree = await schematicRunner.runSchematic('workspace', workspaceOptions);
+ appTree = await schematicRunner.runSchematic('application', { ...appOptions }, appTree);
+ });
+ it('should create a standalone pipe', async () => {
+ const tree = await schematicRunner.runSchematic('pipe', defaultOptions, appTree);
+ const moduleContent = tree.readContent('/projects/bar/src/app/app.module.ts');
+ const pipeContent = tree.readContent('/projects/bar/src/app/foo.pipe.ts');
+ expect(pipeContent).toContain('standalone: true');
+ expect(pipeContent).toContain('class FooPipe');
+ expect(moduleContent).not.toContain('FooPipe');
+ });
+
+ it('should respect the skipTests flag', async () => {
+ const options = { ...defaultOptions, skipTests: true };
+
+ const tree = await schematicRunner.runSchematic('pipe', options, appTree);
+ const files = tree.files;
+ expect(files).not.toContain('/projects/bar/src/app/foo.pipe.spec.ts');
+ expect(files).toContain('/projects/bar/src/app/foo.pipe.ts');
+ });
+
+ it('should error when class name contains invalid characters', async () => {
+ const options = { ...defaultOptions, name: '1Clazz' };
+
+ await expectAsync(
+ schematicRunner.runSchematic('pipe', options, appTree),
+ ).toBeRejectedWithError('Class name "1Clazz" is invalid.');
+ });
});
});
diff --git a/packages/schematics/angular/pipe/schema.json b/packages/schematics/angular/pipe/schema.json
index 0275266eac1a..ce9ba2699b7f 100644
--- a/packages/schematics/angular/pipe/schema.json
+++ b/packages/schematics/angular/pipe/schema.json
@@ -49,7 +49,7 @@
"standalone": {
"description": "Whether the generated pipe is standalone.",
"type": "boolean",
- "default": false,
+ "default": true,
"x-user-analytics": "ep.ng_standalone"
},
"module": {
diff --git a/packages/schematics/angular/server/index_spec.ts b/packages/schematics/angular/server/index_spec.ts
index a296e1562427..615b11995889 100644
--- a/packages/schematics/angular/server/index_spec.ts
+++ b/packages/schematics/angular/server/index_spec.ts
@@ -23,10 +23,6 @@ describe('Server Schematic', () => {
const defaultOptions: ServerOptions = {
project: 'bar',
};
- const workspaceUniversalOptions: ServerOptions = {
- project: 'workspace',
- };
-
const workspaceOptions: WorkspaceOptions = {
name: 'workspace',
newProjectRoot: 'projects',
@@ -43,86 +39,68 @@ describe('Server Schematic', () => {
skipPackageJson: false,
};
- const initialWorkspaceAppOptions: ApplicationOptions = {
- name: 'workspace',
- projectRoot: '',
- inlineStyle: false,
- inlineTemplate: false,
- routing: false,
- style: Style.Css,
- skipTests: false,
- skipPackageJson: false,
- };
-
let appTree: UnitTestTree;
beforeEach(async () => {
appTree = await schematicRunner.runSchematic('workspace', workspaceOptions);
- appTree = await schematicRunner.runSchematic(
- 'application',
- initialWorkspaceAppOptions,
- appTree,
- );
- appTree = await schematicRunner.runSchematic('application', appOptions, 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();
- });
+ describe('non standalone application', () => {
+ beforeEach(async () => {
+ appTree = await schematicRunner.runSchematic(
+ 'application',
+ { ...appOptions, standalone: false },
+ appTree,
+ );
+ });
- 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 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 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 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 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 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']);
- expect(contents.files).toEqual(['src/main.ts', 'src/main.server.ts']);
+ 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']);
+ expect(contents.files).toEqual(['src/main.ts', 'src/main.server.ts']);
+ });
});
describe('standalone application', () => {
- let standaloneAppOptions;
- let defaultStandaloneOptions: ServerOptions;
beforeEach(async () => {
- const standaloneAppName = 'baz';
- standaloneAppOptions = {
- ...appOptions,
- name: standaloneAppName,
- standalone: true,
- };
- defaultStandaloneOptions = {
- project: standaloneAppName,
- };
- appTree = await schematicRunner.runSchematic('application', standaloneAppOptions, appTree);
+ appTree = await schematicRunner.runSchematic('application', appOptions, appTree);
});
it('should create not root module file', async () => {
- const tree = await schematicRunner.runSchematic('server', defaultStandaloneOptions, appTree);
- const filePath = '/projects/baz/src/app/app.module.server.ts';
+ const tree = await schematicRunner.runSchematic('server', defaultOptions, appTree);
+ const filePath = '/projects/bar/src/app/app.module.server.ts';
expect(tree.exists(filePath)).toEqual(false);
});
@@ -136,16 +114,16 @@ describe('Server Schematic', () => {
});
it('should create a main file', async () => {
- const tree = await schematicRunner.runSchematic('server', defaultStandaloneOptions, appTree);
- const filePath = '/projects/baz/src/main.server.ts';
+ 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(AppComponent, config)`);
});
it('should create server app config file', async () => {
- const tree = await schematicRunner.runSchematic('server', defaultStandaloneOptions, appTree);
- const filePath = '/projects/baz/src/app/app.config.server.ts';
+ 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 = {`);
@@ -174,7 +152,8 @@ describe('Server Schematic', () => {
appTree.overwrite('/angular.json', JSON.stringify(config, undefined, 2));
}
- beforeEach(() => {
+ beforeEach(async () => {
+ appTree = await schematicRunner.runSchematic('application', appOptions, appTree);
convertBuilderToLegacyBrowser();
});
diff --git a/packages/schematics/angular/service-worker/index_spec.ts b/packages/schematics/angular/service-worker/index_spec.ts
index 644e98ac00bb..8dc0dcf11fd5 100644
--- a/packages/schematics/angular/service-worker/index_spec.ts
+++ b/packages/schematics/angular/service-worker/index_spec.ts
@@ -63,27 +63,6 @@ describe('Service Worker Schematic', () => {
expect(pkg.dependencies['@angular/service-worker']).toEqual(version);
});
- it('should import ServiceWorkerModule', async () => {
- const tree = await schematicRunner.runSchematic('service-worker', defaultOptions, appTree);
- const pkgText = tree.readContent('/projects/bar/src/app/app.module.ts');
- expect(pkgText).toMatch(/import \{ ServiceWorkerModule \} from '@angular\/service-worker'/);
- });
-
- it('should add the SW import to the NgModule imports', async () => {
- const tree = await schematicRunner.runSchematic('service-worker', defaultOptions, appTree);
- const pkgText = tree.readContent('/projects/bar/src/app/app.module.ts');
- expect(pkgText).toMatch(
- new RegExp(
- "(\\s+)ServiceWorkerModule\\.register\\('ngsw-worker\\.js', \\{\\n" +
- '\\1 enabled: !isDevMode\\(\\),\\n' +
- '\\1 // Register the ServiceWorker as soon as the application is stable\\n' +
- '\\1 // or after 30 seconds \\(whichever comes first\\)\\.\\n' +
- "\\1 registrationStrategy: 'registerWhenStable:30000'\\n" +
- '\\1}\\)',
- ),
- );
- });
-
it('should put the ngsw-config.json file in the project root', async () => {
const tree = await schematicRunner.runSchematic('service-worker', defaultOptions, appTree);
const path = '/projects/bar/ngsw-config.json';
@@ -154,55 +133,72 @@ describe('Service Worker Schematic', () => {
expect(tree.exists('/ngsw-config.json')).toBe(true);
});
- describe('standalone', () => {
+ it(`should add the 'provideServiceWorker' to providers`, async () => {
+ const tree = await schematicRunner.runSchematic('service-worker', defaultOptions, appTree);
+ const content = tree.readContent('/projects/bar/src/app/app.config.ts');
+ expect(tags.oneLine`${content}`).toContain(tags.oneLine`
+ providers: [provideServiceWorker('ngsw-worker.js', {
+ enabled: !isDevMode(),
+ registrationStrategy: 'registerWhenStable:30000'
+ })]
+ `);
+ });
+
+ it(`should import 'isDevMode' from '@angular/core'`, async () => {
+ const tree = await schematicRunner.runSchematic('service-worker', defaultOptions, appTree);
+ const content = tree.readContent('/projects/bar/src/app/app.config.ts');
+ expect(content).toContain(`import { ApplicationConfig, isDevMode } from '@angular/core';`);
+ });
+
+ it(`should import 'provideServiceWorker' from '@angular/service-worker'`, async () => {
+ const tree = await schematicRunner.runSchematic('service-worker', defaultOptions, appTree);
+ const content = tree.readContent('/projects/bar/src/app/app.config.ts');
+ expect(content).toContain(`import { provideServiceWorker } from '@angular/service-worker';`);
+ });
+
+ describe('standalone=false', () => {
const name = 'buz';
- const standaloneAppOptions: ApplicationOptions = {
+ const nonStandaloneAppOptions: ApplicationOptions = {
...appOptions,
name,
- standalone: true,
+ standalone: false,
};
- const standaloneSWOptions: ServiceWorkerOptions = {
+ const nonStandaloneSWOptions: ServiceWorkerOptions = {
...defaultOptions,
project: name,
};
beforeEach(async () => {
- appTree = await schematicRunner.runSchematic('application', standaloneAppOptions, appTree);
+ appTree = await schematicRunner.runSchematic('application', nonStandaloneAppOptions, appTree);
});
- it(`should add the 'provideServiceWorker' to providers`, async () => {
+ it('should import ServiceWorkerModule', async () => {
const tree = await schematicRunner.runSchematic(
'service-worker',
- standaloneSWOptions,
+ nonStandaloneSWOptions,
appTree,
);
- const content = tree.readContent('/projects/buz/src/app/app.config.ts');
- expect(tags.oneLine`${content}`).toContain(tags.oneLine`
- providers: [provideServiceWorker('ngsw-worker.js', {
- enabled: !isDevMode(),
- registrationStrategy: 'registerWhenStable:30000'
- })]
- `);
+ const pkgText = tree.readContent('/projects/buz/src/app/app.module.ts');
+ expect(pkgText).toMatch(/import \{ ServiceWorkerModule \} from '@angular\/service-worker'/);
});
- it(`should import 'isDevMode' from '@angular/core'`, async () => {
+ it('should add the SW import to the NgModule imports', async () => {
const tree = await schematicRunner.runSchematic(
'service-worker',
- standaloneSWOptions,
+ nonStandaloneSWOptions,
appTree,
);
- const content = tree.readContent('/projects/buz/src/app/app.config.ts');
- expect(content).toContain(`import { ApplicationConfig, isDevMode } from '@angular/core';`);
- });
-
- it(`should import 'provideServiceWorker' from '@angular/service-worker'`, async () => {
- const tree = await schematicRunner.runSchematic(
- 'service-worker',
- standaloneSWOptions,
- appTree,
+ const pkgText = tree.readContent('/projects/buz/src/app/app.module.ts');
+ expect(pkgText).toMatch(
+ new RegExp(
+ "(\\s+)ServiceWorkerModule\\.register\\('ngsw-worker\\.js', \\{\\n" +
+ '\\1 enabled: !isDevMode\\(\\),\\n' +
+ '\\1 // Register the ServiceWorker as soon as the application is stable\\n' +
+ '\\1 // or after 30 seconds \\(whichever comes first\\)\\.\\n' +
+ "\\1 registrationStrategy: 'registerWhenStable:30000'\\n" +
+ '\\1}\\)',
+ ),
);
- const content = tree.readContent('/projects/buz/src/app/app.config.ts');
- expect(content).toContain(`import { provideServiceWorker } from '@angular/service-worker';`);
});
});