Skip to content

Commit d1623c6

Browse files
committed
refactor(@schematics/angular): refactor some schematics to use the new JSON parser
1 parent cdff686 commit d1623c6

File tree

6 files changed

+84
-224
lines changed

6 files changed

+84
-224
lines changed

packages/schematics/angular/application/index.ts

Lines changed: 11 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
JsonAstObject,
1010
JsonObject,
1111
JsonParseMode,
12+
JsonValue,
1213
join,
1314
normalize,
1415
parseJsonAst,
@@ -34,7 +35,7 @@ import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
3435
import { Schema as ComponentOptions } from '../component/schema';
3536
import { Schema as E2eOptions } from '../e2e/schema';
3637
import { NodeDependencyType, addPackageJsonDependency } from '../utility/dependencies';
37-
import { findPropertyInAstObject, insertPropertyInAstObjectInOrder } from '../utility/json-utils';
38+
import { JSONFile } from '../utility/json-file';
3839
import { latestVersions } from '../utility/latest-versions';
3940
import { applyLintFix } from '../utility/lint-fix';
4041
import { relativePathToWorkspaceRoot } from '../utility/paths';
@@ -99,48 +100,19 @@ function mergeWithRootTsLint(parentHost: Tree) {
99100
return;
100101
}
101102

102-
const rootTslintConfig = readTsLintConfig(parentHost, tsLintPath);
103-
const appTslintConfig = readTsLintConfig(host, tsLintPath);
103+
const rootTslintConfig = new JSONFile(parentHost, tsLintPath);
104+
const appTslintConfig = new JSONFile(host, tsLintPath);
104105

105-
const recorder = host.beginUpdate(tsLintPath);
106-
rootTslintConfig.properties.forEach(prop => {
107-
if (findPropertyInAstObject(appTslintConfig, prop.key.value)) {
108-
// property already exists. Skip!
109-
return;
106+
for (const [pKey, pValue] of Object.entries(rootTslintConfig.get([]) as Record<string, JsonValue>)) {
107+
if (typeof pValue !== 'object' || Array.isArray(pValue)) {
108+
appTslintConfig.modify([pKey], pValue);
109+
continue;
110110
}
111111

112-
insertPropertyInAstObjectInOrder(
113-
recorder,
114-
appTslintConfig,
115-
prop.key.value,
116-
prop.value.value,
117-
2,
118-
);
119-
});
120-
121-
const rootRules = findPropertyInAstObject(rootTslintConfig, 'rules');
122-
const appRules = findPropertyInAstObject(appTslintConfig, 'rules');
123-
124-
if (!appRules || appRules.kind !== 'object' || !rootRules || rootRules.kind !== 'object') {
125-
// rules are not valid. Skip!
126-
return;
112+
for (const [key, value] of Object.entries(rootTslintConfig.get([pKey]) as Record<string, JsonObject>)) {
113+
appTslintConfig.modify([pKey, key], value);
114+
}
127115
}
128-
129-
rootRules.properties.forEach(prop => {
130-
insertPropertyInAstObjectInOrder(
131-
recorder,
132-
appRules,
133-
prop.key.value,
134-
prop.value.value,
135-
4,
136-
);
137-
});
138-
139-
host.commitUpdate(recorder);
140-
141-
// this shouldn't be needed but at the moment without this formatting is not correct.
142-
const content = readTsLintConfig(host, tsLintPath);
143-
host.overwrite(tsLintPath, JSON.stringify(content.value, undefined, 2));
144116
};
145117
}
146118

packages/schematics/angular/migrations/update-10/add-deprecation-rule-tslint.ts

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
import { JsonAstObject, JsonValue } from '@angular-devkit/core';
8+
import { JsonValue } from '@angular-devkit/core';
99
import { Rule } from '@angular-devkit/schematics';
1010
import { getPackageJsonDependency } from '../../utility/dependencies';
11-
import { findPropertyInAstObject, insertPropertyInAstObjectInOrder, removePropertyInAstObject } from '../../utility/json-utils';
12-
import { readJsonFileAsAstObject } from '../update-9/utils';
11+
import { JSONFile } from '../../utility/json-file';
1312

