Skip to content
Permalink
Browse files

fix(ivy): ngcc - improve the "ngcc version changed" error message (#3…

…2396)

If a project has nested projects that contain node_modules folders
that get processed by ngcc, it can be confusing when the ngcc
version changes since the error message is very generic:

```
The ngcc compiler has changed since the last ngcc build.
Please completely remove `node_modules` and try again.
```

This commit augments the error message with the path of
the entry-point that failed so that it is more obvious which
node_modules folder to remove.

BREAKING CHANGE:

This commit removes the public export of `hasBeenProcessed()`.

This was exported to be availble to the CLI integration but was never
used. The change to the function signature is a breaking change in itself
so we remove the function altogether to simplify and lower the public
API surface going forward.

PR Close #32396
  • Loading branch information...
petebacondarwin authored and mhevery committed Aug 29, 2019
1 parent 63dff9c commit d5101dff3ba97ee1ac9ab7e4de41128368d39768
@@ -8,20 +8,11 @@
import {CachedFileSystem, NodeJSFileSystem, setFileSystem} from '../src/ngtsc/file_system';

import {mainNgcc} from './src/main';
import {hasBeenProcessed as _hasBeenProcessed} from './src/packages/build_marker';
import {EntryPointJsonProperty, EntryPointPackageJson} from './src/packages/entry_point';

export {ConsoleLogger, LogLevel} from './src/logging/console_logger';
export {Logger} from './src/logging/logger';
export {NgccOptions} from './src/main';
export {PathMappings} from './src/utils';

export function hasBeenProcessed(packageJson: object, format: string) {
// Recreate the file system on each call to reset the cache
setFileSystem(new CachedFileSystem(new NodeJSFileSystem()));
return _hasBeenProcessed(packageJson as EntryPointPackageJson, format as EntryPointJsonProperty);
}

export function process(...args: Parameters<typeof mainNgcc>) {
// Recreate the file system on each call to reset the cache
setFileSystem(new CachedFileSystem(new NodeJSFileSystem()));
@@ -111,7 +111,7 @@ export function mainNgcc(

for (const entryPoint of entryPoints) {
const packageJson = entryPoint.packageJson;
const hasProcessedTypings = hasBeenProcessed(packageJson, 'typings');
const hasProcessedTypings = hasBeenProcessed(packageJson, 'typings', entryPoint.path);
const {propertiesToProcess, propertyToPropertiesToMarkAsProcessed} =
getPropertiesToProcessAndMarkAsProcessed(packageJson, supportedPropertiesToConsider);
let processDts = !hasProcessedTypings;
@@ -160,7 +160,7 @@ export function mainNgcc(
}

// The format-path which the property maps to is already processed - nothing to do.
if (hasBeenProcessed(packageJson, formatProperty)) {
if (hasBeenProcessed(packageJson, formatProperty, entryPoint.path)) {
logger.debug(`Skipping ${entryPoint.name} : ${formatProperty} (already compiled).`);
onTaskCompleted(task, TaskProcessingOutcome.AlreadyProcessed);
return;
@@ -316,7 +316,7 @@ function hasProcessedTargetEntryPoint(
for (const property of propertiesToConsider) {
if (packageJson[property]) {
// Here is a property that should be processed
if (hasBeenProcessed(packageJson, property as EntryPointJsonProperty)) {
if (hasBeenProcessed(packageJson, property as EntryPointJsonProperty, targetPath)) {
if (!compileAllFormats) {
// It has been processed and we only need one, so we are done.
return true;
@@ -23,15 +23,16 @@ export const NGCC_VERSION = '0.0.0-PLACEHOLDER';
* @throws Error if the entry-point has already been processed with a different ngcc version.
*/
export function hasBeenProcessed(
packageJson: EntryPointPackageJson, format: EntryPointJsonProperty | 'typings'): boolean {
packageJson: EntryPointPackageJson, format: EntryPointJsonProperty | 'typings',
entryPointPath: AbsoluteFsPath): boolean {
if (!packageJson.__processed_by_ivy_ngcc__) {
return false;
}
if (Object.keys(packageJson.__processed_by_ivy_ngcc__)
.some(property => packageJson.__processed_by_ivy_ngcc__ ![property] !== NGCC_VERSION)) {
throw new Error(
'The ngcc compiler has changed since the last ngcc build.\n' +
'Please completely remove `node_modules` and try again.');
`Please completely remove the "node_modules" folder containing "${entryPointPath}" and try again.`);
}

return packageJson.__processed_by_ivy_ngcc__[format] === NGCC_VERSION;
@@ -5,7 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system';
import {AbsoluteFsPath, absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system';
import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
import {loadTestFiles} from '../../../test/helpers';
import {hasBeenProcessed, markAsProcessed} from '../../src/packages/build_marker';
@@ -150,55 +150,61 @@ runInEachFileSystem(() => {
});

describe('hasBeenProcessed', () => {
let entryPointPath: AbsoluteFsPath;

beforeEach(() => entryPointPath = _('/node_modules/test'));

it('should return true if the marker exists for the given format property', () => {
expect(hasBeenProcessed(
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '0.0.0-PLACEHOLDER'}},
'fesm2015'))
'fesm2015', entryPointPath))
.toBe(true);
});
it('should return false if the marker does not exist for the given format property', () => {
expect(hasBeenProcessed(
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '0.0.0-PLACEHOLDER'}},
'module'))
'module', entryPointPath))
.toBe(false);
});
it('should return false if no markers exist',
() => { expect(hasBeenProcessed({name: 'test'}, 'module')).toBe(false); });
() => { expect(hasBeenProcessed({name: 'test'}, 'module', entryPointPath)).toBe(false); });
it('should throw an Error if the format has been compiled with a different version.', () => {
expect(
() => hasBeenProcessed(
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '8.0.0'}}, 'fesm2015'))
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '8.0.0'}}, 'fesm2015',
entryPointPath))
.toThrowError(
'The ngcc compiler has changed since the last ngcc build.\n' +
'Please completely remove `node_modules` and try again.');
`Please completely remove the "node_modules" folder containing "${entryPointPath}" and try again.`);
});
it('should throw an Error if any format has been compiled with a different version.', () => {
expect(
() => hasBeenProcessed(
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '8.0.0'}}, 'module'))
{name: 'test', __processed_by_ivy_ngcc__: {'fesm2015': '8.0.0'}}, 'module',
entryPointPath))
.toThrowError(
'The ngcc compiler has changed since the last ngcc build.\n' +
'Please completely remove `node_modules` and try again.');
`Please completely remove the "node_modules" folder containing "${entryPointPath}" and try again.`);
expect(
() => hasBeenProcessed(
{
name: 'test',
__processed_by_ivy_ngcc__: {'module': '0.0.0-PLACEHOLDER', 'fesm2015': '8.0.0'}
},
'module'))
'module', entryPointPath))
.toThrowError(
'The ngcc compiler has changed since the last ngcc build.\n' +
'Please completely remove `node_modules` and try again.');
`Please completely remove the "node_modules" folder containing "${entryPointPath}" and try again.`);
expect(
() => hasBeenProcessed(
{
name: 'test',
__processed_by_ivy_ngcc__: {'module': '0.0.0-PLACEHOLDER', 'fesm2015': '8.0.0'}
},
'fesm2015'))
'fesm2015', entryPointPath))
.toThrowError(
'The ngcc compiler has changed since the last ngcc build.\n' +
'Please completely remove `node_modules` and try again.');
`Please completely remove the "node_modules" folder containing "${entryPointPath}" and try again.`);
});
});
});

0 comments on commit d5101df

Please sign in to comment.
You can’t perform that action at this time.