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
This commit is contained in:
Alex Rickabaugh 2020-05-14 11:24:59 -07:00 committed by atscott
parent 965a688c97
commit e648a0c4ca
20 changed files with 185 additions and 88 deletions

View File

@ -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",

View File

@ -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';

View File

@ -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",
],
)

View File

@ -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';

View File

@ -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<ts.ModuleResolutionHost, 'getCurrentDirectory'>,
Pick<ExtendedTsCompilerHost, 'getCurrentDirectory'|ExtendedCompilerHostMethods> {
/**
* 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<ts.SourceFile>;
/**
* 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<AbsoluteFsPath>;
/**
* 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;
}

View File

@ -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<ts.SourceFile>;
readonly ignoreForEmit: Set<ts.SourceFile>;
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<string>|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<unknown, unknown, unknown>[] = [
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,

View File

@ -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<string>;
readonly rootDirs: ReadonlyArray<AbsoluteFsPath>;
@ -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;
}

View File

@ -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",

View File

@ -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 {

View File

@ -59,7 +59,9 @@ export class LogicalFileSystem {
*/
private cache: Map<AbsoluteFsPath, LogicalProjectPath|null> = new Map();
constructor(rootDirs: AbsoluteFsPath[], private compilerHost: ts.CompilerHost) {
constructor(
rootDirs: AbsoluteFsPath[],
private compilerHost: Pick<ts.CompilerHost, 'getCanonicalFileName'>) {
// 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);

View File

@ -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<ts.CompilerHost, 'resolveModuleNames'>,
private moduleResolutionCache: ts.ModuleResolutionCache|null) {}
resolveModule(moduleName: string, containingFile: string): ts.SourceFile|null {
const resolved = resolveModuleName(

View File

@ -6,4 +6,4 @@
* found in the LICENSE file at https://angular.io/license
*/
export {HostResourceLoader} from './src/loader';
export {AdapterResourceLoader} from './src/loader';

View File

@ -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<string, string>();
private fetching = new Map<string, Promise<void>>();
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<void>|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<string>};
// 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(

View File

@ -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<string, FactoryInfo>;
track(sf: ts.SourceFile, factorySymbolName: string): void;
}
export interface FactoryInfo {
sourceFilePath: string;
moduleSymbolNames: Set<string>;
}

View File

@ -8,9 +8,8 @@
/// <reference types="node" />
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';

View File

@ -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<string, FactoryInfo>;
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<string>;
}
export function generatedFactoryTransform(
factoryMap: Map<string, FactoryInfo>,
importRewriter: ImportRewriter): ts.TransformerFactory<ts.SourceFile> {

View File

@ -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<ts.CompilerHost, 'getCanonicalFileName'>,
private priorBuild: IncrementalBuild<unknown, FileTypeCheckingData>) {}
/**

View File

@ -113,7 +113,8 @@ export class TypeCheckContext {
private fileMap = new Map<AbsoluteFsPath, PendingFileTypeCheckingData>();
constructor(
private config: TypeCheckingConfig, private compilerHost: ts.CompilerHost,
private config: TypeCheckingConfig,
private compilerHost: Pick<ts.CompilerHost, 'getCanonicalFileName'>,
private refEmitter: ReferenceEmitter, private reflector: ReflectionHost) {}
/**

View File

@ -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<ts.CompilerHost, 'getCanonicalFileName'>) {
super(
config, new ImportManager(new NoopImportRewriter(), 'i'), refEmitter, reflector,
ts.createSourceFile(

View File

@ -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<ts.CompilerHost, 'resolveModuleNames'>,
moduleResolutionCache: ts.ModuleResolutionCache|null): ts.ResolvedModule|undefined {
if (compilerHost.resolveModuleNames) {
// FIXME: Additional parameters are required in TS3.6, but ignored in 3.5.