diff --git a/goldens/public-api/angular_devkit/schematics/index.md b/goldens/public-api/angular_devkit/schematics/index.md index 0d16c2974b54..05f844128ad5 100644 --- a/goldens/public-api/angular_devkit/schematics/index.md +++ b/goldens/public-api/angular_devkit/schematics/index.md @@ -7,6 +7,7 @@ /// import { BaseException } from '@angular-devkit/core'; +import { JsonValue } from '@angular-devkit/core'; import { logging } from '@angular-devkit/core'; import { Observable } from 'rxjs'; import { Path } from '@angular-devkit/core'; @@ -235,6 +236,10 @@ export class DelegateTree implements Tree_2 { // (undocumented) read(path: string): Buffer | null; // (undocumented) + readJson(path: string): JsonValue; + // (undocumented) + readText(path: string): string; + // (undocumented) rename(from: string, to: string): void; // (undocumented) get root(): DirEntry; @@ -547,6 +552,10 @@ export class HostTree implements Tree_2 { // (undocumented) read(path: string): Buffer | null; // (undocumented) + readJson(path: string): JsonValue; + // (undocumented) + readText(path: string): string; + // (undocumented) rename(from: string, to: string): void; // (undocumented) get root(): DirEntry; diff --git a/goldens/public-api/angular_devkit/schematics/testing/index.md b/goldens/public-api/angular_devkit/schematics/testing/index.md index 9358b5a8390a..06e24643a9fe 100644 --- a/goldens/public-api/angular_devkit/schematics/testing/index.md +++ b/goldens/public-api/angular_devkit/schematics/testing/index.md @@ -6,6 +6,7 @@ /// +import { JsonValue } from '@angular-devkit/core'; import { logging } from '@angular-devkit/core'; import { Observable } from 'rxjs'; import { Path } from '@angular-devkit/core'; diff --git a/goldens/public-api/angular_devkit/schematics/tools/index.md b/goldens/public-api/angular_devkit/schematics/tools/index.md index d968860a625f..b4bf5f2fa17b 100644 --- a/goldens/public-api/angular_devkit/schematics/tools/index.md +++ b/goldens/public-api/angular_devkit/schematics/tools/index.md @@ -8,6 +8,7 @@ import { BaseException } from '@angular-devkit/core'; import { JsonObject } from '@angular-devkit/core'; +import { JsonValue } from '@angular-devkit/core'; import { logging } from '@angular-devkit/core'; import { Observable } from 'rxjs'; import { Path } from '@angular-devkit/core'; diff --git a/packages/angular_devkit/schematics/BUILD.bazel b/packages/angular_devkit/schematics/BUILD.bazel index 0cccea0629fc..d9907050747d 100644 --- a/packages/angular_devkit/schematics/BUILD.bazel +++ b/packages/angular_devkit/schematics/BUILD.bazel @@ -37,6 +37,7 @@ ts_library( "//packages/angular_devkit/core", "//packages/angular_devkit/core/node", # TODO: get rid of this for 6.0 "@npm//@types/node", + "@npm//jsonc-parser", "@npm//magic-string", "@npm//rxjs", ], diff --git a/packages/angular_devkit/schematics/src/tree/delegate.ts b/packages/angular_devkit/schematics/src/tree/delegate.ts index 147997964d59..e1fb75b04f04 100644 --- a/packages/angular_devkit/schematics/src/tree/delegate.ts +++ b/packages/angular_devkit/schematics/src/tree/delegate.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ +import { JsonValue } from '@angular-devkit/core'; import { Action } from './action'; import { DirEntry, @@ -35,6 +36,12 @@ export class DelegateTree implements Tree { read(path: string): Buffer | null { return this._other.read(path); } + readText(path: string): string { + return this._other.readText(path); + } + readJson(path: string): JsonValue { + return this._other.readJson(path); + } exists(path: string): boolean { return this._other.exists(path); } diff --git a/packages/angular_devkit/schematics/src/tree/host-tree.ts b/packages/angular_devkit/schematics/src/tree/host-tree.ts index ba5b6b75f8fb..edcda81b5baf 100644 --- a/packages/angular_devkit/schematics/src/tree/host-tree.ts +++ b/packages/angular_devkit/schematics/src/tree/host-tree.ts @@ -7,6 +7,7 @@ */ import { + JsonValue, Path, PathFragment, PathIsDirectoryException, @@ -16,8 +17,10 @@ import { normalize, virtualFs, } from '@angular-devkit/core'; +import { ParseError, parse as jsoncParse, printParseErrorCode } from 'jsonc-parser'; import { EMPTY, Observable } from 'rxjs'; import { concatMap, map, mergeMap } from 'rxjs/operators'; +import { TextDecoder } from 'util'; import { ContentHasMutatedException, FileAlreadyExistException, @@ -162,10 +165,10 @@ export class HostTree implements Tree { return tree._ancestry.has(this._id); } if (tree instanceof DelegateTree) { - return this.isAncestorOf(((tree as unknown) as { _other: Tree })._other); + return this.isAncestorOf((tree as unknown as { _other: Tree })._other); } if (tree instanceof ScopedTree) { - return this.isAncestorOf(((tree as unknown) as { _base: Tree })._base); + return this.isAncestorOf((tree as unknown as { _base: Tree })._base); } return false; @@ -206,9 +209,9 @@ export class HostTree implements Tree { throw new MergeConflictException(path); } - this._record.overwrite(path, (content as {}) as virtualFs.FileBuffer).subscribe(); + this._record.overwrite(path, content as {} as virtualFs.FileBuffer).subscribe(); } else { - this._record.create(path, (content as {}) as virtualFs.FileBuffer).subscribe(); + this._record.create(path, content as {} as virtualFs.FileBuffer).subscribe(); } return; @@ -234,7 +237,7 @@ export class HostTree implements Tree { } // We use write here as merge validation has already been done, and we want to let // the CordHost do its job. - this._record.write(path, (content as {}) as virtualFs.FileBuffer).subscribe(); + this._record.write(path, content as {} as virtualFs.FileBuffer).subscribe(); return; } @@ -289,6 +292,42 @@ export class HostTree implements Tree { return entry ? entry.content : null; } + + readText(path: string): string { + const data = this.read(path); + if (data === null) { + throw new FileDoesNotExistException(path); + } + + const decoder = new TextDecoder('utf-8', { fatal: true }); + + try { + // With the `fatal` option enabled, invalid data will throw a TypeError + return decoder.decode(data); + } catch (e) { + if (e instanceof TypeError) { + throw new Error(`Failed to decode "${path}" as UTF-8 text.`); + } + throw e; + } + } + + readJson(path: string): JsonValue { + const content = this.readText(path); + const errors: ParseError[] = []; + const result = jsoncParse(content, errors, { allowTrailingComma: true }); + + // If there is a parse error throw with the error information + if (errors[0]) { + const { error, offset } = errors[0]; + throw new Error( + `Failed to parse "${path}" as JSON. ${printParseErrorCode(error)} at offset: ${offset}.`, + ); + } + + return result; + } + exists(path: string): boolean { return this._recordSync.isFile(this._normalizePath(path)); } @@ -337,7 +376,7 @@ export class HostTree implements Tree { throw new FileDoesNotExistException(p); } const c = typeof content == 'string' ? Buffer.from(content) : content; - this._record.overwrite(p, (c as {}) as virtualFs.FileBuffer).subscribe(); + this._record.overwrite(p, c as {} as virtualFs.FileBuffer).subscribe(); } beginUpdate(path: string): UpdateRecorder { const entry = this.get(path); @@ -371,7 +410,7 @@ export class HostTree implements Tree { throw new FileAlreadyExistException(p); } const c = typeof content == 'string' ? Buffer.from(content) : content; - this._record.create(p, (c as {}) as virtualFs.FileBuffer).subscribe(); + this._record.create(p, c as {} as virtualFs.FileBuffer).subscribe(); } delete(path: string): void { this._recordSync.delete(this._normalizePath(path)); @@ -476,7 +515,7 @@ export class FilterHostTree extends HostTree { return EMPTY; } - return newBackend.write(path, (content as {}) as virtualFs.FileBuffer); + return newBackend.write(path, content as {} as virtualFs.FileBuffer); }), ); }; diff --git a/packages/angular_devkit/schematics/src/tree/host-tree_spec.ts b/packages/angular_devkit/schematics/src/tree/host-tree_spec.ts index 9c1a499fd8e3..2687ea14e8ed 100644 --- a/packages/angular_devkit/schematics/src/tree/host-tree_spec.ts +++ b/packages/angular_devkit/schematics/src/tree/host-tree_spec.ts @@ -11,6 +11,84 @@ import { FilterHostTree, HostTree } from './host-tree'; import { MergeStrategy } from './interface'; describe('HostTree', () => { + describe('readText', () => { + it('returns text when reading a file that exists', () => { + const tree = new HostTree(); + tree.create('/textfile1', 'abc'); + tree.create('/textfile2', '123'); + expect(tree.readText('/textfile1')).toEqual('abc'); + expect(tree.readText('/textfile2')).toEqual('123'); + }); + + it('throws an error when a file does not exist', () => { + const tree = new HostTree(); + const path = '/textfile1'; + expect(() => tree.readText(path)).toThrowError(`Path "${path}" does not exist.`); + }); + + it('throws an error when invalid UTF-8 characters are present', () => { + const tree = new HostTree(); + const path = '/textfile1'; + tree.create(path, Buffer.from([0xff, 0xff, 0xff, 0xff])); + expect(() => tree.readText(path)).toThrowError(`Failed to decode "${path}" as UTF-8 text.`); + }); + }); + + describe('readJson', () => { + it('returns a JSON value when reading a file that exists', () => { + const tree = new HostTree(); + tree.create('/textfile1', '{ "a": true, "b": "xyz" }'); + tree.create('/textfile2', '123'); + tree.create('/textfile3', 'null'); + expect(tree.readJson('/textfile1')).toEqual({ a: true, b: 'xyz' }); + expect(tree.readJson('/textfile2')).toEqual(123); + expect(tree.readJson('/textfile3')).toBeNull(); + }); + + it('returns a JSON value when reading a file with comments', () => { + const tree = new HostTree(); + tree.create( + '/textfile1', + '{ "a": true, /* inner object\nmultiline comment\n */ "b": "xyz" }', + ); + tree.create('/textfile2', '123 // number value'); + tree.create('/textfile3', 'null // null value'); + expect(tree.readJson('/textfile1')).toEqual({ a: true, b: 'xyz' }); + expect(tree.readJson('/textfile2')).toEqual(123); + expect(tree.readJson('/textfile3')).toBeNull(); + }); + + it('returns a JSON value when reading a file with trailing commas', () => { + const tree = new HostTree(); + tree.create('/textfile1', '{ "a": true, "b": "xyz", }'); + tree.create('/textfile2', '[5, 4, 3, 2, 1, ]'); + expect(tree.readJson('/textfile1')).toEqual({ a: true, b: 'xyz' }); + expect(tree.readJson('/textfile2')).toEqual([5, 4, 3, 2, 1]); + }); + + it('throws an error when a file does not exist', () => { + const tree = new HostTree(); + const path = '/textfile1'; + expect(() => tree.readJson(path)).toThrowError(`Path "${path}" does not exist.`); + }); + + it('throws an error if the JSON is malformed', () => { + const tree = new HostTree(); + const path = '/textfile1'; + tree.create(path, '{ "a": true;;;;; "b": "xyz" }'); + expect(() => tree.readJson(path)).toThrowError( + `Failed to parse "${path}" as JSON. InvalidSymbol at offset: 7.`, + ); + }); + + it('throws an error when invalid UTF-8 characters are present', () => { + const tree = new HostTree(); + const path = '/textfile1'; + tree.create(path, Buffer.from([0xff, 0xff, 0xff, 0xff])); + expect(() => tree.readJson(path)).toThrowError(`Failed to decode "${path}" as UTF-8 text.`); + }); + }); + describe('merge', () => { it('should create files from each tree', () => { const tree = new HostTree(); diff --git a/packages/angular_devkit/schematics/src/tree/interface.ts b/packages/angular_devkit/schematics/src/tree/interface.ts index e05be51aa6a3..87792b659723 100644 --- a/packages/angular_devkit/schematics/src/tree/interface.ts +++ b/packages/angular_devkit/schematics/src/tree/interface.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import { Path, PathFragment } from '@angular-devkit/core'; +import { JsonValue, Path, PathFragment } from '@angular-devkit/core'; import { Action } from './action'; export enum MergeStrategy { @@ -83,6 +83,31 @@ export interface Tree { // Readonly. read(path: string): Buffer | null; + + /** + * Reads a file from the Tree as a UTF-8 encoded text file. + * + * @param path The path of the file to read. + * @returns A string containing the contents of the file. + * @throws {@link FileDoesNotExistException} if the file is not found. + * @throws An error if the file contains invalid UTF-8 characters. + */ + readText(path: string): string; + + /** + * Reads and parses a file from the Tree as a UTF-8 encoded JSON file. + * Supports parsing JSON (RFC 8259) with the following extensions: + * * Single-line and multi-line JavaScript comments + * * Trailing commas within objects and arrays + * + * @param path The path of the file to read. + * @returns A JsonValue containing the parsed contents of the file. + * @throws {@link FileDoesNotExistException} if the file is not found. + * @throws An error if the file contains invalid UTF-8 characters. + * @throws An error if the file contains malformed JSON. + */ + readJson(path: string): JsonValue; + exists(path: string): boolean; get(path: string): FileEntry | null; getDir(path: string): DirEntry; diff --git a/packages/angular_devkit/schematics/src/tree/null.ts b/packages/angular_devkit/schematics/src/tree/null.ts index 48469a3c3370..cbace2bf3211 100644 --- a/packages/angular_devkit/schematics/src/tree/null.ts +++ b/packages/angular_devkit/schematics/src/tree/null.ts @@ -6,7 +6,15 @@ * found in the LICENSE file at https://angular.io/license */ -import { BaseException, Path, PathFragment, dirname, join, normalize } from '@angular-devkit/core'; +import { + BaseException, + JsonValue, + Path, + PathFragment, + dirname, + join, + normalize, +} from '@angular-devkit/core'; import { FileDoesNotExistException } from '../exception/exception'; import { Action } from './action'; import { DirEntry, MergeStrategy, Tree, TreeSymbol, UpdateRecorder } from './interface'; @@ -57,6 +65,12 @@ export class NullTree implements Tree { read(_path: string) { return null; } + readText(path: string): string { + throw new FileDoesNotExistException(path); + } + readJson(path: string): JsonValue { + throw new FileDoesNotExistException(path); + } get(_path: string) { return null; } diff --git a/packages/angular_devkit/schematics/src/tree/scoped.ts b/packages/angular_devkit/schematics/src/tree/scoped.ts index f77af03635a1..205769204439 100644 --- a/packages/angular_devkit/schematics/src/tree/scoped.ts +++ b/packages/angular_devkit/schematics/src/tree/scoped.ts @@ -7,6 +7,7 @@ */ import { + JsonValue, NormalizedRoot, Path, PathFragment, @@ -113,6 +114,12 @@ export class ScopedTree implements Tree { read(path: string): Buffer | null { return this._base.read(this._fullPath(path)); } + readText(path: string): string { + return this._base.readText(this._fullPath(path)); + } + readJson(path: string): JsonValue { + return this._base.readJson(this._fullPath(path)); + } exists(path: string): boolean { return this._base.exists(this._fullPath(path)); } diff --git a/packages/angular_devkit/schematics_cli/blank/factory.ts b/packages/angular_devkit/schematics_cli/blank/factory.ts index d8eeb16cc3d5..6585ea95b59c 100644 --- a/packages/angular_devkit/schematics_cli/blank/factory.ts +++ b/packages/angular_devkit/schematics_cli/blank/factory.ts @@ -28,13 +28,9 @@ function addSchematicToCollectionJson( description: JsonObject, ): Rule { return (tree: Tree) => { - const collectionJsonContent = tree.read(collectionPath); - if (!collectionJsonContent) { - throw new Error('Invalid collection path: ' + collectionPath); - } + const collectionJson = tree.readJson(collectionPath); - const collectionJson = JSON.parse(collectionJsonContent.toString()); - if (!isJsonObject(collectionJson.schematics)) { + if (!isJsonObject(collectionJson) || !isJsonObject(collectionJson.schematics)) { throw new Error('Invalid collection.json; schematics needs to be an object.'); } @@ -55,16 +51,13 @@ export default function (options: Schema): Rule { let collectionPath: Path | undefined; try { - const packageJsonContent = tree.read('/package.json'); - if (packageJsonContent) { - const packageJson = JSON.parse(packageJsonContent.toString()) as { - schematics: unknown; - }; - if (typeof packageJson.schematics === 'string') { - const p = normalize(packageJson.schematics); - if (tree.exists(p)) { - collectionPath = p; - } + const packageJson = tree.readJson('/package.json') as { + schematics: unknown; + }; + if (typeof packageJson.schematics === 'string') { + const p = normalize(packageJson.schematics); + if (tree.exists(p)) { + collectionPath = p; } } } catch {} diff --git a/packages/schematics/angular/app-shell/index.ts b/packages/schematics/angular/app-shell/index.ts index f7caa44cabcf..0e7e966368e9 100644 --- a/packages/schematics/angular/app-shell/index.ts +++ b/packages/schematics/angular/app-shell/index.ts @@ -35,11 +35,7 @@ import { BrowserBuilderOptions, Builders, ServerBuilderOptions } from '../utilit import { Schema as AppShellOptions } from './schema'; function getSourceFile(host: Tree, path: string): ts.SourceFile { - const buffer = host.read(path); - if (!buffer) { - throw new SchematicsException(`Could not find ${path}.`); - } - const content = buffer.toString(); + const content = host.readText(path); const source = ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true); return source; @@ -82,10 +78,9 @@ function getComponentTemplate(host: Tree, compPath: string, tmplInfo: TemplateIn const templateUrl = (tmplInfo.templateUrlProp.initializer as ts.StringLiteral).text; const dir = dirname(normalize(compPath)); const templatePath = join(dir, templateUrl); - const buffer = host.read(templatePath); - if (buffer) { - template = buffer.toString(); - } + try { + template = host.readText(templatePath); + } catch {} } return template; diff --git a/packages/schematics/angular/component/index.ts b/packages/schematics/angular/component/index.ts index 177b5eac276a..2fbbb7bcb1c0 100644 --- a/packages/schematics/angular/component/index.ts +++ b/packages/schematics/angular/component/index.ts @@ -32,11 +32,7 @@ import { buildDefaultPath, getWorkspace } from '../utility/workspace'; import { Schema as ComponentOptions, Style } from './schema'; function readIntoSourceFile(host: Tree, modulePath: string): ts.SourceFile { - const text = host.read(modulePath); - if (text === null) { - throw new SchematicsException(`File ${modulePath} does not exist.`); - } - const sourceText = text.toString('utf-8'); + const sourceText = host.readText(modulePath); return ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true); } diff --git a/packages/schematics/angular/directive/index.ts b/packages/schematics/angular/directive/index.ts index 666a181b5b48..4a4a1bdbe8b4 100644 --- a/packages/schematics/angular/directive/index.ts +++ b/packages/schematics/angular/directive/index.ts @@ -36,11 +36,7 @@ function addDeclarationToNgModule(options: DirectiveOptions): Rule { } const modulePath = options.module; - const text = host.read(modulePath); - if (text === null) { - throw new SchematicsException(`File ${modulePath} does not exist.`); - } - const sourceText = text.toString('utf-8'); + const sourceText = host.readText(modulePath); const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true); const directivePath = @@ -66,11 +62,7 @@ function addDeclarationToNgModule(options: DirectiveOptions): Rule { if (options.export) { // Need to refresh the AST because we overwrote the file in the host. - const text = host.read(modulePath); - if (text === null) { - throw new SchematicsException(`File ${modulePath} does not exist.`); - } - const sourceText = text.toString('utf-8'); + const sourceText = host.readText(modulePath); const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true); const exportRecorder = host.beginUpdate(modulePath); diff --git a/packages/schematics/angular/module/index.ts b/packages/schematics/angular/module/index.ts index 4c6840011c25..6c599c418bce 100644 --- a/packages/schematics/angular/module/index.ts +++ b/packages/schematics/angular/module/index.ts @@ -55,11 +55,7 @@ function addDeclarationToNgModule(options: ModuleOptions): Rule { const modulePath = options.module; - const text = host.read(modulePath); - if (text === null) { - throw new SchematicsException(`File ${modulePath} does not exist.`); - } - const sourceText = text.toString(); + const sourceText = host.readText(modulePath); const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true); const relativePath = buildRelativeModulePath(options, modulePath); @@ -101,12 +97,8 @@ function addRouteDeclarationToNgModule( path = options.module; } - const text = host.read(path); - if (!text) { - throw new Error(`Couldn't find the module nor its routing module.`); - } + const sourceText = host.readText(path); - const sourceText = text.toString(); const addDeclaration = addRouteDeclarationToModule( ts.createSourceFile(path, sourceText, ts.ScriptTarget.Latest, true), path, diff --git a/packages/schematics/angular/pipe/index.ts b/packages/schematics/angular/pipe/index.ts index e18c432eabc2..e7ab1e7d8e95 100644 --- a/packages/schematics/angular/pipe/index.ts +++ b/packages/schematics/angular/pipe/index.ts @@ -35,11 +35,7 @@ function addDeclarationToNgModule(options: PipeOptions): Rule { } const modulePath = options.module; - const text = host.read(modulePath); - if (text === null) { - throw new SchematicsException(`File ${modulePath} does not exist.`); - } - const sourceText = text.toString('utf-8'); + const sourceText = host.readText(modulePath); const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true); const pipePath = @@ -63,11 +59,7 @@ function addDeclarationToNgModule(options: PipeOptions): Rule { host.commitUpdate(recorder); if (options.export) { - const text = host.read(modulePath); - if (text === null) { - throw new SchematicsException(`File ${modulePath} does not exist.`); - } - const sourceText = text.toString('utf-8'); + const sourceText = host.readText(modulePath); const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true); const exportRecorder = host.beginUpdate(modulePath); diff --git a/packages/schematics/angular/service-worker/index.ts b/packages/schematics/angular/service-worker/index.ts index 774493083855..f35e47019d5f 100644 --- a/packages/schematics/angular/service-worker/index.ts +++ b/packages/schematics/angular/service-worker/index.ts @@ -122,11 +122,7 @@ function updateAppModule(mainPath: string): Rule { } function getTsSourceFile(host: Tree, path: string): ts.SourceFile { - const buffer = host.read(path); - if (!buffer) { - throw new SchematicsException(`Could not read file (${path}).`); - } - const content = buffer.toString(); + const content = host.readText(path); const source = ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true); return source; diff --git a/packages/schematics/angular/universal/index.ts b/packages/schematics/angular/universal/index.ts index 95fe3d26e93f..c660995305bc 100644 --- a/packages/schematics/angular/universal/index.ts +++ b/packages/schematics/angular/universal/index.ts @@ -99,12 +99,7 @@ function updateConfigFile(options: UniversalOptions, tsConfigDirectory: Path): R } function findBrowserModuleImport(host: Tree, modulePath: string): ts.Node { - const moduleBuffer = host.read(modulePath); - if (!moduleBuffer) { - throw new SchematicsException(`Module file (${modulePath}) not found`); - } - const moduleFileText = moduleBuffer.toString('utf-8'); - + const moduleFileText = host.readText(modulePath); const source = ts.createSourceFile(modulePath, moduleFileText, ts.ScriptTarget.Latest, true); const decoratorMetadata = getDecoratorMetadata(source, 'NgModule', '@angular/core')[0]; diff --git a/packages/schematics/angular/utility/json-file.ts b/packages/schematics/angular/utility/json-file.ts index 647199ef848d..6bb532416f2e 100644 --- a/packages/schematics/angular/utility/json-file.ts +++ b/packages/schematics/angular/utility/json-file.ts @@ -27,12 +27,7 @@ export class JSONFile { content: string; constructor(private readonly host: Tree, private readonly path: string) { - const buffer = this.host.read(this.path); - if (buffer) { - this.content = buffer.toString(); - } else { - throw new Error(`Could not read '${path}'.`); - } + this.content = this.host.readText(this.path); } private _jsonAst: Node | undefined; diff --git a/packages/schematics/angular/utility/ng-ast-utils.ts b/packages/schematics/angular/utility/ng-ast-utils.ts index e55effeeea17..c0dfb8374b90 100644 --- a/packages/schematics/angular/utility/ng-ast-utils.ts +++ b/packages/schematics/angular/utility/ng-ast-utils.ts @@ -13,11 +13,7 @@ import * as ts from '../third_party/github.com/Microsoft/TypeScript/lib/typescri import { findNode, getSourceNodes } from '../utility/ast-utils'; export function findBootstrapModuleCall(host: Tree, mainPath: string): ts.CallExpression | null { - const mainBuffer = host.read(mainPath); - if (!mainBuffer) { - throw new SchematicsException(`Main file (${mainPath}) not found`); - } - const mainText = mainBuffer.toString('utf-8'); + const mainText = host.readText(mainPath); const source = ts.createSourceFile(mainPath, mainText, ts.ScriptTarget.Latest, true); const allNodes = getSourceNodes(source); @@ -58,11 +54,7 @@ export function findBootstrapModulePath(host: Tree, mainPath: string): string { const bootstrapModule = bootstrapCall.arguments[0]; - const mainBuffer = host.read(mainPath); - if (!mainBuffer) { - throw new SchematicsException(`Client application main file (${mainPath}) not found`); - } - const mainText = mainBuffer.toString('utf-8'); + const mainText = host.readText(mainPath); const source = ts.createSourceFile(mainPath, mainText, ts.ScriptTarget.Latest, true); const allNodes = getSourceNodes(source); const bootstrapModuleRelativePath = allNodes diff --git a/packages/schematics/angular/utility/workspace.ts b/packages/schematics/angular/utility/workspace.ts index 07bf2c9ffc64..9354f4beab14 100644 --- a/packages/schematics/angular/utility/workspace.ts +++ b/packages/schematics/angular/utility/workspace.ts @@ -6,19 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ -import { json, virtualFs, workspaces } from '@angular-devkit/core'; +import { json, workspaces } from '@angular-devkit/core'; import { Rule, Tree, noop } from '@angular-devkit/schematics'; import { ProjectType } from './workspace-models'; function createHost(tree: Tree): workspaces.WorkspaceHost { return { async readFile(path: string): Promise { - const data = tree.read(path); - if (!data) { - throw new Error('File not found.'); - } - - return virtualFs.fileBufferToString(data); + return tree.readText(path); }, async writeFile(path: string, data: string): Promise { return tree.overwrite(path, data); diff --git a/packages/schematics/angular/web-worker/index.ts b/packages/schematics/angular/web-worker/index.ts index bda1d4740887..1e0564045a75 100644 --- a/packages/schematics/angular/web-worker/index.ts +++ b/packages/schematics/angular/web-worker/index.ts @@ -65,7 +65,7 @@ function addSnippet(options: WebWorkerOptions): Rule { `; // Append the worker creation snippet. - const originalContent = host.read(siblingModulePath); + const originalContent = host.readText(siblingModulePath); host.overwrite(siblingModulePath, originalContent + '\n' + workerCreationSnippet); return host;