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
This commit is contained in:
JoostK 2021-03-14 17:36:47 +01:00 committed by Jessica Janiuk
parent 7d6db009e9
commit 0093b3b19f
2 changed files with 70 additions and 3 deletions

View File

@ -49,11 +49,8 @@ export function getAnalyzeEntryPointsFn(
for (const entryPoint of entryPoints) { for (const entryPoint of entryPoints) {
const packageJson = entryPoint.packageJson; const packageJson = entryPoint.packageJson;
const hasProcessedTypings = hasBeenProcessed(packageJson, 'typings');
const {propertiesToProcess, equivalentPropertiesMap} = getPropertiesToProcess( const {propertiesToProcess, equivalentPropertiesMap} = getPropertiesToProcess(
packageJson, supportedPropertiesToConsider, compileAllFormats, typingsOnly); packageJson, supportedPropertiesToConsider, compileAllFormats, typingsOnly);
let processDts = hasProcessedTypings ? DtsProcessing.No :
typingsOnly ? DtsProcessing.Only : DtsProcessing.Yes;
if (propertiesToProcess.length === 0) { if (propertiesToProcess.length === 0) {
// This entry-point is unprocessable (i.e. there is no format property that is of interest // This entry-point is unprocessable (i.e. there is no format property that is of interest
@ -64,6 +61,16 @@ export function getAnalyzeEntryPointsFn(
continue; 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) { for (const formatProperty of propertiesToProcess) {
if (hasBeenProcessed(entryPoint.packageJson, formatProperty)) { if (hasBeenProcessed(entryPoint.packageJson, formatProperty)) {
// The format-path which the property maps to is already processed - nothing to do. // The format-path which the property maps to is already processed - nothing to do.

View File

@ -1358,6 +1358,66 @@ runInEachFileSystem(() => {
expect(fs.exists(_(`/node_modules/@angular/common/common.d.ts.__ivy_ngcc_bak`))).toBe(true); 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', it('should cope with compiling the same entry-point multiple times with different formats',
() => { () => {
mainNgcc({ mainNgcc({