From e648a0c4ca532a009186855c2ed998188fe0c9e3 Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Thu, 14 May 2020 11:24:59 -0700 Subject: [PATCH] refactor(compiler-cli): extract NgCompilerAdapter interface (#37118) `NgCompiler` is the heart of ngtsc and can be used to analyze and compile Angular programs in a variety of environments. Most of these integrations rely on `NgProgram` and the creation of an `NgCompilerHost` in order to create a `ts.Program` with the right shape for `NgCompiler`. However, certain environments (such as the Angular Language Service) have their own mechanisms for creating `ts.Program`s that don't make use of a `ts.CompilerHost`. In such environments, an `NgCompilerHost` does not make sense. This commit breaks the dependency of `NgCompiler` on `NgCompilerHost` and extracts the specific interface of the host on which `NgCompiler` depends into a new interface, `NgCompilerAdapter`. This interface includes methods from `ts.CompilerHost`, the `ExtendedTsCompilerHost`, as well as APIs from `NgCompilerHost`. A consumer such as the language service can implement this API without needing to jump through hoops to create an `NgCompilerHost` implementation that somehow wraps its specific environment. PR Close #37118 --- .../src/ngtsc/annotations/BUILD.bazel | 2 +- .../src/ngtsc/annotations/src/ng_module.ts | 2 +- .../compiler-cli/src/ngtsc/core/BUILD.bazel | 1 + .../compiler-cli/src/ngtsc/core/api/index.ts | 1 + .../src/ngtsc/core/api/src/adapter.ts | 99 +++++++++++++++++++ .../src/ngtsc/core/src/compiler.ts | 54 +++++----- .../compiler-cli/src/ngtsc/core/src/host.ts | 12 +-- .../src/ngtsc/entry_point/BUILD.bazel | 2 +- .../src/ngtsc/entry_point/src/generator.ts | 2 +- .../src/ngtsc/file_system/src/logical.ts | 4 +- .../src/ngtsc/imports/src/resolver.ts | 5 +- .../compiler-cli/src/ngtsc/resource/index.ts | 2 +- .../src/ngtsc/resource/src/loader.ts | 35 +++---- packages/compiler-cli/src/ngtsc/shims/api.ts | 19 ++++ .../compiler-cli/src/ngtsc/shims/index.ts | 3 +- .../src/ngtsc/shims/src/factory_generator.ts | 21 +--- .../src/ngtsc/typecheck/src/checker.ts | 2 +- .../src/ngtsc/typecheck/src/context.ts | 3 +- .../ngtsc/typecheck/src/type_check_file.ts | 2 +- .../src/ngtsc/util/src/typescript.ts | 2 +- 20 files changed, 185 insertions(+), 88 deletions(-) create mode 100644 packages/compiler-cli/src/ngtsc/core/api/src/adapter.ts diff --git a/packages/compiler-cli/src/ngtsc/annotations/BUILD.bazel b/packages/compiler-cli/src/ngtsc/annotations/BUILD.bazel index 46dc232739..e63dc1852f 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/annotations/BUILD.bazel @@ -20,7 +20,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/reflection", "//packages/compiler-cli/src/ngtsc/routing", "//packages/compiler-cli/src/ngtsc/scope", - "//packages/compiler-cli/src/ngtsc/shims", + "//packages/compiler-cli/src/ngtsc/shims:api", "//packages/compiler-cli/src/ngtsc/transform", "//packages/compiler-cli/src/ngtsc/typecheck", "//packages/compiler-cli/src/ngtsc/util", diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts b/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts index 46aba28cd0..c12cb44722 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts @@ -16,7 +16,7 @@ import {PartialEvaluator, ResolvedValue, ResolvedValueArray} from '../../partial import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral, typeNodeToValueExpr} from '../../reflection'; import {NgModuleRouteAnalyzer} from '../../routing'; import {LocalModuleScopeRegistry, ScopeData} from '../../scope'; -import {FactoryTracker} from '../../shims'; +import {FactoryTracker} from '../../shims/api'; import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence, ResolveResult} from '../../transform'; import {getSourceFile} from '../../util/src/typescript'; diff --git a/packages/compiler-cli/src/ngtsc/core/BUILD.bazel b/packages/compiler-cli/src/ngtsc/core/BUILD.bazel index 6cb55f9bc7..f0c86d51f9 100644 --- a/packages/compiler-cli/src/ngtsc/core/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/core/BUILD.bazel @@ -43,6 +43,7 @@ ts_library( srcs = glob(["api/**/*.ts"]), deps = [ "//packages/compiler-cli/src/ngtsc/file_system", + "//packages/compiler-cli/src/ngtsc/shims:api", "@npm//typescript", ], ) diff --git a/packages/compiler-cli/src/ngtsc/core/api/index.ts b/packages/compiler-cli/src/ngtsc/core/api/index.ts index 6ac04999c4..ede883bf2e 100644 --- a/packages/compiler-cli/src/ngtsc/core/api/index.ts +++ b/packages/compiler-cli/src/ngtsc/core/api/index.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ +export * from './src/adapter'; export * from './src/interfaces'; export * from './src/options'; export * from './src/public_options'; diff --git a/packages/compiler-cli/src/ngtsc/core/api/src/adapter.ts b/packages/compiler-cli/src/ngtsc/core/api/src/adapter.ts new file mode 100644 index 0000000000..ab5a8cf1bd --- /dev/null +++ b/packages/compiler-cli/src/ngtsc/core/api/src/adapter.ts @@ -0,0 +1,99 @@ +/** + * @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 * as ts from 'typescript'; + +import {AbsoluteFsPath} from '../../../file_system'; +import {FactoryTracker} from '../../../shims/api'; + +import {ExtendedTsCompilerHost, UnifiedModulesHost} from './interfaces'; + +/** + * Names of methods from `ExtendedTsCompilerHost` that need to be provided by the + * `NgCompilerAdapter`. + */ +export type ExtendedCompilerHostMethods = + // Used to normalize filenames for the host system. Important for proper case-sensitive file + // handling. + 'getCanonicalFileName'| + // An optional method of `ts.CompilerHost` where an implementer can override module resolution. + 'resolveModuleNames'| + // Retrieve the current working directory. Unlike in `ts.ModuleResolutionHost`, this is a + // required method. + 'getCurrentDirectory'| + // Additional methods of `ExtendedTsCompilerHost` related to resource files (e.g. HTML + // templates). These are optional. + 'getModifiedResourceFiles'|'readResource'|'resourceNameToFileName'; + +/** + * Adapter for `NgCompiler` that allows it to be used in various circumstances, such as + * command-line `ngc`, as a plugin to `ts_library` in Bazel, or from the Language Service. + * + * `NgCompilerAdapter` is a subset of the `NgCompilerHost` implementation of `ts.CompilerHost` + * which is relied upon by `NgCompiler`. A consumer of `NgCompiler` can therefore use the + * `NgCompilerHost` or implement `NgCompilerAdapter` itself. + */ +export interface NgCompilerAdapter extends + // getCurrentDirectory is removed from `ts.ModuleResolutionHost` because it's optional, and + // incompatible with the `ts.CompilerHost` version which isn't. The combination of these two + // still satisfies `ts.ModuleResolutionHost`. + Omit, + Pick { + /** + * A path to a single file which represents the entrypoint of an Angular Package Format library, + * if the current program is one. + * + * This is used to emit a flat module index if requested, and can be left `null` if that is not + * required. + */ + readonly entryPoint: AbsoluteFsPath|null; + + /** + * An array of `ts.Diagnostic`s that occurred during construction of the `ts.Program`. + */ + readonly constructionDiagnostics: ts.Diagnostic[]; + + /** + * A `Set` of `ts.SourceFile`s which are internal to the program and should not be emitted as JS + * files. + * + * Often these are shim files such as `ngtypecheck` shims used for template type-checking in + * command-line ngc. + */ + readonly ignoreForEmit: Set; + + /** + * A tracker for usage of symbols in `.ngfactory` shims. + * + * This can be left `null` if such shims are not a part of the `ts.Program`. + */ + readonly factoryTracker: FactoryTracker|null; + + /** + * A specialized interface provided in some environments (such as Bazel) which overrides how + * import specifiers are generated. + * + * If not required, this can be `null`. + */ + readonly unifiedModulesHost: UnifiedModulesHost|null; + + /** + * Resolved list of root directories explicitly set in, or inferred from, the tsconfig. + */ + readonly rootDirs: ReadonlyArray; + + /** + * Distinguishes between shim files added by Angular to the compilation process (both those + * intended for output, like ngfactory files, as well as internal shims like ngtypecheck files) + * and original files in the user's program. + * + * This is mostly used to limit type-checking operations to only user files. It should return + * `true` if a file was written by the user, and `false` if a file was added by the compiler. + */ + isShim(sf: ts.SourceFile): boolean; +} diff --git a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts index 0f9f29271a..0a54b3d9d5 100644 --- a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts +++ b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts @@ -22,7 +22,7 @@ import {ModuleWithProvidersScanner} from '../../modulewithproviders'; import {PartialEvaluator} from '../../partial_evaluator'; import {NOOP_PERF_RECORDER, PerfRecorder} from '../../perf'; import {TypeScriptReflectionHost} from '../../reflection'; -import {HostResourceLoader} from '../../resource'; +import {AdapterResourceLoader} from '../../resource'; import {entryPointKeyFor, NgModuleRouteAnalyzer} from '../../routing'; import {ComponentScopeReader, LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope'; import {generatedFactoryTransform} from '../../shims'; @@ -30,11 +30,7 @@ import {ivySwitchTransform} from '../../switch'; import {aliasTransformFactory, declarationTransformFactory, DecoratorHandler, DtsTransformRegistry, ivyTransformFactory, TraitCompiler} from '../../transform'; import {isTemplateDiagnostic, TemplateTypeChecker, TypeCheckContext, TypeCheckingConfig, TypeCheckingProgramStrategy} from '../../typecheck'; import {getSourceFileOrNull, isDtsPath, resolveModuleName} from '../../util/src/typescript'; -import {LazyRoute, NgCompilerOptions} from '../api'; - -import {NgCompilerHost} from './host'; - - +import {LazyRoute, NgCompilerAdapter, NgCompilerOptions} from '../api'; /** * State information about a compilation which is only generated once some data is requested from @@ -94,18 +90,18 @@ export class NgCompiler { private nextProgram: ts.Program; private entryPoint: ts.SourceFile|null; private moduleResolver: ModuleResolver; - private resourceManager: HostResourceLoader; + private resourceManager: AdapterResourceLoader; private cycleAnalyzer: CycleAnalyzer; readonly incrementalDriver: IncrementalDriver; readonly ignoreForDiagnostics: Set; readonly ignoreForEmit: Set; constructor( - private host: NgCompilerHost, private options: NgCompilerOptions, + private adapter: NgCompilerAdapter, private options: NgCompilerOptions, private tsProgram: ts.Program, private typeCheckingProgramStrategy: TypeCheckingProgramStrategy, oldProgram: ts.Program|null = null, private perfRecorder: PerfRecorder = NOOP_PERF_RECORDER) { - this.constructionDiagnostics.push(...this.host.diagnostics); + this.constructionDiagnostics.push(...this.adapter.constructionDiagnostics); const incompatibleTypeCheckOptionsDiagnostic = verifyCompatibleTypeCheckOptions(this.options); if (incompatibleTypeCheckOptionsDiagnostic !== null) { this.constructionDiagnostics.push(incompatibleTypeCheckOptionsDiagnostic); @@ -115,18 +111,19 @@ export class NgCompiler { this.closureCompilerEnabled = !!this.options.annotateForClosureCompiler; this.entryPoint = - host.entryPoint !== null ? getSourceFileOrNull(tsProgram, host.entryPoint) : null; + adapter.entryPoint !== null ? getSourceFileOrNull(tsProgram, adapter.entryPoint) : null; const moduleResolutionCache = ts.createModuleResolutionCache( - this.host.getCurrentDirectory(), fileName => this.host.getCanonicalFileName(fileName)); + this.adapter.getCurrentDirectory(), + fileName => this.adapter.getCanonicalFileName(fileName)); this.moduleResolver = - new ModuleResolver(tsProgram, this.options, this.host, moduleResolutionCache); - this.resourceManager = new HostResourceLoader(host, this.options); + new ModuleResolver(tsProgram, this.options, this.adapter, moduleResolutionCache); + this.resourceManager = new AdapterResourceLoader(adapter, this.options); this.cycleAnalyzer = new CycleAnalyzer(new ImportGraph(this.moduleResolver)); let modifiedResourceFiles: Set|null = null; - if (this.host.getModifiedResourceFiles !== undefined) { - modifiedResourceFiles = this.host.getModifiedResourceFiles() || null; + if (this.adapter.getModifiedResourceFiles !== undefined) { + modifiedResourceFiles = this.adapter.getModifiedResourceFiles() || null; } if (oldProgram === null) { @@ -146,9 +143,9 @@ export class NgCompiler { setIncrementalDriver(tsProgram, this.incrementalDriver); this.ignoreForDiagnostics = - new Set(tsProgram.getSourceFiles().filter(sf => this.host.isShim(sf))); + new Set(tsProgram.getSourceFiles().filter(sf => this.adapter.isShim(sf))); - this.ignoreForEmit = this.host.ignoreForEmit; + this.ignoreForEmit = this.adapter.ignoreForEmit; } /** @@ -279,7 +276,7 @@ export class NgCompiler { const containingFile = this.tsProgram.getRootFileNames()[0]; const [entryPath, moduleName] = entryRoute.split('#'); const resolvedModule = - resolveModuleName(entryPath, containingFile, this.options, this.host, null); + resolveModuleName(entryPath, containingFile, this.options, this.adapter, null); if (resolvedModule) { entryRoute = entryPointKeyFor(resolvedModule.resolvedFileName, moduleName); @@ -326,8 +323,9 @@ export class NgCompiler { afterDeclarations.push(aliasTransformFactory(compilation.traitCompiler.exportStatements)); } - if (this.host.factoryTracker !== null) { - before.push(generatedFactoryTransform(this.host.factoryTracker.sourceInfo, importRewriter)); + if (this.adapter.factoryTracker !== null) { + before.push( + generatedFactoryTransform(this.adapter.factoryTracker.sourceInfo, importRewriter)); } before.push(ivySwitchTransform); @@ -499,7 +497,7 @@ export class NgCompiler { const typeCheckSpan = this.perfRecorder.start('typeCheckDiagnostics'); const diagnostics: ts.Diagnostic[] = []; for (const sf of this.tsProgram.getSourceFiles()) { - if (sf.isDeclarationFile || this.host.isShim(sf)) { + if (sf.isDeclarationFile || this.adapter.isShim(sf)) { continue; } @@ -600,7 +598,7 @@ export class NgCompiler { // Construct the ReferenceEmitter. let refEmitter: ReferenceEmitter; let aliasingHost: AliasingHost|null = null; - if (this.host.unifiedModulesHost === null || !this.options._useHostForImportGeneration) { + if (this.adapter.unifiedModulesHost === null || !this.options._useHostForImportGeneration) { let localImportStrategy: ReferenceEmitStrategy; // The strategy used for local, in-project imports depends on whether TS has been configured @@ -613,7 +611,7 @@ export class NgCompiler { // rootDirs logic is in effect - use the `LogicalProjectStrategy` for in-project relative // imports. localImportStrategy = new LogicalProjectStrategy( - reflector, new LogicalFileSystem([...this.host.rootDirs], this.host)); + reflector, new LogicalFileSystem([...this.adapter.rootDirs], this.adapter)); } else { // Plain relative imports are all that's needed. localImportStrategy = new RelativePathStrategy(reflector); @@ -648,9 +646,9 @@ export class NgCompiler { // Then use aliased references (this is a workaround to StrictDeps checks). new AliasStrategy(), // Then use fileNameToModuleName to emit imports. - new UnifiedModulesStrategy(reflector, this.host.unifiedModulesHost), + new UnifiedModulesStrategy(reflector, this.adapter.unifiedModulesHost), ]); - aliasingHost = new UnifiedModulesAliasingHost(this.host.unifiedModulesHost); + aliasingHost = new UnifiedModulesAliasingHost(this.adapter.unifiedModulesHost); } const evaluator = new PartialEvaluator(reflector, checker, this.incrementalDriver.depGraph); @@ -693,7 +691,7 @@ export class NgCompiler { const handlers: DecoratorHandler[] = [ new ComponentDecoratorHandler( reflector, evaluator, metaRegistry, metaReader, scopeReader, scopeRegistry, isCore, - this.resourceManager, this.host.rootDirs, this.options.preserveWhitespaces || false, + this.resourceManager, this.adapter.rootDirs, this.options.preserveWhitespaces || false, this.options.i18nUseExternalIds !== false, this.options.enableI18nLegacyMessageIdFormat !== false, this.options.i18nNormalizeLineEndingsInICUs, this.moduleResolver, this.cycleAnalyzer, @@ -721,7 +719,7 @@ export class NgCompiler { injectableRegistry), new NgModuleDecoratorHandler( reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore, - routeAnalyzer, refEmitter, this.host.factoryTracker, defaultImportTracker, + routeAnalyzer, refEmitter, this.adapter.factoryTracker, defaultImportTracker, this.closureCompilerEnabled, injectableRegistry, this.options.i18nInLocale), ]; @@ -731,7 +729,7 @@ export class NgCompiler { const templateTypeChecker = new TemplateTypeChecker( this.tsProgram, this.typeCheckingProgramStrategy, traitCompiler, - this.getTypeCheckingConfig(), refEmitter, reflector, this.host, this.incrementalDriver); + this.getTypeCheckingConfig(), refEmitter, reflector, this.adapter, this.incrementalDriver); return { isCore, diff --git a/packages/compiler-cli/src/ngtsc/core/src/host.ts b/packages/compiler-cli/src/ngtsc/core/src/host.ts index 0321974318..b7f49060e6 100644 --- a/packages/compiler-cli/src/ngtsc/core/src/host.ts +++ b/packages/compiler-cli/src/ngtsc/core/src/host.ts @@ -11,12 +11,12 @@ import * as ts from 'typescript'; import {ErrorCode, ngErrorCode} from '../../diagnostics'; import {findFlatIndexEntryPoint, FlatIndexGenerator} from '../../entry_point'; import {AbsoluteFsPath, resolve} from '../../file_system'; -import {FactoryGenerator, FactoryTracker, isShim, ShimAdapter, ShimReferenceTagger, SummaryGenerator} from '../../shims'; -import {PerFileShimGenerator, TopLevelShimGenerator} from '../../shims/api'; +import {FactoryGenerator, isShim, ShimAdapter, ShimReferenceTagger, SummaryGenerator} from '../../shims'; +import {FactoryTracker, PerFileShimGenerator, TopLevelShimGenerator} from '../../shims/api'; import {TypeCheckShimGenerator} from '../../typecheck'; import {normalizeSeparators} from '../../util/src/path'; import {getRootDirs, isDtsPath, isNonDeclarationTsPath} from '../../util/src/typescript'; -import {ExtendedTsCompilerHost, NgCompilerOptions, UnifiedModulesHost} from '../api'; +import {ExtendedTsCompilerHost, NgCompilerAdapter, NgCompilerOptions, UnifiedModulesHost} from '../api'; // A persistent source of bugs in CompilerHost delegation has been the addition by TS of new, // optional methods on ts.CompilerHost. Since these methods are optional, it's not a type error that @@ -89,10 +89,10 @@ export class DelegatingCompilerHost implements * `ExtendedTsCompilerHost` methods whenever present. */ export class NgCompilerHost extends DelegatingCompilerHost implements - RequiredCompilerHostDelegations, ExtendedTsCompilerHost { + RequiredCompilerHostDelegations, ExtendedTsCompilerHost, NgCompilerAdapter { readonly factoryTracker: FactoryTracker|null = null; readonly entryPoint: AbsoluteFsPath|null = null; - readonly diagnostics: ts.Diagnostic[]; + readonly constructionDiagnostics: ts.Diagnostic[]; readonly inputFiles: ReadonlyArray; readonly rootDirs: ReadonlyArray; @@ -107,7 +107,7 @@ export class NgCompilerHost extends DelegatingCompilerHost implements this.factoryTracker = factoryTracker; this.entryPoint = entryPoint; - this.diagnostics = diagnostics; + this.constructionDiagnostics = diagnostics; this.inputFiles = [...inputFiles, ...shimAdapter.extraInputFiles]; this.rootDirs = rootDirs; } diff --git a/packages/compiler-cli/src/ngtsc/entry_point/BUILD.bazel b/packages/compiler-cli/src/ngtsc/entry_point/BUILD.bazel index 1ac6df5c26..0bc6323756 100644 --- a/packages/compiler-cli/src/ngtsc/entry_point/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/entry_point/BUILD.bazel @@ -11,7 +11,7 @@ ts_library( deps = [ "//packages/compiler-cli/src/ngtsc/diagnostics", "//packages/compiler-cli/src/ngtsc/file_system", - "//packages/compiler-cli/src/ngtsc/shims", + "//packages/compiler-cli/src/ngtsc/shims:api", "//packages/compiler-cli/src/ngtsc/util", "@npm//@types/node", "@npm//typescript", diff --git a/packages/compiler-cli/src/ngtsc/entry_point/src/generator.ts b/packages/compiler-cli/src/ngtsc/entry_point/src/generator.ts index cd40299f4c..5713dc1055 100644 --- a/packages/compiler-cli/src/ngtsc/entry_point/src/generator.ts +++ b/packages/compiler-cli/src/ngtsc/entry_point/src/generator.ts @@ -11,7 +11,7 @@ import * as ts from 'typescript'; import {AbsoluteFsPath, dirname, join} from '../../file_system'; -import {TopLevelShimGenerator} from '../../shims'; +import {TopLevelShimGenerator} from '../../shims/api'; import {relativePathBetween} from '../../util/src/path'; export class FlatIndexGenerator implements TopLevelShimGenerator { diff --git a/packages/compiler-cli/src/ngtsc/file_system/src/logical.ts b/packages/compiler-cli/src/ngtsc/file_system/src/logical.ts index 4bd796d89e..875828c21c 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/src/logical.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/src/logical.ts @@ -59,7 +59,9 @@ export class LogicalFileSystem { */ private cache: Map = new Map(); - constructor(rootDirs: AbsoluteFsPath[], private compilerHost: ts.CompilerHost) { + constructor( + rootDirs: AbsoluteFsPath[], + private compilerHost: Pick) { // Make a copy and sort it by length in reverse order (longest first). This speeds up lookups, // since there's no need to keep going through the array once a match is found. this.rootDirs = rootDirs.concat([]).sort((a, b) => b.length - a.length); diff --git a/packages/compiler-cli/src/ngtsc/imports/src/resolver.ts b/packages/compiler-cli/src/ngtsc/imports/src/resolver.ts index a1269b1bca..59f27df0ac 100644 --- a/packages/compiler-cli/src/ngtsc/imports/src/resolver.ts +++ b/packages/compiler-cli/src/ngtsc/imports/src/resolver.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import * as ts from 'typescript'; + import {absoluteFrom} from '../../file_system'; import {getSourceFileOrNull, resolveModuleName} from '../../util/src/typescript'; @@ -18,8 +19,8 @@ import {getSourceFileOrNull, resolveModuleName} from '../../util/src/typescript' export class ModuleResolver { constructor( private program: ts.Program, private compilerOptions: ts.CompilerOptions, - private host: ts.CompilerHost, private moduleResolutionCache: ts.ModuleResolutionCache|null) { - } + private host: ts.ModuleResolutionHost&Pick, + private moduleResolutionCache: ts.ModuleResolutionCache|null) {} resolveModule(moduleName: string, containingFile: string): ts.SourceFile|null { const resolved = resolveModuleName( diff --git a/packages/compiler-cli/src/ngtsc/resource/index.ts b/packages/compiler-cli/src/ngtsc/resource/index.ts index ffcfd5f3ea..249715f182 100644 --- a/packages/compiler-cli/src/ngtsc/resource/index.ts +++ b/packages/compiler-cli/src/ngtsc/resource/index.ts @@ -6,4 +6,4 @@ * found in the LICENSE file at https://angular.io/license */ -export {HostResourceLoader} from './src/loader'; +export {AdapterResourceLoader} from './src/loader'; diff --git a/packages/compiler-cli/src/ngtsc/resource/src/loader.ts b/packages/compiler-cli/src/ngtsc/resource/src/loader.ts index f67ddc5b7e..8b94187c9d 100644 --- a/packages/compiler-cli/src/ngtsc/resource/src/loader.ts +++ b/packages/compiler-cli/src/ngtsc/resource/src/loader.ts @@ -9,26 +9,21 @@ import * as ts from 'typescript'; import {ResourceLoader} from '../../annotations'; -import {ExtendedTsCompilerHost} from '../../core/api'; +import {NgCompilerAdapter} from '../../core/api'; import {AbsoluteFsPath, join, PathSegment} from '../../file_system'; -import {getRootDirs} from '../../util/src/typescript'; const CSS_PREPROCESSOR_EXT = /(\.scss|\.sass|\.less|\.styl)$/; /** - * `ResourceLoader` which delegates to a `CompilerHost` resource loading method. + * `ResourceLoader` which delegates to an `NgCompilerAdapter`'s resource loading methods. */ -export class HostResourceLoader implements ResourceLoader { +export class AdapterResourceLoader implements ResourceLoader { private cache = new Map(); private fetching = new Map>(); - private rootDirs: AbsoluteFsPath[]; + canPreload = !!this.adapter.readResource; - canPreload = !!this.host.readResource; - - constructor(private host: ExtendedTsCompilerHost, private options: ts.CompilerOptions) { - this.rootDirs = getRootDirs(host, options); - } + constructor(private adapter: NgCompilerAdapter, private options: ts.CompilerOptions) {} /** * Resolve the url of a resource relative to the file that contains the reference to it. @@ -44,8 +39,8 @@ export class HostResourceLoader implements ResourceLoader { */ resolve(url: string, fromFile: string): string { let resolvedUrl: string|null = null; - if (this.host.resourceNameToFileName) { - resolvedUrl = this.host.resourceNameToFileName(url, fromFile); + if (this.adapter.resourceNameToFileName) { + resolvedUrl = this.adapter.resourceNameToFileName(url, fromFile); } else { resolvedUrl = this.fallbackResolve(url, fromFile); } @@ -67,7 +62,7 @@ export class HostResourceLoader implements ResourceLoader { * @throws An Error if pre-loading is not available. */ preload(resolvedUrl: string): Promise|undefined { - if (!this.host.readResource) { + if (!this.adapter.readResource) { throw new Error( 'HostResourceLoader: the CompilerHost provided does not support pre-loading resources.'); } @@ -77,7 +72,7 @@ export class HostResourceLoader implements ResourceLoader { return this.fetching.get(resolvedUrl); } - const result = this.host.readResource(resolvedUrl); + const result = this.adapter.readResource(resolvedUrl); if (typeof result === 'string') { this.cache.set(resolvedUrl, result); return undefined; @@ -104,8 +99,8 @@ export class HostResourceLoader implements ResourceLoader { return this.cache.get(resolvedUrl)!; } - const result = this.host.readResource ? this.host.readResource(resolvedUrl) : - this.host.readFile(resolvedUrl); + const result = this.adapter.readResource ? this.adapter.readResource(resolvedUrl) : + this.adapter.readFile(resolvedUrl); if (typeof result !== 'string') { throw new Error(`HostResourceLoader: loader(${resolvedUrl}) returned a Promise`); } @@ -134,7 +129,7 @@ export class HostResourceLoader implements ResourceLoader { } for (const candidate of candidateLocations) { - if (this.host.fileExists(candidate)) { + if (this.adapter.fileExists(candidate)) { return candidate; } else if (CSS_PREPROCESSOR_EXT.test(candidate)) { /** @@ -143,7 +138,7 @@ export class HostResourceLoader implements ResourceLoader { * again. */ const cssFallbackUrl = candidate.replace(CSS_PREPROCESSOR_EXT, '.css'); - if (this.host.fileExists(cssFallbackUrl)) { + if (this.adapter.fileExists(cssFallbackUrl)) { return cssFallbackUrl; } } @@ -154,7 +149,7 @@ export class HostResourceLoader implements ResourceLoader { private getRootedCandidateLocations(url: string): AbsoluteFsPath[] { // The path already starts with '/', so add a '.' to make it relative. const segment: PathSegment = ('.' + url) as PathSegment; - return this.rootDirs.map(rootDir => join(rootDir, segment)); + return this.adapter.rootDirs.map(rootDir => join(rootDir, segment)); } /** @@ -172,7 +167,7 @@ export class HostResourceLoader implements ResourceLoader { ts.ResolvedModuleWithFailedLookupLocations&{failedLookupLocations: ReadonlyArray}; // clang-format off - const failedLookup = ts.resolveModuleName(url + '.$ngresource$', fromFile, this.options, this.host) as ResolvedModuleWithFailedLookupLocations; + const failedLookup = ts.resolveModuleName(url + '.$ngresource$', fromFile, this.options, this.adapter) as ResolvedModuleWithFailedLookupLocations; // clang-format on if (failedLookup.failedLookupLocations === undefined) { throw new Error( diff --git a/packages/compiler-cli/src/ngtsc/shims/api.ts b/packages/compiler-cli/src/ngtsc/shims/api.ts index 662f2fab52..68cfbdb017 100644 --- a/packages/compiler-cli/src/ngtsc/shims/api.ts +++ b/packages/compiler-cli/src/ngtsc/shims/api.ts @@ -49,3 +49,22 @@ export interface PerFileShimGenerator { sf: ts.SourceFile, genFilePath: AbsoluteFsPath, priorShimSf: ts.SourceFile|null): ts.SourceFile; } + +/** + * Maintains a mapping of which symbols in a .ngfactory file have been used. + * + * .ngfactory files are generated with one symbol per defined class in the source file, regardless + * of whether the classes in the source files are NgModules (because that isn't known at the time + * the factory files are generated). A `FactoryTracker` supports removing factory symbols which + * didn't end up being NgModules, by tracking the ones which are. + */ +export interface FactoryTracker { + readonly sourceInfo: Map; + + track(sf: ts.SourceFile, factorySymbolName: string): void; +} + +export interface FactoryInfo { + sourceFilePath: string; + moduleSymbolNames: Set; +} \ No newline at end of file diff --git a/packages/compiler-cli/src/ngtsc/shims/index.ts b/packages/compiler-cli/src/ngtsc/shims/index.ts index 4ee4eaf408..d897845c4e 100644 --- a/packages/compiler-cli/src/ngtsc/shims/index.ts +++ b/packages/compiler-cli/src/ngtsc/shims/index.ts @@ -8,9 +8,8 @@ /// -export {PerFileShimGenerator, TopLevelShimGenerator} from './api'; export {ShimAdapter} from './src/adapter'; export {copyFileShimData, isShim} from './src/expando'; -export {FactoryGenerator, FactoryInfo, FactoryTracker, generatedFactoryTransform} from './src/factory_generator'; +export {FactoryGenerator, generatedFactoryTransform} from './src/factory_generator'; export {ShimReferenceTagger} from './src/reference_tagger'; export {SummaryGenerator} from './src/summary_generator'; diff --git a/packages/compiler-cli/src/ngtsc/shims/src/factory_generator.ts b/packages/compiler-cli/src/ngtsc/shims/src/factory_generator.ts index b22f95bd86..8e54e282a5 100644 --- a/packages/compiler-cli/src/ngtsc/shims/src/factory_generator.ts +++ b/packages/compiler-cli/src/ngtsc/shims/src/factory_generator.ts @@ -9,27 +9,13 @@ import * as ts from 'typescript'; import {absoluteFromSourceFile, AbsoluteFsPath, basename} from '../../file_system'; import {ImportRewriter} from '../../imports'; -import {PerFileShimGenerator} from '../api'; +import {FactoryInfo, FactoryTracker, PerFileShimGenerator} from '../api'; import {generatedModuleName} from './util'; const TS_DTS_SUFFIX = /(\.d)?\.ts$/; const STRIP_NG_FACTORY = /(.*)NgFactory$/; -/** - * Maintains a mapping of which symbols in a .ngfactory file have been used. - * - * .ngfactory files are generated with one symbol per defined class in the source file, regardless - * of whether the classes in the source files are NgModules (because that isn't known at the time - * the factory files are generated). A `FactoryTracker` supports removing factory symbols which - * didn't end up being NgModules, by tracking the ones which are. - */ -export interface FactoryTracker { - readonly sourceInfo: Map; - - track(sf: ts.SourceFile, factorySymbolName: string): void; -} - /** * Generates ts.SourceFiles which contain variable declarations for NgFactories for every exported * class of an input ts.SourceFile. @@ -118,11 +104,6 @@ function isExported(decl: ts.Declaration): boolean { decl.modifiers.some(mod => mod.kind == ts.SyntaxKind.ExportKeyword); } -export interface FactoryInfo { - sourceFilePath: string; - moduleSymbolNames: Set; -} - export function generatedFactoryTransform( factoryMap: Map, importRewriter: ImportRewriter): ts.TransformerFactory { diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts index 16da72f24f..0e7e4e4a57 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/checker.ts @@ -39,7 +39,7 @@ export class TemplateTypeChecker { private typeCheckingStrategy: TypeCheckingProgramStrategy, private typeCheckAdapter: ProgramTypeCheckAdapter, private config: TypeCheckingConfig, private refEmitter: ReferenceEmitter, private reflector: ReflectionHost, - private compilerHost: ts.CompilerHost, + private compilerHost: Pick, private priorBuild: IncrementalBuild) {} /** diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/context.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/context.ts index 64347460b1..412ab61ded 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/context.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/context.ts @@ -113,7 +113,8 @@ export class TypeCheckContext { private fileMap = new Map(); constructor( - private config: TypeCheckingConfig, private compilerHost: ts.CompilerHost, + private config: TypeCheckingConfig, + private compilerHost: Pick, private refEmitter: ReferenceEmitter, private reflector: ReflectionHost) {} /** diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_file.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_file.ts index c97a5f02c3..af0eaab646 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_file.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_file.ts @@ -34,7 +34,7 @@ export class TypeCheckFile extends Environment { constructor( readonly fileName: AbsoluteFsPath, config: TypeCheckingConfig, refEmitter: ReferenceEmitter, - reflector: ReflectionHost, compilerHost: ts.CompilerHost) { + reflector: ReflectionHost, compilerHost: Pick) { super( config, new ImportManager(new NoopImportRewriter(), 'i'), refEmitter, reflector, ts.createSourceFile( diff --git a/packages/compiler-cli/src/ngtsc/util/src/typescript.ts b/packages/compiler-cli/src/ngtsc/util/src/typescript.ts index 84a6aa34ba..ee64a577ec 100644 --- a/packages/compiler-cli/src/ngtsc/util/src/typescript.ts +++ b/packages/compiler-cli/src/ngtsc/util/src/typescript.ts @@ -122,7 +122,7 @@ export function nodeDebugInfo(node: ts.Node): string { */ export function resolveModuleName( moduleName: string, containingFile: string, compilerOptions: ts.CompilerOptions, - compilerHost: ts.CompilerHost, + compilerHost: ts.ModuleResolutionHost&Pick, moduleResolutionCache: ts.ModuleResolutionCache|null): ts.ResolvedModule|undefined { if (compilerHost.resolveModuleNames) { // FIXME: Additional parameters are required in TS3.6, but ignored in 3.5.