Revert "Revert "perf(compiler): skip type check and emit in bazel in some cases. (#19646)""
This reverts commit 94a925a1b0
.
This commit is contained in:
parent
94a925a1b0
commit
6b7cead0c5
|
@ -7,6 +7,7 @@ ts_library(
|
||||||
name = "ngc_lib",
|
name = "ngc_lib",
|
||||||
srcs = [
|
srcs = [
|
||||||
"index.ts",
|
"index.ts",
|
||||||
|
"emit_cache.ts",
|
||||||
"extract_i18n.ts",
|
"extract_i18n.ts",
|
||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
/**
|
||||||
|
* @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 ng from '@angular/compiler-cli';
|
||||||
|
import {CompilerHost, debug, fixUmdModuleDeclarations} from '@bazel/typescript';
|
||||||
|
import * as tsickle from 'tsickle';
|
||||||
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
|
interface EmitCacheEntry {
|
||||||
|
emitResult: tsickle.EmitResult;
|
||||||
|
writtenFiles: Array<{fileName: string, content: string, sourceFiles?: ts.SourceFile[]}>;
|
||||||
|
generatedFile: ng.GeneratedFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SourceFileWithEmitCache extends ts.SourceFile {
|
||||||
|
emitCache: Map<string, EmitCacheEntry>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCache(sf: ts.SourceFile, genFileName?: string): EmitCacheEntry|undefined {
|
||||||
|
const emitCache = (sf as SourceFileWithEmitCache).emitCache;
|
||||||
|
return emitCache ? emitCache.get(genFileName || sf.fileName) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCache(sf: ts.SourceFile, entry: EmitCacheEntry) {
|
||||||
|
let emitCache = (sf as SourceFileWithEmitCache).emitCache;
|
||||||
|
if (!emitCache) {
|
||||||
|
emitCache = new Map();
|
||||||
|
(sf as SourceFileWithEmitCache).emitCache = emitCache;
|
||||||
|
}
|
||||||
|
emitCache.set(entry.generatedFile ? entry.generatedFile.genFileName : sf.fileName, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCachedGeneratedFile(sf: ts.SourceFile, genFileName: string): ng.GeneratedFile|
|
||||||
|
undefined {
|
||||||
|
const cacheEntry = getCache(sf, genFileName);
|
||||||
|
return cacheEntry ? cacheEntry.generatedFile : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function emitWithCache(
|
||||||
|
program: ng.Program, inputsChanged: boolean, targetFileNames: string[],
|
||||||
|
compilerOpts: ng.CompilerOptions, host: CompilerHost): tsickle.EmitResult {
|
||||||
|
const emitCallback: ng.EmitCallback = ({
|
||||||
|
targetSourceFiles,
|
||||||
|
writeFile,
|
||||||
|
cancellationToken,
|
||||||
|
emitOnlyDtsFiles,
|
||||||
|
customTransformers = {}
|
||||||
|
}) => {
|
||||||
|
if (!targetSourceFiles) {
|
||||||
|
// Note: we know that we always have targetSourceFiles
|
||||||
|
// as we called `ng.Program.emit` with `targetFileNames`.
|
||||||
|
throw new Error('Unexpected state: no targetSourceFiles!');
|
||||||
|
}
|
||||||
|
let cacheHits = 0;
|
||||||
|
const mergedEmitResult = tsickle.mergeEmitResults(targetSourceFiles.map(targetSourceFile => {
|
||||||
|
const targetGeneratedFile = program.getGeneratedFile(targetSourceFile.fileName);
|
||||||
|
const cacheSf = targetGeneratedFile ?
|
||||||
|
program.getTsProgram().getSourceFile(targetGeneratedFile.srcFileName) :
|
||||||
|
targetSourceFile;
|
||||||
|
const cacheEntry = getCache(cacheSf, targetGeneratedFile && targetGeneratedFile.genFileName);
|
||||||
|
if (cacheEntry) {
|
||||||
|
let useEmitCache = false;
|
||||||
|
if (targetGeneratedFile && !program.hasChanged(targetSourceFile.fileName)) {
|
||||||
|
// we emitted a GeneratedFile with the same content as before -> use the cache
|
||||||
|
useEmitCache = true;
|
||||||
|
} else if (!inputsChanged && !targetGeneratedFile) {
|
||||||
|
// this is an input and no inputs have changed -> use the cache
|
||||||
|
useEmitCache = true;
|
||||||
|
}
|
||||||
|
if (useEmitCache) {
|
||||||
|
cacheHits++;
|
||||||
|
cacheEntry.writtenFiles.forEach(
|
||||||
|
({fileName, content, sourceFiles}) => writeFile(
|
||||||
|
fileName, content, /*writeByteOrderMark*/ false, /*onError*/ undefined,
|
||||||
|
sourceFiles));
|
||||||
|
return cacheEntry.emitResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const writtenFiles:
|
||||||
|
Array<{fileName: string, content: string, sourceFiles?: ts.SourceFile[]}> = [];
|
||||||
|
const recordingWriteFile =
|
||||||
|
(fileName: string, content: string, writeByteOrderMark: boolean,
|
||||||
|
onError?: (message: string) => void, sourceFiles?: ts.SourceFile[]) => {
|
||||||
|
writtenFiles.push({fileName, content, sourceFiles});
|
||||||
|
writeFile(fileName, content, writeByteOrderMark, onError, sourceFiles);
|
||||||
|
};
|
||||||
|
const emitResult = tsickle.emitWithTsickle(
|
||||||
|
program.getTsProgram(), host, host, compilerOpts, targetSourceFile, recordingWriteFile,
|
||||||
|
cancellationToken, emitOnlyDtsFiles, {
|
||||||
|
beforeTs: customTransformers.before,
|
||||||
|
afterTs: [
|
||||||
|
...(customTransformers.after || []),
|
||||||
|
fixUmdModuleDeclarations((sf: ts.SourceFile) => host.amdModuleName(sf)),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
setCache(cacheSf, {
|
||||||
|
emitResult,
|
||||||
|
writtenFiles,
|
||||||
|
generatedFile: targetGeneratedFile,
|
||||||
|
});
|
||||||
|
return emitResult;
|
||||||
|
}));
|
||||||
|
debug(`Emitted ${targetSourceFiles.length} files with ${cacheHits} cache hits`);
|
||||||
|
return mergedEmitResult;
|
||||||
|
};
|
||||||
|
return program
|
||||||
|
.emit({
|
||||||
|
targetFileNames,
|
||||||
|
emitCallback,
|
||||||
|
emitFlags: ng.EmitFlags.DTS | ng.EmitFlags.JS | ng.EmitFlags.Codegen
|
||||||
|
}) as tsickle.EmitResult;
|
||||||
|
}
|
|
@ -11,6 +11,6 @@
|
||||||
// Entry point
|
// Entry point
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
const args = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
console.error('>>> now yet implemented!');
|
console.error('>>> not yet implemented!');
|
||||||
process.exitCode = 1;
|
process.exitCode = 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,14 @@
|
||||||
// TODO(tbosch): figure out why we need this as it breaks node code within ngc-wrapped
|
// TODO(tbosch): figure out why we need this as it breaks node code within ngc-wrapped
|
||||||
/// <reference types="node" />
|
/// <reference types="node" />
|
||||||
import * as ng from '@angular/compiler-cli';
|
import * as ng from '@angular/compiler-cli';
|
||||||
import {BazelOptions, CachedFileLoader, CompilerHost, FileCache, FileLoader, UncachedFileLoader, constructManifest, debug, fixUmdModuleDeclarations, parseTsconfig, runAsWorker, runWorkerLoop} from '@bazel/typescript';
|
import {BazelOptions, CachedFileLoader, CompilerHost, FileCache, FileLoader, UncachedFileLoader, constructManifest, debug, parseTsconfig, runAsWorker, runWorkerLoop} from '@bazel/typescript';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as tsickle from 'tsickle';
|
import * as tsickle from 'tsickle';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
|
import {emitWithCache, getCachedGeneratedFile} from './emit_cache';
|
||||||
|
|
||||||
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
||||||
const NGC_GEN_FILES = /^(.*?)\.(ngfactory|ngsummary|ngstyle|shim\.ngstyle)(.*)$/;
|
const NGC_GEN_FILES = /^(.*?)\.(ngfactory|ngsummary|ngstyle|shim\.ngstyle)(.*)$/;
|
||||||
// FIXME: we should be able to add the assets to the tsconfig so FileLoader
|
// FIXME: we should be able to add the assets to the tsconfig so FileLoader
|
||||||
|
@ -74,23 +76,32 @@ export function relativeToRootDirs(filePath: string, rootDirs: string[]): string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true, compilerOpts,
|
export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true, compilerOpts,
|
||||||
tsHost, bazelOpts, files, inputs, expectedOuts, gatherDiagnostics}: {
|
tsHost, bazelOpts, files, inputs, expectedOuts,
|
||||||
|
gatherDiagnostics = defaultGatherDiagnostics}: {
|
||||||
allowNonHermeticReads: boolean,
|
allowNonHermeticReads: boolean,
|
||||||
allDepsCompiledWithBazel?: boolean,
|
allDepsCompiledWithBazel?: boolean,
|
||||||
compilerOpts: ng.CompilerOptions,
|
compilerOpts: ng.CompilerOptions,
|
||||||
tsHost: ts.CompilerHost, inputs?: {[path: string]: string},
|
tsHost: ts.CompilerHost, inputs?: {[path: string]: string},
|
||||||
bazelOpts: BazelOptions,
|
bazelOpts: BazelOptions,
|
||||||
files: string[],
|
files: string[],
|
||||||
expectedOuts: string[], gatherDiagnostics?: (program: ng.Program) => ng.Diagnostics
|
expectedOuts: string[],
|
||||||
|
gatherDiagnostics?: (program: ng.Program, inputsToCheck: ts.SourceFile[],
|
||||||
|
genFilesToCheck: ng.GeneratedFile[]) => ng.Diagnostics
|
||||||
}): {diagnostics: ng.Diagnostics, program: ng.Program} {
|
}): {diagnostics: ng.Diagnostics, program: ng.Program} {
|
||||||
let fileLoader: FileLoader;
|
let fileLoader: FileLoader;
|
||||||
|
const oldFiles = new Map<string, ts.SourceFile>();
|
||||||
|
|
||||||
if (inputs) {
|
if (inputs) {
|
||||||
fileLoader = new CachedFileLoader(fileCache, allowNonHermeticReads);
|
fileLoader = new CachedFileLoader(fileCache, allowNonHermeticReads);
|
||||||
// Resolve the inputs to absolute paths to match TypeScript internals
|
// Resolve the inputs to absolute paths to match TypeScript internals
|
||||||
const resolvedInputs: {[path: string]: string} = {};
|
const resolvedInputs: {[path: string]: string} = {};
|
||||||
for (const key of Object.keys(inputs)) {
|
for (const key of Object.keys(inputs)) {
|
||||||
resolvedInputs[path.resolve(key)] = inputs[key];
|
const resolvedKey = path.resolve(key);
|
||||||
|
resolvedInputs[resolvedKey] = inputs[key];
|
||||||
|
const cachedSf = fileCache.getCache(resolvedKey);
|
||||||
|
if (cachedSf) {
|
||||||
|
oldFiles.set(resolvedKey, cachedSf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fileCache.updateCache(resolvedInputs);
|
fileCache.updateCache(resolvedInputs);
|
||||||
} else {
|
} else {
|
||||||
|
@ -178,40 +189,56 @@ export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true,
|
||||||
path.resolve(bazelBin, fileName) + '.d.ts';
|
path.resolve(bazelBin, fileName) + '.d.ts';
|
||||||
}
|
}
|
||||||
|
|
||||||
const emitCallback: ng.TsEmitCallback = ({
|
const oldProgram = {
|
||||||
program,
|
getSourceFile: (fileName: string) => { return oldFiles.get(fileName); },
|
||||||
targetSourceFile,
|
getGeneratedFile: (srcFileName: string, genFileName: string) => {
|
||||||
writeFile,
|
const sf = oldFiles.get(srcFileName);
|
||||||
cancellationToken,
|
return sf ? getCachedGeneratedFile(sf, genFileName) : undefined;
|
||||||
emitOnlyDtsFiles,
|
},
|
||||||
customTransformers = {},
|
};
|
||||||
}) =>
|
const program =
|
||||||
tsickle.emitWithTsickle(
|
ng.createProgram({rootNames: files, host: ngHost, options: compilerOpts, oldProgram});
|
||||||
program, bazelHost, bazelHost, compilerOpts, targetSourceFile, writeFile,
|
let inputsChanged = files.some(fileName => program.hasChanged(fileName));
|
||||||
cancellationToken, emitOnlyDtsFiles, {
|
|
||||||
beforeTs: customTransformers.before,
|
|
||||||
afterTs: [
|
|
||||||
...(customTransformers.after || []),
|
|
||||||
fixUmdModuleDeclarations((sf: ts.SourceFile) => bazelHost.amdModuleName(sf)),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!gatherDiagnostics) {
|
let genFilesToCheck: ng.GeneratedFile[];
|
||||||
gatherDiagnostics = (program) =>
|
let inputsToCheck: ts.SourceFile[];
|
||||||
gatherDiagnosticsForInputsOnly(compilerOpts, bazelOpts, program);
|
if (inputsChanged) {
|
||||||
|
// if an input file changed, we need to type check all
|
||||||
|
// of our compilation sources as well as all generated files.
|
||||||
|
inputsToCheck = bazelOpts.compilationTargetSrc.map(
|
||||||
|
fileName => program.getTsProgram().getSourceFile(fileName));
|
||||||
|
genFilesToCheck = program.getGeneratedFiles().filter(gf => gf.genFileName.endsWith('.ts'));
|
||||||
|
} else {
|
||||||
|
// if no input file changed, only type check the changed generated files
|
||||||
|
// as these don't influence each other nor the type check of the input files.
|
||||||
|
inputsToCheck = [];
|
||||||
|
genFilesToCheck = program.getGeneratedFiles().filter(
|
||||||
|
gf => program.hasChanged(gf.genFileName) && gf.genFileName.endsWith('.ts'));
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(
|
||||||
|
`TypeChecking ${inputsToCheck ? inputsToCheck.length : 'all'} inputs and ${genFilesToCheck ? genFilesToCheck.length : 'all'} generated files`);
|
||||||
|
const diagnostics = [...gatherDiagnostics(program !, inputsToCheck, genFilesToCheck)];
|
||||||
|
let emitResult: tsickle.EmitResult|undefined;
|
||||||
|
if (!diagnostics.length) {
|
||||||
|
const targetFileNames = [...bazelOpts.compilationTargetSrc];
|
||||||
|
for (const genFile of program.getGeneratedFiles()) {
|
||||||
|
if (genFile.genFileName.endsWith('.ts')) {
|
||||||
|
targetFileNames.push(genFile.genFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emitResult = emitWithCache(program, inputsChanged, targetFileNames, compilerOpts, bazelHost);
|
||||||
|
diagnostics.push(...emitResult.diagnostics);
|
||||||
}
|
}
|
||||||
const {diagnostics, emitResult, program} = ng.performCompilation(
|
|
||||||
{rootNames: files, options: compilerOpts, host: ngHost, emitCallback, gatherDiagnostics});
|
|
||||||
const tsickleEmitResult = emitResult as tsickle.EmitResult;
|
|
||||||
let externs = '/** @externs */\n';
|
let externs = '/** @externs */\n';
|
||||||
if (diagnostics.length) {
|
if (diagnostics.length) {
|
||||||
console.error(ng.formatDiagnostics(compilerOpts, diagnostics));
|
console.error(ng.formatDiagnostics(compilerOpts, diagnostics));
|
||||||
} else {
|
} else if (emitResult) {
|
||||||
if (bazelOpts.tsickleGenerateExterns) {
|
if (bazelOpts.tsickleGenerateExterns) {
|
||||||
externs += tsickle.getGeneratedExterns(tsickleEmitResult.externs);
|
externs += tsickle.getGeneratedExterns(emitResult.externs);
|
||||||
}
|
}
|
||||||
if (bazelOpts.manifest) {
|
if (bazelOpts.manifest) {
|
||||||
const manifest = constructManifest(tsickleEmitResult.modulesManifest, bazelHost);
|
const manifest = constructManifest(emitResult.modulesManifest, bazelHost);
|
||||||
fs.writeFileSync(bazelOpts.manifest, manifest);
|
fs.writeFileSync(bazelOpts.manifest, manifest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,14 +257,9 @@ export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true,
|
||||||
return {program, diagnostics};
|
return {program, diagnostics};
|
||||||
}
|
}
|
||||||
|
|
||||||
function isCompilationTarget(bazelOpts: BazelOptions, sf: ts.SourceFile): boolean {
|
function defaultGatherDiagnostics(
|
||||||
return !NGC_GEN_FILES.test(sf.fileName) &&
|
ngProgram: ng.Program, inputsToCheck: ts.SourceFile[],
|
||||||
(bazelOpts.compilationTargetSrc.indexOf(sf.fileName) !== -1);
|
genFilesToCheck: ng.GeneratedFile[]): (ng.Diagnostic | ts.Diagnostic)[] {
|
||||||
}
|
|
||||||
|
|
||||||
function gatherDiagnosticsForInputsOnly(
|
|
||||||
options: ng.CompilerOptions, bazelOpts: BazelOptions,
|
|
||||||
ngProgram: ng.Program): (ng.Diagnostic | ts.Diagnostic)[] {
|
|
||||||
const tsProgram = ngProgram.getTsProgram();
|
const tsProgram = ngProgram.getTsProgram();
|
||||||
const diagnostics: (ng.Diagnostic | ts.Diagnostic)[] = [];
|
const diagnostics: (ng.Diagnostic | ts.Diagnostic)[] = [];
|
||||||
// These checks mirror ts.getPreEmitDiagnostics, with the important
|
// These checks mirror ts.getPreEmitDiagnostics, with the important
|
||||||
|
@ -245,7 +267,7 @@ function gatherDiagnosticsForInputsOnly(
|
||||||
// program.getDeclarationDiagnostics() it somehow corrupts the emit.
|
// program.getDeclarationDiagnostics() it somehow corrupts the emit.
|
||||||
diagnostics.push(...tsProgram.getOptionsDiagnostics());
|
diagnostics.push(...tsProgram.getOptionsDiagnostics());
|
||||||
diagnostics.push(...tsProgram.getGlobalDiagnostics());
|
diagnostics.push(...tsProgram.getGlobalDiagnostics());
|
||||||
for (const sf of tsProgram.getSourceFiles().filter(f => isCompilationTarget(bazelOpts, f))) {
|
for (const sf of inputsToCheck) {
|
||||||
// Note: We only get the diagnostics for individual files
|
// Note: We only get the diagnostics for individual files
|
||||||
// to e.g. not check libraries.
|
// to e.g. not check libraries.
|
||||||
diagnostics.push(...tsProgram.getSyntacticDiagnostics(sf));
|
diagnostics.push(...tsProgram.getSyntacticDiagnostics(sf));
|
||||||
|
@ -255,7 +277,9 @@ function gatherDiagnosticsForInputsOnly(
|
||||||
// only gather the angular diagnostics if we have no diagnostics
|
// only gather the angular diagnostics if we have no diagnostics
|
||||||
// in any other files.
|
// in any other files.
|
||||||
diagnostics.push(...ngProgram.getNgStructuralDiagnostics());
|
diagnostics.push(...ngProgram.getNgStructuralDiagnostics());
|
||||||
diagnostics.push(...ngProgram.getNgSemanticDiagnostics());
|
for (const genFile of genFilesToCheck) {
|
||||||
|
diagnostics.push(...ngProgram.getNgSemanticDiagnostics(genFile));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return diagnostics;
|
return diagnostics;
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,8 +58,8 @@ export class CodeGenerator {
|
||||||
private emit(analyzedModules: compiler.NgAnalyzedModules) {
|
private emit(analyzedModules: compiler.NgAnalyzedModules) {
|
||||||
const generatedModules = this.compiler.emitAllImpls(analyzedModules);
|
const generatedModules = this.compiler.emitAllImpls(analyzedModules);
|
||||||
return generatedModules.map(generatedModule => {
|
return generatedModules.map(generatedModule => {
|
||||||
const sourceFile = this.program.getSourceFile(generatedModule.srcFileUrl);
|
const sourceFile = this.program.getSourceFile(generatedModule.srcFileName);
|
||||||
const emitPath = this.ngCompilerHost.calculateEmitPath(generatedModule.genFileUrl);
|
const emitPath = this.ngCompilerHost.calculateEmitPath(generatedModule.genFileName);
|
||||||
const source = generatedModule.source || compiler.toTypeScript(generatedModule, PREAMBLE);
|
const source = generatedModule.source || compiler.toTypeScript(generatedModule, PREAMBLE);
|
||||||
this.host.writeFile(emitPath, source, false, () => {}, [sourceFile]);
|
this.host.writeFile(emitPath, source, false, () => {}, [sourceFile]);
|
||||||
return emitPath;
|
return emitPath;
|
||||||
|
|
|
@ -39,7 +39,7 @@ export function main(
|
||||||
return reportErrorsAndExit(options, compileDiags, consoleError);
|
return reportErrorsAndExit(options, compileDiags, consoleError);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|undefined {
|
function createEmitCallback(options: api.CompilerOptions): api.EmitCallback|undefined {
|
||||||
const transformDecorators = options.annotationsAs !== 'decorators';
|
const transformDecorators = options.annotationsAs !== 'decorators';
|
||||||
const transformTypesToClosure = options.annotateForClosureCompiler;
|
const transformTypesToClosure = options.annotateForClosureCompiler;
|
||||||
if (!transformDecorators && !transformTypesToClosure) {
|
if (!transformDecorators && !transformTypesToClosure) {
|
||||||
|
@ -58,20 +58,30 @@ function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|un
|
||||||
|
|
||||||
return ({
|
return ({
|
||||||
program,
|
program,
|
||||||
targetSourceFile,
|
targetSourceFiles,
|
||||||
writeFile,
|
writeFile,
|
||||||
cancellationToken,
|
cancellationToken,
|
||||||
emitOnlyDtsFiles,
|
emitOnlyDtsFiles,
|
||||||
customTransformers = {},
|
customTransformers = {},
|
||||||
host,
|
host,
|
||||||
options
|
options
|
||||||
}) =>
|
}) => {
|
||||||
tsickle.emitWithTsickle(
|
if (targetSourceFiles) {
|
||||||
program, tsickleHost, host, options, targetSourceFile, writeFile,
|
return tsickle.mergeEmitResults(targetSourceFiles.map(
|
||||||
cancellationToken, emitOnlyDtsFiles, {
|
targetSourceFile => tsickle.emitWithTsickle(
|
||||||
beforeTs: customTransformers.before,
|
program, tsickleHost, host, options, targetSourceFile, writeFile, cancellationToken,
|
||||||
afterTs: customTransformers.after,
|
emitOnlyDtsFiles, {
|
||||||
});
|
beforeTs: customTransformers.before,
|
||||||
|
afterTs: customTransformers.after,
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
return tsickle.emitWithTsickle(
|
||||||
|
program, tsickleHost, host, options, /*targetSourceFile*/ undefined, writeFile,
|
||||||
|
cancellationToken, emitOnlyDtsFiles, {
|
||||||
|
beforeTs: customTransformers.before,
|
||||||
|
afterTs: customTransformers.after,
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NgcParsedConfiguration extends ParsedConfiguration { watch?: boolean; }
|
export interface NgcParsedConfiguration extends ParsedConfiguration { watch?: boolean; }
|
||||||
|
|
|
@ -103,8 +103,7 @@ export interface Program {
|
||||||
getNgStructuralDiagnostics(cancellationToken?: ts.CancellationToken): Diagnostic[];
|
getNgStructuralDiagnostics(cancellationToken?: ts.CancellationToken): Diagnostic[];
|
||||||
getTsSemanticDiagnostics(sourceFile?: ts.SourceFile, cancellationToken?: ts.CancellationToken):
|
getTsSemanticDiagnostics(sourceFile?: ts.SourceFile, cancellationToken?: ts.CancellationToken):
|
||||||
ts.Diagnostic[];
|
ts.Diagnostic[];
|
||||||
getNgSemanticDiagnostics(fileName?: string, cancellationToken?: ts.CancellationToken):
|
getNgSemanticDiagnostics(genFile?: any, cancellationToken?: ts.CancellationToken): Diagnostic[];
|
||||||
Diagnostic[];
|
|
||||||
loadNgStructureAsync(): Promise<void>;
|
loadNgStructureAsync(): Promise<void>;
|
||||||
emit({emitFlags, cancellationToken, customTransformers, emitCallback}: {
|
emit({emitFlags, cancellationToken, customTransformers, emitCallback}: {
|
||||||
emitFlags?: EmitFlags,
|
emitFlags?: EmitFlags,
|
||||||
|
|
|
@ -144,7 +144,7 @@ export function performCompilation({rootNames, options, host, oldProgram, emitCa
|
||||||
options: api.CompilerOptions,
|
options: api.CompilerOptions,
|
||||||
host?: api.CompilerHost,
|
host?: api.CompilerHost,
|
||||||
oldProgram?: api.Program,
|
oldProgram?: api.Program,
|
||||||
emitCallback?: api.TsEmitCallback,
|
emitCallback?: api.EmitCallback,
|
||||||
gatherDiagnostics?: (program: api.Program) => Diagnostics,
|
gatherDiagnostics?: (program: api.Program) => Diagnostics,
|
||||||
customTransformers?: api.CustomTransformers,
|
customTransformers?: api.CustomTransformers,
|
||||||
emitFlags?: api.EmitFlags
|
emitFlags?: api.EmitFlags
|
||||||
|
|
|
@ -40,7 +40,7 @@ export interface PerformWatchHost {
|
||||||
reportDiagnostics(diagnostics: Diagnostics): void;
|
reportDiagnostics(diagnostics: Diagnostics): void;
|
||||||
readConfiguration(): ParsedConfiguration;
|
readConfiguration(): ParsedConfiguration;
|
||||||
createCompilerHost(options: api.CompilerOptions): api.CompilerHost;
|
createCompilerHost(options: api.CompilerOptions): api.CompilerHost;
|
||||||
createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|undefined;
|
createEmitCallback(options: api.CompilerOptions): api.EmitCallback|undefined;
|
||||||
onFileChange(
|
onFileChange(
|
||||||
options: api.CompilerOptions, listener: (event: FileChangeEvent, fileName: string) => void,
|
options: api.CompilerOptions, listener: (event: FileChangeEvent, fileName: string) => void,
|
||||||
ready: () => void): {close: () => void};
|
ready: () => void): {close: () => void};
|
||||||
|
@ -51,7 +51,7 @@ export interface PerformWatchHost {
|
||||||
export function createPerformWatchHost(
|
export function createPerformWatchHost(
|
||||||
configFileName: string, reportDiagnostics: (diagnostics: Diagnostics) => void,
|
configFileName: string, reportDiagnostics: (diagnostics: Diagnostics) => void,
|
||||||
existingOptions?: ts.CompilerOptions, createEmitCallback?: (options: api.CompilerOptions) =>
|
existingOptions?: ts.CompilerOptions, createEmitCallback?: (options: api.CompilerOptions) =>
|
||||||
api.TsEmitCallback | undefined): PerformWatchHost {
|
api.EmitCallback | undefined): PerformWatchHost {
|
||||||
return {
|
return {
|
||||||
reportDiagnostics: reportDiagnostics,
|
reportDiagnostics: reportDiagnostics,
|
||||||
createCompilerHost: options => createCompilerHost({options}),
|
createCompilerHost: options => createCompilerHost({options}),
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {GeneratedFile, ParseSourceSpan} from '@angular/compiler';
|
import {GeneratedFile as GeneratedFileImpl, ParseSourceSpan} from '@angular/compiler';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
export const DEFAULT_ERROR_CODE = 100;
|
export const DEFAULT_ERROR_CODE = 100;
|
||||||
|
@ -207,18 +207,32 @@ export interface CustomTransformers {
|
||||||
afterTs?: ts.TransformerFactory<ts.SourceFile>[];
|
afterTs?: ts.TransformerFactory<ts.SourceFile>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TsEmitArguments {
|
export interface EmitArguments {
|
||||||
program: ts.Program;
|
program: ts.Program;
|
||||||
host: CompilerHost;
|
host: CompilerHost;
|
||||||
options: CompilerOptions;
|
options: CompilerOptions;
|
||||||
targetSourceFile?: ts.SourceFile;
|
targetSourceFiles?: ts.SourceFile[];
|
||||||
writeFile?: ts.WriteFileCallback;
|
writeFile?: ts.WriteFileCallback;
|
||||||
cancellationToken?: ts.CancellationToken;
|
cancellationToken?: ts.CancellationToken;
|
||||||
emitOnlyDtsFiles?: boolean;
|
emitOnlyDtsFiles?: boolean;
|
||||||
customTransformers?: ts.CustomTransformers;
|
customTransformers?: ts.CustomTransformers;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TsEmitCallback { (args: TsEmitArguments): ts.EmitResult; }
|
export interface EmitCallback { (args: EmitArguments): ts.EmitResult; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a generated file that has not yet been emitted.
|
||||||
|
*/
|
||||||
|
export interface GeneratedFile {
|
||||||
|
/**
|
||||||
|
* The file name of the sourceFile from which this file was generated.
|
||||||
|
*/
|
||||||
|
srcFileName: string;
|
||||||
|
/**
|
||||||
|
* The file name of the generated but not yet emitted file.
|
||||||
|
*/
|
||||||
|
genFileName: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
|
@ -282,7 +296,7 @@ export interface Program {
|
||||||
*
|
*
|
||||||
* Angular structural information is required to produce these diagnostics.
|
* Angular structural information is required to produce these diagnostics.
|
||||||
*/
|
*/
|
||||||
getNgSemanticDiagnostics(fileName?: string, cancellationToken?: ts.CancellationToken):
|
getNgSemanticDiagnostics(genFile?: GeneratedFile, cancellationToken?: ts.CancellationToken):
|
||||||
Diagnostic[];
|
Diagnostic[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -293,16 +307,33 @@ export interface Program {
|
||||||
*/
|
*/
|
||||||
loadNgStructureAsync(): Promise<void>;
|
loadNgStructureAsync(): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the GeneratedFile with the given fileName.
|
||||||
|
*/
|
||||||
|
getGeneratedFile(genFileName: string): GeneratedFile|undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retriesves all GeneratedFiles.
|
||||||
|
*/
|
||||||
|
getGeneratedFiles(): GeneratedFile[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates whether the given file has changed since the `oldProgram` that was passed
|
||||||
|
* to `createProgram`.
|
||||||
|
*/
|
||||||
|
hasChanged(fileName: string): boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emit the files requested by emitFlags implied by the program.
|
* Emit the files requested by emitFlags implied by the program.
|
||||||
*
|
*
|
||||||
* Angular structural information is required to emit files.
|
* Angular structural information is required to emit files.
|
||||||
*/
|
*/
|
||||||
emit({emitFlags, cancellationToken, customTransformers, emitCallback}?: {
|
emit({targetFileNames, emitFlags, cancellationToken, customTransformers, emitCallback}?: {
|
||||||
|
targetFileNames?: string[],
|
||||||
emitFlags?: EmitFlags,
|
emitFlags?: EmitFlags,
|
||||||
cancellationToken?: ts.CancellationToken,
|
cancellationToken?: ts.CancellationToken,
|
||||||
customTransformers?: CustomTransformers,
|
customTransformers?: CustomTransformers,
|
||||||
emitCallback?: TsEmitCallback
|
emitCallback?: EmitCallback
|
||||||
}): ts.EmitResult;
|
}): ts.EmitResult;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -317,10 +348,25 @@ export interface Program {
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
getEmittedGeneratedFiles(): Map<string, GeneratedFile>;
|
getEmittedGeneratedFiles(): Map<string, GeneratedFileImpl>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
getEmittedSourceFiles(): Map<string, ts.SourceFile>;
|
getEmittedSourceFiles(): Map<string, ts.SourceFile>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type OldProgram = Program | CachedFiles;
|
||||||
|
|
||||||
|
export interface CachedFiles {
|
||||||
|
getSourceFile(fileName: string): ts.SourceFile|undefined;
|
||||||
|
getGeneratedFile(srcFileName: string, genFileName: string): GeneratedFile|undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateProgram {
|
||||||
|
({rootNames, options, host, oldProgram}: {
|
||||||
|
rootNames: string[],
|
||||||
|
options: CompilerOptions,
|
||||||
|
host: CompilerHost, oldProgram?: OldProgram
|
||||||
|
}): Program;
|
||||||
|
}
|
||||||
|
|
|
@ -15,11 +15,10 @@ import {TypeCheckHost} from '../diagnostics/translate_diagnostics';
|
||||||
import {ModuleMetadata} from '../metadata/index';
|
import {ModuleMetadata} from '../metadata/index';
|
||||||
|
|
||||||
import {CompilerHost, CompilerOptions, LibrarySummary} from './api';
|
import {CompilerHost, CompilerOptions, LibrarySummary} from './api';
|
||||||
import {GENERATED_FILES} from './util';
|
import {EXT, GENERATED_FILES} from './util';
|
||||||
|
|
||||||
const NODE_MODULES_PACKAGE_NAME = /node_modules\/((\w|-)+|(@(\w|-)+\/(\w|-)+))/;
|
const NODE_MODULES_PACKAGE_NAME = /node_modules\/((\w|-)+|(@(\w|-)+\/(\w|-)+))/;
|
||||||
const DTS = /\.d\.ts$/;
|
const DTS = /\.d\.ts$/;
|
||||||
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
|
||||||
|
|
||||||
export function createCompilerHost(
|
export function createCompilerHost(
|
||||||
{options, tsHost = ts.createCompilerHost(options, true)}:
|
{options, tsHost = ts.createCompilerHost(options, true)}:
|
||||||
|
@ -262,11 +261,12 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter extends
|
||||||
updateGeneratedFile(genFile: GeneratedFile): ts.SourceFile {
|
updateGeneratedFile(genFile: GeneratedFile): ts.SourceFile {
|
||||||
if (!genFile.stmts) {
|
if (!genFile.stmts) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Invalid Argument: Expected a GenerateFile with statements. ${genFile.genFileUrl}`);
|
`Invalid Argument: Expected a GenerateFile with statements. ${genFile.genFileName}`);
|
||||||
}
|
}
|
||||||
const oldGenFile = this.generatedSourceFiles.get(genFile.genFileUrl);
|
const oldGenFile = this.generatedSourceFiles.get(genFile.genFileName);
|
||||||
if (!oldGenFile) {
|
if (!oldGenFile) {
|
||||||
throw new Error(`Illegal State: previous GeneratedFile not found for ${genFile.genFileUrl}.`);
|
throw new Error(
|
||||||
|
`Illegal State: previous GeneratedFile not found for ${genFile.genFileName}.`);
|
||||||
}
|
}
|
||||||
const newRefs = genFileExternalReferences(genFile);
|
const newRefs = genFileExternalReferences(genFile);
|
||||||
const oldRefs = oldGenFile.externalReferences;
|
const oldRefs = oldGenFile.externalReferences;
|
||||||
|
@ -276,7 +276,7 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter extends
|
||||||
}
|
}
|
||||||
if (!refsAreEqual) {
|
if (!refsAreEqual) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Illegal State: external references changed in ${genFile.genFileUrl}.\nOld: ${Array.from(oldRefs)}.\nNew: ${Array.from(newRefs)}`);
|
`Illegal State: external references changed in ${genFile.genFileName}.\nOld: ${Array.from(oldRefs)}.\nNew: ${Array.from(newRefs)}`);
|
||||||
}
|
}
|
||||||
return this.addGeneratedFile(genFile, newRefs);
|
return this.addGeneratedFile(genFile, newRefs);
|
||||||
}
|
}
|
||||||
|
@ -284,14 +284,14 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter extends
|
||||||
private addGeneratedFile(genFile: GeneratedFile, externalReferences: Set<string>): ts.SourceFile {
|
private addGeneratedFile(genFile: GeneratedFile, externalReferences: Set<string>): ts.SourceFile {
|
||||||
if (!genFile.stmts) {
|
if (!genFile.stmts) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Invalid Argument: Expected a GenerateFile with statements. ${genFile.genFileUrl}`);
|
`Invalid Argument: Expected a GenerateFile with statements. ${genFile.genFileName}`);
|
||||||
}
|
}
|
||||||
const {sourceText, context} = this.emitter.emitStatementsAndContext(
|
const {sourceText, context} = this.emitter.emitStatementsAndContext(
|
||||||
genFile.genFileUrl, genFile.stmts, /* preamble */ '',
|
genFile.genFileName, genFile.stmts, /* preamble */ '',
|
||||||
/* emitSourceMaps */ false);
|
/* emitSourceMaps */ false);
|
||||||
const sf = ts.createSourceFile(
|
const sf = ts.createSourceFile(
|
||||||
genFile.genFileUrl, sourceText, this.options.target || ts.ScriptTarget.Latest);
|
genFile.genFileName, sourceText, this.options.target || ts.ScriptTarget.Latest);
|
||||||
this.generatedSourceFiles.set(genFile.genFileUrl, {
|
this.generatedSourceFiles.set(genFile.genFileName, {
|
||||||
sourceFile: sf,
|
sourceFile: sf,
|
||||||
emitCtx: context, externalReferences,
|
emitCtx: context, externalReferences,
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,3 +12,4 @@ import {CompilerHost, CompilerOptions, Program} from './api';
|
||||||
|
|
||||||
export {createCompilerHost} from './compiler_host';
|
export {createCompilerHost} from './compiler_host';
|
||||||
export {createProgram} from './program';
|
export {createProgram} from './program';
|
||||||
|
export {isGeneratedFile} from './util';
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
import {GeneratedFile} from '@angular/compiler';
|
import {GeneratedFile} from '@angular/compiler';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
|
import {Program} from './api';
|
||||||
import {TypeScriptNodeEmitter} from './node_emitter';
|
import {TypeScriptNodeEmitter} from './node_emitter';
|
||||||
import {GENERATED_FILES} from './util';
|
import {GENERATED_FILES} from './util';
|
||||||
|
|
||||||
|
@ -19,12 +20,12 @@ const PREAMBLE = `/**
|
||||||
* tslint:disable
|
* tslint:disable
|
||||||
*/`;
|
*/`;
|
||||||
|
|
||||||
export function getAngularEmitterTransformFactory(generatedFiles: Map<string, GeneratedFile>): () =>
|
export function getAngularEmitterTransformFactory(program: Program): () =>
|
||||||
(sourceFile: ts.SourceFile) => ts.SourceFile {
|
(sourceFile: ts.SourceFile) => ts.SourceFile {
|
||||||
return function() {
|
return function() {
|
||||||
const emitter = new TypeScriptNodeEmitter();
|
const emitter = new TypeScriptNodeEmitter();
|
||||||
return function(sourceFile: ts.SourceFile): ts.SourceFile {
|
return function(sourceFile: ts.SourceFile): ts.SourceFile {
|
||||||
const g = generatedFiles.get(sourceFile.fileName);
|
const g = program.getGeneratedFile(sourceFile.fileName) as GeneratedFile;
|
||||||
if (g && g.stmts) {
|
if (g && g.stmts) {
|
||||||
const [newSourceFile] = emitter.updateSourceFile(sourceFile, g.stmts, PREAMBLE);
|
const [newSourceFile] = emitter.updateSourceFile(sourceFile, g.stmts, PREAMBLE);
|
||||||
return newSourceFile;
|
return newSourceFile;
|
||||||
|
|
|
@ -14,11 +14,12 @@ import * as ts from 'typescript';
|
||||||
import {TypeCheckHost, translateDiagnostics} from '../diagnostics/translate_diagnostics';
|
import {TypeCheckHost, translateDiagnostics} from '../diagnostics/translate_diagnostics';
|
||||||
import {ModuleMetadata, createBundleIndexHost} from '../metadata/index';
|
import {ModuleMetadata, createBundleIndexHost} from '../metadata/index';
|
||||||
|
|
||||||
import {CompilerHost, CompilerOptions, CustomTransformers, DEFAULT_ERROR_CODE, Diagnostic, EmitFlags, LibrarySummary, Program, SOURCE, TsEmitArguments, TsEmitCallback} from './api';
|
import {CachedFiles, CompilerHost, CompilerOptions, CreateProgram, CustomTransformers, DEFAULT_ERROR_CODE, Diagnostic, EmitArguments, EmitCallback, EmitFlags, LibrarySummary, OldProgram, Program, SOURCE} from './api';
|
||||||
import {CodeGenerator, TsCompilerAotCompilerTypeCheckHostAdapter, getOriginalReferences} from './compiler_host';
|
import {CodeGenerator, TsCompilerAotCompilerTypeCheckHostAdapter, getOriginalReferences} from './compiler_host';
|
||||||
import {LowerMetadataCache, getExpressionLoweringTransformFactory} from './lower_expressions';
|
import {LowerMetadataCache, getExpressionLoweringTransformFactory} from './lower_expressions';
|
||||||
import {getAngularEmitterTransformFactory} from './node_emitter_transform';
|
import {getAngularEmitterTransformFactory} from './node_emitter_transform';
|
||||||
import {GENERATED_FILES, StructureIsReused, createMessageDiagnostic, tsStructureIsReused} from './util';
|
import {EXT, GENERATED_FILES, StructureIsReused, createMessageDiagnostic, tsStructureIsReused} from './util';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum number of files that are emitable via calling ts.Program.emit
|
* Maximum number of files that are emitable via calling ts.Program.emit
|
||||||
|
@ -32,17 +33,34 @@ const emptyModules: NgAnalyzedModules = {
|
||||||
files: []
|
files: []
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultEmitCallback: TsEmitCallback =
|
const defaultEmitCallback: EmitCallback =
|
||||||
({program, targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles,
|
({program, targetSourceFiles, writeFile, cancellationToken, emitOnlyDtsFiles,
|
||||||
customTransformers}) =>
|
customTransformers}) => {
|
||||||
program.emit(
|
if (targetSourceFiles) {
|
||||||
targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
|
const diagnostics: ts.Diagnostic[] = [];
|
||||||
|
let emitSkipped = false;
|
||||||
|
const emittedFiles: string[] = [];
|
||||||
|
for (const targetSourceFile of targetSourceFiles) {
|
||||||
|
const er = program.emit(
|
||||||
|
targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
|
||||||
|
diagnostics.push(...er.diagnostics);
|
||||||
|
emitSkipped = emitSkipped || er.emitSkipped;
|
||||||
|
emittedFiles.push(...er.emittedFiles);
|
||||||
|
}
|
||||||
|
return {diagnostics, emitSkipped, emittedFiles};
|
||||||
|
} else {
|
||||||
|
return program.emit(
|
||||||
|
/*targetSourceFile*/ undefined, writeFile, cancellationToken, emitOnlyDtsFiles,
|
||||||
|
customTransformers);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class AngularCompilerProgram implements Program {
|
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;
|
private oldProgramEmittedSourceFiles: Map<string, ts.SourceFile>|undefined;
|
||||||
|
private oldProgramCachedFiles: CachedFiles|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;
|
||||||
|
@ -56,20 +74,33 @@ class AngularCompilerProgram implements Program {
|
||||||
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;
|
||||||
|
private _generatedFilesMap: Map<string, GeneratedFile>|undefined;
|
||||||
|
private _generatedFiles: GeneratedFile[]|undefined;
|
||||||
|
private _changedFileNames = new Map<string, boolean>();
|
||||||
private _optionsDiagnostics: Diagnostic[] = [];
|
private _optionsDiagnostics: Diagnostic[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private rootNames: string[], private options: CompilerOptions, private host: CompilerHost,
|
private rootNames: string[], private options: CompilerOptions, private host: CompilerHost,
|
||||||
private oldProgram?: Program) {
|
oldProgram?: OldProgram) {
|
||||||
const [major, minor] = ts.version.split('.');
|
const [major, minor] = ts.version.split('.');
|
||||||
if (Number(major) < 2 || (Number(major) === 2 && Number(minor) < 4)) {
|
if (Number(major) < 2 || (Number(major) === 2 && Number(minor) < 4)) {
|
||||||
throw new Error('The Angular Compiler requires TypeScript >= 2.4.');
|
throw new Error('The Angular Compiler requires TypeScript >= 2.4.');
|
||||||
}
|
}
|
||||||
this.oldTsProgram = oldProgram ? oldProgram.getTsProgram() : undefined;
|
|
||||||
if (oldProgram) {
|
if (oldProgram) {
|
||||||
this.oldProgramLibrarySummaries = oldProgram.getLibrarySummaries();
|
if ((oldProgram as Program).getTsProgram) {
|
||||||
this.oldProgramEmittedGeneratedFiles = oldProgram.getEmittedGeneratedFiles();
|
const oldNgProgram = oldProgram as Program;
|
||||||
this.oldProgramEmittedSourceFiles = oldProgram.getEmittedSourceFiles();
|
this.oldTsProgram = oldNgProgram.getTsProgram();
|
||||||
|
this.oldProgramLibrarySummaries = oldNgProgram.getLibrarySummaries();
|
||||||
|
this.oldProgramEmittedGeneratedFiles = oldNgProgram.getEmittedGeneratedFiles();
|
||||||
|
this.oldProgramEmittedSourceFiles = oldNgProgram.getEmittedSourceFiles();
|
||||||
|
this.oldProgramCachedFiles = {
|
||||||
|
getSourceFile: (fileName: string) => this.oldProgramEmittedSourceFiles !.get(fileName),
|
||||||
|
getGeneratedFile: (srcFileName: string, genFileName: string) =>
|
||||||
|
this.oldProgramEmittedGeneratedFiles !.get(genFileName),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this.oldProgramCachedFiles = oldProgram as CachedFiles;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.flatModuleOutFile) {
|
if (options.flatModuleOutFile) {
|
||||||
|
@ -110,7 +141,9 @@ class AngularCompilerProgram implements Program {
|
||||||
(genFile, fileName) => result.set(fileName, genFile));
|
(genFile, fileName) => result.set(fileName, genFile));
|
||||||
}
|
}
|
||||||
if (this.emittedGeneratedFiles) {
|
if (this.emittedGeneratedFiles) {
|
||||||
this.emittedGeneratedFiles.forEach((genFile) => result.set(genFile.genFileUrl, genFile));
|
for (const genFile of this.emittedGeneratedFiles) {
|
||||||
|
result.set(genFile.genFileName, genFile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -121,7 +154,9 @@ class AngularCompilerProgram implements Program {
|
||||||
this.oldProgramEmittedSourceFiles.forEach((sf, fileName) => result.set(fileName, sf));
|
this.oldProgramEmittedSourceFiles.forEach((sf, fileName) => result.set(fileName, sf));
|
||||||
}
|
}
|
||||||
if (this.emittedSourceFiles) {
|
if (this.emittedSourceFiles) {
|
||||||
this.emittedSourceFiles.forEach((sf) => result.set(sf.fileName, sf));
|
for (const sf of this.emittedSourceFiles) {
|
||||||
|
result.set(sf.fileName, sf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -151,22 +186,27 @@ class AngularCompilerProgram implements Program {
|
||||||
return this.tsProgram.getSemanticDiagnostics(sourceFile, cancellationToken);
|
return this.tsProgram.getSemanticDiagnostics(sourceFile, cancellationToken);
|
||||||
}
|
}
|
||||||
let diags: ts.Diagnostic[] = [];
|
let diags: ts.Diagnostic[] = [];
|
||||||
this.tsProgram.getSourceFiles().forEach(sf => {
|
for (const sf of this.tsProgram.getSourceFiles()) {
|
||||||
if (!GENERATED_FILES.test(sf.fileName)) {
|
if (!GENERATED_FILES.test(sf.fileName)) {
|
||||||
diags.push(...this.tsProgram.getSemanticDiagnostics(sf, cancellationToken));
|
diags.push(...this.tsProgram.getSemanticDiagnostics(sf, cancellationToken));
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
return diags;
|
return diags;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNgSemanticDiagnostics(fileName?: string, cancellationToken?: ts.CancellationToken):
|
getNgSemanticDiagnostics(genFile?: GeneratedFile, cancellationToken?: ts.CancellationToken):
|
||||||
Diagnostic[] {
|
Diagnostic[] {
|
||||||
let diags: ts.Diagnostic[] = [];
|
let diags: ts.Diagnostic[] = [];
|
||||||
this.tsProgram.getSourceFiles().forEach(sf => {
|
if (genFile) {
|
||||||
if (GENERATED_FILES.test(sf.fileName) && !sf.isDeclarationFile) {
|
diags.push(...this.tsProgram.getSemanticDiagnostics(
|
||||||
diags.push(...this.tsProgram.getSemanticDiagnostics(sf, cancellationToken));
|
this.tsProgram.getSourceFile(genFile.genFileName), cancellationToken));
|
||||||
|
} else {
|
||||||
|
for (const sf of this.tsProgram.getSourceFiles()) {
|
||||||
|
if (GENERATED_FILES.test(sf.fileName) && !sf.isDeclarationFile) {
|
||||||
|
diags.push(...this.tsProgram.getSemanticDiagnostics(sf, cancellationToken));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
const {ng} = translateDiagnostics(this.typeCheckHost, diags);
|
const {ng} = translateDiagnostics(this.typeCheckHost, diags);
|
||||||
return ng;
|
return ng;
|
||||||
}
|
}
|
||||||
|
@ -187,13 +227,42 @@ class AngularCompilerProgram implements Program {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getGeneratedFile(genFileName: string): GeneratedFile|undefined {
|
||||||
|
return this.generatedFilesMap.get(genFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
getGeneratedFiles(): GeneratedFile[] { return this.generatedFiles; }
|
||||||
|
|
||||||
|
hasChanged(fileName: string) {
|
||||||
|
if (!this.oldProgramCachedFiles) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
let changed = this._changedFileNames.get(fileName);
|
||||||
|
if (typeof changed === 'boolean') {
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
const genFile = this.getGeneratedFile(fileName);
|
||||||
|
if (genFile) {
|
||||||
|
const oldGenFile = this.oldProgramCachedFiles !.getGeneratedFile(
|
||||||
|
genFile.srcFileName, genFile.genFileName) as GeneratedFile;
|
||||||
|
changed = !oldGenFile || !genFile.isEquivalent(oldGenFile);
|
||||||
|
} else {
|
||||||
|
const sf = this.tsProgram.getSourceFile(fileName);
|
||||||
|
const oldSf = this.oldProgramCachedFiles !.getSourceFile(fileName);
|
||||||
|
changed = !oldSf || sf !== oldSf;
|
||||||
|
}
|
||||||
|
this._changedFileNames.set(fileName, changed);
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
{emitFlags = EmitFlags.Default, cancellationToken, customTransformers,
|
{targetFileNames, emitFlags = EmitFlags.Default, cancellationToken, customTransformers,
|
||||||
emitCallback = defaultEmitCallback}: {
|
emitCallback = defaultEmitCallback}: {
|
||||||
|
targetFileNames?: string[],
|
||||||
emitFlags?: EmitFlags,
|
emitFlags?: EmitFlags,
|
||||||
cancellationToken?: ts.CancellationToken,
|
cancellationToken?: ts.CancellationToken,
|
||||||
customTransformers?: CustomTransformers,
|
customTransformers?: CustomTransformers,
|
||||||
emitCallback?: TsEmitCallback
|
emitCallback?: EmitCallback
|
||||||
} = {}): ts.EmitResult {
|
} = {}): ts.EmitResult {
|
||||||
const emitStart = Date.now();
|
const emitStart = Date.now();
|
||||||
if (emitFlags & EmitFlags.I18nBundle) {
|
if (emitFlags & EmitFlags.I18nBundle) {
|
||||||
|
@ -203,88 +272,48 @@ class AngularCompilerProgram implements Program {
|
||||||
const bundle = this.compiler.emitMessageBundle(this.analyzedModules, locale);
|
const bundle = this.compiler.emitMessageBundle(this.analyzedModules, locale);
|
||||||
i18nExtract(format, file, this.host, this.options, bundle);
|
i18nExtract(format, file, this.host, this.options, bundle);
|
||||||
}
|
}
|
||||||
|
const emitSkippedResult = {emitSkipped: true, diagnostics: [], emittedFiles: []};
|
||||||
if ((emitFlags & (EmitFlags.JS | EmitFlags.DTS | EmitFlags.Metadata | EmitFlags.Codegen)) ===
|
if ((emitFlags & (EmitFlags.JS | EmitFlags.DTS | EmitFlags.Metadata | EmitFlags.Codegen)) ===
|
||||||
0) {
|
0) {
|
||||||
return {emitSkipped: true, diagnostics: [], emittedFiles: []};
|
return emitSkippedResult;
|
||||||
}
|
}
|
||||||
let {genFiles, genDiags} = this.generateFilesForEmit(emitFlags);
|
const {srcFilesToEmit, jsonFilesToEmit, emitAllFiles} = this.calcFilesToEmit(targetFileNames);
|
||||||
if (genDiags.length) {
|
|
||||||
return {
|
|
||||||
diagnostics: genDiags,
|
|
||||||
emitSkipped: true,
|
|
||||||
emittedFiles: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
this.emittedGeneratedFiles = genFiles;
|
|
||||||
const outSrcMapping: Array<{sourceFile: ts.SourceFile, outFileName: string}> = [];
|
const outSrcMapping: Array<{sourceFile: ts.SourceFile, outFileName: string}> = [];
|
||||||
const genFileByFileName = new Map<string, GeneratedFile>();
|
|
||||||
genFiles.forEach(genFile => genFileByFileName.set(genFile.genFileUrl, genFile));
|
|
||||||
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;
|
||||||
let genFile: GeneratedFile|undefined;
|
let genFile: GeneratedFile|undefined;
|
||||||
if (sourceFile) {
|
if (sourceFile) {
|
||||||
outSrcMapping.push({outFileName: outFileName, sourceFile});
|
outSrcMapping.push({outFileName: outFileName, sourceFile});
|
||||||
genFile = genFileByFileName.get(sourceFile.fileName);
|
if (emitFlags & EmitFlags.Codegen) {
|
||||||
if (!sourceFile.isDeclarationFile && !GENERATED_FILES.test(sourceFile.fileName)) {
|
genFile = this.getGeneratedFile(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, sourceFiles, genFile);
|
||||||
};
|
};
|
||||||
const tsCustomTansformers = this.calculateTransforms(genFileByFileName, customTransformers);
|
const tsCustomTansformers = this.calculateTransforms(customTransformers);
|
||||||
const emitOnlyDtsFiles = (emitFlags & (EmitFlags.DTS | EmitFlags.JS)) == EmitFlags.DTS;
|
const emitOnlyDtsFiles = (emitFlags & (EmitFlags.DTS | EmitFlags.JS)) == EmitFlags.DTS;
|
||||||
// Restore the original references before we emit so TypeScript doesn't emit
|
// Restore the original references before we emit so TypeScript doesn't emit
|
||||||
// a reference to the .d.ts file.
|
// a reference to the .d.ts file.
|
||||||
const augmentedReferences = new Map<ts.SourceFile, ts.FileReference[]>();
|
const augmentedReferences = new Map<ts.SourceFile, ts.FileReference[]>();
|
||||||
for (const sourceFile of this.tsProgram.getSourceFiles()) {
|
for (const sourceFile of srcFilesToEmit) {
|
||||||
const originalReferences = getOriginalReferences(sourceFile);
|
const originalReferences = getOriginalReferences(sourceFile);
|
||||||
if (originalReferences) {
|
if (originalReferences) {
|
||||||
augmentedReferences.set(sourceFile, sourceFile.referencedFiles);
|
augmentedReferences.set(sourceFile, sourceFile.referencedFiles);
|
||||||
sourceFile.referencedFiles = originalReferences;
|
sourceFile.referencedFiles = originalReferences;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const genTsFiles: GeneratedFile[] = [];
|
|
||||||
const genJsonFiles: GeneratedFile[] = [];
|
|
||||||
genFiles.forEach(gf => {
|
|
||||||
if (gf.stmts) {
|
|
||||||
genTsFiles.push(gf);
|
|
||||||
}
|
|
||||||
if (gf.source) {
|
|
||||||
genJsonFiles.push(gf);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let emitResult: ts.EmitResult;
|
let emitResult: ts.EmitResult;
|
||||||
let emittedUserTsCount: number;
|
|
||||||
try {
|
try {
|
||||||
const sourceFilesToEmit = this.getSourceFilesForEmit();
|
emitResult = emitCallback({
|
||||||
if (sourceFilesToEmit &&
|
program: this.tsProgram,
|
||||||
(sourceFilesToEmit.length + genTsFiles.length) < MAX_FILE_COUNT_FOR_SINGLE_FILE_EMIT) {
|
host: this.host,
|
||||||
const fileNamesToEmit =
|
options: this.options,
|
||||||
[...sourceFilesToEmit.map(sf => sf.fileName), ...genTsFiles.map(gf => gf.genFileUrl)];
|
writeFile: writeTsFile, emitOnlyDtsFiles,
|
||||||
emitResult = mergeEmitResults(
|
customTransformers: tsCustomTansformers,
|
||||||
fileNamesToEmit.map((fileName) => emitResult = emitCallback({
|
targetSourceFiles: emitAllFiles ? undefined : srcFilesToEmit,
|
||||||
program: this.tsProgram,
|
});
|
||||||
host: this.host,
|
|
||||||
options: this.options,
|
|
||||||
writeFile: writeTsFile, emitOnlyDtsFiles,
|
|
||||||
customTransformers: tsCustomTansformers,
|
|
||||||
targetSourceFile: this.tsProgram.getSourceFile(fileName),
|
|
||||||
})));
|
|
||||||
emittedUserTsCount = sourceFilesToEmit.length;
|
|
||||||
} else {
|
|
||||||
emitResult = emitCallback({
|
|
||||||
program: this.tsProgram,
|
|
||||||
host: this.host,
|
|
||||||
options: this.options,
|
|
||||||
writeFile: writeTsFile, emitOnlyDtsFiles,
|
|
||||||
customTransformers: tsCustomTansformers
|
|
||||||
});
|
|
||||||
emittedUserTsCount = this.tsProgram.getSourceFiles().length - genTsFiles.length;
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
// Restore the references back to the augmented value to ensure that the
|
// Restore the references back to the augmented value to ensure that the
|
||||||
// checks that TypeScript makes for project structure reuse will succeed.
|
// checks that TypeScript makes for project structure reuse will succeed.
|
||||||
|
@ -292,7 +321,6 @@ 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
|
||||||
|
@ -309,35 +337,99 @@ class AngularCompilerProgram implements Program {
|
||||||
const srcToOutPath =
|
const srcToOutPath =
|
||||||
createSrcToOutPathMapper(this.options.outDir, sampleSrcFileName, sampleOutFileName);
|
createSrcToOutPathMapper(this.options.outDir, sampleSrcFileName, sampleOutFileName);
|
||||||
if (emitFlags & EmitFlags.Codegen) {
|
if (emitFlags & EmitFlags.Codegen) {
|
||||||
genJsonFiles.forEach(gf => {
|
this.emitNgSummaryJsonFiles(jsonFilesToEmit, srcToOutPath);
|
||||||
const outFileName = srcToOutPath(gf.genFileUrl);
|
|
||||||
this.writeFile(outFileName, gf.source !, false, undefined, gf);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
let metadataJsonCount = 0;
|
|
||||||
if (emitFlags & EmitFlags.Metadata) {
|
if (emitFlags & EmitFlags.Metadata) {
|
||||||
this.tsProgram.getSourceFiles().forEach(sf => {
|
this.emitMetadataJsonFiles(srcFilesToEmit, srcToOutPath);
|
||||||
if (!sf.isDeclarationFile && !GENERATED_FILES.test(sf.fileName)) {
|
|
||||||
metadataJsonCount++;
|
|
||||||
const metadata = this.metadataCache.getMetadata(sf);
|
|
||||||
const metadataText = JSON.stringify([metadata]);
|
|
||||||
const outFileName = srcToOutPath(sf.fileName.replace(/\.ts$/, '.metadata.json'));
|
|
||||||
this.writeFile(outFileName, metadataText, false, undefined, undefined, [sf]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
const emitEnd = Date.now();
|
const emitEnd = Date.now();
|
||||||
if (this.options.diagnostics) {
|
if (this.options.diagnostics) {
|
||||||
|
const genTsFileCount = srcFilesToEmit.filter(sf => GENERATED_FILES.test(sf.fileName)).length;
|
||||||
|
let genJsonCount = jsonFilesToEmit.length;
|
||||||
|
if (emitFlags & EmitFlags.Metadata) {
|
||||||
|
genJsonCount += genTsFileCount;
|
||||||
|
}
|
||||||
emitResult.diagnostics.push(createMessageDiagnostic([
|
emitResult.diagnostics.push(createMessageDiagnostic([
|
||||||
`Emitted in ${emitEnd - emitStart}ms`,
|
`Emitted in ${emitEnd - emitStart}ms`,
|
||||||
`- ${emittedUserTsCount} user ts files`,
|
`- ${srcFilesToEmit.length - genTsFileCount} user ts files`,
|
||||||
`- ${genTsFiles.length} generated ts files`,
|
`- ${genTsFileCount} generated ts files`,
|
||||||
`- ${genJsonFiles.length + metadataJsonCount} generated json files`,
|
`- ${genJsonCount} generated json files`,
|
||||||
].join('\n')));
|
].join('\n')));
|
||||||
}
|
}
|
||||||
return emitResult;
|
return emitResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private emitNgSummaryJsonFiles(
|
||||||
|
jsonFilesToEmit: GeneratedFile[], srcToOutPath: (srcFileName: string) => string) {
|
||||||
|
for (const gf of jsonFilesToEmit) {
|
||||||
|
const outFileName = srcToOutPath(gf.genFileName);
|
||||||
|
this.writeFile(outFileName, gf.source !, false, undefined, undefined, gf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private emitMetadataJsonFiles(
|
||||||
|
srcFilesToEmit: ts.SourceFile[], srcToOutPath: (srcFileName: string) => string) {
|
||||||
|
for (const sf of srcFilesToEmit) {
|
||||||
|
if (!GENERATED_FILES.test(sf.fileName)) {
|
||||||
|
const metadata = this.metadataCache.getMetadata(sf);
|
||||||
|
const metadataText = JSON.stringify([metadata]);
|
||||||
|
const outFileName = srcToOutPath(sf.fileName.replace(/\.ts$/, '.metadata.json'));
|
||||||
|
this.writeFile(outFileName, metadataText, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private calcFilesToEmit(targetFileNames?: string[]):
|
||||||
|
{srcFilesToEmit: ts.SourceFile[]; jsonFilesToEmit: GeneratedFile[]; emitAllFiles: boolean;} {
|
||||||
|
let srcFilesToEmit: ts.SourceFile[];
|
||||||
|
let jsonFilesToEmit: GeneratedFile[];
|
||||||
|
let emitAllFiles: boolean;
|
||||||
|
if (targetFileNames) {
|
||||||
|
emitAllFiles = false;
|
||||||
|
srcFilesToEmit = [];
|
||||||
|
jsonFilesToEmit = [];
|
||||||
|
for (const fileName of targetFileNames) {
|
||||||
|
const sf = this.tsProgram.getSourceFile(fileName);
|
||||||
|
if (!sf || sf.isDeclarationFile) continue;
|
||||||
|
srcFilesToEmit.push(sf);
|
||||||
|
if (!GENERATED_FILES.test(sf.fileName)) {
|
||||||
|
// find the .ngsummary.json file and mark it for emit as well
|
||||||
|
const ngSummaryFileName = sf.fileName.replace(EXT, '') + '.ngsummary.json';
|
||||||
|
const ngSummaryGenFile = this.getGeneratedFile(ngSummaryFileName);
|
||||||
|
if (ngSummaryGenFile) {
|
||||||
|
jsonFilesToEmit.push(ngSummaryGenFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const changedFiles: ts.SourceFile[] = [];
|
||||||
|
let useChangedFiles = !!this.oldProgramCachedFiles;
|
||||||
|
if (useChangedFiles) {
|
||||||
|
for (const sf of this.tsProgram.getSourceFiles()) {
|
||||||
|
if (sf.isDeclarationFile) continue;
|
||||||
|
if (this.hasChanged(sf.fileName)) {
|
||||||
|
changedFiles.push(sf);
|
||||||
|
}
|
||||||
|
if (changedFiles.length > MAX_FILE_COUNT_FOR_SINGLE_FILE_EMIT) {
|
||||||
|
useChangedFiles = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (useChangedFiles) {
|
||||||
|
emitAllFiles = false;
|
||||||
|
srcFilesToEmit = changedFiles;
|
||||||
|
jsonFilesToEmit = this.getGeneratedFiles().filter(
|
||||||
|
genFile => !!genFile.source && this.hasChanged(genFile.genFileName));
|
||||||
|
} else {
|
||||||
|
emitAllFiles = true;
|
||||||
|
srcFilesToEmit = this.tsProgram.getSourceFiles().filter(sf => !sf.isDeclarationFile);
|
||||||
|
jsonFilesToEmit = this.getGeneratedFiles().filter(genFile => !!genFile.source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {srcFilesToEmit, jsonFilesToEmit, emitAllFiles};
|
||||||
|
}
|
||||||
|
|
||||||
// Private members
|
// Private members
|
||||||
private get compiler(): AotCompiler {
|
private get compiler(): AotCompiler {
|
||||||
if (!this._compiler) {
|
if (!this._compiler) {
|
||||||
|
@ -374,14 +466,26 @@ class AngularCompilerProgram implements Program {
|
||||||
return this._typeCheckHost !;
|
return this._typeCheckHost !;
|
||||||
}
|
}
|
||||||
|
|
||||||
private calculateTransforms(
|
private get generatedFilesMap(): Map<string, GeneratedFile> {
|
||||||
genFiles: Map<string, GeneratedFile>,
|
if (!this._generatedFilesMap) {
|
||||||
customTransformers?: CustomTransformers): ts.CustomTransformers {
|
this.createGenerateFiles();
|
||||||
|
}
|
||||||
|
return this._generatedFilesMap !;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get generatedFiles(): GeneratedFile[] {
|
||||||
|
if (!this._generatedFilesMap) {
|
||||||
|
this.createGenerateFiles();
|
||||||
|
}
|
||||||
|
return this._generatedFiles !;
|
||||||
|
}
|
||||||
|
|
||||||
|
private calculateTransforms(customTransformers?: CustomTransformers): ts.CustomTransformers {
|
||||||
const beforeTs: ts.TransformerFactory<ts.SourceFile>[] = [];
|
const beforeTs: ts.TransformerFactory<ts.SourceFile>[] = [];
|
||||||
if (!this.options.disableExpressionLowering) {
|
if (!this.options.disableExpressionLowering) {
|
||||||
beforeTs.push(getExpressionLoweringTransformFactory(this.metadataCache));
|
beforeTs.push(getExpressionLoweringTransformFactory(this.metadataCache));
|
||||||
}
|
}
|
||||||
beforeTs.push(getAngularEmitterTransformFactory(genFiles));
|
beforeTs.push(getAngularEmitterTransformFactory(this));
|
||||||
if (customTransformers && customTransformers.beforeTs) {
|
if (customTransformers && customTransformers.beforeTs) {
|
||||||
beforeTs.push(...customTransformers.beforeTs);
|
beforeTs.push(...customTransformers.beforeTs);
|
||||||
}
|
}
|
||||||
|
@ -433,20 +537,20 @@ class AngularCompilerProgram implements Program {
|
||||||
let rootNames =
|
let rootNames =
|
||||||
this.rootNames.filter(fn => !GENERATED_FILES.test(fn) || !hostAdapter.isSourceFile(fn));
|
this.rootNames.filter(fn => !GENERATED_FILES.test(fn) || !hostAdapter.isSourceFile(fn));
|
||||||
if (this.options.noResolve) {
|
if (this.options.noResolve) {
|
||||||
this.rootNames.forEach(rootName => {
|
for (const rootName of this.rootNames) {
|
||||||
if (hostAdapter.shouldGenerateFilesFor(rootName)) {
|
if (hostAdapter.shouldGenerateFilesFor(rootName)) {
|
||||||
rootNames.push(...this._compiler.findGeneratedFileNames(rootName));
|
rootNames.push(...this._compiler.findGeneratedFileNames(rootName));
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const tmpProgram = ts.createProgram(rootNames, this.options, hostAdapter, oldTsProgram);
|
const tmpProgram = ts.createProgram(rootNames, this.options, hostAdapter, oldTsProgram);
|
||||||
const sourceFiles: string[] = [];
|
const sourceFiles: string[] = [];
|
||||||
tmpProgram.getSourceFiles().forEach(sf => {
|
for (const sf of tmpProgram.getSourceFiles()) {
|
||||||
if (hostAdapter.isSourceFile(sf.fileName)) {
|
if (hostAdapter.isSourceFile(sf.fileName)) {
|
||||||
sourceFiles.push(sf.fileName);
|
sourceFiles.push(sf.fileName);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
return {tmpProgram, sourceFiles, hostAdapter, rootNames};
|
return {tmpProgram, sourceFiles, hostAdapter, rootNames};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -455,7 +559,7 @@ class AngularCompilerProgram implements Program {
|
||||||
hostAdapter: TsCompilerAotCompilerTypeCheckHostAdapter, rootNames: string[]) {
|
hostAdapter: TsCompilerAotCompilerTypeCheckHostAdapter, rootNames: string[]) {
|
||||||
this._analyzedModules = analyzedModules || emptyModules;
|
this._analyzedModules = analyzedModules || emptyModules;
|
||||||
if (analyzedModules) {
|
if (analyzedModules) {
|
||||||
tmpProgram.getSourceFiles().forEach(sf => {
|
for (const sf of tmpProgram.getSourceFiles()) {
|
||||||
if (sf.fileName.endsWith('.ngfactory.ts')) {
|
if (sf.fileName.endsWith('.ngfactory.ts')) {
|
||||||
const {generate, baseFileName} = hostAdapter.shouldGenerateFile(sf.fileName);
|
const {generate, baseFileName} = hostAdapter.shouldGenerateFile(sf.fileName);
|
||||||
if (generate) {
|
if (generate) {
|
||||||
|
@ -467,7 +571,7 @@ class AngularCompilerProgram implements Program {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
this._tsProgram = ts.createProgram(rootNames, this.options, hostAdapter, tmpProgram);
|
this._tsProgram = ts.createProgram(rootNames, this.options, hostAdapter, tmpProgram);
|
||||||
// Note: the new ts program should be completely reusable by TypeScript as:
|
// Note: the new ts program should be completely reusable by TypeScript as:
|
||||||
|
@ -504,95 +608,64 @@ class AngularCompilerProgram implements Program {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: this returns a ts.Diagnostic so that we
|
private createGenerateFiles() {
|
||||||
// can return errors in a ts.EmitResult
|
this._generatedFiles = this.compiler.emitAllImpls(this.analyzedModules);
|
||||||
private generateFilesForEmit(emitFlags: EmitFlags):
|
this._generatedFilesMap = new Map<string, GeneratedFile>();
|
||||||
{genFiles: GeneratedFile[], genDiags: ts.Diagnostic[]} {
|
for (const genFile of this._generatedFiles) {
|
||||||
try {
|
this._generatedFilesMap !.set(genFile.genFileName, genFile);
|
||||||
if (!(emitFlags & EmitFlags.Codegen)) {
|
|
||||||
return {genFiles: [], genDiags: []};
|
|
||||||
}
|
|
||||||
let genFiles = this.compiler.emitAllImpls(this.analyzedModules);
|
|
||||||
if (this.oldProgramEmittedGeneratedFiles) {
|
|
||||||
const oldProgramEmittedGeneratedFiles = this.oldProgramEmittedGeneratedFiles;
|
|
||||||
genFiles = genFiles.filter(genFile => {
|
|
||||||
const oldGenFile = oldProgramEmittedGeneratedFiles.get(genFile.genFileUrl);
|
|
||||||
return !oldGenFile || !genFile.isEquivalent(oldGenFile);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return {genFiles, genDiags: []};
|
|
||||||
} catch (e) {
|
|
||||||
// TODO(tbosch): check whether we can actually have syntax errors here,
|
|
||||||
// as we already parsed the metadata and templates before to create the type check block.
|
|
||||||
if (isSyntaxError(e)) {
|
|
||||||
const genDiags: ts.Diagnostic[] = [{
|
|
||||||
file: undefined,
|
|
||||||
start: undefined,
|
|
||||||
length: undefined,
|
|
||||||
messageText: e.message,
|
|
||||||
category: ts.DiagnosticCategory.Error,
|
|
||||||
source: SOURCE,
|
|
||||||
code: DEFAULT_ERROR_CODE
|
|
||||||
}];
|
|
||||||
return {genFiles: [], genDiags};
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 writeFile(
|
private writeFile(
|
||||||
outFileName: string, outData: string, writeByteOrderMark: boolean,
|
outFileName: string, outData: string, writeByteOrderMark: boolean,
|
||||||
onError?: (message: string) => void, genFile?: GeneratedFile, sourceFiles?: ts.SourceFile[]) {
|
onError?: (message: string) => void, sourceFiles?: ts.SourceFile[], genFile?: GeneratedFile) {
|
||||||
|
const isGenerated = GENERATED_FILES.test(outFileName);
|
||||||
|
if (!isGenerated && sourceFiles && sourceFiles.length === 1) {
|
||||||
|
const sf = sourceFiles[0];
|
||||||
|
if (!this.emittedSourceFiles) {
|
||||||
|
this.emittedSourceFiles = [];
|
||||||
|
}
|
||||||
|
// Note: sourceFile is the transformed sourcefile, not the original one!
|
||||||
|
this.emittedSourceFiles.push(this.tsProgram.getSourceFile(sf.fileName));
|
||||||
|
}
|
||||||
// collect emittedLibrarySummaries
|
// collect emittedLibrarySummaries
|
||||||
let baseFile: ts.SourceFile|undefined;
|
let baseFile: ts.SourceFile|undefined;
|
||||||
if (genFile) {
|
if (genFile) {
|
||||||
baseFile = this.tsProgram.getSourceFile(genFile.srcFileUrl);
|
baseFile = this.tsProgram.getSourceFile(genFile.srcFileName);
|
||||||
if (baseFile) {
|
if (baseFile) {
|
||||||
if (!this.emittedLibrarySummaries) {
|
if (!this.emittedLibrarySummaries) {
|
||||||
this.emittedLibrarySummaries = [];
|
this.emittedLibrarySummaries = [];
|
||||||
}
|
}
|
||||||
if (genFile.genFileUrl.endsWith('.ngsummary.json') && baseFile.fileName.endsWith('.d.ts')) {
|
if (genFile.genFileName.endsWith('.ngsummary.json') &&
|
||||||
|
baseFile.fileName.endsWith('.d.ts')) {
|
||||||
this.emittedLibrarySummaries.push({
|
this.emittedLibrarySummaries.push({
|
||||||
fileName: baseFile.fileName,
|
fileName: baseFile.fileName,
|
||||||
text: baseFile.text,
|
text: baseFile.text,
|
||||||
sourceFile: baseFile,
|
sourceFile: baseFile,
|
||||||
});
|
});
|
||||||
this.emittedLibrarySummaries.push({fileName: genFile.genFileUrl, text: outData});
|
this.emittedLibrarySummaries.push({fileName: genFile.genFileName, text: outData});
|
||||||
if (!this.options.declaration) {
|
if (!this.options.declaration) {
|
||||||
// If we don't emit declarations, still record an empty .ngfactory.d.ts file,
|
// If we don't emit declarations, still record an empty .ngfactory.d.ts file,
|
||||||
// as we might need it lateron for resolving module names from summaries.
|
// as we might need it lateron for resolving module names from summaries.
|
||||||
const ngFactoryDts =
|
const ngFactoryDts = genFile.genFileName.substring(0, genFile.genFileName.length - 15) +
|
||||||
genFile.genFileUrl.substring(0, genFile.genFileUrl.length - 15) + '.ngfactory.d.ts';
|
'.ngfactory.d.ts';
|
||||||
this.emittedLibrarySummaries.push({fileName: ngFactoryDts, text: ''});
|
this.emittedLibrarySummaries.push({fileName: ngFactoryDts, text: ''});
|
||||||
}
|
}
|
||||||
} else if (outFileName.endsWith('.d.ts') && baseFile.fileName.endsWith('.d.ts')) {
|
} else if (outFileName.endsWith('.d.ts') && baseFile.fileName.endsWith('.d.ts')) {
|
||||||
const dtsSourceFilePath = genFile.genFileUrl.replace(/\.ts$/, '.d.ts');
|
const dtsSourceFilePath = genFile.genFileName.replace(/\.ts$/, '.d.ts');
|
||||||
// Note: Don't use sourceFiles here as the created .d.ts has a path in the outDir,
|
// Note: Don't use sourceFiles here as the created .d.ts has a path in the outDir,
|
||||||
// but we need one that is next to the .ts file
|
// but we need one that is next to the .ts file
|
||||||
this.emittedLibrarySummaries.push({fileName: dtsSourceFilePath, text: outData});
|
this.emittedLibrarySummaries.push({fileName: dtsSourceFilePath, text: outData});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!this.emittedGeneratedFiles) {
|
||||||
|
this.emittedGeneratedFiles = [];
|
||||||
|
}
|
||||||
|
this.emittedGeneratedFiles.push(genFile);
|
||||||
}
|
}
|
||||||
// Filter out generated files for which we didn't generate code.
|
// Filter out generated files for which we didn't generate code.
|
||||||
// This can happen as the stub caclulation is not completely exact.
|
// This can happen as the stub calculation is not completely exact.
|
||||||
// Note: sourceFile refers to the .ngfactory.ts / .ngsummary.ts file
|
// Note: sourceFile refers to the .ngfactory.ts / .ngsummary.ts file
|
||||||
const isGenerated = GENERATED_FILES.test(outFileName);
|
|
||||||
if (isGenerated) {
|
if (isGenerated) {
|
||||||
if (!genFile || !genFile.stmts || genFile.stmts.length === 0) {
|
if (!genFile || !genFile.stmts || genFile.stmts.length === 0) {
|
||||||
if (this.options.allowEmptyCodegenFiles) {
|
if (this.options.allowEmptyCodegenFiles) {
|
||||||
|
@ -609,12 +682,13 @@ class AngularCompilerProgram implements Program {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createProgram(
|
export const createProgram: CreateProgram = ({rootNames, options, host, oldProgram}: {
|
||||||
{rootNames, options, host, oldProgram}:
|
rootNames: string[],
|
||||||
{rootNames: string[], options: CompilerOptions, host: CompilerHost, oldProgram?: Program}):
|
options: CompilerOptions,
|
||||||
Program {
|
host: CompilerHost, oldProgram?: OldProgram
|
||||||
|
}) => {
|
||||||
return new AngularCompilerProgram(rootNames, options, host, oldProgram);
|
return new AngularCompilerProgram(rootNames, options, host, oldProgram);
|
||||||
}
|
};
|
||||||
|
|
||||||
// Compute the AotCompiler options
|
// Compute the AotCompiler options
|
||||||
function getAotCompilerOptions(options: CompilerOptions): AotCompilerOptions {
|
function getAotCompilerOptions(options: CompilerOptions): AotCompilerOptions {
|
||||||
|
@ -768,15 +842,3 @@ export function i18nGetExtension(formatName: string): string {
|
||||||
|
|
||||||
throw new Error(`Unsupported format "${formatName}"`);
|
throw new Error(`Unsupported format "${formatName}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function mergeEmitResults(emitResults: ts.EmitResult[]): ts.EmitResult {
|
|
||||||
const diagnostics: ts.Diagnostic[] = [];
|
|
||||||
let emitSkipped = false;
|
|
||||||
const emittedFiles: string[] = [];
|
|
||||||
for (const er of emitResults) {
|
|
||||||
diagnostics.push(...er.diagnostics);
|
|
||||||
emitSkipped = emitSkipped || er.emitSkipped;
|
|
||||||
emittedFiles.push(...er.emittedFiles);
|
|
||||||
}
|
|
||||||
return {diagnostics, emitSkipped, emittedFiles};
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import * as ts from 'typescript';
|
||||||
import {DEFAULT_ERROR_CODE, Diagnostic, SOURCE} from './api';
|
import {DEFAULT_ERROR_CODE, Diagnostic, SOURCE} from './api';
|
||||||
|
|
||||||
export const GENERATED_FILES = /(.*?)\.(ngfactory|shim\.ngstyle|ngstyle|ngsummary)\.(js|d\.ts|ts)$/;
|
export const GENERATED_FILES = /(.*?)\.(ngfactory|shim\.ngstyle|ngstyle|ngsummary)\.(js|d\.ts|ts)$/;
|
||||||
|
export const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
||||||
|
|
||||||
export const enum StructureIsReused {Not = 0, SafeModules = 1, Completely = 2}
|
export const enum StructureIsReused {Not = 0, SafeModules = 1, Completely = 2}
|
||||||
|
|
||||||
|
@ -29,3 +30,7 @@ export function createMessageDiagnostic(messageText: string): ts.Diagnostic&Diag
|
||||||
source: SOURCE,
|
source: SOURCE,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isGeneratedFile(fileName: string): boolean {
|
||||||
|
return GENERATED_FILES.test(fileName);
|
||||||
|
}
|
||||||
|
|
|
@ -58,7 +58,8 @@ describe('ng program', () => {
|
||||||
|
|
||||||
function compile(
|
function compile(
|
||||||
oldProgram?: ng.Program, overrideOptions?: ng.CompilerOptions, rootNames?: string[],
|
oldProgram?: ng.Program, overrideOptions?: ng.CompilerOptions, rootNames?: string[],
|
||||||
host?: CompilerHost): {program: ng.Program, emitResult: ts.EmitResult} {
|
host?: CompilerHost,
|
||||||
|
targetFileNames?: string[]): {program: ng.Program, emitResult: ts.EmitResult} {
|
||||||
const options = testSupport.createCompilerOptions(overrideOptions);
|
const options = testSupport.createCompilerOptions(overrideOptions);
|
||||||
if (!rootNames) {
|
if (!rootNames) {
|
||||||
rootNames = [path.resolve(testSupport.basePath, 'src/index.ts')];
|
rootNames = [path.resolve(testSupport.basePath, 'src/index.ts')];
|
||||||
|
@ -73,7 +74,7 @@ describe('ng program', () => {
|
||||||
oldProgram,
|
oldProgram,
|
||||||
});
|
});
|
||||||
expectNoDiagnosticsInProgram(options, program);
|
expectNoDiagnosticsInProgram(options, program);
|
||||||
const emitResult = program.emit();
|
const emitResult = program.emit({targetFileNames});
|
||||||
return {emitResult, program};
|
return {emitResult, program};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -475,6 +476,38 @@ describe('ng program', () => {
|
||||||
testSupport.shouldNotExist('built/node_modules/lib/index.ngsummary.json');
|
testSupport.shouldNotExist('built/node_modules/lib/index.ngsummary.json');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should only emit the given files', () => {
|
||||||
|
testSupport.writeFiles({
|
||||||
|
'src/main.ts': createModuleAndCompSource('main'),
|
||||||
|
'src/main2.ts': createModuleAndCompSource('main2'),
|
||||||
|
'src/index.ts': `
|
||||||
|
export * from './main';
|
||||||
|
export * from './main2';
|
||||||
|
`
|
||||||
|
});
|
||||||
|
compile(undefined, undefined, undefined, undefined, [
|
||||||
|
path.resolve(testSupport.basePath, 'src/index.ts'),
|
||||||
|
path.resolve(testSupport.basePath, 'src/index.ngfactory.ts'),
|
||||||
|
path.resolve(testSupport.basePath, 'src/main.ts'),
|
||||||
|
path.resolve(testSupport.basePath, 'src/main.ngfactory.ts')
|
||||||
|
]);
|
||||||
|
testSupport.shouldExist('built/src/main.js');
|
||||||
|
testSupport.shouldExist('built/src/main.d.ts');
|
||||||
|
testSupport.shouldExist('built/src/main.ngfactory.js');
|
||||||
|
testSupport.shouldExist('built/src/main.ngfactory.d.ts');
|
||||||
|
testSupport.shouldExist('built/src/main.ngsummary.json');
|
||||||
|
testSupport.shouldNotExist('built/src/main2.js');
|
||||||
|
testSupport.shouldNotExist('built/src/main2.d.ts');
|
||||||
|
testSupport.shouldNotExist('built/src/main2.ngfactory.js');
|
||||||
|
testSupport.shouldNotExist('built/src/main2.ngfactory.d.ts');
|
||||||
|
testSupport.shouldNotExist('built/src/main2.ngsummary.json');
|
||||||
|
testSupport.shouldNotExist('built/node_modules/lib/index.js');
|
||||||
|
testSupport.shouldNotExist('built/node_modules/lib/index.d.ts');
|
||||||
|
testSupport.shouldNotExist('built/node_modules/lib/index.ngfactory.js');
|
||||||
|
testSupport.shouldNotExist('built/node_modules/lib/index.ngfactory.d.ts');
|
||||||
|
testSupport.shouldNotExist('built/node_modules/lib/index.ngsummary.json');
|
||||||
|
});
|
||||||
|
|
||||||
describe('createSrcToOutPathMapper', () => {
|
describe('createSrcToOutPathMapper', () => {
|
||||||
it('should return identity mapping if no outDir is present', () => {
|
it('should return identity mapping if no outDir is present', () => {
|
||||||
const mapper = createSrcToOutPathMapper(undefined, undefined, undefined);
|
const mapper = createSrcToOutPathMapper(undefined, undefined, undefined);
|
||||||
|
|
|
@ -14,7 +14,7 @@ export class GeneratedFile {
|
||||||
public stmts: Statement[]|null;
|
public stmts: Statement[]|null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public srcFileUrl: string, public genFileUrl: string, sourceOrStmts: string|Statement[]) {
|
public srcFileName: string, public genFileName: string, sourceOrStmts: string|Statement[]) {
|
||||||
if (typeof sourceOrStmts === 'string') {
|
if (typeof sourceOrStmts === 'string') {
|
||||||
this.source = sourceOrStmts;
|
this.source = sourceOrStmts;
|
||||||
this.stmts = null;
|
this.stmts = null;
|
||||||
|
@ -25,7 +25,7 @@ export class GeneratedFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
isEquivalent(other: GeneratedFile): boolean {
|
isEquivalent(other: GeneratedFile): boolean {
|
||||||
if (this.genFileUrl !== other.genFileUrl) {
|
if (this.genFileName !== other.genFileName) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (this.source) {
|
if (this.source) {
|
||||||
|
@ -42,7 +42,7 @@ export class GeneratedFile {
|
||||||
|
|
||||||
export function toTypeScript(file: GeneratedFile, preamble: string = ''): string {
|
export function toTypeScript(file: GeneratedFile, preamble: string = ''): string {
|
||||||
if (!file.stmts) {
|
if (!file.stmts) {
|
||||||
throw new Error(`Illegal state: No stmts present on GeneratedFile ${file.genFileUrl}`);
|
throw new Error(`Illegal state: No stmts present on GeneratedFile ${file.genFileName}`);
|
||||||
}
|
}
|
||||||
return new TypeScriptEmitter().emitStatements(file.genFileUrl, file.stmts, preamble);
|
return new TypeScriptEmitter().emitStatements(file.genFileName, file.stmts, preamble);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,8 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
describe('Quickstart', () => {
|
describe('Quickstart', () => {
|
||||||
it('should compile', () => {
|
it('should compile', () => {
|
||||||
const {genFiles} = compile([QUICKSTART, angularFiles]);
|
const {genFiles} = compile([QUICKSTART, angularFiles]);
|
||||||
expect(genFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined();
|
expect(genFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileName))).toBeDefined();
|
||||||
expect(genFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined();
|
expect(genFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileName))).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
function compileApp(): GeneratedFile {
|
function compileApp(): GeneratedFile {
|
||||||
const {genFiles} = compile([rootDir, angularFiles]);
|
const {genFiles} = compile([rootDir, angularFiles]);
|
||||||
return genFiles.find(
|
return genFiles.find(
|
||||||
genFile => genFile.srcFileUrl === componentPath && genFile.genFileUrl.endsWith('.ts'));
|
genFile => genFile.srcFileName === componentPath && genFile.genFileName.endsWith('.ts'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function findLineAndColumn(
|
function findLineAndColumn(
|
||||||
|
@ -127,7 +127,7 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
const genFile = compileApp();
|
const genFile = compileApp();
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
const sourceMap = extractSourceMap(genSource) !;
|
const sourceMap = extractSourceMap(genSource) !;
|
||||||
expect(sourceMap.file).toEqual(genFile.genFileUrl);
|
expect(sourceMap.file).toEqual(genFile.genFileName);
|
||||||
|
|
||||||
// Note: the generated file also contains code that is not mapped to
|
// Note: the generated file also contains code that is not mapped to
|
||||||
// the template (e.g. import statements, ...)
|
// the template (e.g. import statements, ...)
|
||||||
|
@ -322,7 +322,7 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
const genFilePreamble = '/* Hello world! */';
|
const genFilePreamble = '/* Hello world! */';
|
||||||
const {genFiles} = compile([FILES, angularFiles]);
|
const {genFiles} = compile([FILES, angularFiles]);
|
||||||
const genFile =
|
const genFile =
|
||||||
genFiles.find(gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'));
|
genFiles.find(gf => gf.srcFileName === '/app/app.ts' && gf.genFileName.endsWith('.ts'));
|
||||||
const genSource = toTypeScript(genFile, genFilePreamble);
|
const genSource = toTypeScript(genFile, genFilePreamble);
|
||||||
expect(genSource.startsWith(genFilePreamble)).toBe(true);
|
expect(genSource.startsWith(genFilePreamble)).toBe(true);
|
||||||
});
|
});
|
||||||
|
@ -407,7 +407,7 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
};
|
};
|
||||||
const result = compile([FILES, angularFiles]);
|
const result = compile([FILES, angularFiles]);
|
||||||
const appModuleFactory =
|
const appModuleFactory =
|
||||||
result.genFiles.find(f => /my-component\.ngfactory/.test(f.genFileUrl));
|
result.genFiles.find(f => /my-component\.ngfactory/.test(f.genFileName));
|
||||||
expect(appModuleFactory).toBeDefined();
|
expect(appModuleFactory).toBeDefined();
|
||||||
if (appModuleFactory) {
|
if (appModuleFactory) {
|
||||||
expect(toTypeScript(appModuleFactory)).toContain('MyComponentNgFactory');
|
expect(toTypeScript(appModuleFactory)).toContain('MyComponentNgFactory');
|
||||||
|
@ -441,7 +441,7 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const {genFiles} = compile([FILES, angularFiles]);
|
const {genFiles} = compile([FILES, angularFiles]);
|
||||||
const genFile = genFiles.find(genFile => genFile.srcFileUrl === '/app/app.ts');
|
const genFile = genFiles.find(genFile => genFile.srcFileName === '/app/app.ts');
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
const createComponentFactoryCall = /ɵccf\([^)]*\)/m.exec(genSource) ![0].replace(/\s*/g, '');
|
const createComponentFactoryCall = /ɵccf\([^)]*\)/m.exec(genSource) ![0].replace(/\s*/g, '');
|
||||||
// selector
|
// selector
|
||||||
|
@ -472,7 +472,7 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
};
|
};
|
||||||
const {genFiles} = compile([FILES, angularFiles]);
|
const {genFiles} = compile([FILES, angularFiles]);
|
||||||
const genFile =
|
const genFile =
|
||||||
genFiles.find(gf => gf.srcFileUrl === '/app/app.ts' && gf.genFileUrl.endsWith('.ts'));
|
genFiles.find(gf => gf.srcFileName === '/app/app.ts' && gf.genFileName.endsWith('.ts'));
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
expect(genSource).not.toContain('check(');
|
expect(genSource).not.toContain('check(');
|
||||||
|
|
||||||
|
@ -510,7 +510,7 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
const {outDir: libOutDir} = compile([libInput, angularSummaryFiles], {useSummaries: true});
|
const {outDir: libOutDir} = compile([libInput, angularSummaryFiles], {useSummaries: true});
|
||||||
const {genFiles: appGenFiles} =
|
const {genFiles: appGenFiles} =
|
||||||
compile([appInput, libOutDir, angularSummaryFiles], {useSummaries: true});
|
compile([appInput, libOutDir, angularSummaryFiles], {useSummaries: true});
|
||||||
const appNgFactory = appGenFiles.find((f) => f.genFileUrl === '/app/main.ngfactory.ts');
|
const appNgFactory = appGenFiles.find((f) => f.genFileName === '/app/main.ngfactory.ts');
|
||||||
expect(toTypeScript(appNgFactory)).not.toContain('AType');
|
expect(toTypeScript(appNgFactory)).not.toContain('AType');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -555,7 +555,7 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
compile([libInput, getAngularSummaryFiles()], {useSummaries: true});
|
compile([libInput, getAngularSummaryFiles()], {useSummaries: true});
|
||||||
const {genFiles} =
|
const {genFiles} =
|
||||||
compile([libOutDir, appInput, getAngularSummaryFiles()], {useSummaries: true});
|
compile([libOutDir, appInput, getAngularSummaryFiles()], {useSummaries: true});
|
||||||
return genFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
|
return genFiles.find(gf => gf.srcFileName === '/app/main.ts');
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should inherit ctor and lifecycle hooks from classes in other compilation units', () => {
|
it('should inherit ctor and lifecycle hooks from classes in other compilation units', () => {
|
||||||
|
@ -592,7 +592,7 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
compile([libInput, getAngularSummaryFiles()], {useSummaries: true});
|
compile([libInput, getAngularSummaryFiles()], {useSummaries: true});
|
||||||
const {genFiles} =
|
const {genFiles} =
|
||||||
compile([libOutDir, appInput, getAngularSummaryFiles()], {useSummaries: true});
|
compile([libOutDir, appInput, getAngularSummaryFiles()], {useSummaries: true});
|
||||||
const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
|
const mainNgFactory = genFiles.find(gf => gf.srcFileName === '/app/main.ts');
|
||||||
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
|
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
|
||||||
expect(toTypeScript(mainNgFactory))
|
expect(toTypeScript(mainNgFactory))
|
||||||
.toContain(`${flags},(null as any),0,i1.Extends,[i2.AParam]`);
|
.toContain(`${flags},(null as any),0,i1.Extends,[i2.AParam]`);
|
||||||
|
@ -645,7 +645,7 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
compile([lib1OutDir, lib2Input, getAngularSummaryFiles()], {useSummaries: true});
|
compile([lib1OutDir, lib2Input, getAngularSummaryFiles()], {useSummaries: true});
|
||||||
const {genFiles} =
|
const {genFiles} =
|
||||||
compile([lib2OutDir, appInput, getAngularSummaryFiles()], {useSummaries: true});
|
compile([lib2OutDir, appInput, getAngularSummaryFiles()], {useSummaries: true});
|
||||||
const mainNgFactory = genFiles.find(gf => gf.srcFileUrl === '/app/main.ts');
|
const mainNgFactory = genFiles.find(gf => gf.srcFileName === '/app/main.ts');
|
||||||
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
|
const flags = NodeFlags.TypeDirective | NodeFlags.Component | NodeFlags.OnDestroy;
|
||||||
expect(toTypeScript(mainNgFactory))
|
expect(toTypeScript(mainNgFactory))
|
||||||
.toContain(`${flags},(null as any),0,i1.Extends,[i2.AParam_2]`);
|
.toContain(`${flags},(null as any),0,i1.Extends,[i2.AParam_2]`);
|
||||||
|
@ -803,8 +803,8 @@ describe('compiler (bundled Angular)', () => {
|
||||||
describe('Quickstart', () => {
|
describe('Quickstart', () => {
|
||||||
it('should compile', () => {
|
it('should compile', () => {
|
||||||
const {genFiles} = compile([QUICKSTART, angularFiles]);
|
const {genFiles} = compile([QUICKSTART, angularFiles]);
|
||||||
expect(genFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined();
|
expect(genFiles.find(f => /app\.component\.ngfactory\.ts/.test(f.genFileName))).toBeDefined();
|
||||||
expect(genFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileUrl))).toBeDefined();
|
expect(genFiles.find(f => /app\.module\.ngfactory\.ts/.test(f.genFileName))).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ describe('aot summaries for jit', () => {
|
||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileName === '/app/app.module.ngsummary.ts');
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
||||||
|
@ -70,7 +70,7 @@ describe('aot summaries for jit', () => {
|
||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileName === '/app/app.module.ngsummary.ts');
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
||||||
|
@ -99,7 +99,7 @@ describe('aot summaries for jit', () => {
|
||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileName === '/app/app.module.ngsummary.ts');
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
||||||
|
@ -125,7 +125,7 @@ describe('aot summaries for jit', () => {
|
||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileName === '/app/app.module.ngsummary.ts');
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
expect(genSource).toContain(`import * as i0 from '/app/app.module'`);
|
||||||
|
@ -164,7 +164,7 @@ describe('aot summaries for jit', () => {
|
||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileName === '/app/app.module.ngsummary.ts');
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
expect(genSource).toMatch(/useClass:\{\s*reference:i1.MyService/);
|
expect(genSource).toMatch(/useClass:\{\s*reference:i1.MyService/);
|
||||||
|
@ -198,7 +198,7 @@ describe('aot summaries for jit', () => {
|
||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileName === '/app/app.module.ngsummary.ts');
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
expect(genSource).toMatch(/useClass:\{\s*reference:i1.MyService/);
|
expect(genSource).toMatch(/useClass:\{\s*reference:i1.MyService/);
|
||||||
|
@ -225,7 +225,7 @@ describe('aot summaries for jit', () => {
|
||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileName === '/app/app.module.ngsummary.ts');
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
expect(genSource).toMatch(
|
expect(genSource).toMatch(
|
||||||
|
@ -247,7 +247,7 @@ describe('aot summaries for jit', () => {
|
||||||
const rootDir = {'app': appDir};
|
const rootDir = {'app': appDir};
|
||||||
|
|
||||||
const genFile =
|
const genFile =
|
||||||
compileApp(rootDir).genFiles.find(f => f.genFileUrl === '/app/app.module.ngsummary.ts');
|
compileApp(rootDir).genFiles.find(f => f.genFileName === '/app/app.module.ngsummary.ts');
|
||||||
const genSource = toTypeScript(genFile);
|
const genSource = toTypeScript(genFile);
|
||||||
|
|
||||||
expect(genSource).toMatch(
|
expect(genSource).toMatch(
|
||||||
|
@ -294,9 +294,9 @@ describe('aot summaries for jit', () => {
|
||||||
};
|
};
|
||||||
const {outDir: lib3In, genFiles: lib2Gen} = compileApp(lib2In, {useSummaries: true});
|
const {outDir: lib3In, genFiles: lib2Gen} = compileApp(lib2In, {useSummaries: true});
|
||||||
|
|
||||||
const lib2ModuleNgSummary = lib2Gen.find(f => f.genFileUrl === '/lib2/module.ngsummary.ts');
|
const lib2ModuleNgSummary = lib2Gen.find(f => f.genFileName === '/lib2/module.ngsummary.ts');
|
||||||
const lib2ReexportNgSummary =
|
const lib2ReexportNgSummary =
|
||||||
lib2Gen.find(f => f.genFileUrl === '/lib2/reexport.ngsummary.ts');
|
lib2Gen.find(f => f.genFileName === '/lib2/reexport.ngsummary.ts');
|
||||||
|
|
||||||
// ngsummaries should add reexports for imported NgModules from a direct dependency
|
// ngsummaries should add reexports for imported NgModules from a direct dependency
|
||||||
expect(toTypeScript(lib2ModuleNgSummary))
|
expect(toTypeScript(lib2ModuleNgSummary))
|
||||||
|
@ -325,9 +325,9 @@ describe('aot summaries for jit', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const lib3Gen = compileApp(lib3In, {useSummaries: true}).genFiles;
|
const lib3Gen = compileApp(lib3In, {useSummaries: true}).genFiles;
|
||||||
const lib3ModuleNgSummary = lib3Gen.find(f => f.genFileUrl === '/lib3/module.ngsummary.ts');
|
const lib3ModuleNgSummary = lib3Gen.find(f => f.genFileName === '/lib3/module.ngsummary.ts');
|
||||||
const lib3ReexportNgSummary =
|
const lib3ReexportNgSummary =
|
||||||
lib3Gen.find(f => f.genFileUrl === '/lib3/reexport.ngsummary.ts');
|
lib3Gen.find(f => f.genFileName === '/lib3/reexport.ngsummary.ts');
|
||||||
|
|
||||||
// ngsummary.ts files should use the reexported values from direct and deep deps
|
// ngsummary.ts files should use the reexported values from direct and deep deps
|
||||||
const lib3ModuleNgSummarySource = toTypeScript(lib3ModuleNgSummary);
|
const lib3ModuleNgSummarySource = toTypeScript(lib3ModuleNgSummary);
|
||||||
|
|
|
@ -27,6 +27,6 @@ describe('regressions', () => {
|
||||||
const {genFiles} = compile(
|
const {genFiles} = compile(
|
||||||
[rootDir, angularFiles], {postCompile: expectNoDiagnostics},
|
[rootDir, angularFiles], {postCompile: expectNoDiagnostics},
|
||||||
{noUnusedLocals: true, noUnusedParameters: true});
|
{noUnusedLocals: true, noUnusedParameters: true});
|
||||||
expect(genFiles.find((f) => f.genFileUrl === '/app/app.module.ngfactory.ts')).toBeTruthy();
|
expect(genFiles.find((f) => f.genFileName === '/app/app.module.ngfactory.ts')).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -658,10 +658,10 @@ export function compile(
|
||||||
const genFiles = compiler.emitAllImpls(analyzedModules);
|
const genFiles = compiler.emitAllImpls(analyzedModules);
|
||||||
genFiles.forEach((file) => {
|
genFiles.forEach((file) => {
|
||||||
const source = file.source || toTypeScript(file);
|
const source = file.source || toTypeScript(file);
|
||||||
if (isSource(file.genFileUrl)) {
|
if (isSource(file.genFileName)) {
|
||||||
host.addScript(file.genFileUrl, source);
|
host.addScript(file.genFileName, source);
|
||||||
} else {
|
} else {
|
||||||
host.override(file.genFileUrl, source);
|
host.override(file.genFileName, source);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const newProgram = ts.createProgram(host.scriptNames.slice(0), tsSettings, host);
|
const newProgram = ts.createProgram(host.scriptNames.slice(0), tsSettings, host);
|
||||||
|
@ -671,7 +671,7 @@ export function compile(
|
||||||
}
|
}
|
||||||
let outDir: MockDirectory = {};
|
let outDir: MockDirectory = {};
|
||||||
if (emit) {
|
if (emit) {
|
||||||
const dtsFilesWithGenFiles = new Set<string>(genFiles.map(gf => gf.srcFileUrl).filter(isDts));
|
const dtsFilesWithGenFiles = new Set<string>(genFiles.map(gf => gf.srcFileName).filter(isDts));
|
||||||
outDir =
|
outDir =
|
||||||
arrayToMockDir(toMockFileArray([host.writtenFiles, host.overrides])
|
arrayToMockDir(toMockFileArray([host.writtenFiles, host.overrides])
|
||||||
.filter((entry) => !isSource(entry.fileName))
|
.filter((entry) => !isSource(entry.fileName))
|
||||||
|
|
Loading…
Reference in New Issue