From aef432384aafe4712f3250f8fe9ce74b94b5b2f5 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Sat, 21 Mar 2020 22:18:50 +0000 Subject: [PATCH] fix(ngcc): use preserve whitespaces from tsconfig if provided (#36189) Previously ngcc never preserved whitespaces but this is at odds with how the ViewEngine compiler works. In ViewEngine, library templates are recompiled with the current application's tsconfig settings, which meant that whitespace preservation could be set in the application tsconfig file. This commit allows ngcc to use the `preserveWhitespaces` setting from tsconfig when compiling library templates. One should be aware that this disallows different projects with different tsconfig settings to share the same node_modules folder, with regard to whitespace preservation. But this is already the case in the current ngcc since this configuration is hard coded right now. Fixes #35871 PR Close #36189 --- .../ngcc/src/analysis/decoration_analyzer.ts | 7 +++- packages/compiler-cli/ngcc/src/main.ts | 2 +- .../ngcc/src/packages/transformer.ts | 7 +++- .../ngcc/test/integration/ngcc_spec.ts | 40 ++++++++++++++++++- 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts b/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts index a4c5f105f6678..a8e02ccc3f141 100644 --- a/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts +++ b/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts @@ -8,6 +8,7 @@ import {ConstantPool} from '@angular/compiler'; import * as ts from 'typescript'; +import {ParsedConfiguration} from '../../..'; import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, PipeDecoratorHandler, ReferencesRegistry, ResourceLoader} from '../../../src/ngtsc/annotations'; import {CycleAnalyzer, ImportGraph} from '../../../src/ngtsc/cycles'; import {isFatalDiagnosticError} from '../../../src/ngtsc/diagnostics'; @@ -55,6 +56,7 @@ export class DecorationAnalyzer { private rootDirs = this.bundle.rootDirs; private packagePath = this.bundle.entryPoint.package; private isCore = this.bundle.isCore; + private compilerOptions = this.tsConfig !== null? this.tsConfig.options: {}; moduleResolver = new ModuleResolver(this.program, this.options, this.host, /* moduleResolutionCache */ null); @@ -87,7 +89,7 @@ export class DecorationAnalyzer { new ComponentDecoratorHandler( this.reflectionHost, this.evaluator, this.fullRegistry, this.fullMetaReader, this.scopeRegistry, this.scopeRegistry, this.isCore, this.resourceManager, this.rootDirs, - /* defaultPreserveWhitespaces */ false, + !!this.compilerOptions.preserveWhitespaces, /* i18nUseExternalIds */ true, this.bundle.enableI18nLegacyMessageIdFormat, this.moduleResolver, this.cycleAnalyzer, this.refEmitter, NOOP_DEFAULT_IMPORT_RECORDER, NOOP_DEPENDENCY_TRACKER, this.injectableRegistry, /* annotateForClosureCompiler */ false), @@ -123,7 +125,8 @@ export class DecorationAnalyzer { constructor( private fs: FileSystem, private bundle: EntryPointBundle, private reflectionHost: NgccReflectionHost, private referencesRegistry: ReferencesRegistry, - private diagnosticHandler: (error: ts.Diagnostic) => void = () => {}) {} + private diagnosticHandler: (error: ts.Diagnostic) => void = () => {}, + private tsConfig: ParsedConfiguration|null = null) {} /** * Analyze a program to find all the decorated files should be transformed. diff --git a/packages/compiler-cli/ngcc/src/main.ts b/packages/compiler-cli/ngcc/src/main.ts index 6a202be471355..a069b8b03b63d 100644 --- a/packages/compiler-cli/ngcc/src/main.ts +++ b/packages/compiler-cli/ngcc/src/main.ts @@ -307,7 +307,7 @@ export function mainNgcc( const createCompileFn: CreateCompileFn = onTaskCompleted => { const fileWriter = getFileWriter( fileSystem, logger, pkgJsonUpdater, createNewEntryPointFormats, errorOnFailedEntryPoint); - const transformer = new Transformer(fileSystem, logger); + const transformer = new Transformer(fileSystem, logger, tsConfig); return (task: Task) => { const {entryPoint, formatProperty, formatPropertiesToMarkAsProcessed, processDts} = task; diff --git a/packages/compiler-cli/ngcc/src/packages/transformer.ts b/packages/compiler-cli/ngcc/src/packages/transformer.ts index 6ae9785929eef..f7a30944f7851 100644 --- a/packages/compiler-cli/ngcc/src/packages/transformer.ts +++ b/packages/compiler-cli/ngcc/src/packages/transformer.ts @@ -7,6 +7,7 @@ */ import * as ts from 'typescript'; +import {ParsedConfiguration} from '../../..'; import {FileSystem} from '../../../src/ngtsc/file_system'; import {TypeScriptReflectionHost} from '../../../src/ngtsc/reflection'; import {DecorationAnalyzer} from '../analysis/decoration_analyzer'; @@ -63,7 +64,9 @@ export type TransformResult = { * - Some formats may contain multiple "modules" in a single file. */ export class Transformer { - constructor(private fs: FileSystem, private logger: Logger) {} + constructor( + private fs: FileSystem, private logger: Logger, + private tsConfig: ParsedConfiguration|null = null) {} /** * Transform the source (and typings) files of a bundle. @@ -146,7 +149,7 @@ export class Transformer { const diagnostics: ts.Diagnostic[] = []; const decorationAnalyzer = new DecorationAnalyzer( this.fs, bundle, reflectionHost, referencesRegistry, - diagnostic => diagnostics.push(diagnostic)); + diagnostic => diagnostics.push(diagnostic), this.tsConfig); const decorationAnalyses = decorationAnalyzer.analyzeProgram(); const moduleWithProvidersAnalyzer = diff --git a/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts b/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts index 328ea2ee4406a..4cc67792d5820 100644 --- a/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts +++ b/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts @@ -1373,6 +1373,44 @@ runInEachFileSystem(() => { }); }); + describe('whitespace preservation', () => { + it('should default not to preserve whitespace', () => { + mainNgcc({basePath: '/dist', propertiesToConsider: ['es2015']}); + expect(loadPackage('local-package', _('/dist')).__processed_by_ivy_ngcc__).toEqual({ + es2015: '0.0.0-PLACEHOLDER', + typings: '0.0.0-PLACEHOLDER', + }); + expect(fs.readFile(_('/dist/local-package/index.js'))) + .toMatch(/ɵɵtext\(\d+, " Hello\\n"\);/); + }); + + it('should preserve whitespace if set in a loaded tsconfig.json', () => { + fs.writeFile( + _('/tsconfig.json'), + JSON.stringify({angularCompilerOptions: {preserveWhitespaces: true}})); + mainNgcc({basePath: '/dist', propertiesToConsider: ['es2015']}); + expect(loadPackage('local-package', _('/dist')).__processed_by_ivy_ngcc__).toEqual({ + es2015: '0.0.0-PLACEHOLDER', + typings: '0.0.0-PLACEHOLDER', + }); + expect(fs.readFile(_('/dist/local-package/index.js'))) + .toMatch(/ɵɵtext\(\d+, "\\n Hello\\n"\);/); + }); + + it('should not preserve whitespace if set to false in a loaded tsconfig.json', () => { + fs.writeFile( + _('/tsconfig.json'), + JSON.stringify({angularCompilerOptions: {preserveWhitespaces: false}})); + mainNgcc({basePath: '/dist', propertiesToConsider: ['es2015']}); + expect(loadPackage('local-package', _('/dist')).__processed_by_ivy_ngcc__).toEqual({ + es2015: '0.0.0-PLACEHOLDER', + typings: '0.0.0-PLACEHOLDER', + }); + expect(fs.readFile(_('/dist/local-package/index.js'))) + .toMatch(/ɵɵtext\(\d+, " Hello\\n"\);/); + }); + }); + describe('with configuration files', () => { it('should process a configured deep-import as an entry-point', () => { loadTestFiles([ @@ -1883,7 +1921,7 @@ runInEachFileSystem(() => { { name: _('/dist/local-package/index.js'), contents: - `import {Component} from '@angular/core';\nexport class AppComponent {};\nAppComponent.decorators = [\n{ type: Component, args: [{selector: 'app', template: '

Hello

'}] }\n];` + `import {Component} from '@angular/core';\nexport class AppComponent {};\nAppComponent.decorators = [\n{ type: Component, args: [{selector: 'app', template: '

\\n Hello\\n

'}] }\n];` }, { name: _('/dist/local-package/index.d.ts'),