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:
parent
965a688c97
commit
e648a0c4ca
|
@ -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",
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -6,4 +6,4 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export {HostResourceLoader} from './src/loader';
|
||||
export {AdapterResourceLoader} from './src/loader';
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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>;
|
||||
}
|
|
@ -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';
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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>) {}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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) {}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue