feat(compiler): generate shallow imports compiler generated references (#14388)
This commit is contained in:

committed by
Igor Minar

parent
e4e9dbe33d
commit
8b81bb1eb6
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import {AotCompiler, AotCompilerHost, createAotCompiler} from '@angular/compiler';
|
||||
import {RenderComponentType} from '@angular/core';
|
||||
import {async} from '@angular/core/testing';
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
@ -16,20 +17,26 @@ import {EmittingCompilerHost, MockAotCompilerHost, MockCompilerHost, MockData, s
|
||||
|
||||
const DTS = /\.d\.ts$/;
|
||||
|
||||
// These are the files that contain the well known annotations.
|
||||
const CORE_FILES = [
|
||||
'@angular/core/src/metadata.ts', '@angular/core/src/di/metadata.ts',
|
||||
'@angular/core/src/di/injection_token.ts', '@angular/core/src/animation/metadata.ts',
|
||||
'@angular/core/src/di/provider.ts', '@angular/core/src/linker/view.ts'
|
||||
];
|
||||
const minCoreIndex = `
|
||||
export * from './src/metadata';
|
||||
export * from './src/di/metadata';
|
||||
export * from './src/di/injector';
|
||||
export * from './src/di/injection_token';
|
||||
export * from './src/animation/metadata';
|
||||
export * from './src/linker';
|
||||
export * from './src/render';
|
||||
export * from './src/codegen_private_exports';
|
||||
`;
|
||||
|
||||
describe('compiler', () => {
|
||||
let angularFiles: Map<string, string>;
|
||||
|
||||
beforeAll(() => {
|
||||
const emittingHost = new EmittingCompilerHost(CORE_FILES);
|
||||
const emittingHost = new EmittingCompilerHost([], {emitMetadata: true});
|
||||
emittingHost.addScript('@angular/core/index.ts', minCoreIndex);
|
||||
const emittingProgram = ts.createProgram(emittingHost.scripts, settings, emittingHost);
|
||||
emittingProgram.emit();
|
||||
|
||||
angularFiles = emittingHost.written;
|
||||
});
|
||||
|
||||
@ -147,7 +154,8 @@ function summaryCompile(
|
||||
function compile(
|
||||
host: MockCompilerHost, aotHost: AotCompilerHost, preCompile?: (program: ts.Program) => void,
|
||||
postCompile: (program: ts.Program) => void = expectNoDiagnostics) {
|
||||
const program = ts.createProgram(host.scriptNames, settings, host);
|
||||
const scripts = host.scriptNames.slice(0);
|
||||
const program = ts.createProgram(scripts, settings, host);
|
||||
if (preCompile) preCompile(program);
|
||||
const {compiler, reflector} = createAotCompiler(aotHost, {});
|
||||
return compiler.compileAll(program.getSourceFiles().map(sf => sf.fileName))
|
||||
@ -155,7 +163,8 @@ function compile(
|
||||
generatedFiles.forEach(
|
||||
file => isSource(file.genFileUrl) ? host.addScript(file.genFileUrl, file.source) :
|
||||
host.override(file.genFileUrl, file.source));
|
||||
const newProgram = ts.createProgram(host.scriptNames, settings, host, program);
|
||||
const scripts = host.scriptNames.slice(0);
|
||||
const newProgram = ts.createProgram(scripts, settings, host);
|
||||
if (postCompile) postCompile(newProgram);
|
||||
return generatedFiles;
|
||||
});
|
||||
@ -166,7 +175,7 @@ const FILES: MockData = {
|
||||
quickstart: {
|
||||
app: {
|
||||
'app.component.ts': `
|
||||
import {Component} from '@angular/core/src/metadata';
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: '<h1>Hello {{name}}</h1>'
|
||||
@ -176,7 +185,7 @@ const FILES: MockData = {
|
||||
}
|
||||
`,
|
||||
'app.module.ts': `
|
||||
import { NgModule } from '@angular/core/src/metadata';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
|
@ -41,15 +41,18 @@ export const settings: ts.CompilerOptions = {
|
||||
types: []
|
||||
};
|
||||
|
||||
export interface EmitterOptions { emitMetadata: boolean; }
|
||||
|
||||
export class EmittingCompilerHost implements ts.CompilerHost {
|
||||
private angularSourcePath: string|undefined;
|
||||
private nodeModulesPath: string|undefined;
|
||||
private addedFiles = new Map<string, string>();
|
||||
private writtenFiles = new Map<string, string>();
|
||||
private scriptNames: string[];
|
||||
private root = '/';
|
||||
private collector = new MetadataCollector();
|
||||
|
||||
constructor(scriptNames: string[]) {
|
||||
constructor(scriptNames: string[], private options: EmitterOptions) {
|
||||
const moduleFilename = module.filename.replace(/\\/g, '/');
|
||||
const distIndex = moduleFilename.indexOf('/dist/all');
|
||||
if (distIndex >= 0) {
|
||||
@ -59,13 +62,27 @@ export class EmittingCompilerHost implements ts.CompilerHost {
|
||||
|
||||
// Rewrite references to scripts with '@angular' to its corresponding location in
|
||||
// the source tree.
|
||||
this.scriptNames = scriptNames.map(
|
||||
f => f.startsWith('@angular/') ? path.join(this.angularSourcePath, f) : f);
|
||||
this.scriptNames = scriptNames.map(f => this.effectiveName(f));
|
||||
|
||||
this.root = root;
|
||||
}
|
||||
}
|
||||
|
||||
public addScript(fileName: string, content: string) {
|
||||
const scriptName = this.effectiveName(fileName);
|
||||
this.addedFiles.set(scriptName, content);
|
||||
this.scriptNames.push(scriptName);
|
||||
}
|
||||
|
||||
public override(fileName: string, content: string) {
|
||||
const scriptName = this.effectiveName(fileName);
|
||||
this.addedFiles.set(scriptName, content);
|
||||
}
|
||||
|
||||
public addWrittenFile(fileName: string, content: string) {
|
||||
this.writtenFiles.set(this.effectiveName(fileName), content);
|
||||
}
|
||||
|
||||
public getWrittenFiles(): {name: string, content: string}[] {
|
||||
return Array.from(this.writtenFiles).map(f => ({name: f[0], content: f[1]}));
|
||||
}
|
||||
@ -74,10 +91,19 @@ export class EmittingCompilerHost implements ts.CompilerHost {
|
||||
|
||||
public get written(): Map<string, string> { return this.writtenFiles; }
|
||||
|
||||
public effectiveName(fileName: string): string {
|
||||
return fileName.startsWith('@angular/') ? path.join(this.angularSourcePath, fileName) :
|
||||
fileName;
|
||||
}
|
||||
|
||||
// ts.ModuleResolutionHost
|
||||
fileExists(fileName: string): boolean { return fs.existsSync(fileName); }
|
||||
fileExists(fileName: string): boolean {
|
||||
return this.addedFiles.has(fileName) || fs.existsSync(fileName);
|
||||
}
|
||||
|
||||
readFile(fileName: string): string {
|
||||
const result = this.addedFiles.get(fileName);
|
||||
if (result) return result;
|
||||
let basename = path.basename(fileName);
|
||||
if (/^lib.*\.d\.ts$/.test(basename)) {
|
||||
let libPath = ts.getDefaultLibFilePath(settings);
|
||||
@ -106,7 +132,7 @@ export class EmittingCompilerHost implements ts.CompilerHost {
|
||||
onError?: (message: string) => void): ts.SourceFile {
|
||||
const content = this.readFile(fileName);
|
||||
if (content) {
|
||||
return ts.createSourceFile(fileName, content, languageVersion);
|
||||
return ts.createSourceFile(fileName, content, languageVersion, /* setParentNodes */ true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,11 +141,13 @@ export class EmittingCompilerHost implements ts.CompilerHost {
|
||||
writeFile: ts.WriteFileCallback =
|
||||
(fileName: string, data: string, writeByteOrderMark: boolean,
|
||||
onError?: (message: string) => void, sourceFiles?: ts.SourceFile[]) => {
|
||||
this.writtenFiles.set(fileName, data);
|
||||
if (sourceFiles && sourceFiles.length && DTS.test(fileName)) {
|
||||
this.addWrittenFile(fileName, data);
|
||||
if (this.options.emitMetadata && sourceFiles && sourceFiles.length && DTS.test(fileName)) {
|
||||
const metadataFilePath = fileName.replace(DTS, '.metadata.json');
|
||||
const metadata = this.collector.getMetadata(sourceFiles[0]);
|
||||
if (metadata) this.writtenFiles.set(metadataFilePath, JSON.stringify(metadata));
|
||||
if (metadata) {
|
||||
this.addWrittenFile(metadataFilePath, JSON.stringify(metadata));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user