Skip to content

Commit 1f137a3

Browse files
authored
feat(module:schematics): improve migration and component schematics (#8577)
* refactor(module:schematics): improve migration schematic * refactor(module:schematics): improve component schematic
1 parent 512a577 commit 1f137a3

22 files changed

Lines changed: 244 additions & 194 deletions

docs/animations.en-US.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ Add the `nzNoAnimation` directive to the component.
4040
```HTML
4141
<nz-modal nzNoAnimation></nz-modal>
4242
<ul nz-menu nzNoAnimation></ul>
43-
<nz-form-explain [nzNoAnimation]="true"></nz-form-explain>
4443
```
4544

4645
### Turn Off In Services

docs/animations.zh-CN.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ import { NzNoAnimationModule } from 'ng-zorro-antd/core/no-animation';
3838
```HTML
3939
<nz-modal nzNoAnimation></nz-modal>
4040
<ul nz-menu nzNoAnimation></ul>
41-
<nz-form-explain [nzNoAnimation]="true"></nz-form-explain>
4241
```
4342

4443
### 在服务中关闭
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<% if(displayBlock){ if(style != 'sass') { %>:host {
2+
display: block;
3+
}
4+
<% } else { %>\:host
5+
display: block;
6+
<% }} %>
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
<p>
2-
<%= dasherize(name) %> works!
3-
</p>
1+
<p><%= dasherize(name) %> works!</p>

schematics/ng-component/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.spec.ts.template

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
1-
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
22

33
import { <%= classify(name) %>Component } from './<%= dasherize(name) %>.component';
44

55
describe('<%= classify(name) %>Component', () => {
66
let component: <%= classify(name) %>Component;
77
let fixture: ComponentFixture<<%= classify(name) %>Component>;
88

9-
beforeEach(async(() => {
10-
TestBed.configureTestingModule({
11-
declarations: [ <%= classify(name) %>Component ]
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
<%= standalone ? 'imports' : 'declarations' %>: [<%= classify(name) %>Component]
1212
})
1313
.compileComponents();
14-
}));
1514

16-
beforeEach(() => {
1715
fixture = TestBed.createComponent(<%= classify(name) %>Component);
1816
component = fixture.componentInstance;
1917
fixture.detectChanges();
Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
1-
import { Component, OnInit<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%> } from '@angular/core';
1+
import { <% if(changeDetection !== 'Default') { %>ChangeDetectionStrategy, <% }%>Component<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%> } from '@angular/core';
22

33
@Component({
4-
selector: '<%= selector %>',<% if(inlineTemplate) { %>
4+
selector: '<%= selector %>',<% if(standalone) {%>
5+
standalone: true,
6+
imports: [],<%}%><% if(inlineTemplate) { %>
57
template: `
68
<p>
79
<%= dasherize(name) %> works!
810
</p>
9-
`,<% } else { %>
10-
templateUrl: './<%= dasherize(name) %>.component.html',<% } if(inlineStyle) { %>
11-
styles: []<% } else { %>
12-
styleUrls: ['./<%= dasherize(name) %>.component.<%= style %>']<% } %><% if(!!viewEncapsulation) { %>,
11+
`<% } else { %>
12+
templateUrl: './<%= dasherize(name) %>.component.html'<% } if(inlineStyle) { %>,
13+
styles: `<% if(displayBlock){ %>
14+
:host {
15+
display: block;
16+
}
17+
<% } %>`<% } else if (style !== 'none') { %>,
18+
styleUrl: './<%= dasherize(name) %>.component.<%= style %>'<% } %><% if(!!viewEncapsulation) { %>,
1319
encapsulation: ViewEncapsulation.<%= viewEncapsulation %><% } if (changeDetection !== 'Default') { %>,
14-
changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %>,
15-
standalone: true
20+
changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %>
1621
})
17-
export class <%= classify(name) %>Component implements OnInit {
18-
19-
constructor() { }
20-
21-
ngOnInit() {
22-
}
22+
export class <%= classify(name) %>Component {
2323

2424
}

schematics/ng-component/index.spec.ts

Lines changed: 124 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { Tree } from '@angular-devkit/schematics';
22
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
33
import { ChangeDetection, Style } from '@schematics/angular/component/schema';
44

5+
import { describe } from 'node:test'
6+
57
import { createTestApp } from '../testing/test-app';
68

79
const appOptions = {
@@ -51,70 +53,139 @@ describe('ng-component schematic', () => {
5153
);
5254
});
5355

54-
it('should create a flat component', async () => {
55-
const options = { ...defaultOptions, flat: true };
56-
const tree = await runner.runSchematic('component', options, appTree);
57-
const files = tree.files;
58-
59-
expect(files).toEqual(
60-
jasmine.arrayContaining([
61-
'/projects/ng-zorro/src/app/test.component.less',
62-
'/projects/ng-zorro/src/app/test.component.html',
63-
'/projects/ng-zorro/src/app/test.component.spec.ts',
64-
'/projects/ng-zorro/src/app/test.component.ts'
65-
])
66-
);
56+
describe('style', () => {
57+
it('should create specified style', async() => {
58+
const options = { ...defaultOptions, style: Style.Sass };
59+
const tree = await runner.runSchematic('component', options, appTree);
60+
const files = tree.files.filter(file => file.startsWith('/projects/ng-zorro/src/app/test/'));
61+
62+
expect(files).toEqual(
63+
jasmine.arrayContaining([
64+
'/projects/ng-zorro/src/app/test/test.component.sass',
65+
'/projects/ng-zorro/src/app/test/test.component.html',
66+
'/projects/ng-zorro/src/app/test/test.component.spec.ts',
67+
'/projects/ng-zorro/src/app/test/test.component.ts'
68+
])
69+
);
70+
});
71+
72+
it('should not create style file when inlineStyle is true', async() => {
73+
const options = { ...defaultOptions, inlineStyle: true };
74+
const tree = await runner.runSchematic('component', options, appTree);
75+
const files = tree.files.filter(file => file.startsWith('/projects/ng-zorro/src/app/test/'));
76+
77+
expect(files.length).toEqual(3);
78+
expect(files).toEqual(
79+
jasmine.arrayContaining([
80+
'/projects/ng-zorro/src/app/test/test.component.html',
81+
'/projects/ng-zorro/src/app/test/test.component.spec.ts',
82+
'/projects/ng-zorro/src/app/test/test.component.ts'
83+
])
84+
);
85+
});
86+
87+
it('should not create style file when style is none', async() => {
88+
const options = { ...defaultOptions, style: Style.None };
89+
const tree = await runner.runSchematic('component', options, appTree);
90+
const files = tree.files.filter(file => file.startsWith('/projects/ng-zorro/src/app/test/'));
91+
92+
expect(files.length).toEqual(3);
93+
expect(files).toEqual(
94+
jasmine.arrayContaining([
95+
'/projects/ng-zorro/src/app/test/test.component.html',
96+
'/projects/ng-zorro/src/app/test/test.component.spec.ts',
97+
'/projects/ng-zorro/src/app/test/test.component.ts'
98+
])
99+
);
100+
});
67101
});
68102

69-
it('should find the closest module', async () => {
70-
const options = { ...defaultOptions, standalone: false };
71-
const closestModule = '/projects/ng-zorro/src/app/test/test.module.ts';
103+
describe('displayBlock', () => {
104+
it('should add display block styles to the component', async () => {
105+
const options = { ...defaultOptions, displayBlock: true };
106+
const tree = await runner.runSchematic('component', options, appTree);
107+
const content = tree.readContent('/projects/ng-zorro/src/app/test/test.component.less');
72108

73-
appTree.create(
74-
closestModule,
75-
`
76-
import { NgModule } from '@angular/core';
77-
@NgModule({
78-
imports: [],
79-
declarations: []
80-
})
81-
export class ClosestModule { }
82-
`
83-
);
84-
const tree = await runner.runSchematic('component', options, appTree);
85-
const fooModuleContent = tree.readContent(closestModule);
109+
expect(content).toMatch(/display: block/);
110+
});
86111

87-
expect(fooModuleContent).toMatch(/import { TestComponent } from '.\/test.component'/);
112+
it('should add display block styles to the component when inlineStyle is true', async () => {
113+
const options = { ...defaultOptions, displayBlock: true, inlineStyle: true };
114+
const tree = await runner.runSchematic('component', options, appTree);
115+
const content = tree.readContent('/projects/ng-zorro/src/app/test/test.component.ts');
116+
117+
expect(content).toMatch(/display: block/);
118+
});
88119
});
89120

90-
it('should set classname with the closest module', async () => {
91-
const options = { ...defaultOptions, classnameWithModule: true, standalone: false };
92-
const testModule = '/projects/ng-zorro/src/app/test/test.module.ts';
121+
describe('flat', () => {
122+
it('should create a flat component', async () => {
123+
const options = { ...defaultOptions, flat: true };
124+
const tree = await runner.runSchematic('component', options, appTree);
125+
const files = tree.files;
126+
127+
expect(files).toEqual(
128+
jasmine.arrayContaining([
129+
'/projects/ng-zorro/src/app/test.component.less',
130+
'/projects/ng-zorro/src/app/test.component.html',
131+
'/projects/ng-zorro/src/app/test.component.spec.ts',
132+
'/projects/ng-zorro/src/app/test.component.ts'
133+
])
134+
);
135+
});
136+
});
93137

94-
appTree.create(
95-
testModule,
138+
describe('classnameWithModule', () => {
139+
it('should find the closest module', async () => {
140+
const options = { ...defaultOptions, standalone: false };
141+
const closestModule = '/projects/ng-zorro/src/app/test/test.module.ts';
142+
143+
appTree.create(
144+
closestModule,
145+
`
146+
import { NgModule } from '@angular/core';
147+
@NgModule({
148+
imports: [],
149+
declarations: []
150+
})
151+
export class ClosestModule { }
96152
`
97-
import { NgModule } from '@angular/core';
98-
@NgModule({
99-
imports: [],
100-
declarations: []
101-
})
102-
export class TestModule { }
103-
`
104-
);
153+
);
154+
const tree = await runner.runSchematic('component', options, appTree);
155+
const fooModuleContent = tree.readContent(closestModule);
156+
157+
expect(fooModuleContent).toMatch(/import { TestComponent } from '.\/test.component'/);
158+
});
159+
160+
it('should set classname with the closest module', async () => {
161+
const options = { ...defaultOptions, classnameWithModule: true, standalone: false };
162+
const testModule = '/projects/ng-zorro/src/app/test/test.module.ts';
163+
164+
appTree.create(
165+
testModule,
166+
`
167+
import { NgModule } from '@angular/core';
168+
@NgModule({
169+
imports: [],
170+
declarations: []
171+
})
172+
export class TestModule { }
173+
`
174+
);
105175

106-
const tree = await runner.runSchematic('component', options, appTree);
107-
const fooModuleContent = tree.readContent(testModule);
176+
const tree = await runner.runSchematic('component', options, appTree);
177+
const fooModuleContent = tree.readContent(testModule);
108178

109-
expect(fooModuleContent).toMatch(/import { TestTestComponent } from '.\/test.component'/);
110-
});
179+
expect(fooModuleContent).toMatch(/import { TestTestComponent } from '.\/test.component'/);
180+
});
111181

112-
it('should set classname with the specified module', async () => {
113-
const options = { ...defaultOptions, classnameWithModule: true, module: 'app.module.ts', standalone: false };
114-
const app = await createTestApp(runner, { ...appOptions, standalone: false });
115-
const tree = await runner.runSchematic('component', options, app);
182+
it('should set classname with the specified module', async () => {
183+
const options = { ...defaultOptions, classnameWithModule: true, module: 'app.module.ts', standalone: false };
184+
const app = await createTestApp(runner, { ...appOptions, standalone: false });
185+
const tree = await runner.runSchematic('component', options, app);
116186

117-
const appComponentContent = tree.readContent('/projects/ng-zorro/src/app/app.module.ts');
118-
expect(appComponentContent).toMatch(/import { AppTestComponent } from '.\/test\/test.component'/);
187+
const appComponentContent = tree.readContent('/projects/ng-zorro/src/app/app.module.ts');
188+
expect(appComponentContent).toMatch(/import { AppTestComponent } from '.\/test\/test.component'/);
189+
});
119190
});
120191
});

schematics/ng-component/schema.json

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"$schema": "http://json-schema.org/schema",
2+
"$schema": "http://json-schema.org/draft-07/schema",
33
"$id": "PLACEHOLDER_SCHEMATICS_ID",
44
"title": "PLACEHOLDER_SCHEMATICS_TITLE",
55
"type": "object",
@@ -26,6 +26,12 @@
2626
},
2727
"x-prompt": "What should be the name of the component?"
2828
},
29+
"displayBlock": {
30+
"description": "Specifies if the style will contain `:host { display: block; }`.",
31+
"type": "boolean",
32+
"default": false,
33+
"alias": "b"
34+
},
2935
"inlineStyle": {
3036
"description": "When true, includes styles inline in the component.ts file. Only CSS styles can be included inline. By default, an external styles file is created and referenced in the component.ts file.",
3137
"type": "boolean",
@@ -41,8 +47,7 @@
4147
"standalone": {
4248
"description": "Whether the generated component is standalone.",
4349
"type": "boolean",
44-
"default": true,
45-
"x-user-analytics": "ep.ng_standalone"
50+
"default": true
4651
},
4752
"viewEncapsulation": {
4853
"description": "The view encapsulation strategy to use in the new component.",
@@ -71,33 +76,23 @@
7176
}
7277
]
7378
},
74-
"styleext": {
75-
"description": "The file extension to use for style files.",
76-
"type": "string",
77-
"default": "css",
78-
"x-deprecated": "Use \"style\" instead."
79-
},
8079
"style": {
81-
"description": "The file extension or preprocessor to use for style files.",
80+
"description": "The file extension or preprocessor to use for style files, or 'none' to skip generating the style file.",
8281
"type": "string",
8382
"default": "css",
8483
"enum": [
8584
"css",
8685
"scss",
8786
"sass",
8887
"less",
89-
"styl"
88+
"styl",
89+
"none"
9090
]
9191
},
92-
"spec": {
93-
"type": "boolean",
94-
"description": "When true (the default), generates a \"spec.ts\" test file for the new component.",
95-
"default": true,
96-
"x-deprecated": "Use \"skipTests\" instead."
97-
},
9892
"skipTests": {
9993
"type": "boolean",
100-
"description": "When true, does not create \"spec.ts\" test files for the new component."
94+
"description": "When true, does not create \"spec.ts\" test files for the new component.",
95+
"default": false
10196
},
10297
"flat": {
10398
"type": "boolean",
@@ -106,7 +101,8 @@
106101
},
107102
"skipImport": {
108103
"type": "boolean",
109-
"description": "When true, does not import this component into the owning NgModule."
104+
"description": "When true, does not import this component into the owning NgModule.",
105+
"default": false
110106
},
111107
"selector": {
112108
"type": "string",

0 commit comments

Comments
 (0)