Revert "refactor(compiler): refactor and export Bazel host creation. (#34686)" (#35063)

This reverts commit cb142b6df9.

The intention of this commit was for a consumer of the `compile` function to
pass the `bazelHost` it returns into future invocations, reusing the
`FileCache` between them. However, first-party ngc_wrapped does not do this,
which caused a performance regression as the `FileCache` was no longer
shared between compilations.

PR Close #35063
This commit is contained in:
Alex Rickabaugh 2020-01-30 11:04:27 -08:00 committed by Andrew Kushnir
parent cd9ae66b35
commit 9f5b490800
1 changed files with 64 additions and 72 deletions

View File

@ -7,7 +7,7 @@
*/ */
import * as ng from '@angular/compiler-cli'; import * as ng from '@angular/compiler-cli';
import {BazelOptions, CachedFileLoader, CompilerHost as BazelHost, FileCache, FileLoader, UncachedFileLoader, constructManifest, debug, parseTsconfig, resolveNormalizedPath, runAsWorker, runWorkerLoop} from '@bazel/typescript'; import {BazelOptions, CachedFileLoader, CompilerHost, FileCache, FileLoader, UncachedFileLoader, constructManifest, debug, parseTsconfig, resolveNormalizedPath, 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';
@ -35,6 +35,9 @@ export function main(args) {
return 0; return 0;
} }
/** The one FileCache instance used in this process. */
const fileCache = new FileCache<ts.SourceFile>(debug);
export function runOneBuild(args: string[], inputs?: {[path: string]: string}): boolean { export function runOneBuild(args: string[], inputs?: {[path: string]: string}): boolean {
if (args[0] === '-p') args.shift(); if (args[0] === '-p') args.shift();
// Strip leading at-signs, used to indicate a params file // Strip leading at-signs, used to indicate a params file
@ -141,10 +144,8 @@ export function relativeToRootDirs(filePath: string, rootDirs: string[]): string
return filePath; return filePath;
} }
export function compile({ export function compile({allDepsCompiledWithBazel = true, compilerOpts, tsHost, bazelOpts, files,
allDepsCompiledWithBazel = true, compilerOpts, tsHost, bazelOpts, files, inputs, expectedOuts, inputs, expectedOuts, gatherDiagnostics, bazelHost}: {
gatherDiagnostics, bazelHost,
}: {
allDepsCompiledWithBazel?: boolean, allDepsCompiledWithBazel?: boolean,
compilerOpts: ng.CompilerOptions, compilerOpts: ng.CompilerOptions,
tsHost: ts.CompilerHost, inputs?: {[path: string]: string}, tsHost: ts.CompilerHost, inputs?: {[path: string]: string},
@ -152,15 +153,41 @@ export function compile({
files: string[], files: string[],
expectedOuts: string[], expectedOuts: string[],
gatherDiagnostics?: (program: ng.Program) => ng.Diagnostics, gatherDiagnostics?: (program: ng.Program) => ng.Diagnostics,
bazelHost?: BazelHost, bazelHost?: CompilerHost,
}): {diagnostics: ng.Diagnostics, program: ng.Program} { }): {diagnostics: ng.Diagnostics, program: ng.Program} {
let fileLoader: FileLoader;
if (bazelOpts.maxCacheSizeMb !== undefined) {
const maxCacheSizeBytes = bazelOpts.maxCacheSizeMb * (1 << 20);
fileCache.setMaxCacheSize(maxCacheSizeBytes);
} else {
fileCache.resetMaxCacheSize();
}
if (inputs) {
fileLoader = new CachedFileLoader(fileCache);
// Resolve the inputs to absolute paths to match TypeScript internals
const resolvedInputs = new Map<string, string>();
const inputKeys = Object.keys(inputs);
for (let i = 0; i < inputKeys.length; i++) {
const key = inputKeys[i];
resolvedInputs.set(resolveNormalizedPath(key), inputs[key]);
}
fileCache.updateCache(resolvedInputs);
} else {
fileLoader = new UncachedFileLoader();
}
if (!bazelOpts.es5Mode) { if (!bazelOpts.es5Mode) {
compilerOpts.annotateForClosureCompiler = true; compilerOpts.annotateForClosureCompiler = true;
compilerOpts.annotationsAs = 'static fields'; compilerOpts.annotationsAs = 'static fields';
} }
// Detect from compilerOpts whether the entrypoint is being invoked in Ivy mode.
const isInIvyMode = compilerOpts.enableIvy === 'ngtsc';
// Disable downleveling and Closure annotation if in Ivy mode. // Disable downleveling and Closure annotation if in Ivy mode.
if (compilerOpts.enableIvy) { if (isInIvyMode) {
compilerOpts.annotationsAs = 'decorators'; compilerOpts.annotationsAs = 'decorators';
} }
@ -185,14 +212,41 @@ export function compile({
} }
}; };
// Patch fileExists when resolving modules, so that CompilerHost can ask TypeScript to
// resolve non-existing generated files that don't exist on disk, but are
// synthetic and added to the `programWithStubs` based on real inputs.
const generatedFileModuleResolverHost = Object.create(tsHost);
generatedFileModuleResolverHost.fileExists = (fileName: string) => {
const match = NGC_GEN_FILES.exec(fileName);
if (match) {
const [, file, suffix, ext] = match;
// Performance: skip looking for files other than .d.ts or .ts
if (ext !== '.ts' && ext !== '.d.ts') return false;
if (suffix.indexOf('ngstyle') >= 0) {
// Look for foo.css on disk
fileName = file;
} else {
// Look for foo.d.ts or foo.ts on disk
fileName = file + (ext || '');
}
}
return tsHost.fileExists(fileName);
};
function generatedFileModuleResolver(
moduleName: string, containingFile: string,
compilerOptions: ts.CompilerOptions): ts.ResolvedModuleWithFailedLookupLocations {
return ts.resolveModuleName(
moduleName, containingFile, compilerOptions, generatedFileModuleResolverHost);
}
if (!bazelHost) { if (!bazelHost) {
const fileLoader = createFileLoader(inputs, bazelOpts); bazelHost = new CompilerHost(
bazelHost = new BazelHost(
files, compilerOpts, bazelOpts, tsHost, fileLoader, generatedFileModuleResolver); files, compilerOpts, bazelOpts, tsHost, fileLoader, generatedFileModuleResolver);
} }
// Also need to disable decorator downleveling in the BazelHost in Ivy mode. // Also need to disable decorator downleveling in the BazelHost in Ivy mode.
if (compilerOpts.enableIvy) { if (isInIvyMode) {
bazelHost.transformDecorators = false; bazelHost.transformDecorators = false;
} }
@ -379,68 +433,6 @@ export function compile({
return {program, diagnostics}; return {program, diagnostics};
} }
/** A module resolver for handling generated files in Bazel. */
export function generatedFileModuleResolver(
moduleName: string, containingFile: string, compilerOptions: ts.CompilerOptions,
host: ts.ModuleResolutionHost, ): ts.ResolvedModuleWithFailedLookupLocations {
// Patch fileExists when resolving modules, so that CompilerHost can ask
// TypeScript to resolve non-existing generated files that don't exist on
// disk, but are synthetic and added to the `programWithStubs` based on real
// inputs.
const generatedFileModuleResolverHost = Object.assign({}, host, {
fileExists: (...[fileName, ...rest]: Parameters<typeof host.fileExists>):
ReturnType<typeof host.fileExists> => {
const match = NGC_GEN_FILES.exec(fileName);
if (match) {
const [, file, suffix, ext] = match;
// Performance: skip looking for files other than .d.ts or .ts
if (ext !== '.ts' && ext !== '.d.ts') return false;
if (suffix.indexOf('ngstyle') >= 0) {
// Look for foo.css on disk
fileName = file;
} else {
// Look for foo.d.ts or foo.ts on disk
fileName = file + (ext || '');
}
}
return host.fileExists(fileName, ...rest);
},
});
return ts.resolveModuleName(
moduleName, containingFile, compilerOptions, generatedFileModuleResolverHost);
}
/** Creates a {@link FileLoader} to cache Bazel inputs.*/
export function createFileLoader(
inputs: {[key: string]: string} | undefined, bazelOpts: BazelOptions): FileLoader {
/** The one FileCache instance used in this process. */
const fileCache = new FileCache<ts.SourceFile>(debug);
if (bazelOpts.maxCacheSizeMb !== undefined) {
const maxCacheSizeBytes = bazelOpts.maxCacheSizeMb * (1 << 20);
fileCache.setMaxCacheSize(maxCacheSizeBytes);
} else {
fileCache.resetMaxCacheSize();
}
if (inputs) {
const fileLoader = new CachedFileLoader(fileCache);
// Resolve the inputs to absolute paths to match TypeScript internals
const resolvedInputs = new Map<string, string>();
const inputKeys = Object.keys(inputs);
for (let i = 0; i < inputKeys.length; i++) {
const key = inputKeys[i];
resolvedInputs.set(resolveNormalizedPath(key), inputs[key]);
}
fileCache.updateCache(resolvedInputs);
return fileLoader;
} else {
return new UncachedFileLoader();
}
}
/** /**
* Generate metadata.json for the specified `files`. By default, metadata.json * Generate metadata.json for the specified `files`. By default, metadata.json
* is only generated by the compiler if --flatModuleOutFile is specified. But * is only generated by the compiler if --flatModuleOutFile is specified. But