From f9ade9f6cfcdd412306e0aaf43994b2eaf2097b2 Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Thu, 12 Aug 2021 17:30:00 +0200 Subject: [PATCH 1/3] refactor(@angular-devkit/core): remove deprecated JSON parser BREAKING CHANGE: The deprecated JSON parser has been removed from public API. [jsonc-parser](https://www.npmjs.com/package/jsonc-parser) should be used instead. --- .../angular_devkit/core/src/index.md | 238 +----------------- packages/angular_devkit/core/package.json | 2 +- .../angular_devkit/core/src/json/index.ts | 3 +- .../angular_devkit/core/src/json/parser.ts | 12 +- .../src/json/{interface.ts => parser_ast.ts} | 19 +- .../core/src/json/schema/interface.ts | 2 +- .../core/src/json/schema/registry.ts | 2 +- .../core/src/json/schema/schema.ts | 2 +- .../core/src/json/schema/transforms.ts | 2 +- .../core/src/json/schema/utility.ts | 2 +- .../core/src/json/schema/visitor.ts | 2 +- .../angular_devkit/core/src/json/utils.ts | 24 ++ .../angular_devkit/core/src/logger/level.ts | 2 +- .../angular_devkit/core/src/logger/logger.ts | 2 +- .../core/src/logger/logger_spec.ts | 6 +- .../core/src/workspace/json/metadata.ts | 3 +- .../core/src/workspace/json/reader.ts | 13 +- .../core/src/workspace/json/utilities.ts | 10 +- .../core/src/workspace/json/writer.ts | 3 +- 19 files changed, 61 insertions(+), 288 deletions(-) rename packages/angular_devkit/core/src/json/{interface.ts => parser_ast.ts} (81%) create mode 100644 packages/angular_devkit/core/src/json/utils.ts diff --git a/goldens/public-api/angular_devkit/core/src/index.md b/goldens/public-api/angular_devkit/core/src/index.md index 2bdb179410ad..864944c96356 100644 --- a/goldens/public-api/angular_devkit/core/src/index.md +++ b/goldens/public-api/angular_devkit/core/src/index.md @@ -10,7 +10,7 @@ import { Observable } from 'rxjs'; import { Observer } from 'rxjs'; import { Operator } from 'rxjs'; import { PartialObserver } from 'rxjs'; -import { Position as Position_2 } from 'source-map'; +import { Position } from 'source-map'; import { Subject } from 'rxjs'; import { SubscribableOrPromise } from 'rxjs'; import { Subscription } from 'rxjs'; @@ -530,19 +530,6 @@ class IndentLogger extends Logger { constructor(name: string, parent?: Logger | null, indentation?: string); } -// @public @deprecated -export class InvalidJsonCharacterException extends JsonException { - constructor(context: JsonParserContext); - // (undocumented) - character: number; - // (undocumented) - invalidChar: string; - // (undocumented) - line: number; - // (undocumented) - offset: number; -} - // @public (undocumented) export class InvalidPathException extends BaseException { constructor(path: string); @@ -855,201 +842,23 @@ declare namespace json { schema, isJsonObject, isJsonArray, - Position, - JsonAstNode, - JsonAstNodeBase, - JsonAstNumber, - JsonAstString, - JsonAstIdentifier, JsonArray, - JsonAstArray, JsonObject, - JsonAstKeyValue, - JsonAstObject, - JsonAstConstantFalse, - JsonAstConstantNull, - JsonAstConstantTrue, - JsonAstMultilineComment, - JsonAstComment, - JsonValue, - parseJsonAst, - parseJson, - JsonException, - InvalidJsonCharacterException, - UnexpectedEndOfInputException, - PathSpecificJsonException, - JsonParserContext, - JsonParseMode, - ParseJsonOptions + JsonValue } } export { json } -// @public (undocumented) +// @public export interface JsonArray extends Array { } -// @public (undocumented) -export interface JsonAstArray extends JsonAstNodeBase { - // (undocumented) - readonly elements: JsonAstNode[]; - // (undocumented) - readonly kind: 'array'; - // (undocumented) - readonly value: JsonArray; -} - -// @public (undocumented) -export interface JsonAstComment extends JsonAstNodeBase { - // (undocumented) - readonly content: string; - // (undocumented) - readonly kind: 'comment'; -} - -// @public (undocumented) -export interface JsonAstConstantFalse extends JsonAstNodeBase { - // (undocumented) - readonly kind: 'false'; - // (undocumented) - readonly value: false; -} - -// @public (undocumented) -export interface JsonAstConstantNull extends JsonAstNodeBase { - // (undocumented) - readonly kind: 'null'; - // (undocumented) - readonly value: null; -} - -// @public (undocumented) -export interface JsonAstConstantTrue extends JsonAstNodeBase { - // (undocumented) - readonly kind: 'true'; - // (undocumented) - readonly value: true; -} - -// @public (undocumented) -export interface JsonAstIdentifier extends JsonAstNodeBase { - // (undocumented) - readonly kind: 'identifier'; - // (undocumented) - readonly value: string; -} - -// @public (undocumented) -export interface JsonAstKeyValue extends JsonAstNodeBase { - // (undocumented) - readonly key: JsonAstString | JsonAstIdentifier; - // (undocumented) - readonly kind: 'keyvalue'; - // (undocumented) - readonly value: JsonAstNode; -} - -// @public (undocumented) -export interface JsonAstMultilineComment extends JsonAstNodeBase { - // (undocumented) - readonly content: string; - // (undocumented) - readonly kind: 'multicomment'; -} - -// @public (undocumented) -export type JsonAstNode = JsonAstNumber | JsonAstString | JsonAstIdentifier | JsonAstArray | JsonAstObject | JsonAstConstantFalse | JsonAstConstantNull | JsonAstConstantTrue; - -// @public (undocumented) -export interface JsonAstNodeBase { - // (undocumented) - readonly comments?: (JsonAstComment | JsonAstMultilineComment)[]; - // (undocumented) - readonly end: Position; - // (undocumented) - readonly start: Position; - // (undocumented) - readonly text: string; -} - -// @public (undocumented) -export interface JsonAstNumber extends JsonAstNodeBase { - // (undocumented) - readonly kind: 'number'; - // (undocumented) - readonly value: number; -} - -// @public (undocumented) -export interface JsonAstObject extends JsonAstNodeBase { - // (undocumented) - readonly kind: 'object'; - // (undocumented) - readonly properties: JsonAstKeyValue[]; - // (undocumented) - readonly value: JsonObject; -} - -// @public (undocumented) -export interface JsonAstString extends JsonAstNodeBase { - // (undocumented) - readonly kind: 'string'; - // (undocumented) - readonly value: string; -} - -// @public (undocumented) -export class JsonException extends BaseException { -} - // @public (undocumented) export interface JsonObject { // (undocumented) [prop: string]: JsonValue; } -// @public -export enum JsonParseMode { - // (undocumented) - CommentsAllowed = 1, - // (undocumented) - Default = 0, - // (undocumented) - HexadecimalNumberAllowed = 16, - // (undocumented) - IdentifierKeyNamesAllowed = 4, - // (undocumented) - Json = 0, - // (undocumented) - Json5 = 255, - // (undocumented) - LaxNumberParsingAllowed = 64, - // (undocumented) - Loose = 255, - // (undocumented) - MultiLineStringAllowed = 32, - // (undocumented) - NumberConstantsAllowed = 128, - // (undocumented) - SingleQuotesAllowed = 2, - // (undocumented) - Strict = 0, - // (undocumented) - TrailingCommasAllowed = 8 -} - -// @public @deprecated -export interface JsonParserContext { - // (undocumented) - readonly mode: JsonParseMode; - // (undocumented) - readonly original: string; - // (undocumented) - position: Position; - // (undocumented) - previous: Position; -} - // @public (undocumented) type JsonPointer = string & { __PRIVATE_DEVKIT_JSON_POINTER: void; @@ -1065,7 +874,7 @@ interface JsonSchemaVisitor { } // @public (undocumented) -export type JsonValue = JsonAstNode['value']; +export type JsonValue = boolean | string | number | JsonArray | JsonObject | null; // @public (undocumented) interface JsonVisitor { @@ -1368,17 +1177,6 @@ interface PageviewOptions extends CustomDimensionsAndMetricsOptions { title?: string; } -// @public @deprecated -export function parseJson(input: string, mode?: JsonParseMode, options?: ParseJsonOptions): JsonValue; - -// @public @deprecated -export function parseJsonAst(input: string, mode?: JsonParseMode): JsonAstNode; - -// @public @deprecated -export interface ParseJsonOptions { - path?: string; -} - // @public (undocumented) function parseJsonPointer(pointer: JsonPointer): string[]; @@ -1440,15 +1238,6 @@ export class PathMustBeAbsoluteException extends BaseException { constructor(path: string); } -// @public @deprecated -export class PathSpecificJsonException extends JsonException { - constructor(path: string, exception: JsonException); - // (undocumented) - exception: JsonException; - // (undocumented) - path: string; -} - // @public (undocumented) class PatternMatchingHost extends ResolverHost { // (undocumented) @@ -1459,16 +1248,6 @@ class PatternMatchingHost extends ResolverHost {} - export interface JsonAstArray extends JsonAstNodeBase { readonly kind: 'array'; readonly elements: JsonAstNode[]; readonly value: JsonArray; } -export interface JsonObject { - [prop: string]: JsonValue; -} - export interface JsonAstKeyValue extends JsonAstNodeBase { readonly kind: 'keyvalue'; readonly key: JsonAstString | JsonAstIdentifier; @@ -95,13 +90,3 @@ export interface JsonAstComment extends JsonAstNodeBase { readonly kind: 'comment'; readonly content: string; } - -export type JsonValue = JsonAstNode['value']; - -export function isJsonObject(value: JsonValue): value is JsonObject { - return value != null && typeof value === 'object' && !Array.isArray(value); -} - -export function isJsonArray(value: JsonValue): value is JsonArray { - return Array.isArray(value); -} diff --git a/packages/angular_devkit/core/src/json/schema/interface.ts b/packages/angular_devkit/core/src/json/schema/interface.ts index e56a45d34f35..8b8dac6debdf 100644 --- a/packages/angular_devkit/core/src/json/schema/interface.ts +++ b/packages/angular_devkit/core/src/json/schema/interface.ts @@ -8,7 +8,7 @@ import { ErrorObject, Format } from 'ajv'; import { Observable, SubscribableOrPromise } from 'rxjs'; -import { JsonArray, JsonObject, JsonValue } from '../interface'; +import { JsonArray, JsonObject, JsonValue } from '../utils'; export type JsonPointer = string & { __PRIVATE_DEVKIT_JSON_POINTER: void; diff --git a/packages/angular_devkit/core/src/json/schema/registry.ts b/packages/angular_devkit/core/src/json/schema/registry.ts index bc8262ba8540..9e04efbf5b39 100644 --- a/packages/angular_devkit/core/src/json/schema/registry.ts +++ b/packages/angular_devkit/core/src/json/schema/registry.ts @@ -15,7 +15,7 @@ import { map } from 'rxjs/operators'; import * as Url from 'url'; import { BaseException } from '../../exception/exception'; import { PartiallyOrderedSet, deepCopy } from '../../utils'; -import { JsonArray, JsonObject, JsonValue, isJsonObject } from '../interface'; +import { JsonArray, JsonObject, JsonValue, isJsonObject } from '../utils'; import { JsonPointer, JsonVisitor, diff --git a/packages/angular_devkit/core/src/json/schema/schema.ts b/packages/angular_devkit/core/src/json/schema/schema.ts index c0161d20ab8a..e4e747116def 100644 --- a/packages/angular_devkit/core/src/json/schema/schema.ts +++ b/packages/angular_devkit/core/src/json/schema/schema.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import { JsonObject, JsonValue, isJsonObject } from '../interface'; +import { JsonObject, JsonValue, isJsonObject } from '../utils'; /** * A specialized interface for JsonSchema (to come). JsonSchemas are also JsonObject. diff --git a/packages/angular_devkit/core/src/json/schema/transforms.ts b/packages/angular_devkit/core/src/json/schema/transforms.ts index d207688a4b3d..362d2b667465 100644 --- a/packages/angular_devkit/core/src/json/schema/transforms.ts +++ b/packages/angular_devkit/core/src/json/schema/transforms.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import { JsonObject, JsonValue, isJsonArray, isJsonObject } from '../interface'; +import { JsonObject, JsonValue, isJsonArray, isJsonObject } from '../utils'; import { JsonPointer } from './interface'; import { JsonSchema } from './schema'; import { getTypesOfSchema } from './utility'; diff --git a/packages/angular_devkit/core/src/json/schema/utility.ts b/packages/angular_devkit/core/src/json/schema/utility.ts index aadaed835bf5..7f08e7abbda2 100644 --- a/packages/angular_devkit/core/src/json/schema/utility.ts +++ b/packages/angular_devkit/core/src/json/schema/utility.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import { JsonObject, isJsonArray, isJsonObject } from '../interface'; +import { JsonObject, isJsonArray, isJsonObject } from '../utils'; import { JsonSchema } from './schema'; const allTypes = ['string', 'integer', 'number', 'object', 'array', 'boolean', 'null']; diff --git a/packages/angular_devkit/core/src/json/schema/visitor.ts b/packages/angular_devkit/core/src/json/schema/visitor.ts index be8471337b6c..c3f7ad18df78 100644 --- a/packages/angular_devkit/core/src/json/schema/visitor.ts +++ b/packages/angular_devkit/core/src/json/schema/visitor.ts @@ -8,7 +8,7 @@ import { Observable, concat, from, isObservable, of as observableOf } from 'rxjs'; import { concatMap, ignoreElements, mergeMap, tap } from 'rxjs/operators'; -import { JsonArray, JsonObject, JsonValue } from '../interface'; +import { JsonArray, JsonObject, JsonValue } from '../utils'; import { JsonPointer, JsonSchemaVisitor, JsonVisitor } from './interface'; import { buildJsonPointer, joinJsonPointer } from './pointer'; import { JsonSchema } from './schema'; diff --git a/packages/angular_devkit/core/src/json/utils.ts b/packages/angular_devkit/core/src/json/utils.ts new file mode 100644 index 000000000000..edbcd70c2b47 --- /dev/null +++ b/packages/angular_devkit/core/src/json/utils.ts @@ -0,0 +1,24 @@ +/** + * @license + * Copyright Google LLC 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 + */ + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface JsonArray extends Array {} + +export interface JsonObject { + [prop: string]: JsonValue; +} + +export type JsonValue = boolean | string | number | JsonArray | JsonObject | null; + +export function isJsonObject(value: JsonValue): value is JsonObject { + return value != null && typeof value === 'object' && !Array.isArray(value); +} + +export function isJsonArray(value: JsonValue): value is JsonArray { + return Array.isArray(value); +} diff --git a/packages/angular_devkit/core/src/logger/level.ts b/packages/angular_devkit/core/src/logger/level.ts index b5094ed42b52..b43d1b8b1e5d 100644 --- a/packages/angular_devkit/core/src/logger/level.ts +++ b/packages/angular_devkit/core/src/logger/level.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import { JsonObject } from '../json/interface'; +import { JsonObject } from '../json/utils'; import { LogLevel, Logger } from './logger'; export class LevelTransformLogger extends Logger { diff --git a/packages/angular_devkit/core/src/logger/logger.ts b/packages/angular_devkit/core/src/logger/logger.ts index 4a00cb6c19f6..f8b9a451da7e 100644 --- a/packages/angular_devkit/core/src/logger/logger.ts +++ b/packages/angular_devkit/core/src/logger/logger.ts @@ -7,7 +7,7 @@ */ import { Observable, Operator, PartialObserver, Subject, Subscription, empty } from 'rxjs'; -import { JsonObject } from '../json/interface'; +import { JsonObject } from '../json/utils'; export interface LoggerMetadata extends JsonObject { name: string; diff --git a/packages/angular_devkit/core/src/logger/logger_spec.ts b/packages/angular_devkit/core/src/logger/logger_spec.ts index fe68b4cdaa5a..d3da5b331e85 100644 --- a/packages/angular_devkit/core/src/logger/logger_spec.ts +++ b/packages/angular_devkit/core/src/logger/logger_spec.ts @@ -8,7 +8,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { toArray } from 'rxjs/operators'; -import { JsonValue } from '../json/interface'; +import { JsonValue } from '../json/utils'; import { Logger } from './logger'; describe('Logger', () => { @@ -19,8 +19,8 @@ describe('Logger', () => { .toPromise() .then((observed: JsonValue[]) => { expect(observed).toEqual([ - jasmine.objectContaining({ message: 'hello', level: 'debug', name: 'test' }) as any, - jasmine.objectContaining({ message: 'world', level: 'info', name: 'test' }) as any, + jasmine.objectContaining({ message: 'hello', level: 'debug', name: 'test' }), + jasmine.objectContaining({ message: 'world', level: 'info', name: 'test' }), ]); }) .then( diff --git a/packages/angular_devkit/core/src/workspace/json/metadata.ts b/packages/angular_devkit/core/src/workspace/json/metadata.ts index a9886176b991..1c5860a2b3cb 100644 --- a/packages/angular_devkit/core/src/workspace/json/metadata.ts +++ b/packages/angular_devkit/core/src/workspace/json/metadata.ts @@ -6,7 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import { JsonAstArray, JsonAstKeyValue, JsonAstNode, JsonAstObject, JsonValue } from '../../json'; +import { JsonValue } from '../../json'; +import { JsonAstArray, JsonAstKeyValue, JsonAstNode, JsonAstObject } from '../../json/parser_ast'; import { ProjectDefinition, TargetDefinition, WorkspaceDefinition } from '../definitions'; export const JsonWorkspaceSymbol = Symbol.for('@angular/core:workspace-json'); diff --git a/packages/angular_devkit/core/src/workspace/json/reader.ts b/packages/angular_devkit/core/src/workspace/json/reader.ts index 927fb0fba0c0..3dd27b28bb11 100644 --- a/packages/angular_devkit/core/src/workspace/json/reader.ts +++ b/packages/angular_devkit/core/src/workspace/json/reader.ts @@ -6,14 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import { - JsonAstKeyValue, - JsonAstNode, - JsonAstObject, - JsonParseMode, - JsonValue, - parseJsonAst, -} from '../../json'; +import { JsonParseMode, parseJsonAst } from '../../json/parser'; +import { JsonAstKeyValue, JsonAstNode, JsonAstObject } from '../../json/parser_ast'; +import { JsonValue } from '../../json/utils'; import { DefinitionCollectionListener, ProjectDefinition, @@ -306,7 +301,7 @@ function parseTargetsObject( }, }); } else { - targets[name] = (value.value as unknown) as TargetDefinition; + targets[name] = value.value as unknown as TargetDefinition; } } diff --git a/packages/angular_devkit/core/src/workspace/json/utilities.ts b/packages/angular_devkit/core/src/workspace/json/utilities.ts index 157d9ef44933..59de10b91317 100644 --- a/packages/angular_devkit/core/src/workspace/json/utilities.ts +++ b/packages/angular_devkit/core/src/workspace/json/utilities.ts @@ -6,14 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import { - JsonAstArray, - JsonAstKeyValue, - JsonAstNode, - JsonAstObject, - JsonObject, - JsonValue, -} from '../../json'; +import { JsonObject, JsonValue } from '../../json'; +import { JsonAstArray, JsonAstKeyValue, JsonAstNode, JsonAstObject } from '../../json/parser_ast'; const stableStringify = require('fast-json-stable-stringify'); diff --git a/packages/angular_devkit/core/src/workspace/json/writer.ts b/packages/angular_devkit/core/src/workspace/json/writer.ts index 8968c34b42d1..a934791b3599 100644 --- a/packages/angular_devkit/core/src/workspace/json/writer.ts +++ b/packages/angular_devkit/core/src/workspace/json/writer.ts @@ -7,7 +7,8 @@ */ import MagicString from 'magic-string'; -import { JsonAstKeyValue, JsonAstNode, JsonObject, JsonValue } from '../../json'; +import { JsonObject, JsonValue } from '../../json'; +import { JsonAstKeyValue, JsonAstNode } from '../../json/parser_ast'; import { ProjectDefinition, TargetDefinition, WorkspaceDefinition } from '../definitions'; import { WorkspaceHost } from '../host'; import { From c97aa783e1ff10a1b26cdb078c7ff01c760e993d Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Thu, 12 Aug 2021 17:30:43 +0200 Subject: [PATCH 2/3] refactor(@angular-devkit/schematics-cli): remove usage of removed json parser --- .../schematics_cli/blank/factory.ts | 60 +++---------------- 1 file changed, 7 insertions(+), 53 deletions(-) diff --git a/packages/angular_devkit/schematics_cli/blank/factory.ts b/packages/angular_devkit/schematics_cli/blank/factory.ts index 3911d70ee70e..d8eeb16cc3d5 100644 --- a/packages/angular_devkit/schematics_cli/blank/factory.ts +++ b/packages/angular_devkit/schematics_cli/blank/factory.ts @@ -6,21 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ -import { - JsonAstObject, - JsonObject, - JsonValue, - Path, - normalize, - parseJsonAst, - strings, -} from '@angular-devkit/core'; +import { JsonObject, Path, isJsonObject, normalize, strings } from '@angular-devkit/core'; import { Rule, SchematicContext, SchematicsException, Tree, - UpdateRecorder, apply, chain, mergeWith, @@ -31,29 +22,6 @@ import { import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks'; import { Schema } from './schema'; -function appendPropertyInAstObject( - recorder: UpdateRecorder, - node: JsonAstObject, - propertyName: string, - value: JsonValue, - indent = 4, -) { - const indentStr = '\n' + new Array(indent + 1).join(' '); - - if (node.properties.length > 0) { - // Insert comma. - const last = node.properties[node.properties.length - 1]; - recorder.insertRight(last.start.offset + last.text.replace(/\s+$/, '').length, ','); - } - - recorder.insertLeft( - node.end.offset - 1, - ' ' + - `"${propertyName}": ${JSON.stringify(value, null, 2).replace(/\n/g, indentStr)}` + - indentStr.slice(0, -2), - ); -} - function addSchematicToCollectionJson( collectionPath: Path, schematicName: string, @@ -64,26 +32,14 @@ function addSchematicToCollectionJson( if (!collectionJsonContent) { throw new Error('Invalid collection path: ' + collectionPath); } - const collectionJsonAst = parseJsonAst(collectionJsonContent.toString('utf-8')); - if (collectionJsonAst.kind !== 'object') { - throw new Error('Invalid collection content.'); - } - - for (const property of collectionJsonAst.properties) { - if (property.key.value == 'schematics') { - if (property.value.kind !== 'object') { - throw new Error('Invalid collection.json; schematics needs to be an object.'); - } - const recorder = tree.beginUpdate(collectionPath); - appendPropertyInAstObject(recorder, property.value, schematicName, description); - tree.commitUpdate(recorder); - - return tree; - } + const collectionJson = JSON.parse(collectionJsonContent.toString()); + if (!isJsonObject(collectionJson.schematics)) { + throw new Error('Invalid collection.json; schematics needs to be an object.'); } - throw new Error('Could not find the "schematics" property in collection.json.'); + collectionJson['schematics'][schematicName] = description; + tree.overwrite(collectionPath, JSON.stringify(collectionJson, undefined, 2)); }; } @@ -101,9 +57,7 @@ export default function (options: Schema): Rule { try { const packageJsonContent = tree.read('/package.json'); if (packageJsonContent) { - // In google3 the return value of JSON.parse() must be immediately typed, - // otherwise it defaults to `any`, which is prohibited. - const packageJson = JSON.parse(packageJsonContent.toString('utf-8')) as { + const packageJson = JSON.parse(packageJsonContent.toString()) as { schematics: unknown; }; if (typeof packageJson.schematics === 'string') { From 04c6acce767b75b07bbc3fc977d10a5ac82f8fcf Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Thu, 12 Aug 2021 17:31:49 +0200 Subject: [PATCH 3/3] refactor(@angular-devkit/schematics): replace usage of deprecated JSON parser with `jsonc-parser` --- .../angular_devkit/schematics/tools/index.md | 4 +- .../angular_devkit/schematics/package.json | 1 + .../schematics/tools/BUILD.bazel | 1 + .../tools/file-system-engine-host-base.ts | 17 +----- .../schematics/tools/file-system-utility.ts | 55 ++++++------------- .../tools/node-module-engine-host.ts | 23 +------- 6 files changed, 26 insertions(+), 75 deletions(-) diff --git a/goldens/public-api/angular_devkit/schematics/tools/index.md b/goldens/public-api/angular_devkit/schematics/tools/index.md index 325fb9680214..392e77515ff4 100644 --- a/goldens/public-api/angular_devkit/schematics/tools/index.md +++ b/goldens/public-api/angular_devkit/schematics/tools/index.md @@ -8,14 +8,12 @@ import { analytics } from '@angular-devkit/core'; import { BaseException } from '@angular-devkit/core'; -import { InvalidJsonCharacterException } from '@angular-devkit/core'; import { JsonObject } from '@angular-devkit/core'; import { logging } from '@angular-devkit/core'; import { Observable } from 'rxjs'; import { Path } from '@angular-devkit/core'; import { PathFragment } from '@angular-devkit/core'; import { schema } from '@angular-devkit/core'; -import { UnexpectedEndOfInputException } from '@angular-devkit/core'; import { Url } from 'url'; import { virtualFs } from '@angular-devkit/core'; import { workflow } from '@angular-devkit/schematics'; @@ -177,7 +175,7 @@ export interface FileSystemSchematicJsonDescription { // @public (undocumented) export class InvalidCollectionJsonException extends BaseException { - constructor(_name: string, path: string, jsonException?: UnexpectedEndOfInputException | InvalidJsonCharacterException); + constructor(_name: string, path: string, jsonException?: Error); } // @public diff --git a/packages/angular_devkit/schematics/package.json b/packages/angular_devkit/schematics/package.json index 1556fca42172..fe5cf86d705c 100644 --- a/packages/angular_devkit/schematics/package.json +++ b/packages/angular_devkit/schematics/package.json @@ -14,6 +14,7 @@ ], "dependencies": { "@angular-devkit/core": "0.0.0", + "jsonc-parser": "3.0.0", "ora": "5.4.1", "rxjs": "6.6.7" } diff --git a/packages/angular_devkit/schematics/tools/BUILD.bazel b/packages/angular_devkit/schematics/tools/BUILD.bazel index 56a3203f06a1..c7514fb510cd 100644 --- a/packages/angular_devkit/schematics/tools/BUILD.bazel +++ b/packages/angular_devkit/schematics/tools/BUILD.bazel @@ -31,6 +31,7 @@ ts_library( "//packages/angular_devkit/schematics/tasks", "//packages/angular_devkit/schematics/tasks/node", "@npm//@types/node", + "@npm//jsonc-parser", "@npm//rxjs", ], ) diff --git a/packages/angular_devkit/schematics/tools/file-system-engine-host-base.ts b/packages/angular_devkit/schematics/tools/file-system-engine-host-base.ts index c2b8a8f656e5..6670cdb8fbb5 100644 --- a/packages/angular_devkit/schematics/tools/file-system-engine-host-base.ts +++ b/packages/angular_devkit/schematics/tools/file-system-engine-host-base.ts @@ -6,14 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import { - BaseException, - InvalidJsonCharacterException, - JsonObject, - UnexpectedEndOfInputException, - normalize, - virtualFs, -} from '@angular-devkit/core'; +import { BaseException, JsonObject, normalize, virtualFs } from '@angular-devkit/core'; import { NodeJsSyncHost } from '@angular-devkit/core/node'; import { existsSync, statSync } from 'fs'; import { dirname, isAbsolute, join, resolve } from 'path'; @@ -51,11 +44,7 @@ export class CollectionCannotBeResolvedException extends BaseException { } } export class InvalidCollectionJsonException extends BaseException { - constructor( - _name: string, - path: string, - jsonException?: UnexpectedEndOfInputException | InvalidJsonCharacterException, - ) { + constructor(_name: string, path: string, jsonException?: Error) { let msg = `Collection JSON at path ${JSON.stringify(path)} is invalid.`; if (jsonException) { @@ -320,7 +309,7 @@ export abstract class FileSystemEngineHostBase implements FileSystemEngineHost { return transformedOptions; }; - return (observableFrom(transform()) as unknown) as Observable; + return observableFrom(transform()) as unknown as Observable; } transformContext(context: FileSystemSchematicContext): FileSystemSchematicContext { diff --git a/packages/angular_devkit/schematics/tools/file-system-utility.ts b/packages/angular_devkit/schematics/tools/file-system-utility.ts index dc21f3d66762..48b50e23c3a0 100644 --- a/packages/angular_devkit/schematics/tools/file-system-utility.ts +++ b/packages/angular_devkit/schematics/tools/file-system-utility.ts @@ -6,48 +6,27 @@ * found in the LICENSE file at https://angular.io/license */ -import { - FileDoesNotExistException, - JsonParseMode, - JsonValue, - parseJson, -} from '@angular-devkit/core'; +import { JsonValue } from '@angular-devkit/core'; +import { FileDoesNotExistException } from '@angular-devkit/schematics'; import { existsSync, readFileSync } from 'fs'; +import { ParseError, parse, printParseErrorCode } from 'jsonc-parser'; -/** - * Read a file and returns its content. This supports different file encoding. - */ -export function readFile(fileName: string): string { - if (!existsSync(fileName)) { - throw new FileDoesNotExistException(fileName); +export function readJsonFile(path: string): JsonValue { + if (!existsSync(path)) { + throw new FileDoesNotExistException(path); } - const buffer = readFileSync(fileName); - let len = buffer.length; - if (len >= 2 && buffer[0] === 0xfe && buffer[1] === 0xff) { - // Big endian UTF-16 byte order mark detected. Since big endian is not supported by node.js, - // flip all byte pairs and treat as little endian. - len &= ~1; - for (let i = 0; i < len; i += 2) { - const temp = buffer[i]; - buffer[i] = buffer[i + 1]; - buffer[i + 1] = temp; - } - return buffer.toString('utf16le', 2); - } - if (len >= 2 && buffer[0] === 0xff && buffer[1] === 0xfe) { - // Little endian UTF-16 byte order mark detected - return buffer.toString('utf16le', 2); - } - if (len >= 3 && buffer[0] === 0xef && buffer[1] === 0xbb && buffer[2] === 0xbf) { - // UTF-8 byte order mark detected - return buffer.toString('utf8', 3); - } + const errors: ParseError[] = []; + const content = parse(readFileSync(path, 'utf-8'), errors, { allowTrailingComma: true }); - // Default is UTF-8 with no byte order mark - return buffer.toString('utf8'); -} + if (errors.length) { + const { error, offset } = errors[0]; + throw new Error( + `Failed to parse "${path}" as JSON AST Object. ${printParseErrorCode( + error, + )} at location: ${offset}.`, + ); + } -export function readJsonFile(path: string): JsonValue { - return parseJson(readFile(path), JsonParseMode.Loose); + return content; } diff --git a/packages/angular_devkit/schematics/tools/node-module-engine-host.ts b/packages/angular_devkit/schematics/tools/node-module-engine-host.ts index d2a745a16e9d..94afed6dc898 100644 --- a/packages/angular_devkit/schematics/tools/node-module-engine-host.ts +++ b/packages/angular_devkit/schematics/tools/node-module-engine-host.ts @@ -6,11 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import { - BaseException, - InvalidJsonCharacterException, - UnexpectedEndOfInputException, -} from '@angular-devkit/core'; +import { BaseException } from '@angular-devkit/core'; import { dirname, join, resolve } from 'path'; import { RuleFactory } from '../src'; import { FileSystemCollectionDesc, FileSystemSchematicDesc } from './description'; @@ -19,7 +15,6 @@ import { CollectionCannotBeResolvedException, CollectionMissingSchematicsMapException, FileSystemEngineHostBase, - InvalidCollectionJsonException, SchematicMissingFieldsException, } from './file-system-engine-host-base'; import { readJsonFile } from './file-system-utility'; @@ -98,21 +93,9 @@ export class NodeModulesEngineHost extends FileSystemEngineHostBase { protected _resolveCollectionPath(name: string, requester?: string): string { const collectionPath = this.resolve(name, requester); + readJsonFile(collectionPath); - try { - readJsonFile(collectionPath); - - return collectionPath; - } catch (e) { - if ( - e instanceof InvalidJsonCharacterException || - e instanceof UnexpectedEndOfInputException - ) { - throw new InvalidCollectionJsonException(name, collectionPath, e); - } else { - throw e; - } - } + return collectionPath; } protected _resolveReferenceString(refString: string, parentPath: string) {