diff --git a/packages/core/schematics/migrations/dynamic-queries/index.ts b/packages/core/schematics/migrations/dynamic-queries/index.ts index 5a1ab2dbac..2d2b267bb7 100644 --- a/packages/core/schematics/migrations/dynamic-queries/index.ts +++ b/packages/core/schematics/migrations/dynamic-queries/index.ts @@ -11,10 +11,13 @@ import {dirname, relative} from 'path'; import * as ts from 'typescript'; import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; +import {createMigrationCompilerHost} from '../../utils/typescript/compiler_host'; import {parseTsconfigFile} from '../../utils/typescript/parse_tsconfig'; + import {identifyDynamicQueryNodes, removeOptionsParameter, removeStaticFlag} from './util'; + /** * Runs the dynamic queries migration for all TypeScript projects in the current CLI workspace. */ @@ -39,20 +42,7 @@ export default function(): Rule { function runDynamicQueryMigration(tree: Tree, tsconfigPath: string, basePath: string) { const parsed = parseTsconfigFile(tsconfigPath, dirname(tsconfigPath)); - const host = ts.createCompilerHost(parsed.options, true); - - // We need to overwrite the host "readFile" method, as we want the TypeScript - // program to be based on the file contents in the virtual file tree. Otherwise - // if we run the migration for multiple tsconfig files which have intersecting - // source files, it can end up updating query definitions multiple times. - host.readFile = fileName => { - const buffer = tree.read(relative(basePath, fileName)); - // Strip BOM as otherwise TSC methods (Ex: getWidth) will return an offset which - // which breaks the CLI UpdateRecorder. - // See: https://github.com/angular/angular/pull/30719 - return buffer ? buffer.toString().replace(/^\uFEFF/, '') : undefined; - }; - + const host = createMigrationCompilerHost(tree, parsed.options, basePath); const program = ts.createProgram(parsed.fileNames, parsed.options, host); const typeChecker = program.getTypeChecker(); const sourceFiles = program.getSourceFiles().filter( diff --git a/packages/core/schematics/migrations/missing-injectable/index.ts b/packages/core/schematics/migrations/missing-injectable/index.ts index 76a7cd1537..6c21d43689 100644 --- a/packages/core/schematics/migrations/missing-injectable/index.ts +++ b/packages/core/schematics/migrations/missing-injectable/index.ts @@ -9,10 +9,9 @@ import {Rule, SchematicContext, SchematicsException, Tree} from '@angular-devkit/schematics'; import {dirname, relative} from 'path'; import * as ts from 'typescript'; - import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; +import {createMigrationCompilerHost} from '../../utils/typescript/compiler_host'; import {parseTsconfigFile} from '../../utils/typescript/parse_tsconfig'; - import {NgModuleCollector} from './module_collector'; import {MissingInjectableTransform} from './transform'; import {UpdateRecorder} from './update_recorder'; @@ -49,20 +48,9 @@ export default function(): Rule { function runMissingInjectableMigration( tree: Tree, tsconfigPath: string, basePath: string): string[] { const parsed = parseTsconfigFile(tsconfigPath, dirname(tsconfigPath)); - const host = ts.createCompilerHost(parsed.options, true); + const host = createMigrationCompilerHost(tree, parsed.options, basePath); const failures: string[] = []; - // We need to overwrite the host "readFile" method, as we want the TypeScript - // program to be based on the file contents in the virtual file tree. - host.readFile = fileName => { - const buffer = tree.read(relative(basePath, fileName)); - // Strip BOM because TypeScript respects this character and it ultimately - // results in shifted offsets since the CLI UpdateRecorder tries to - // automatically account for the BOM character. - // https://github.com/angular/angular-cli/issues/14558 - return buffer ? buffer.toString().replace(/^\uFEFF/, '') : undefined; - }; - const program = ts.createProgram(parsed.fileNames, parsed.options, host); const typeChecker = program.getTypeChecker(); const moduleCollector = new NgModuleCollector(typeChecker); diff --git a/packages/core/schematics/migrations/move-document/index.ts b/packages/core/schematics/migrations/move-document/index.ts index 30d0cc5597..2fb42f0753 100644 --- a/packages/core/schematics/migrations/move-document/index.ts +++ b/packages/core/schematics/migrations/move-document/index.ts @@ -11,11 +11,14 @@ import {dirname, relative} from 'path'; import * as ts from 'typescript'; import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; +import {createMigrationCompilerHost} from '../../utils/typescript/compiler_host'; import {parseTsconfigFile} from '../../utils/typescript/parse_tsconfig'; + import {COMMON_IMPORT, DOCUMENT_TOKEN_NAME, DocumentImportVisitor, ResolvedDocumentImport} from './document_import_visitor'; import {addToImport, createImport, removeFromImport} from './move-import'; + /** Entry point for the V8 move-document migration. */ export default function(): Rule { return (tree: Tree) => { @@ -40,19 +43,7 @@ export default function(): Rule { */ function runMoveDocumentMigration(tree: Tree, tsconfigPath: string, basePath: string) { const parsed = parseTsconfigFile(tsconfigPath, dirname(tsconfigPath)); - const host = ts.createCompilerHost(parsed.options, true); - - // We need to overwrite the host "readFile" method, as we want the TypeScript - // program to be based on the file contents in the virtual file tree. Otherwise - // if we run the migration for multiple tsconfig files which have intersecting - // source files, it can end up updating query definitions multiple times. - host.readFile = fileName => { - const buffer = tree.read(relative(basePath, fileName)); - // Strip BOM as otherwise TSC methods (Ex: getWidth) will return an offset which - // which breaks the CLI UpdateRecorder. - // See: https://github.com/angular/angular/pull/30719 - return buffer ? buffer.toString().replace(/^\uFEFF/, '') : undefined; - }; + const host = createMigrationCompilerHost(tree, parsed.options, basePath); const program = ts.createProgram(parsed.fileNames, parsed.options, host); const typeChecker = program.getTypeChecker(); diff --git a/packages/core/schematics/migrations/renderer-to-renderer2/index.ts b/packages/core/schematics/migrations/renderer-to-renderer2/index.ts index 57eda170e7..1843b86ce8 100644 --- a/packages/core/schematics/migrations/renderer-to-renderer2/index.ts +++ b/packages/core/schematics/migrations/renderer-to-renderer2/index.ts @@ -11,6 +11,7 @@ import {dirname, relative} from 'path'; import * as ts from 'typescript'; import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; +import {createMigrationCompilerHost} from '../../utils/typescript/compiler_host'; import {parseTsconfigFile} from '../../utils/typescript/parse_tsconfig'; import {HelperFunction, getHelper} from './helpers'; @@ -18,6 +19,7 @@ import {migrateExpression, replaceImport} from './migration'; import {findCoreImport, findRendererReferences} from './util'; + /** * Migration that switches from `Renderer` to `Renderer2`. More information on how it works: * https://hackmd.angular.io/UTzUZTnPRA-cSa_4mHyfYw @@ -46,17 +48,7 @@ export default function(): Rule { function runRendererToRenderer2Migration(tree: Tree, tsconfigPath: string, basePath: string) { const parsed = parseTsconfigFile(tsconfigPath, dirname(tsconfigPath)); - const host = ts.createCompilerHost(parsed.options, true); - - // We need to overwrite the host "readFile" method, as we want the TypeScript - // program to be based on the file contents in the virtual file tree. Otherwise - // if we run the migration for multiple tsconfig files which have intersecting - // source files, it can end up updating query definitions multiple times. - host.readFile = fileName => { - const buffer = tree.read(relative(basePath, fileName)); - return buffer ? buffer.toString() : undefined; - }; - + const host = createMigrationCompilerHost(tree, parsed.options, basePath); const program = ts.createProgram(parsed.fileNames, parsed.options, host); const typeChecker = program.getTypeChecker(); const printer = ts.createPrinter(); diff --git a/packages/core/schematics/migrations/static-queries/index.ts b/packages/core/schematics/migrations/static-queries/index.ts index 207531bbeb..f01b9da611 100644 --- a/packages/core/schematics/migrations/static-queries/index.ts +++ b/packages/core/schematics/migrations/static-queries/index.ts @@ -14,6 +14,7 @@ import * as ts from 'typescript'; import {NgComponentTemplateVisitor} from '../../utils/ng_component_template'; import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; +import {createMigrationCompilerHost} from '../../utils/typescript/compiler_host'; import {parseTsconfigFile} from '../../utils/typescript/parse_tsconfig'; import {NgQueryResolveVisitor} from './angular/ng_query_visitor'; @@ -117,20 +118,7 @@ function analyzeProject( logger: logging.LoggerApi): AnalyzedProject|null { const parsed = parseTsconfigFile(tsconfigPath, dirname(tsconfigPath)); - const host = ts.createCompilerHost(parsed.options, true); - - // We need to overwrite the host "readFile" method, as we want the TypeScript - // program to be based on the file contents in the virtual file tree. Otherwise - // if we run the migration for multiple tsconfig files which have intersecting - // source files, it can end up updating query definitions multiple times. - host.readFile = fileName => { - const buffer = tree.read(relative(basePath, fileName)); - // Strip BOM as otherwise TSC methods (Ex: getWidth) will return an offset which - // which breaks the CLI UpdateRecorder. - // See: https://github.com/angular/angular/pull/30719 - return buffer ? buffer.toString().replace(/^\uFEFF/, '') : undefined; - }; - + const host = createMigrationCompilerHost(tree, parsed.options, basePath); const program = ts.createProgram(parsed.fileNames, parsed.options, host); const syntacticDiagnostics = program.getSyntacticDiagnostics(); diff --git a/packages/core/schematics/migrations/template-var-assignment/index.ts b/packages/core/schematics/migrations/template-var-assignment/index.ts index a7fce2081b..e0ece7a7fd 100644 --- a/packages/core/schematics/migrations/template-var-assignment/index.ts +++ b/packages/core/schematics/migrations/template-var-assignment/index.ts @@ -13,6 +13,7 @@ import * as ts from 'typescript'; import {NgComponentTemplateVisitor} from '../../utils/ng_component_template'; import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; +import {createMigrationCompilerHost} from '../../utils/typescript/compiler_host'; import {parseTsconfigFile} from '../../utils/typescript/parse_tsconfig'; import {analyzeResolvedTemplate} from './analyze_template'; @@ -47,18 +48,7 @@ export default function(): Rule { function runTemplateVariableAssignmentCheck( tree: Tree, tsconfigPath: string, basePath: string, logger: Logger) { const parsed = parseTsconfigFile(tsconfigPath, dirname(tsconfigPath)); - const host = ts.createCompilerHost(parsed.options, true); - - // We need to overwrite the host "readFile" method, as we want the TypeScript - // program to be based on the file contents in the virtual file tree. - host.readFile = fileName => { - const buffer = tree.read(relative(basePath, fileName)); - // Strip BOM as otherwise TSC methods (Ex: getWidth) will return an offset which - // which breaks the CLI UpdateRecorder. - // See: https://github.com/angular/angular/pull/30719 - return buffer ? buffer.toString().replace(/^\uFEFF/, '') : undefined; - }; - + const host = createMigrationCompilerHost(tree, parsed.options, basePath); const program = ts.createProgram(parsed.fileNames, parsed.options, host); const typeChecker = program.getTypeChecker(); const templateVisitor = new NgComponentTemplateVisitor(typeChecker); diff --git a/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/index.ts b/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/index.ts index 0873860139..b75a8b7a30 100644 --- a/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/index.ts +++ b/packages/core/schematics/migrations/undecorated-classes-with-decorated-fields/index.ts @@ -9,8 +9,8 @@ import {Rule, SchematicContext, SchematicsException, Tree} from '@angular-devkit/schematics'; import {dirname, relative} from 'path'; import * as ts from 'typescript'; - import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; +import {createMigrationCompilerHost} from '../../utils/typescript/compiler_host'; import {parseTsconfigFile} from '../../utils/typescript/parse_tsconfig'; import {FALLBACK_DECORATOR, addImport, getNamedImports, getUndecoratedClassesWithDecoratedFields, hasNamedImport} from './utils'; @@ -44,20 +44,7 @@ export default function(): Rule { function runUndecoratedClassesMigration(tree: Tree, tsconfigPath: string, basePath: string) { const parsed = parseTsconfigFile(tsconfigPath, dirname(tsconfigPath)); - const host = ts.createCompilerHost(parsed.options, true); - - // We need to overwrite the host "readFile" method, as we want the TypeScript - // program to be based on the file contents in the virtual file tree. Otherwise - // if we run the migration for multiple tsconfig files which have intersecting - // source files, it can end up updating them multiple times. - host.readFile = fileName => { - const buffer = tree.read(relative(basePath, fileName)); - // Strip BOM as otherwise TSC methods (Ex: getWidth) will return an offset which - // which breaks the CLI UpdateRecorder. - // See: https://github.com/angular/angular/pull/30719 - return buffer ? buffer.toString().replace(/^\uFEFF/, '') : undefined; - }; - + const host = createMigrationCompilerHost(tree, parsed.options, basePath); const program = ts.createProgram(parsed.fileNames, parsed.options, host); const typeChecker = program.getTypeChecker(); const printer = ts.createPrinter(); diff --git a/packages/core/schematics/migrations/undecorated-classes-with-di/index.ts b/packages/core/schematics/migrations/undecorated-classes-with-di/index.ts index 3689f1aa9a..e64d979448 100644 --- a/packages/core/schematics/migrations/undecorated-classes-with-di/index.ts +++ b/packages/core/schematics/migrations/undecorated-classes-with-di/index.ts @@ -9,12 +9,14 @@ import {logging} from '@angular-devkit/core'; import {Rule, SchematicContext, SchematicsException, Tree} from '@angular-devkit/schematics'; import {AotCompiler} from '@angular/compiler'; +import {createCompilerHost} from '@angular/compiler-cli'; import {PartialEvaluator} from '@angular/compiler-cli/src/ngtsc/partial_evaluator'; import {TypeScriptReflectionHost} from '@angular/compiler-cli/src/ngtsc/reflection'; import {relative} from 'path'; import * as ts from 'typescript'; import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; +import {createMigrationCompilerHost} from '../../utils/typescript/compiler_host'; import {createNgcProgram} from './create_ngc_program'; import {NgDeclarationCollector} from './ng_declaration_collector'; @@ -146,21 +148,8 @@ function gracefullyCreateProgram( tree: Tree, basePath: string, tsconfigPath: string, logger: logging.LoggerApi): {compiler: AotCompiler, program: ts.Program}|null { try { - const {ngcProgram, host, program, compiler} = createNgcProgram((options) => { - const host = ts.createCompilerHost(options, true); - - // We need to overwrite the host "readFile" method, as we want the TypeScript - // program to be based on the file contents in the virtual file tree. - host.readFile = fileName => { - const buffer = tree.read(relative(basePath, fileName)); - // Strip BOM as otherwise TSC methods (Ex: getWidth) will return an offset which - // which breaks the CLI UpdateRecorder. - // See: https://github.com/angular/angular/pull/30719 - return buffer ? buffer.toString().replace(/^\uFEFF/, '') : undefined; - }; - - return host; - }, tsconfigPath); + const {ngcProgram, host, program, compiler} = createNgcProgram( + (options) => createMigrationCompilerHost(tree, options, basePath), tsconfigPath); const syntacticDiagnostics = ngcProgram.getTsSyntacticDiagnostics(); const structuralDiagnostics = ngcProgram.getNgStructuralDiagnostics(); diff --git a/packages/core/schematics/utils/typescript/compiler_host.ts b/packages/core/schematics/utils/typescript/compiler_host.ts new file mode 100644 index 0000000000..7fcf339a39 --- /dev/null +++ b/packages/core/schematics/utils/typescript/compiler_host.ts @@ -0,0 +1,29 @@ +/** + * @license + * Copyright Google Inc. 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 + */ +import {Tree} from '@angular-devkit/schematics'; +import {relative} from 'path'; +import * as ts from 'typescript'; + +export function createMigrationCompilerHost( + tree: Tree, options: ts.CompilerOptions, basePath: string): ts.CompilerHost { + const host = ts.createCompilerHost(options, true); + + // We need to overwrite the host "readFile" method, as we want the TypeScript + // program to be based on the file contents in the virtual file tree. Otherwise + // if we run multiple migrations we might have intersecting changes and + // source files. + host.readFile = fileName => { + const buffer = tree.read(relative(basePath, fileName)); + // Strip BOM as otherwise TSC methods (Ex: getWidth) will return an offset which + // which breaks the CLI UpdateRecorder. + // See: https://github.com/angular/angular/pull/30719 + return buffer ? buffer.toString().replace(/^\uFEFF/, '') : undefined; + }; + + return host; +}