From 9f5b490800997183e8f90b097d19a37f3f3c77a9 Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Thu, 30 Jan 2020 11:04:27 -0800 Subject: [PATCH] Revert "refactor(compiler): refactor and export Bazel host creation. (#34686)" (#35063) This reverts commit cb142b6df9fa3b43e6905358c880c11f6a9abb45. 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 --- packages/bazel/src/ngc-wrapped/index.ts | 136 +++++++++++------------- 1 file changed, 64 insertions(+), 72 deletions(-) diff --git a/packages/bazel/src/ngc-wrapped/index.ts b/packages/bazel/src/ngc-wrapped/index.ts index 4c49ef1ec8..9d05a986ed 100644 --- a/packages/bazel/src/ngc-wrapped/index.ts +++ b/packages/bazel/src/ngc-wrapped/index.ts @@ -7,7 +7,7 @@ */ 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 path from 'path'; import * as tsickle from 'tsickle'; @@ -35,6 +35,9 @@ export function main(args) { return 0; } +/** The one FileCache instance used in this process. */ +const fileCache = new FileCache(debug); + export function runOneBuild(args: string[], inputs?: {[path: string]: string}): boolean { if (args[0] === '-p') args.shift(); // Strip leading at-signs, used to indicate a params file @@ -141,10 +144,8 @@ export function relativeToRootDirs(filePath: string, rootDirs: string[]): string return filePath; } -export function compile({ - allDepsCompiledWithBazel = true, compilerOpts, tsHost, bazelOpts, files, inputs, expectedOuts, - gatherDiagnostics, bazelHost, -}: { +export function compile({allDepsCompiledWithBazel = true, compilerOpts, tsHost, bazelOpts, files, + inputs, expectedOuts, gatherDiagnostics, bazelHost}: { allDepsCompiledWithBazel?: boolean, compilerOpts: ng.CompilerOptions, tsHost: ts.CompilerHost, inputs?: {[path: string]: string}, @@ -152,15 +153,41 @@ export function compile({ files: string[], expectedOuts: string[], gatherDiagnostics?: (program: ng.Program) => ng.Diagnostics, - bazelHost?: BazelHost, + bazelHost?: CompilerHost, }): {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(); + 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) { compilerOpts.annotateForClosureCompiler = true; 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. - if (compilerOpts.enableIvy) { + if (isInIvyMode) { 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) { - const fileLoader = createFileLoader(inputs, bazelOpts); - bazelHost = new BazelHost( + bazelHost = new CompilerHost( files, compilerOpts, bazelOpts, tsHost, fileLoader, generatedFileModuleResolver); } // Also need to disable decorator downleveling in the BazelHost in Ivy mode. - if (compilerOpts.enableIvy) { + if (isInIvyMode) { bazelHost.transformDecorators = false; } @@ -379,68 +433,6 @@ export function compile({ 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): - ReturnType => { - 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(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(); - 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 * is only generated by the compiler if --flatModuleOutFile is specified. But