Skip to content

Commit

Permalink
fix(@schematics/angular): migrate spec and styleext schematic options
Browse files Browse the repository at this point in the history
Fixes: #16857
(cherry picked from commit e7852b2)
  • Loading branch information
clydin authored and dgp1130 committed Feb 11, 2020
1 parent ad5e1bb commit 7202785
Show file tree
Hide file tree
Showing 3 changed files with 270 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@
"version": "9.0.0-next.6",
"factory": "./update-8/#updateLazyModulePaths",
"description": "Lazy loading syntax migration. Update lazy loading syntax to use dynamic imports."
},
"schematic-options-9": {
"version": "9.0.2",
"factory": "./update-9/schematic-options",
"description": "Replace deprecated 'styleext' and 'spec' Angular schematic options."
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { JsonAstObject } from '@angular-devkit/core';
import { Rule, UpdateRecorder } from '@angular-devkit/schematics';
import { getWorkspacePath } from '../../utility/config';
import { findPropertyInAstObject } from '../../utility/json-utils';
import { getWorkspace } from './utils';

export default function(): Rule {
return tree => {
const workspacePath = getWorkspacePath(tree);
const workspace = getWorkspace(tree);
const recorder = tree.beginUpdate(workspacePath);

const rootSchematics = findSchematicsField(workspace);
if (rootSchematics) {
updateSchematicsField(rootSchematics, recorder);
}

const projects = findPropertyInAstObject(workspace, 'projects');
if (!projects || projects.kind !== 'object' || !projects.properties) {
return;
}

for (const { value } of projects.properties) {
if (value.kind !== 'object') {
continue;
}

const projectSchematics = findSchematicsField(value);
if (!projectSchematics) {
continue;
}

updateSchematicsField(projectSchematics, recorder);
}

tree.commitUpdate(recorder);

return tree;
};
}

function findSchematicsField(value: JsonAstObject): JsonAstObject | null {
const schematics = findPropertyInAstObject(value, 'schematics');
if (schematics && schematics.kind == 'object') {
return schematics;
}

return null;
}

function updateSchematicsField(schematics: JsonAstObject, recorder: UpdateRecorder): void {
for (const {
key: { value: schematicName },
value: schematicValue,
} of schematics.properties) {
if (schematicValue.kind !== 'object') {
continue;
}

if (!schematicName.startsWith('@schematics/angular:')) {
continue;
}

for (const { key: optionKey, value: optionValue } of schematicValue.properties) {
if (optionKey.value === 'styleext') {
// Rename `styleext` to `style
const offset = optionKey.start.offset + 1;
recorder.remove(offset, optionKey.value.length);
recorder.insertLeft(offset, 'style');
} else if (optionKey.value === 'spec') {
// Rename `spec` to `skipTests`
const offset = optionKey.start.offset + 1;
recorder.remove(offset, optionKey.value.length);
recorder.insertLeft(offset, 'skipTests');

// invert value
const { start, end } = optionValue;
recorder.remove(start.offset, end.offset - start.offset);
recorder.insertLeft(start.offset, `${!optionValue.value}`);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { EmptyTree } from '@angular-devkit/schematics';
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';

const workspacePath = '/angular.json';

describe('Migration to version 9', () => {
describe('Migrate workspace config', () => {
const schematicRunner = new SchematicTestRunner(
'migrations',
require.resolve('../migration-collection.json'),
);

let tree: UnitTestTree;

beforeEach(async () => {
tree = new UnitTestTree(new EmptyTree());
tree = await schematicRunner
.runExternalSchematicAsync(
require.resolve('../../collection.json'),
'ng-new',
{
name: 'migration-test',
version: '1.2.3',
directory: '.',
},
tree,
)
.toPromise();
});

describe('schematic options', () => {
function getI18NConfig(localId: string): object {
return {
outputPath: `dist/my-project/${localId}/`,
i18nFile: `src/locale/messages.${localId}.xlf`,
i18nFormat: 'xlf',
i18nLocale: localId,
};
}

it('should replace styleext with style', async () => {
const workspace = JSON.parse(tree.readContent(workspacePath));
workspace.schematics = {
'@schematics/angular:component': {
styleext: 'scss',
},
};
tree.overwrite(workspacePath, JSON.stringify(workspace, undefined, 2));

const tree2 = await schematicRunner.runSchematicAsync('schematic-options-9', {}, tree.branch()).toPromise();
const { schematics } = JSON.parse(tree2.readContent(workspacePath));
expect(schematics['@schematics/angular:component'].styleext).toBeUndefined();
expect(schematics['@schematics/angular:component'].style).toBe('scss');
});

it('should not replace styleext with style in non-Angular schematic', async () => {
const workspace = JSON.parse(tree.readContent(workspacePath));
workspace.schematics = {
'@schematics/some-other:component': {
styleext: 'scss',
},
};
tree.overwrite(workspacePath, JSON.stringify(workspace, undefined, 2));

const tree2 = await schematicRunner.runSchematicAsync('schematic-options-9', {}, tree.branch()).toPromise();
const { schematics } = JSON.parse(tree2.readContent(workspacePath));
expect(schematics['@schematics/some-other:component'].styleext).toBe('scss');
expect(schematics['@schematics/some-other:component'].style).toBeUndefined();
});

it('should replace spec (false) with skipTests (true)', async () => {
const workspace = JSON.parse(tree.readContent(workspacePath));
workspace.schematics = {
'@schematics/angular:component': {
spec: false,
},
};
tree.overwrite(workspacePath, JSON.stringify(workspace, undefined, 2));

const tree2 = await schematicRunner.runSchematicAsync('schematic-options-9', {}, tree.branch()).toPromise();
const { schematics } = JSON.parse(tree2.readContent(workspacePath));
expect(schematics['@schematics/angular:component'].spec).toBeUndefined();
expect(schematics['@schematics/angular:component'].skipTests).toBe(true);
});

it('should replace spec (true) with skipTests (false)', async () => {
const workspace = JSON.parse(tree.readContent(workspacePath));
workspace.schematics = {
'@schematics/angular:component': {
spec: true,
},
};
tree.overwrite(workspacePath, JSON.stringify(workspace, undefined, 2));

const tree2 = await schematicRunner.runSchematicAsync('schematic-options-9', {}, tree.branch()).toPromise();
const { schematics } = JSON.parse(tree2.readContent(workspacePath));
expect(schematics['@schematics/angular:component'].spec).toBeUndefined();
expect(schematics['@schematics/angular:component'].skipTests).toBe(false);
});

it('should replace spec with skipTests for multiple Angular schematics', async () => {
const workspace = JSON.parse(tree.readContent(workspacePath));
workspace.schematics = {
'@schematics/angular:component': {
spec: false,
},
'@schematics/angular:directive': {
spec: false,
},
'@schematics/angular:service': {
spec: true,
},
'@schematics/angular:module': {
spec: false,
},
'@schematics/angular:guard': {
spec: true,
},
};
tree.overwrite(workspacePath, JSON.stringify(workspace, undefined, 2));

const tree2 = await schematicRunner.runSchematicAsync('schematic-options-9', {}, tree.branch()).toPromise();
const { schematics } = JSON.parse(tree2.readContent(workspacePath));
for (const key of Object.keys(workspace.schematics)) {
expect(schematics[key].spec).toBeUndefined();
expect(schematics[key].skipTests).toBe(!workspace.schematics[key].spec);
}
});

it('should replace both styleext with style and spec with skipTests', async () => {
const workspace = JSON.parse(tree.readContent(workspacePath));
workspace.schematics = {
'@schematics/angular:component': {
styleext: 'scss',
spec: false,
},
};
tree.overwrite(workspacePath, JSON.stringify(workspace, undefined, 2));

const tree2 = await schematicRunner.runSchematicAsync('schematic-options-9', {}, tree.branch()).toPromise();
const { schematics } = JSON.parse(tree2.readContent(workspacePath));
expect(schematics['@schematics/angular:component'].styleext).toBeUndefined();
expect(schematics['@schematics/angular:component'].style).toBe('scss');
expect(schematics['@schematics/angular:component'].spec).toBeUndefined();
expect(schematics['@schematics/angular:component'].skipTests).toBe(true);
});

it('should replace both styleext with style and spec with skipTests in a project', async () => {
const workspace = JSON.parse(tree.readContent(workspacePath));
workspace.projects['migration-test'].schematics = {
'@schematics/angular:component': {
styleext: 'scss',
spec: false,
},
};
tree.overwrite(workspacePath, JSON.stringify(workspace, undefined, 2));

const tree2 = await schematicRunner.runSchematicAsync('schematic-options-9', {}, tree.branch()).toPromise();
const { projects: { 'migration-test': { schematics } } } = JSON.parse(tree2.readContent(workspacePath));
expect(schematics['@schematics/angular:component'].styleext).toBeUndefined();
expect(schematics['@schematics/angular:component'].style).toBe('scss');
expect(schematics['@schematics/angular:component'].spec).toBeUndefined();
expect(schematics['@schematics/angular:component'].skipTests).toBe(true);
});

});
});
});

0 comments on commit 7202785

Please sign in to comment.