Skip to content

Commit 0bc1597

Browse files
committed
refactor(@schematics/angular): use jsonc-parser instead of devkit parser
1 parent a038699 commit 0bc1597

File tree

13 files changed

+66
-126
lines changed

13 files changed

+66
-126
lines changed

packages/angular/cli/commands/config-impl.ts

Lines changed: 23 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -16,50 +16,15 @@ import {
1616
validateWorkspace,
1717
} from '../utilities/config';
1818
import { JSONFile, parseJson } from '../utilities/json-file';
19-
import { Schema as ConfigCommandSchema, Value as ConfigCommandSchemaValue } from './config';
20-
21-
function _validateBoolean(value: string) {
22-
if (('' + value).trim() === 'true') {
23-
return true;
24-
} else if (('' + value).trim() === 'false') {
25-
return false;
26-
} else {
27-
throw new Error(`Invalid value type; expected Boolean, received ${JSON.stringify(value)}.`);
28-
}
29-
}
30-
function _validateString(value: string) {
31-
return value;
32-
}
33-
function _validateAnalytics(value: string) {
34-
if (value === '') {
35-
// Disable analytics.
36-
return null;
37-
} else {
38-
return value;
39-
}
40-
}
41-
function _validateAnalyticsSharingUuid(value: string) {
42-
if (value == '') {
43-
return uuidV4();
44-
} else {
45-
return value;
46-
}
47-
}
48-
function _validateAnalyticsSharingTracking(value: string) {
49-
if (!value.match(/^GA-\d+-\d+$/)) {
50-
throw new Error(`Invalid GA property ID: ${JSON.stringify(value)}.`);
51-
}
52-
53-
return value;
54-
}
55-
56-
const validCliPaths = new Map<string, (arg: string) => JsonValue>([
57-
['cli.warnings.versionMismatch', _validateBoolean],
58-
['cli.defaultCollection', _validateString],
59-
['cli.packageManager', _validateString],
60-
['cli.analytics', _validateAnalytics],
61-
['cli.analyticsSharing.tracking', _validateAnalyticsSharingTracking],
62-
['cli.analyticsSharing.uuid', _validateAnalyticsSharingUuid],
19+
import { Schema as ConfigCommandSchema } from './config';
20+
21+
const validCliPaths = new Map<string, ((arg: string | number | boolean | undefined) => string) | undefined>([
22+
['cli.warnings.versionMismatch', undefined],
23+
['cli.defaultCollection', undefined],
24+
['cli.packageManager', undefined],
25+
['cli.analytics', undefined],
26+
['cli.analyticsSharing.tracking', undefined],
27+
['cli.analyticsSharing.uuid', v => v ? `${v}` : uuidV4()],
6328
]);
6429

6530
/**
@@ -98,21 +63,17 @@ function parseJsonPath(path: string): (string | number)[] {
9863
return result.filter(fragment => fragment != null);
9964
}
10065

101-
function normalizeValue(value: ConfigCommandSchemaValue, path: string): JsonValue {
102-
const cliOptionType = validCliPaths.get(path);
103-
if (cliOptionType) {
104-
return cliOptionType('' + value);
105-
}
106-
107-
if (typeof value === 'string') {
108-
try {
109-
return parseJson(value);
110-
} catch (e) {
111-
throw e;
112-
}
66+
function normalizeValue(value: string | undefined | boolean | number): JsonValue | undefined {
67+
const valueString = `${value}`.trim();
68+
if (valueString === 'true') {
69+
return true;
70+
} else if (valueString === 'false') {
71+
return false;
72+
} else if (isFinite(+valueString)) {
73+
return +valueString;
11374
}
11475

115-
return value;
76+
return value || undefined;
11677
}
11778

11879
export class ConfigCommand extends Command<ConfigCommandSchema> {
@@ -133,7 +94,7 @@ export class ConfigCommand extends Command<ConfigCommandSchema> {
13394
We found a global configuration that was used in Angular CLI 1.
13495
It has been automatically migrated.`);
13596
}
136-
} catch {}
97+
} catch { }
13798
}
13899

139100
if (options.value == undefined) {
@@ -171,7 +132,7 @@ export class ConfigCommand extends Command<ConfigCommandSchema> {
171132
}
172133

173134
private async set(options: ConfigCommandSchema) {
174-
if (!options.jsonPath || !options.jsonPath.trim()) {
135+
if (!options.jsonPath?.trim()) {
175136
throw new Error('Invalid Path.');
176137
}
177138

@@ -190,11 +151,9 @@ export class ConfigCommand extends Command<ConfigCommandSchema> {
190151
return 1;
191152
}
192153

193-
// TODO: Modify & save without destroying comments
194-
const value = normalizeValue(options.value || '', options.jsonPath);
195-
196154
const jsonPath = parseJsonPath(options.jsonPath);
197-
const modified = config.modify(jsonPath, value);
155+
const value = validCliPaths.get(options.jsonPath)?.(options.value) ?? options.value;
156+
const modified = config.modify(jsonPath, normalizeValue(value));
198157

199158
if (!modified) {
200159
this.logger.error('Value cannot be found.');
@@ -210,7 +169,7 @@ export class ConfigCommand extends Command<ConfigCommandSchema> {
210169
return 1;
211170
}
212171

213-
config.write();
172+
config.save();
214173

215174
return 0;
216175
}

packages/angular/cli/lib/config/schema.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,20 @@
6464
"analytics": {
6565
"type": ["boolean", "string"],
6666
"description": "Share anonymous usage data with the Angular Team at Google."
67+
},
68+
"analyticsSharing": {
69+
"type": "object",
70+
"properties": {
71+
"tracking": {
72+
"description": "Analytics sharing info tracking ID.",
73+
"type": "string",
74+
"pattern": "^GA-\\d+-\\d+$"
75+
},
76+
"uuid": {
77+
"description": "Analytics sharing info universally unique identifier.",
78+
"type": "string"
79+
}
80+
}
6781
}
6882
},
6983
"additionalProperties": false

packages/angular/cli/models/analytics.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ export function setAnalyticsConfig(level: 'global' | 'local', value: string | bo
367367
}
368368

369369
config.modify(['cli', 'analytics'], value);
370-
config.write();
370+
config.save();
371371

372372
analyticsDebug('done');
373373

packages/schematics/angular/application/index_spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,16 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88
// tslint:disable:no-big-function
9-
import { JsonParseMode, parseJson } from '@angular-devkit/core';
109
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
10+
import { parse as parseJson } from 'jsonc-parser';
1111
import { latestVersions } from '../utility/latest-versions';
1212
import { getFileContent } from '../utility/test';
1313
import { Schema as WorkspaceOptions } from '../workspace/schema';
1414
import { Schema as ApplicationOptions, Style, ViewEncapsulation } from './schema';
1515

1616
// tslint:disable-next-line: no-any
1717
function readJsonFile(tree: UnitTestTree, path: string): any {
18-
return parseJson(tree.readContent(path).toString(), JsonParseMode.Loose);
18+
return parseJson(tree.readContent(path).toString());
1919
}
2020

2121
describe('Application Schematic', () => {

packages/schematics/angular/library/index.ts

Lines changed: 6 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
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 { JsonParseMode, join, normalize, parseJson, strings } from '@angular-devkit/core';
8+
import { join, normalize, strings } from '@angular-devkit/core';
99
import {
1010
Rule,
1111
SchematicContext,
@@ -22,6 +22,7 @@ import {
2222
} from '@angular-devkit/schematics';
2323
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
2424
import { NodeDependencyType, addPackageJsonDependency } from '../utility/dependencies';
25+
import { JSONFile } from '../utility/json-file';
2526
import { latestVersions } from '../utility/latest-versions';
2627
import { applyLintFix } from '../utility/lint-fix';
2728
import { relativePathToWorkspaceRoot } from '../utility/paths';
@@ -30,45 +31,14 @@ import { getWorkspace, updateWorkspace } from '../utility/workspace';
3031
import { Builders, ProjectType } from '../utility/workspace-models';
3132
import { Schema as LibraryOptions } from './schema';
3233

33-
interface UpdateJsonFn<T> {
34-
(obj: T): T | void;
35-
}
36-
37-
type TsConfigPartialType = {
38-
compilerOptions: {
39-
baseUrl: string,
40-
paths: {
41-
[key: string]: string[];
42-
},
43-
},
44-
};
45-
46-
function updateJsonFile<T>(host: Tree, path: string, callback: UpdateJsonFn<T>): Tree {
47-
const source = host.read(path);
48-
if (source) {
49-
const sourceText = source.toString('utf-8');
50-
const json = parseJson(sourceText, JsonParseMode.Loose);
51-
callback(json as {} as T);
52-
host.overwrite(path, JSON.stringify(json, null, 2));
53-
}
54-
55-
return host;
56-
}
57-
5834
function updateTsConfig(packageName: string, ...paths: string[]) {
59-
6035
return (host: Tree) => {
6136
if (!host.exists('tsconfig.json')) { return host; }
6237

63-
return updateJsonFile(host, 'tsconfig.json', (tsconfig: TsConfigPartialType) => {
64-
if (!tsconfig.compilerOptions.paths) {
65-
tsconfig.compilerOptions.paths = {};
66-
}
67-
if (!tsconfig.compilerOptions.paths[packageName]) {
68-
tsconfig.compilerOptions.paths[packageName] = [];
69-
}
70-
tsconfig.compilerOptions.paths[packageName].push(...paths);
71-
});
38+
const file = new JSONFile(host, 'tsconfig.json');
39+
const jsonPath = ['compilerOptions', 'paths', packageName];
40+
const value = file.get(jsonPath);
41+
file.modify(jsonPath, Array.isArray(value) ? [...value, ...paths] : paths);
7242
};
7343
}
7444

packages/schematics/angular/library/index_spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88
// tslint:disable:no-big-function
9-
import { JsonParseMode, parseJson } from '@angular-devkit/core';
109
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
10+
import { parse as parseJson } from 'jsonc-parser';
1111
import { getFileContent } from '../../angular/utility/test';
1212
import { Schema as ComponentOptions } from '../component/schema';
1313
import { latestVersions } from '../utility/latest-versions';
@@ -16,7 +16,7 @@ import { Schema as GenerateLibrarySchema } from './schema';
1616

1717
// tslint:disable-next-line: no-any
1818
function getJsonFileContent(tree: UnitTestTree, path: string): any {
19-
return parseJson(tree.readContent(path).toString(), JsonParseMode.Loose);
19+
return parseJson(tree.readContent(path).toString());
2020
}
2121

2222
describe('Library Schematic', () => {

packages/schematics/angular/migrations/update-10/remove-solution-style-tsconfig_spec.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
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 { JsonParseMode, parseJson } from '@angular-devkit/core';
98
import { EmptyTree } from '@angular-devkit/schematics';
109
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
10+
import { parse as parseJson } from 'jsonc-parser';
1111
import { Builders, ProjectType, WorkspaceSchema } from '../../utility/workspace-models';
1212

1313
describe('Migration to remove "Solution Style" tsconfig', () => {
@@ -24,8 +24,7 @@ describe('Migration to remove "Solution Style" tsconfig', () => {
2424

2525
// tslint:disable-next-line: no-any
2626
function readJsonFile(tree: UnitTestTree, filePath: string): any {
27-
// tslint:disable-next-line: no-any
28-
return parseJson(tree.readContent(filePath).toString(), JsonParseMode.Loose) as any;
27+
return parseJson(tree.readContent(filePath).toString());
2928
}
3029

3130
function createWorkSpaceConfig(tree: UnitTestTree) {

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
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 { JsonParseMode, parseJson } from '@angular-devkit/core';
98
import { EmptyTree } from '@angular-devkit/schematics';
109
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
10+
import { parse as parseJson } from 'jsonc-parser';
1111
import { Builders, ProjectType, WorkspaceSchema } from '../../utility/workspace-models';
1212

1313
describe('Migration to update target and module compiler options', () => {
@@ -24,8 +24,7 @@ describe('Migration to update target and module compiler options', () => {
2424

2525
// tslint:disable-next-line: no-any
2626
function readJsonFile(tree: UnitTestTree, filePath: string): any {
27-
// tslint:disable-next-line: no-any
28-
return parseJson(tree.readContent(filePath).toString(), JsonParseMode.Loose) as any;
27+
return parseJson(tree.readContent(filePath).toString());
2928
}
3029

3130
function createWorkSpaceConfig(tree: UnitTestTree) {

packages/schematics/angular/migrations/update-6/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,10 @@
88
import {
99
JsonArray,
1010
JsonObject,
11-
JsonParseMode,
1211
Path,
1312
join,
1413
logging,
1514
normalize,
16-
parseJson,
1715
tags,
1816
} from '@angular-devkit/core';
1917
import {
@@ -24,6 +22,7 @@ import {
2422
chain,
2523
} from '@angular-devkit/schematics';
2624
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
25+
import { parse as parseJson } from 'jsonc-parser';
2726
import {
2827
NodeDependency,
2928
NodeDependencyType,
@@ -745,7 +744,7 @@ export default function (): Rule {
745744
if (configBuffer == null) {
746745
throw new SchematicsException(`Could not find configuration file (${configPath})`);
747746
}
748-
const config = parseJson(configBuffer.toString(), JsonParseMode.Loose);
747+
const config = parseJson(configBuffer.toString());
749748

750749
if (typeof config != 'object' || Array.isArray(config) || config === null) {
751750
throw new SchematicsException('Invalid angular-cli.json configuration; expected an object.');

packages/schematics/angular/migrations/update-9/update-app-tsconfigs_spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import { JsonParseMode, parseJson } from '@angular-devkit/core';
109
import { EmptyTree } from '@angular-devkit/schematics';
1110
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
11+
import { parse as parseJson } from 'jsonc-parser';
1212
import { getWorkspaceTargets, updateWorkspaceTargets } from './update-workspace-config_spec';
1313

1414
// tslint:disable-next-line: no-any
1515
function readJsonFile(tree: UnitTestTree, path: string): any {
16-
return parseJson(tree.readContent(path).toString(), JsonParseMode.Loose);
16+
return parseJson(tree.readContent(path).toString());
1717
}
1818

1919
function overrideJsonFile(tree: UnitTestTree, path: string, newContent: object) {

packages/schematics/angular/universal/index_spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
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 { JsonParseMode, parseJson } from '@angular-devkit/core';
98
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
9+
import { parse as parseJson } from 'jsonc-parser';
1010
import { Schema as ApplicationOptions, Style } from '../application/schema';
1111
import { NodeDependencyType, addPackageJsonDependency } from '../utility/dependencies';
1212
import { Schema as WorkspaceOptions } from '../workspace/schema';
@@ -88,7 +88,7 @@ describe('Universal Schematic', () => {
8888
const filePath = '/tsconfig.server.json';
8989
expect(tree.exists(filePath)).toEqual(true);
9090
// tslint:disable-next-line: no-any
91-
const contents = parseJson(tree.readContent(filePath).toString(), JsonParseMode.Loose) as any;
91+
const contents = parseJson(tree.readContent(filePath).toString()) as any;
9292
expect(contents).toEqual({
9393
extends: './tsconfig.app.json',
9494
compilerOptions: {
@@ -114,7 +114,7 @@ describe('Universal Schematic', () => {
114114
const filePath = '/projects/bar/tsconfig.server.json';
115115
expect(tree.exists(filePath)).toEqual(true);
116116
// tslint:disable-next-line: no-any
117-
const contents = parseJson(tree.readContent(filePath).toString(), JsonParseMode.Loose) as any;
117+
const contents = parseJson(tree.readContent(filePath).toString()) as any;
118118
expect(contents).toEqual({
119119
extends: './tsconfig.app.json',
120120
compilerOptions: {

0 commit comments

Comments
 (0)