Skip to content

Commit 7202785

Browse files
clydindgp1130
authored andcommitted
fix(@schematics/angular): migrate spec and styleext schematic options
Fixes: #16857 (cherry picked from commit e7852b2)
1 parent ad5e1bb commit 7202785

File tree

3 files changed

+270
-0
lines changed

3 files changed

+270
-0
lines changed

packages/schematics/angular/migrations/migration-collection.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@
4949
"version": "9.0.0-next.6",
5050
"factory": "./update-8/#updateLazyModulePaths",
5151
"description": "Lazy loading syntax migration. Update lazy loading syntax to use dynamic imports."
52+
},
53+
"schematic-options-9": {
54+
"version": "9.0.2",
55+
"factory": "./update-9/schematic-options",
56+
"description": "Replace deprecated 'styleext' and 'spec' Angular schematic options."
5257
}
5358
}
5459
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import { JsonAstObject } from '@angular-devkit/core';
9+
import { Rule, UpdateRecorder } from '@angular-devkit/schematics';
10+
import { getWorkspacePath } from '../../utility/config';
11+
import { findPropertyInAstObject } from '../../utility/json-utils';
12+
import { getWorkspace } from './utils';
13+
14+
export default function(): Rule {
15+
return tree => {
16+
const workspacePath = getWorkspacePath(tree);
17+
const workspace = getWorkspace(tree);
18+
const recorder = tree.beginUpdate(workspacePath);
19+
20+
const rootSchematics = findSchematicsField(workspace);
21+
if (rootSchematics) {
22+
updateSchematicsField(rootSchematics, recorder);
23+
}
24+
25+
const projects = findPropertyInAstObject(workspace, 'projects');
26+
if (!projects || projects.kind !== 'object' || !projects.properties) {
27+
return;
28+
}
29+
30+
for (const { value } of projects.properties) {
31+
if (value.kind !== 'object') {
32+
continue;
33+
}
34+
35+
const projectSchematics = findSchematicsField(value);
36+
if (!projectSchematics) {
37+
continue;
38+
}
39+
40+
updateSchematicsField(projectSchematics, recorder);
41+
}
42+
43+
tree.commitUpdate(recorder);
44+
45+
return tree;
46+
};
47+
}
48+
49+
function findSchematicsField(value: JsonAstObject): JsonAstObject | null {
50+
const schematics = findPropertyInAstObject(value, 'schematics');
51+
if (schematics && schematics.kind == 'object') {
52+
return schematics;
53+
}
54+
55+
return null;
56+
}
57+
58+
function updateSchematicsField(schematics: JsonAstObject, recorder: UpdateRecorder): void {
59+
for (const {
60+
key: { value: schematicName },
61+
value: schematicValue,
62+
} of schematics.properties) {
63+
if (schematicValue.kind !== 'object') {
64+
continue;
65+
}
66+
67+
if (!schematicName.startsWith('@schematics/angular:')) {
68+
continue;
69+
}
70+
71+
for (const { key: optionKey, value: optionValue } of schematicValue.properties) {
72+
if (optionKey.value === 'styleext') {
73+
// Rename `styleext` to `style
74+
const offset = optionKey.start.offset + 1;
75+
recorder.remove(offset, optionKey.value.length);
76+
recorder.insertLeft(offset, 'style');
77+
} else if (optionKey.value === 'spec') {
78+
// Rename `spec` to `skipTests`
79+
const offset = optionKey.start.offset + 1;
80+
recorder.remove(offset, optionKey.value.length);
81+
recorder.insertLeft(offset, 'skipTests');
82+
83+
// invert value
84+
const { start, end } = optionValue;
85+
recorder.remove(start.offset, end.offset - start.offset);
86+
recorder.insertLeft(start.offset, `${!optionValue.value}`);
87+
}
88+
}
89+
}
90+
}
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import { EmptyTree } from '@angular-devkit/schematics';
9+
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
10+
11+
const workspacePath = '/angular.json';
12+
13+
describe('Migration to version 9', () => {
14+
describe('Migrate workspace config', () => {
15+
const schematicRunner = new SchematicTestRunner(
16+
'migrations',
17+
require.resolve('../migration-collection.json'),
18+
);
19+
20+
let tree: UnitTestTree;
21+
22+
beforeEach(async () => {
23+
tree = new UnitTestTree(new EmptyTree());
24+
tree = await schematicRunner
25+
.runExternalSchematicAsync(
26+
require.resolve('../../collection.json'),
27+
'ng-new',
28+
{
29+
name: 'migration-test',
30+
version: '1.2.3',
31+
directory: '.',
32+
},
33+
tree,
34+
)
35+
.toPromise();
36+
});
37+
38+
describe('schematic options', () => {
39+
function getI18NConfig(localId: string): object {
40+
return {
41+
outputPath: `dist/my-project/${localId}/`,
42+
i18nFile: `src/locale/messages.${localId}.xlf`,
43+
i18nFormat: 'xlf',
44+
i18nLocale: localId,
45+
};
46+
}
47+
48+
it('should replace styleext with style', async () => {
49+
const workspace = JSON.parse(tree.readContent(workspacePath));
50+
workspace.schematics = {
51+
'@schematics/angular:component': {
52+
styleext: 'scss',
53+
},
54+
};
55+
tree.overwrite(workspacePath, JSON.stringify(workspace, undefined, 2));
56+
57+
const tree2 = await schematicRunner.runSchematicAsync('schematic-options-9', {}, tree.branch()).toPromise();
58+
const { schematics } = JSON.parse(tree2.readContent(workspacePath));
59+
expect(schematics['@schematics/angular:component'].styleext).toBeUndefined();
60+
expect(schematics['@schematics/angular:component'].style).toBe('scss');
61+
});
62+
63+
it('should not replace styleext with style in non-Angular schematic', async () => {
64+
const workspace = JSON.parse(tree.readContent(workspacePath));
65+
workspace.schematics = {
66+
'@schematics/some-other:component': {
67+
styleext: 'scss',
68+
},
69+
};
70+
tree.overwrite(workspacePath, JSON.stringify(workspace, undefined, 2));
71+
72+
const tree2 = await schematicRunner.runSchematicAsync('schematic-options-9', {}, tree.branch()).toPromise();
73+
const { schematics } = JSON.parse(tree2.readContent(workspacePath));
74+
expect(schematics['@schematics/some-other:component'].styleext).toBe('scss');
75+
expect(schematics['@schematics/some-other:component'].style).toBeUndefined();
76+
});
77+
78+
it('should replace spec (false) with skipTests (true)', async () => {
79+
const workspace = JSON.parse(tree.readContent(workspacePath));
80+
workspace.schematics = {
81+
'@schematics/angular:component': {
82+
spec: false,
83+
},
84+
};
85+
tree.overwrite(workspacePath, JSON.stringify(workspace, undefined, 2));
86+
87+
const tree2 = await schematicRunner.runSchematicAsync('schematic-options-9', {}, tree.branch()).toPromise();
88+
const { schematics } = JSON.parse(tree2.readContent(workspacePath));
89+
expect(schematics['@schematics/angular:component'].spec).toBeUndefined();
90+
expect(schematics['@schematics/angular:component'].skipTests).toBe(true);
91+
});
92+
93+
it('should replace spec (true) with skipTests (false)', async () => {
94+
const workspace = JSON.parse(tree.readContent(workspacePath));
95+
workspace.schematics = {
96+
'@schematics/angular:component': {
97+
spec: true,
98+
},
99+
};
100+
tree.overwrite(workspacePath, JSON.stringify(workspace, undefined, 2));
101+
102+
const tree2 = await schematicRunner.runSchematicAsync('schematic-options-9', {}, tree.branch()).toPromise();
103+
const { schematics } = JSON.parse(tree2.readContent(workspacePath));
104+
expect(schematics['@schematics/angular:component'].spec).toBeUndefined();
105+
expect(schematics['@schematics/angular:component'].skipTests).toBe(false);
106+
});
107+
108+
it('should replace spec with skipTests for multiple Angular schematics', async () => {
109+
const workspace = JSON.parse(tree.readContent(workspacePath));
110+
workspace.schematics = {
111+
'@schematics/angular:component': {
112+
spec: false,
113+
},
114+
'@schematics/angular:directive': {
115+
spec: false,
116+
},
117+
'@schematics/angular:service': {
118+
spec: true,
119+
},
120+
'@schematics/angular:module': {
121+
spec: false,
122+
},
123+
'@schematics/angular:guard': {
124+
spec: true,
125+
},
126+
};
127+
tree.overwrite(workspacePath, JSON.stringify(workspace, undefined, 2));
128+
129+
const tree2 = await schematicRunner.runSchematicAsync('schematic-options-9', {}, tree.branch()).toPromise();
130+
const { schematics } = JSON.parse(tree2.readContent(workspacePath));
131+
for (const key of Object.keys(workspace.schematics)) {
132+
expect(schematics[key].spec).toBeUndefined();
133+
expect(schematics[key].skipTests).toBe(!workspace.schematics[key].spec);
134+
}
135+
});
136+
137+
it('should replace both styleext with style and spec with skipTests', async () => {
138+
const workspace = JSON.parse(tree.readContent(workspacePath));
139+
workspace.schematics = {
140+
'@schematics/angular:component': {
141+
styleext: 'scss',
142+
spec: false,
143+
},
144+
};
145+
tree.overwrite(workspacePath, JSON.stringify(workspace, undefined, 2));
146+
147+
const tree2 = await schematicRunner.runSchematicAsync('schematic-options-9', {}, tree.branch()).toPromise();
148+
const { schematics } = JSON.parse(tree2.readContent(workspacePath));
149+
expect(schematics['@schematics/angular:component'].styleext).toBeUndefined();
150+
expect(schematics['@schematics/angular:component'].style).toBe('scss');
151+
expect(schematics['@schematics/angular:component'].spec).toBeUndefined();
152+
expect(schematics['@schematics/angular:component'].skipTests).toBe(true);
153+
});
154+
155+
it('should replace both styleext with style and spec with skipTests in a project', async () => {
156+
const workspace = JSON.parse(tree.readContent(workspacePath));
157+
workspace.projects['migration-test'].schematics = {
158+
'@schematics/angular:component': {
159+
styleext: 'scss',
160+
spec: false,
161+
},
162+
};
163+
tree.overwrite(workspacePath, JSON.stringify(workspace, undefined, 2));
164+
165+
const tree2 = await schematicRunner.runSchematicAsync('schematic-options-9', {}, tree.branch()).toPromise();
166+
const { projects: { 'migration-test': { schematics } } } = JSON.parse(tree2.readContent(workspacePath));
167+
expect(schematics['@schematics/angular:component'].styleext).toBeUndefined();
168+
expect(schematics['@schematics/angular:component'].style).toBe('scss');
169+
expect(schematics['@schematics/angular:component'].spec).toBeUndefined();
170+
expect(schematics['@schematics/angular:component'].skipTests).toBe(true);
171+
});
172+
173+
});
174+
});
175+
});

0 commit comments

Comments
 (0)