Skip to content

Commit 1ff591f

Browse files
committed
feat(@schematics/angular): tslint migration for 8
Migration of the `tslint.json` and `package.json` files required by the refactoring of codelyzer. For more information check this PR mgechev/codelyzer#754.
1 parent 4a093e5 commit 1ff591f

File tree

3 files changed

+193
-0
lines changed

3 files changed

+193
-0
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@
2929
"version": "7.0.3",
3030
"factory": "./update-7/index#updateDevkitBuildNgPackagr",
3131
"description": "Update an Angular CLI project to version 7."
32+
},
33+
"migration-07": {
34+
"version": "8.0.0",
35+
"factory": "./update-8",
36+
"description": "Update an Angular CLI project to version 8."
3237
}
3338
}
3439
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
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 { JsonParseMode, parseJsonAst } from '@angular-devkit/core';
9+
import {
10+
Rule,
11+
Tree,
12+
chain,
13+
SchematicContext,
14+
} from '@angular-devkit/schematics';
15+
import {
16+
NodeDependency,
17+
NodeDependencyType,
18+
addPackageJsonDependency,
19+
} from '../../utility/dependencies';
20+
import { findPropertyInAstObject } from '../../utility/json-utils';
21+
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
22+
23+
const ruleMapping: {[key: string]: string} = {
24+
'contextual-life-cycle': 'contextual-lifecycle',
25+
'no-conflicting-life-cycle-hooks': 'no-conflicting-lifecycle',
26+
'no-life-cycle-call': 'no-lifecycle-call',
27+
'use-life-cycle-interface': 'use-lifecycle-interface',
28+
'decorator-not-allowed': 'contextual-decorator',
29+
'enforce-component-selector': 'use-component-selector',
30+
'no-output-named-after-standard-event': 'no-output-native',
31+
'use-host-property-decorator': 'no-host-metadata-property',
32+
'use-input-property-decorator': 'no-inputs-metadata-property',
33+
'use-output-property-decorator': 'no-outputs-metadata-property',
34+
'no-queries-parameter': 'no-queries-metadata-property',
35+
'pipe-impure': 'no-pipe-impure',
36+
'use-view-encapsulation': 'use-component-view-encapsulation',
37+
i18n: 'template-i18n',
38+
'banana-in-box': 'template-banana-in-box',
39+
'no-template-call-expression': 'template-no-call-expression',
40+
'templates-no-negated-async': 'template-no-negated-async',
41+
'trackBy-function': 'template-use-track-by-function',
42+
'no-attribute-parameter-decorator': 'no-attribute-decorator',
43+
'max-inline-declarations': 'component-max-inline-declarations',
44+
};
45+
46+
function updateTsLintConfig(): Rule {
47+
return (host: Tree) => {
48+
const tsLintPath = '/tslint.json';
49+
const buffer = host.read(tsLintPath);
50+
if (!buffer) {
51+
return host;
52+
}
53+
const tsCfgAst = parseJsonAst(buffer.toString(), JsonParseMode.Loose);
54+
55+
if (tsCfgAst.kind != 'object') {
56+
return host;
57+
}
58+
59+
const rulesNode = findPropertyInAstObject(tsCfgAst, 'rules');
60+
if (!rulesNode || rulesNode.kind != 'object') {
61+
return host;
62+
}
63+
64+
const recorder = host.beginUpdate(tsLintPath);
65+
66+
rulesNode.properties.forEach(prop => {
67+
const mapping = ruleMapping[prop.key.value];
68+
if (mapping) {
69+
recorder.remove(prop.key.start.offset + 1, prop.key.value.length);
70+
recorder.insertLeft(prop.key.start.offset + 1, mapping);
71+
}
72+
});
73+
74+
host.commitUpdate(recorder);
75+
76+
return host;
77+
};
78+
}
79+
80+
function updatePackageJson() {
81+
return (host: Tree, context: SchematicContext) => {
82+
const dependency: NodeDependency = {
83+
type: NodeDependencyType.Dev,
84+
name: 'codelyzer',
85+
version: '^5.0.0',
86+
overwrite: true,
87+
};
88+
89+
addPackageJsonDependency(host, dependency);
90+
context.addTask(new NodePackageInstallTask())
91+
92+
return host;
93+
};
94+
}
95+
96+
export default function(): Rule {
97+
return () => {
98+
return chain([
99+
updateTsLintConfig(),
100+
updatePackageJson(),
101+
]);
102+
};
103+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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+
// tslint:disable:no-big-function
9+
import { EmptyTree } from '@angular-devkit/schematics';
10+
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
11+
12+
const renames = [
13+
'use-lifecycle-interface',
14+
'no-host-metadata-property',
15+
'no-outputs-metadata-property',
16+
'no-inputs-metadata-property',
17+
];
18+
19+
describe('Migration to version 8', () => {
20+
const schematicRunner = new SchematicTestRunner(
21+
'migrations',
22+
require.resolve('../migration-collection.json'),
23+
);
24+
25+
let tree: UnitTestTree;
26+
const tslintPath = '/tslint.json';
27+
const packageJsonPath = '/package.json';
28+
const baseConfig = {};
29+
const defaultOptions = {};
30+
const configPath = `/angular-cli.json`;
31+
const tslintConfig = {
32+
rules: {
33+
'directive-selector': [
34+
true,
35+
'attribute',
36+
'app',
37+
'camelCase',
38+
],
39+
'component-selector': [
40+
true,
41+
'element',
42+
'app',
43+
'kebab-case',
44+
],
45+
'no-output-on-prefix': true,
46+
'use-input-property-decorator': true,
47+
'use-output-property-decorator': true,
48+
'use-host-property-decorator': true,
49+
'no-input-rename': true,
50+
'no-output-rename': true,
51+
'use-life-cycle-interface': true,
52+
'use-pipe-transform-interface': true,
53+
'component-class-suffix': true,
54+
'directive-class-suffix': true,
55+
},
56+
};
57+
const packageJson = {
58+
devDependencies: {
59+
codelyzer: '^4.5.0',
60+
},
61+
};
62+
63+
describe('Migration of codelyzer to version 5', () => {
64+
beforeEach(() => {
65+
tree = new UnitTestTree(new EmptyTree());
66+
tree.create(configPath, JSON.stringify(baseConfig, null, 2));
67+
tree.create(packageJsonPath, JSON.stringify(packageJson, null, 2));
68+
tree.create(tslintPath, JSON.stringify(tslintConfig, null, 2));
69+
});
70+
71+
it('should rename all previous rules', () => {
72+
tree = schematicRunner.runSchematic('migration-07', defaultOptions, tree);
73+
const tslint = JSON.parse(tree.readContent(tslintPath));
74+
for (const rule of renames) {
75+
expect(rule in tslint.rules).toBeTruthy(`Rule ${rule} not renamed`);
76+
}
77+
});
78+
79+
it('should update codelyzer\'s version', () => {
80+
tree = schematicRunner.runSchematic('migration-07', defaultOptions, tree);
81+
const packageJson = JSON.parse(tree.readContent(packageJsonPath));
82+
expect(packageJson.devDependencies.codelyzer).toBe('^5.0.0');
83+
});
84+
});
85+
});

0 commit comments

Comments
 (0)