fix(compiler): only don’t emit already emitted files in incremental compilation
This commit is contained in:
parent
f3f4c3d835
commit
caa51950e8
|
@ -318,4 +318,9 @@ export interface Program {
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
getEmittedGeneratedFiles(): Map<string, GeneratedFile>;
|
getEmittedGeneratedFiles(): Map<string, GeneratedFile>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
getEmittedSourceFiles(): Map<string, ts.SourceFile>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,16 +42,17 @@ class AngularCompilerProgram implements Program {
|
||||||
private metadataCache: LowerMetadataCache;
|
private metadataCache: LowerMetadataCache;
|
||||||
private oldProgramLibrarySummaries: Map<string, LibrarySummary>|undefined;
|
private oldProgramLibrarySummaries: Map<string, LibrarySummary>|undefined;
|
||||||
private oldProgramEmittedGeneratedFiles: Map<string, GeneratedFile>|undefined;
|
private oldProgramEmittedGeneratedFiles: Map<string, GeneratedFile>|undefined;
|
||||||
|
private oldProgramEmittedSourceFiles: Map<string, ts.SourceFile>|undefined;
|
||||||
// Note: This will be cleared out as soon as we create the _tsProgram
|
// Note: This will be cleared out as soon as we create the _tsProgram
|
||||||
private oldTsProgram: ts.Program|undefined;
|
private oldTsProgram: ts.Program|undefined;
|
||||||
private emittedLibrarySummaries: LibrarySummary[]|undefined;
|
private emittedLibrarySummaries: LibrarySummary[]|undefined;
|
||||||
private emittedGeneratedFiles: GeneratedFile[]|undefined;
|
private emittedGeneratedFiles: GeneratedFile[]|undefined;
|
||||||
|
private emittedSourceFiles: ts.SourceFile[]|undefined;
|
||||||
|
|
||||||
// Lazily initialized fields
|
// Lazily initialized fields
|
||||||
private _typeCheckHost: TypeCheckHost;
|
private _typeCheckHost: TypeCheckHost;
|
||||||
private _compiler: AotCompiler;
|
private _compiler: AotCompiler;
|
||||||
private _tsProgram: ts.Program;
|
private _tsProgram: ts.Program;
|
||||||
private _changedNonGenFileNames: string[]|undefined;
|
|
||||||
private _analyzedModules: NgAnalyzedModules|undefined;
|
private _analyzedModules: NgAnalyzedModules|undefined;
|
||||||
private _structuralDiagnostics: Diagnostic[]|undefined;
|
private _structuralDiagnostics: Diagnostic[]|undefined;
|
||||||
private _programWithStubs: ts.Program|undefined;
|
private _programWithStubs: ts.Program|undefined;
|
||||||
|
@ -69,6 +70,7 @@ class AngularCompilerProgram implements Program {
|
||||||
if (oldProgram) {
|
if (oldProgram) {
|
||||||
this.oldProgramLibrarySummaries = oldProgram.getLibrarySummaries();
|
this.oldProgramLibrarySummaries = oldProgram.getLibrarySummaries();
|
||||||
this.oldProgramEmittedGeneratedFiles = oldProgram.getEmittedGeneratedFiles();
|
this.oldProgramEmittedGeneratedFiles = oldProgram.getEmittedGeneratedFiles();
|
||||||
|
this.oldProgramEmittedSourceFiles = oldProgram.getEmittedSourceFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.flatModuleOutFile) {
|
if (options.flatModuleOutFile) {
|
||||||
|
@ -114,6 +116,17 @@ class AngularCompilerProgram implements Program {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getEmittedSourceFiles(): Map<string, ts.SourceFile> {
|
||||||
|
const result = new Map<string, ts.SourceFile>();
|
||||||
|
if (this.oldProgramEmittedSourceFiles) {
|
||||||
|
this.oldProgramEmittedSourceFiles.forEach((sf, fileName) => result.set(fileName, sf));
|
||||||
|
}
|
||||||
|
if (this.emittedSourceFiles) {
|
||||||
|
this.emittedSourceFiles.forEach((sf) => result.set(sf.fileName, sf));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
getTsProgram(): ts.Program { return this.tsProgram; }
|
getTsProgram(): ts.Program { return this.tsProgram; }
|
||||||
|
|
||||||
getTsOptionDiagnostics(cancellationToken?: ts.CancellationToken) {
|
getTsOptionDiagnostics(cancellationToken?: ts.CancellationToken) {
|
||||||
|
@ -192,6 +205,7 @@ class AngularCompilerProgram implements Program {
|
||||||
const genFileByFileName = new Map<string, GeneratedFile>();
|
const genFileByFileName = new Map<string, GeneratedFile>();
|
||||||
genFiles.forEach(genFile => genFileByFileName.set(genFile.genFileUrl, genFile));
|
genFiles.forEach(genFile => genFileByFileName.set(genFile.genFileUrl, genFile));
|
||||||
this.emittedLibrarySummaries = [];
|
this.emittedLibrarySummaries = [];
|
||||||
|
const emittedSourceFiles = [] as ts.SourceFile[];
|
||||||
const writeTsFile: ts.WriteFileCallback =
|
const writeTsFile: ts.WriteFileCallback =
|
||||||
(outFileName, outData, writeByteOrderMark, onError?, sourceFiles?) => {
|
(outFileName, outData, writeByteOrderMark, onError?, sourceFiles?) => {
|
||||||
const sourceFile = sourceFiles && sourceFiles.length == 1 ? sourceFiles[0] : null;
|
const sourceFile = sourceFiles && sourceFiles.length == 1 ? sourceFiles[0] : null;
|
||||||
|
@ -199,6 +213,10 @@ class AngularCompilerProgram implements Program {
|
||||||
if (sourceFile) {
|
if (sourceFile) {
|
||||||
outSrcMapping.push({outFileName: outFileName, sourceFile});
|
outSrcMapping.push({outFileName: outFileName, sourceFile});
|
||||||
genFile = genFileByFileName.get(sourceFile.fileName);
|
genFile = genFileByFileName.get(sourceFile.fileName);
|
||||||
|
if (!sourceFile.isDeclarationFile && !GENERATED_FILES.test(sourceFile.fileName)) {
|
||||||
|
// Note: sourceFile is the transformed sourcefile, not the original one!
|
||||||
|
emittedSourceFiles.push(this.tsProgram.getSourceFile(sourceFile.fileName));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.writeFile(outFileName, outData, writeByteOrderMark, onError, genFile, sourceFiles);
|
this.writeFile(outFileName, outData, writeByteOrderMark, onError, genFile, sourceFiles);
|
||||||
};
|
};
|
||||||
|
@ -227,12 +245,11 @@ class AngularCompilerProgram implements Program {
|
||||||
let emitResult: ts.EmitResult;
|
let emitResult: ts.EmitResult;
|
||||||
let emittedUserTsCount: number;
|
let emittedUserTsCount: number;
|
||||||
try {
|
try {
|
||||||
const useSingleFileEmit = this._changedNonGenFileNames &&
|
const sourceFilesToEmit = this.getSourceFilesForEmit();
|
||||||
(this._changedNonGenFileNames.length + genTsFiles.length) <
|
if (sourceFilesToEmit &&
|
||||||
MAX_FILE_COUNT_FOR_SINGLE_FILE_EMIT;
|
(sourceFilesToEmit.length + genTsFiles.length) < MAX_FILE_COUNT_FOR_SINGLE_FILE_EMIT) {
|
||||||
if (useSingleFileEmit) {
|
|
||||||
const fileNamesToEmit =
|
const fileNamesToEmit =
|
||||||
[...this._changedNonGenFileNames !, ...genTsFiles.map(gf => gf.genFileUrl)];
|
[...sourceFilesToEmit.map(sf => sf.fileName), ...genTsFiles.map(gf => gf.genFileUrl)];
|
||||||
emitResult = mergeEmitResults(
|
emitResult = mergeEmitResults(
|
||||||
fileNamesToEmit.map((fileName) => emitResult = emitCallback({
|
fileNamesToEmit.map((fileName) => emitResult = emitCallback({
|
||||||
program: this.tsProgram,
|
program: this.tsProgram,
|
||||||
|
@ -242,7 +259,7 @@ class AngularCompilerProgram implements Program {
|
||||||
customTransformers: tsCustomTansformers,
|
customTransformers: tsCustomTansformers,
|
||||||
targetSourceFile: this.tsProgram.getSourceFile(fileName),
|
targetSourceFile: this.tsProgram.getSourceFile(fileName),
|
||||||
})));
|
})));
|
||||||
emittedUserTsCount = this._changedNonGenFileNames !.length;
|
emittedUserTsCount = sourceFilesToEmit.length;
|
||||||
} else {
|
} else {
|
||||||
emitResult = emitCallback({
|
emitResult = emitCallback({
|
||||||
program: this.tsProgram,
|
program: this.tsProgram,
|
||||||
|
@ -260,6 +277,7 @@ class AngularCompilerProgram implements Program {
|
||||||
sourceFile.referencedFiles = references;
|
sourceFile.referencedFiles = references;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.emittedSourceFiles = emittedSourceFiles;
|
||||||
|
|
||||||
if (!outSrcMapping.length) {
|
if (!outSrcMapping.length) {
|
||||||
// if no files were emitted by TypeScript, also don't emit .json files
|
// if no files were emitted by TypeScript, also don't emit .json files
|
||||||
|
@ -419,16 +437,6 @@ class AngularCompilerProgram implements Program {
|
||||||
sourceFiles.push(sf.fileName);
|
sourceFiles.push(sf.fileName);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (oldTsProgram) {
|
|
||||||
// TODO(tbosch): if one of the files contains a `const enum`
|
|
||||||
// always emit all files!
|
|
||||||
const changedNonGenFileNames = this._changedNonGenFileNames = [] as string[];
|
|
||||||
tmpProgram.getSourceFiles().forEach(sf => {
|
|
||||||
if (!GENERATED_FILES.test(sf.fileName) && oldTsProgram.getSourceFile(sf.fileName) !== sf) {
|
|
||||||
changedNonGenFileNames.push(sf.fileName);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return {tmpProgram, sourceFiles, hostAdapter, rootNames};
|
return {tmpProgram, sourceFiles, hostAdapter, rootNames};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -522,6 +530,22 @@ class AngularCompilerProgram implements Program {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns undefined if all files should be emitted.
|
||||||
|
*/
|
||||||
|
private getSourceFilesForEmit(): ts.SourceFile[]|undefined {
|
||||||
|
// TODO(tbosch): if one of the files contains a `const enum`
|
||||||
|
// always emit all files -> return undefined!
|
||||||
|
let sourceFilesToEmit: ts.SourceFile[]|undefined;
|
||||||
|
if (this.oldProgramEmittedSourceFiles) {
|
||||||
|
sourceFilesToEmit = this.tsProgram.getSourceFiles().filter(sf => {
|
||||||
|
const oldFile = this.oldProgramEmittedSourceFiles !.get(sf.fileName);
|
||||||
|
return !sf.isDeclarationFile && !GENERATED_FILES.test(sf.fileName) && sf !== oldFile;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return sourceFilesToEmit;
|
||||||
|
}
|
||||||
|
|
||||||
private generateSemanticDiagnostics(): {ts: ts.Diagnostic[], ng: Diagnostic[]} {
|
private generateSemanticDiagnostics(): {ts: ts.Diagnostic[], ng: Diagnostic[]} {
|
||||||
return translateDiagnostics(this.typeCheckHost, this.tsProgram.getSemanticDiagnostics());
|
return translateDiagnostics(this.typeCheckHost, this.tsProgram.getSemanticDiagnostics());
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,7 +180,7 @@ describe('ng program', () => {
|
||||||
// compile libraries
|
// compile libraries
|
||||||
const p1 = compile(undefined, options, undefined, host).program;
|
const p1 = compile(undefined, options, undefined, host).program;
|
||||||
|
|
||||||
// first compile without libraries
|
// compile without libraries
|
||||||
const p2 = compile(p1, options, undefined, host).program;
|
const p2 = compile(p1, options, undefined, host).program;
|
||||||
expect(written.has(path.resolve(testSupport.basePath, 'built/src/index.js'))).toBe(true);
|
expect(written.has(path.resolve(testSupport.basePath, 'built/src/index.js'))).toBe(true);
|
||||||
let ngFactoryContent =
|
let ngFactoryContent =
|
||||||
|
@ -202,11 +202,22 @@ describe('ng program', () => {
|
||||||
// change a file that is input to generated files
|
// change a file that is input to generated files
|
||||||
written.clear();
|
written.clear();
|
||||||
testSupport.writeFiles({'src/index.html': 'Hello'});
|
testSupport.writeFiles({'src/index.html': 'Hello'});
|
||||||
compile(p4, options, undefined, host);
|
const p5 = compile(p4, options, undefined, host).program;
|
||||||
expect(written.size).toBe(1);
|
expect(written.size).toBe(1);
|
||||||
ngFactoryContent =
|
ngFactoryContent =
|
||||||
written.get(path.resolve(testSupport.basePath, 'built/src/index.ngfactory.js'));
|
written.get(path.resolve(testSupport.basePath, 'built/src/index.ngfactory.js'));
|
||||||
expect(ngFactoryContent).toMatch(/Hello/);
|
expect(ngFactoryContent).toMatch(/Hello/);
|
||||||
|
|
||||||
|
// change a file and create an intermediate program that is not emitted
|
||||||
|
written.clear();
|
||||||
|
fileCache.delete(path.resolve(testSupport.basePath, 'src/index.ts'));
|
||||||
|
const p6 = ng.createProgram({
|
||||||
|
rootNames: [path.resolve(testSupport.basePath, 'src/index.ts')],
|
||||||
|
options: testSupport.createCompilerOptions(options), host,
|
||||||
|
oldProgram: p5
|
||||||
|
});
|
||||||
|
const p7 = compile(p6, options, undefined, host).program;
|
||||||
|
expect(written.size).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set emitSkipped to false for full and incremental emit', () => {
|
it('should set emitSkipped to false for full and incremental emit', () => {
|
||||||
|
|
Loading…
Reference in New Issue