Skip to content

Commit 7b78b78

Browse files
crisbetoclydin
authored andcommitted
feat(@schematics/angular): add --standalone to ng generate
Adds the `--standalone` flag when generating components, directives or pipes through `ng generate`.
1 parent 7abe212 commit 7b78b78

File tree

15 files changed

+69
-9
lines changed

15 files changed

+69
-9
lines changed

docs/design/analytics.md

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ Note: There's a limit of 20 custom dimensions.
5858
| 12 | `Flag: --skip-tests` | `boolean` |
5959
| 13 | `Flag: --aot` | `boolean` |
6060
| 14 | `Flag: --minimal` | `boolean` |
61+
| 15 | `Flag: --standalone` | `boolean` |
6162
| 16 | `Flag: --optimization` | `boolean` |
6263
| 17 | `Flag: --routing` | `boolean` |
6364
| 18 | `Flag: --skip-import` | `boolean` |

packages/schematics/angular/component/files/__name@dasherize@if-flat__/__name@dasherize__.__type@dasherize__.spec.ts.template

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe('<%= classify(name) %><%= classify(type) %>', () => {
88

99
beforeEach(async () => {
1010
await TestBed.configureTestingModule({
11-
declarations: [ <%= classify(name) %><%= classify(type) %> ]
11+
<%= standalone ? 'imports' : 'declarations' %>: [ <%= classify(name) %><%= classify(type) %> ]
1212
})
1313
.compileComponents();
1414

packages/schematics/angular/component/files/__name@dasherize@if-flat__/__name@dasherize__.__type@dasherize__.ts.template

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { Component, OnInit<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%> } from '@angular/core';
22

33
@Component({<% if(!skipSelector) {%>
4-
selector: '<%= selector %>',<%}%><% if(inlineTemplate) { %>
4+
selector: '<%= selector %>',<%}%><% if(standalone) {%>
5+
standalone: true,<%}%><% if(inlineTemplate) { %>
56
template: `
67
<p>
78
<%= dasherize(name) %> works!

packages/schematics/angular/component/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ function readIntoSourceFile(host: Tree, modulePath: string): ts.SourceFile {
3939

4040
function addDeclarationToNgModule(options: ComponentOptions): Rule {
4141
return (host: Tree) => {
42-
if (options.skipImport || !options.module) {
42+
if (options.skipImport || options.standalone || !options.module) {
4343
return host;
4444
}
4545

packages/schematics/angular/component/index_spec.ts

+18
Original file line numberDiff line numberDiff line change
@@ -420,4 +420,22 @@ describe('Component Schematic', () => {
420420
expect(content).toMatch(/template: `(\n(.|)*){3}\n\s*`,\n/);
421421
expect(content).toMatch(/changeDetection: ChangeDetectionStrategy.OnPush/);
422422
});
423+
424+
it('should create a standalone component', async () => {
425+
const options = { ...defaultOptions, standalone: true };
426+
const tree = await schematicRunner.runSchematicAsync('component', options, appTree).toPromise();
427+
const moduleContent = tree.readContent('/projects/bar/src/app/app.module.ts');
428+
const componentContent = tree.readContent('/projects/bar/src/app/foo/foo.component.ts');
429+
expect(componentContent).toContain('standalone: true');
430+
expect(componentContent).toContain('class FooComponent');
431+
expect(moduleContent).not.toContain('FooComponent');
432+
});
433+
434+
it('should declare standalone components in the `imports` of a test', async () => {
435+
const options = { ...defaultOptions, standalone: true };
436+
const tree = await schematicRunner.runSchematicAsync('component', options, appTree).toPromise();
437+
const testContent = tree.readContent('/projects/bar/src/app/foo/foo.component.spec.ts');
438+
expect(testContent).toContain('imports: [ FooComponent ]');
439+
expect(testContent).not.toContain('declarations');
440+
});
423441
});

packages/schematics/angular/component/schema.json

+6
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@
4848
"alias": "t",
4949
"x-user-analytics": 10
5050
},
51+
"standalone": {
52+
"description": "Whether the generated component is standalone.",
53+
"type": "boolean",
54+
"default": false,
55+
"x-user-analytics": 15
56+
},
5157
"viewEncapsulation": {
5258
"description": "The view encapsulation strategy to use in the new component.",
5359
"enum": ["Emulated", "None", "ShadowDom"],

packages/schematics/angular/directive/files/__name@dasherize@if-flat__/__name@dasherize__.directive.ts.template

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { Directive } from '@angular/core';
22

33
@Directive({
4-
selector: '[<%= selector %>]'
4+
selector: '[<%= selector %>]'<% if(standalone) {%>,
5+
standalone: true<%}%>
56
})
67
export class <%= classify(name) %>Directive {
78

packages/schematics/angular/directive/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import { Schema as DirectiveOptions } from './schema';
3131

3232
function addDeclarationToNgModule(options: DirectiveOptions): Rule {
3333
return (host: Tree) => {
34-
if (options.skipImport || !options.module) {
34+
if (options.skipImport || options.standalone || !options.module) {
3535
return host;
3636
}
3737

packages/schematics/angular/directive/index_spec.ts

+10
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,14 @@ describe('Directive Schematic', () => {
183183
expect(files).toContain('/projects/bar/src/app/foo.directive.ts');
184184
expect(files).not.toContain('/projects/bar/src/app/foo.directive.spec.ts');
185185
});
186+
187+
it('should create a standalone directive', async () => {
188+
const options = { ...defaultOptions, standalone: true };
189+
const tree = await schematicRunner.runSchematicAsync('directive', options, appTree).toPromise();
190+
const moduleContent = tree.readContent('/projects/bar/src/app/app.module.ts');
191+
const directiveContent = tree.readContent('/projects/bar/src/app/foo.directive.ts');
192+
expect(directiveContent).toContain('standalone: true');
193+
expect(directiveContent).toContain('class FooDirective');
194+
expect(moduleContent).not.toContain('FooDirective');
195+
});
186196
});

packages/schematics/angular/directive/schema.json

+6
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@
5959
"format": "html-selector",
6060
"description": "The HTML selector to use for this directive."
6161
},
62+
"standalone": {
63+
"description": "Whether the generated directive is standalone.",
64+
"type": "boolean",
65+
"default": false,
66+
"x-user-analytics": 15
67+
},
6268
"flat": {
6369
"type": "boolean",
6470
"description": "When true (the default), creates the new files at the top level of the current project.",

packages/schematics/angular/pipe/files/__name@dasherize@if-flat__/__name@dasherize__.pipe.ts.template

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { Pipe, PipeTransform } from '@angular/core';
22

33
@Pipe({
4-
name: '<%= camelize(name) %>'
4+
name: '<%= camelize(name) %>'<% if(standalone) {%>,
5+
standalone: true<%}%>
56
})
67
export class <%= classify(name) %>Pipe implements PipeTransform {
78

packages/schematics/angular/pipe/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import { Schema as PipeOptions } from './schema';
3030

3131
function addDeclarationToNgModule(options: PipeOptions): Rule {
3232
return (host: Tree) => {
33-
if (options.skipImport || !options.module) {
33+
if (options.skipImport || options.standalone || !options.module) {
3434
return host;
3535
}
3636

packages/schematics/angular/pipe/index_spec.ts

+10
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,14 @@ describe('Pipe Schematic', () => {
147147
expect(files).not.toContain('/projects/bar/src/app/foo.pipe.spec.ts');
148148
expect(files).toContain('/projects/bar/src/app/foo.pipe.ts');
149149
});
150+
151+
it('should create a standalone pipe', async () => {
152+
const options = { ...defaultOptions, standalone: true };
153+
const tree = await schematicRunner.runSchematicAsync('pipe', options, appTree).toPromise();
154+
const moduleContent = tree.readContent('/projects/bar/src/app/app.module.ts');
155+
const pipeContent = tree.readContent('/projects/bar/src/app/foo.pipe.ts');
156+
expect(pipeContent).toContain('standalone: true');
157+
expect(pipeContent).toContain('class FooPipe');
158+
expect(moduleContent).not.toContain('FooPipe');
159+
});
150160
});

packages/schematics/angular/pipe/schema.json

+6
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@
4545
"description": "Do not import this pipe into the owning NgModule.",
4646
"x-user-analytics": 18
4747
},
48+
"standalone": {
49+
"description": "Whether the generated pipe is standalone.",
50+
"type": "boolean",
51+
"default": false,
52+
"x-user-analytics": 15
53+
},
4854
"module": {
4955
"type": "string",
5056
"description": "The declaring NgModule.",

packages/schematics/angular/utility/find-module.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export interface ModuleOptions {
1717
skipImport?: boolean;
1818
moduleExt?: string;
1919
routingModuleExt?: string;
20+
standalone?: boolean;
2021
}
2122

2223
export const MODULE_EXT = '.module.ts';
@@ -26,8 +27,7 @@ export const ROUTING_MODULE_EXT = '-routing.module.ts';
2627
* Find the module referred by a set of options passed to the schematics.
2728
*/
2829
export function findModuleFromOptions(host: Tree, options: ModuleOptions): Path | undefined {
29-
// eslint-disable-next-line no-prototype-builtins
30-
if (options.hasOwnProperty('skipImport') && options.skipImport) {
30+
if (options.standalone || options.skipImport) {
3131
return undefined;
3232
}
3333

0 commit comments

Comments
 (0)