feat(ngcc): enable private NgModule re-exports in ngcc on request (#33177)

This commit adapts the private NgModule re-export system (using aliasing) to
ngcc. Not all ngcc compilations are compatible with these re-exports, as
they assume a 1:1 correspondence between .js and .d.ts files. The primary
concern here is supporting them for commonjs-only packages.

PR Close #33177
This commit is contained in:
Alex Rickabaugh
2019-10-14 13:04:42 -07:00
committed by Matias Niemelä
parent c4733c15c0
commit e030375d9a
17 changed files with 287 additions and 11 deletions

View File

@ -11,9 +11,10 @@ import {BaseDefDecoratorHandler, ComponentDecoratorHandler, DirectiveDecoratorHa
import {CycleAnalyzer, ImportGraph} from '../../../src/ngtsc/cycles';
import {isFatalDiagnosticError} from '../../../src/ngtsc/diagnostics';
import {FileSystem, LogicalFileSystem, absoluteFrom, dirname, resolve} from '../../../src/ngtsc/file_system';
import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../../src/ngtsc/imports';
import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, PrivateExportAliasingHost, Reexport, ReferenceEmitter} from '../../../src/ngtsc/imports';
import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, LocalMetadataRegistry} from '../../../src/ngtsc/metadata';
import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator';
import {ClassDeclaration} from '../../../src/ngtsc/reflection';
import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../../src/ngtsc/scope';
import {CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence} from '../../../src/ngtsc/transform';
import {NgccClassSymbol, NgccReflectionHost} from '../host/ngcc_host';
@ -48,6 +49,11 @@ export class DecorationAnalyzer {
private rootDirs = this.bundle.rootDirs;
private packagePath = this.bundle.entryPoint.package;
private isCore = this.bundle.isCore;
/**
* Map of NgModule declarations to the re-exports for that NgModule.
*/
private reexportMap = new Map<ts.Declaration, Map<string, [string, string]>>();
resourceManager = new NgccResourceLoader(this.fs);
metaRegistry = new LocalMetadataRegistry();
dtsMetaReader = new DtsMetadataReader(this.typeChecker, this.reflectionHost);
@ -61,10 +67,12 @@ export class DecorationAnalyzer {
// based on whether a bestGuessOwningModule is present in the Reference.
new LogicalProjectStrategy(this.reflectionHost, new LogicalFileSystem(this.rootDirs)),
]);
aliasingHost = this.bundle.entryPoint.generateDeepReexports?
new PrivateExportAliasingHost(this.reflectionHost): null;
dtsModuleScopeResolver =
new MetadataDtsModuleScopeResolver(this.dtsMetaReader, /* aliasGenerator */ null);
new MetadataDtsModuleScopeResolver(this.dtsMetaReader, this.aliasingHost);
scopeRegistry = new LocalModuleScopeRegistry(
this.metaRegistry, this.dtsModuleScopeResolver, this.refEmitter, /* aliasGenerator */ null);
this.metaRegistry, this.dtsModuleScopeResolver, this.refEmitter, this.aliasingHost);
fullRegistry = new CompoundMetadataRegistry([this.metaRegistry, this.scopeRegistry]);
evaluator = new PartialEvaluator(this.reflectionHost, this.typeChecker);
moduleResolver = new ModuleResolver(this.program, this.options, this.host);
@ -161,7 +169,9 @@ export class DecorationAnalyzer {
const constantPool = new ConstantPool();
const compiledClasses: CompiledClass[] = analyzedFile.analyzedClasses.map(analyzedClass => {
const compilation = this.compileClass(analyzedClass, constantPool);
return {...analyzedClass, compilation};
const declaration = analyzedClass.declaration;
const reexports: Reexport[] = this.getReexportsForClass(declaration);
return {...analyzedClass, compilation, reexports};
});
return {constantPool, sourceFile: analyzedFile.sourceFile, compiledClasses};
}
@ -187,9 +197,30 @@ export class DecorationAnalyzer {
analyzedFile.analyzedClasses.forEach(({declaration, matches}) => {
matches.forEach(({handler, analysis}) => {
if ((handler.resolve !== undefined) && analysis) {
handler.resolve(declaration, analysis);
const res = handler.resolve(declaration, analysis);
if (res.reexports !== undefined) {
this.addReexports(res.reexports, declaration);
}
}
});
});
}
private getReexportsForClass(declaration: ClassDeclaration<ts.Declaration>) {
const reexports: Reexport[] = [];
if (this.reexportMap.has(declaration)) {
this.reexportMap.get(declaration) !.forEach(([fromModule, symbolName], asAlias) => {
reexports.push({asAlias, fromModule, symbolName});
});
}
return reexports;
}
private addReexports(reexports: Reexport[], declaration: ClassDeclaration<ts.Declaration>) {
const map = new Map<string, [string, string]>();
for (const reexport of reexports) {
map.set(reexport.asAlias, [reexport.fromModule, reexport.symbolName]);
}
this.reexportMap.set(declaration, map);
}
}

View File

@ -7,6 +7,7 @@
*/
import {ConstantPool} from '@angular/compiler';
import * as ts from 'typescript';
import {Reexport} from '../../../src/ngtsc/imports';
import {ClassDeclaration, Decorator} from '../../../src/ngtsc/reflection';
import {CompileResult, DecoratorHandler} from '../../../src/ngtsc/transform';
@ -23,7 +24,14 @@ export interface AnalyzedClass {
matches: {handler: DecoratorHandler<any, any>; analysis: any;}[];
}
export interface CompiledClass extends AnalyzedClass { compilation: CompileResult[]; }
export interface CompiledClass extends AnalyzedClass {
compilation: CompileResult[];
/**
* Any re-exports which should be added next to this class, both in .js and (if possible) .d.ts.
*/
reexports: Reexport[];
}
export interface CompiledFile {
compiledClasses: CompiledClass[];