From 0093b3b19f2efe48973acc50471917b6b5913dcf Mon Sep 17 00:00:00 2001 From: JoostK Date: Sun, 14 Mar 2021 17:36:47 +0100 Subject: [PATCH] fix(ngcc): do not compile JavaScript sources if typings-only processing is repeated (#41209) The recently introduced typings-only mode in ngcc would incorrectly write compiled JavaScript files if typings-only mode was requested, in case the typings of the entry-point had already been processed in a prior run of ngcc. The corresponding format property for which the JavaScript files were written were not marked as processed, though, as the typings-only mode excluded the format property itself from being marked as processed. Consequently, subsequent runs of ngcc would not consider the entry-point to have been processed and recompile the JavaScript bundle once more, resulting in duplicate ngcc imports. Fixes #41198 PR Close #41209 --- .../src/execution/analyze_entry_points.ts | 13 +++- .../ngcc/test/integration/ngcc_spec.ts | 60 +++++++++++++++++++ 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/packages/compiler-cli/ngcc/src/execution/analyze_entry_points.ts b/packages/compiler-cli/ngcc/src/execution/analyze_entry_points.ts index 196db381b4..1a876eb53d 100644 --- a/packages/compiler-cli/ngcc/src/execution/analyze_entry_points.ts +++ b/packages/compiler-cli/ngcc/src/execution/analyze_entry_points.ts @@ -49,11 +49,8 @@ export function getAnalyzeEntryPointsFn( for (const entryPoint of entryPoints) { const packageJson = entryPoint.packageJson; - const hasProcessedTypings = hasBeenProcessed(packageJson, 'typings'); const {propertiesToProcess, equivalentPropertiesMap} = getPropertiesToProcess( packageJson, supportedPropertiesToConsider, compileAllFormats, typingsOnly); - let processDts = hasProcessedTypings ? DtsProcessing.No : - typingsOnly ? DtsProcessing.Only : DtsProcessing.Yes; if (propertiesToProcess.length === 0) { // This entry-point is unprocessable (i.e. there is no format property that is of interest @@ -64,6 +61,16 @@ export function getAnalyzeEntryPointsFn( continue; } + const hasProcessedTypings = hasBeenProcessed(packageJson, 'typings'); + if (hasProcessedTypings && typingsOnly) { + // Typings for this entry-point have already been processed and we're in typings-only mode, + // so no task has to be created for this entry-point. + logger.debug(`Skipping ${entryPoint.name} : typings have already been processed.`); + continue; + } + let processDts = hasProcessedTypings ? DtsProcessing.No : + typingsOnly ? DtsProcessing.Only : DtsProcessing.Yes; + for (const formatProperty of propertiesToProcess) { if (hasBeenProcessed(entryPoint.packageJson, formatProperty)) { // The format-path which the property maps to is already processed - nothing to do. diff --git a/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts b/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts index 068cb77a56..1cab9d8902 100644 --- a/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts +++ b/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts @@ -1358,6 +1358,66 @@ runInEachFileSystem(() => { expect(fs.exists(_(`/node_modules/@angular/common/common.d.ts.__ivy_ngcc_bak`))).toBe(true); }); + it('should not compile anything when typings have already been processed', () => { + let logger = new MockLogger(); + mainNgcc({ + basePath: '/node_modules', + propertiesToConsider: ['esm2015'], + targetEntryPointPath: '@angular/core', + typingsOnly: true, + logger, + }); + expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({ + typings: '0.0.0-PLACEHOLDER', + }); + expect(fs.readFile(_(`/node_modules/@angular/core/esm2015/src/application_init.js`))) + .not.toMatch(ANGULAR_CORE_IMPORT_REGEX); + expect(logger.logs.debug).toContain([' Successfully compiled @angular/core : esm2015']); + + // Try to process the typings for @angular/core again, now using a different format + // property, to verify that it does not process the entry-point again and that the JS + // files are still untouched. + logger = new MockLogger(); + mainNgcc({ + basePath: '/node_modules', + propertiesToConsider: ['main'], + targetEntryPointPath: '@angular/core', + typingsOnly: true, + logger, + }); + expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({ + typings: '0.0.0-PLACEHOLDER', + }); + expect(fs.readFile(_(`/node_modules/@angular/core/esm2015/src/application_init.js`))) + .not.toMatch(ANGULAR_CORE_IMPORT_REGEX); + expect(logger.logs.debug).toContain([ + 'Skipping @angular/core : typings have already been processed.' + ]); + + // Now also process the typings for @angular/common to verify that its dependency on + // @angular/core, which has already been processed and will therefore be skipped, is able + // to succeed. + logger = new MockLogger(); + mainNgcc({ + basePath: '/node_modules', + propertiesToConsider: ['esm2015'], + targetEntryPointPath: '@angular/common', + typingsOnly: true, + logger, + }); + expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual({ + typings: '0.0.0-PLACEHOLDER', + }); + expect(fs.readFile(_(`/node_modules/@angular/core/esm2015/src/application_init.js`))) + .not.toMatch(ANGULAR_CORE_IMPORT_REGEX); + expect(fs.readFile(_(`/node_modules/@angular/common/esm2015/src/common_module.js`))) + .not.toMatch(ANGULAR_CORE_IMPORT_REGEX); + expect(logger.logs.debug).toContain([ + 'Skipping @angular/core : typings have already been processed.' + ]); + expect(logger.logs.debug).toContain([' Successfully compiled @angular/common : esm2015']); + }); + it('should cope with compiling the same entry-point multiple times with different formats', () => { mainNgcc({