1413
const TSLINT_CONFIG_PATH = '/tslint.json';
1514
const RULES_TO_ADD: Record<string, JsonValue> = {
@@ -32,33 +31,23 @@ export default function (): Rule {
3231
}
3332

3433
// Update tslint config.
35-
const tslintJsonAst = readJsonFileAsAstObject(tree, TSLINT_CONFIG_PATH);
36-
if (!tslintJsonAst) {
34+
const json = new JSONFile(tree, TSLINT_CONFIG_PATH);
35+
if (json.error) {
3736
const config = ['tslint.js', 'tslint.yaml'].find(c => tree.exists(c));
3837
if (config) {
39-
logger.warn(`Skipping: Expected a JSON configuration file but found "${config}".`);
38+
logger.warn(`Expected a JSON configuration file but found "${config}".`);
4039
} else {
41-
logger.warn('Skipping: Cannot find "tslint.json" configuration file.');
40+
logger.warn('Cannot find "tslint.json" configuration file.');
4241
}
4342

4443
return;
4544
}
4645

4746
for (const [name, value] of Object.entries(RULES_TO_ADD)) {
48-
const tslintJsonAst = readJsonFileAsAstObject(tree, TSLINT_CONFIG_PATH) as JsonAstObject;
49-
const rulesAst = findPropertyInAstObject(tslintJsonAst, 'rules');
50-
if (rulesAst?.kind !== 'object') {
51-
break;
47+
const ruleName = ['rules', name];
48+
if (json.get(ruleName) === undefined) {
49+
json.modify(ruleName, value);
5250
}
53-
54-
if (findPropertyInAstObject(rulesAst, name)) {
55-
// Skip as rule already exists.
56-
continue;
57-
}
58-
59-
const recorder = tree.beginUpdate(TSLINT_CONFIG_PATH);
60-
insertPropertyInAstObjectInOrder(recorder, rulesAst, name, value, 4);
61-
tree.commitUpdate(recorder);
6251
}
6352
};
6453
}

packages/schematics/angular/migrations/update-10/update-module-and-target-compiler-options.ts

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77
*/
88
import { dirname, join, normalize } from '@angular-devkit/core';
99
import { Rule, Tree } from '@angular-devkit/schematics';
10-
import { appendPropertyInAstObject, findPropertyInAstObject, removePropertyInAstObject } from '../../utility/json-utils';
10+
import { JSONFile } from '../../utility/json-file';
1111
import { getWorkspace } from '../../utility/workspace';
1212
import { Builders } from '../../utility/workspace-models';
13-
import { readJsonFileAsAstObject } from '../update-9/utils';
1413

1514

1615
interface ModuleAndTargetReplamenent {
@@ -91,41 +90,26 @@ export default function (): Rule {
9190
}
9291

9392
function updateModuleAndTarget(host: Tree, tsConfigPath: string, replacements: ModuleAndTargetReplamenent) {
94-
const jsonAst = readJsonFileAsAstObject(host, tsConfigPath);
95-
if (!jsonAst) {
96-
return;
97-
}
98-
99-
const compilerOptionsAst = findPropertyInAstObject(jsonAst, 'compilerOptions');
100-
if (compilerOptionsAst?.kind !== 'object') {
93+
const json = new JSONFile(host, tsConfigPath);
94+
if (json.error) {
10195
return;
10296
}
10397

10498
const { oldTarget, newTarget, newModule, oldModule } = replacements;
105-
106-
const recorder = host.beginUpdate(tsConfigPath);
10799
if (newTarget) {
108-
const targetAst = findPropertyInAstObject(compilerOptionsAst, 'target');
100+
const target = json.get(['compilerOptions', 'target']);
109101

110-
if (!targetAst && !oldTarget) {
111-
appendPropertyInAstObject(recorder, compilerOptionsAst, 'target', newTarget, 4);
112-
} else if (targetAst?.kind === 'string' && (!oldTarget || oldTarget === targetAst.value.toLowerCase())) {
113-
const offset = targetAst.start.offset + 1;
114-
recorder.remove(offset, targetAst.value.length);
115-
recorder.insertLeft(offset, newTarget);
102+
if ((typeof target === 'string' && (!oldTarget || oldTarget === target.toLowerCase())) || !target) {
103+
json.modify(['compilerOptions', 'target'], newTarget);
116104
}
117105
}
118106

119107
if (newModule === false) {
120-
removePropertyInAstObject(recorder, compilerOptionsAst, 'module');
108+
json.remove(['compilerOptions', 'module']);
121109
} else if (newModule) {
122-
const moduleAst = findPropertyInAstObject(compilerOptionsAst, 'module');
123-
if (moduleAst?.kind === 'string' && oldModule === moduleAst.value.toLowerCase()) {
124-
const offset = moduleAst.start.offset + 1;
125-
recorder.remove(offset, moduleAst.value.length);
126-
recorder.insertLeft(offset, newModule);
110+
const module = json.get(['compilerOptions', 'module']);
111+
if (typeof module === 'string' && oldModule === module.toLowerCase()) {
112+
json.modify(['compilerOptions', 'module'], newModule);
127113
}
128114
}
129-
130-
host.commitUpdate(recorder);
131115
}

packages/schematics/angular/migrations/update-10/update-tslint.ts

Lines changed: 10 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
import { JsonAstObject, JsonValue } from '@angular-devkit/core';
8+
import { JsonValue } from '@angular-devkit/core';
99
import { Rule } from '@angular-devkit/schematics';
1010
import { addPackageJsonDependency, getPackageJsonDependency } from '../../utility/dependencies';
11-
import { findPropertyInAstObject, insertPropertyInAstObjectInOrder, removePropertyInAstObject } from '../../utility/json-utils';
12-
import { readJsonFileAsAstObject } from '../update-9/utils';
11+
import { JSONFile } from '../../utility/json-file';
1312

1413
export const TSLINT_VERSION = '~6.1.0';
1514
const TSLINT_CONFIG_PATH = '/tslint.json';
@@ -96,8 +95,8 @@ export default function (): Rule {
9695
}
9796

9897
// Update tslint config.
99-
const tslintJsonAst = readJsonFileAsAstObject(tree, TSLINT_CONFIG_PATH);
100-
if (!tslintJsonAst) {
98+
const json = new JSONFile(tree, TSLINT_CONFIG_PATH);
99+
if (json.error) {
101100
const config = ['tslint.js', 'tslint.yaml'].find(c => tree.exists(c));
102101
if (config) {
103102
logger.warn(`Expected a JSON configuration file but found "${config}".`);
@@ -110,24 +109,14 @@ export default function (): Rule {
110109

111110
// Remove old/deprecated rules.
112111
for (const rule of RULES_TO_DELETE) {
113-
const tslintJsonAst = readJsonFileAsAstObject(tree, TSLINT_CONFIG_PATH) as JsonAstObject;
114-
const rulesAst = findPropertyInAstObject(tslintJsonAst, 'rules');
115-
if (rulesAst?.kind !== 'object') {
116-
break;
117-
}
118-
119-
const recorder = tree.beginUpdate(TSLINT_CONFIG_PATH);
120-
removePropertyInAstObject(recorder, rulesAst, rule);
121-
tree.commitUpdate(recorder);
112+
json.remove(['rules', rule]);
122113
}
123114

124115
// Add new rules only iif the configuration extends 'tslint:recommended'.
125116
// This is because some rules conflict with prettier or other tools.
126-
const extendsAst = findPropertyInAstObject(tslintJsonAst, 'extends');
117+
const extend = json.get(['extends']);
127118
if (
128-
!extendsAst ||
129-
(extendsAst.kind === 'string' && extendsAst.value !== 'tslint:recommended') ||
130-
(extendsAst.kind === 'array' && extendsAst.elements.some(e => e.value !== 'tslint:recommended'))
119+
extend !== 'tslint:recommended' || (Array.isArray(extend) && extend.some(e => e.value !== 'tslint:recommended'))
131120
) {
132121
logger.warn(`tslint configuration does not extend "tslint:recommended" or it extends multiple configurations.`
133122
+ '\nSkipping rule changes as some rules might conflict.');
@@ -136,21 +125,10 @@ export default function (): Rule {
136125
}
137126

138127
for (const [name, value] of Object.entries(RULES_TO_ADD)) {
139-
const tslintJsonAst = readJsonFileAsAstObject(tree, TSLINT_CONFIG_PATH) as JsonAstObject;
140-
const rulesAst = findPropertyInAstObject(tslintJsonAst, 'rules');
141-
if (rulesAst?.kind !== 'object') {
142-
break;
143-
}
144-
145-
if (findPropertyInAstObject(rulesAst, name)) {
146-
// Skip as rule already exists.
147-
continue;
128+
const ruleName = ['rules', name];
129+
if (json.get(ruleName) === undefined) {
130+
json.modify(ruleName, value);
148131
}
149-
150-
const recorder = tree.beginUpdate(TSLINT_CONFIG_PATH);
151-
insertPropertyInAstObjectInOrder(recorder, rulesAst, name, value, 4);
152-
tree.commitUpdate(recorder);
153132
}
154-
155133
};
156134
}

0 commit comments

Comments
 (0)