fix(ngcc): do not crash on entry-point that fails to compile (#36083)
Previously, when an entry-point contained code that caused its compilation to fail, ngcc would exit in the middle of processing, possibly leaving other entry-points in a corrupt state. This change adds a new `errorOnFailedEntryPoint` option to `mainNgcc` that specifies whether ngcc should exit immediately or log an error and continue processing other entry-points. The default is `false` so that ngcc will not error but continue processing as much as possible. This is useful in post-install hooks, and async CLI integration, where we do not have as much control over which entry-points should be processed. The option is forced to true if the `targetEntryPointPath` is provided, such as the sync integration with the CLI, since in that case it is targeting an entry-point that will actually be used in the current project so we do want ngcc to exit with an error at that point. PR Close #36083
This commit is contained in:

committed by
Andrew Kushnir

parent
1790b63a5d
commit
ff665b9e6a
@ -9,6 +9,7 @@
|
||||
import {PartiallyOrderedTasks, TaskQueue} from '../../../../src/execution/tasks/api';
|
||||
import {ParallelTaskQueue} from '../../../../src/execution/tasks/queues/parallel_task_queue';
|
||||
import {computeTaskDependencies} from '../../../../src/execution/tasks/utils';
|
||||
import {MockLogger} from '../../../helpers/mock_logger';
|
||||
import {createTasksAndGraph} from '../../helpers';
|
||||
|
||||
describe('ParallelTaskQueue', () => {
|
||||
@ -36,7 +37,7 @@ describe('ParallelTaskQueue', () => {
|
||||
const {tasks, graph} =
|
||||
createTasksAndGraph(entryPointCount, tasksPerEntryPointCount, entryPointDeps);
|
||||
const dependencies = computeTaskDependencies(tasks, graph);
|
||||
return {tasks, queue: new ParallelTaskQueue(tasks.slice(), dependencies)};
|
||||
return {tasks, queue: new ParallelTaskQueue(new MockLogger(), tasks.slice(), dependencies)};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -11,6 +11,7 @@ import {PartiallyOrderedTasks, Task, TaskQueue} from '../../../../src/execution/
|
||||
import {SerialTaskQueue} from '../../../../src/execution/tasks/queues/serial_task_queue';
|
||||
import {computeTaskDependencies} from '../../../../src/execution/tasks/utils';
|
||||
import {EntryPoint} from '../../../../src/packages/entry_point';
|
||||
import {MockLogger} from '../../../helpers/mock_logger';
|
||||
|
||||
|
||||
describe('SerialTaskQueue', () => {
|
||||
@ -38,7 +39,7 @@ describe('SerialTaskQueue', () => {
|
||||
graph.addNode(entryPoint.path);
|
||||
}
|
||||
const dependencies = computeTaskDependencies(tasks, graph);
|
||||
return {tasks, queue: new SerialTaskQueue(tasks.slice(), dependencies)};
|
||||
return {tasks, queue: new SerialTaskQueue(new MockLogger(), tasks.slice(), dependencies)};
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1085,45 +1085,130 @@ runInEachFileSystem(() => {
|
||||
});
|
||||
|
||||
describe('diagnostics', () => {
|
||||
it('should fail with formatted diagnostics when an error diagnostic is produced', () => {
|
||||
loadTestFiles([
|
||||
{
|
||||
name: _('/node_modules/fatal-error/package.json'),
|
||||
contents: '{"name": "fatal-error", "es2015": "./index.js", "typings": "./index.d.ts"}',
|
||||
},
|
||||
{name: _('/node_modules/fatal-error/index.metadata.json'), contents: 'DUMMY DATA'},
|
||||
{
|
||||
name: _('/node_modules/fatal-error/index.js'),
|
||||
contents: `
|
||||
it('should fail with formatted diagnostics when an error diagnostic is produced, if targetEntryPointPath is provided',
|
||||
() => {
|
||||
loadTestFiles([
|
||||
{
|
||||
name: _('/node_modules/fatal-error/package.json'),
|
||||
contents:
|
||||
'{"name": "fatal-error", "es2015": "./index.js", "typings": "./index.d.ts"}',
|
||||
},
|
||||
{name: _('/node_modules/fatal-error/index.metadata.json'), contents: 'DUMMY DATA'},
|
||||
{
|
||||
name: _('/node_modules/fatal-error/index.js'),
|
||||
contents: `
|
||||
import {Component} from '@angular/core';
|
||||
export class FatalError {}
|
||||
FatalError.decorators = [
|
||||
{type: Component, args: [{selector: 'fatal-error'}]}
|
||||
];
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/fatal-error/index.d.ts'),
|
||||
contents: `
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/fatal-error/index.d.ts'),
|
||||
contents: `
|
||||
export declare class FatalError {}
|
||||
`,
|
||||
},
|
||||
]);
|
||||
},
|
||||
]);
|
||||
|
||||
try {
|
||||
mainNgcc({
|
||||
basePath: '/node_modules',
|
||||
targetEntryPointPath: 'fatal-error',
|
||||
propertiesToConsider: ['es2015']
|
||||
});
|
||||
fail('should have thrown');
|
||||
} catch (e) {
|
||||
expect(e.message).toContain(
|
||||
'Failed to compile entry-point fatal-error (es2015 as esm2015) due to compilation errors:');
|
||||
expect(e.message).toContain('NG2001');
|
||||
expect(e.message).toContain('component is missing a template');
|
||||
}
|
||||
});
|
||||
try {
|
||||
mainNgcc({
|
||||
basePath: '/node_modules',
|
||||
targetEntryPointPath: 'fatal-error',
|
||||
propertiesToConsider: ['es2015']
|
||||
});
|
||||
fail('should have thrown');
|
||||
} catch (e) {
|
||||
expect(e.message).toContain(
|
||||
'Failed to compile entry-point fatal-error (es2015 as esm2015) due to compilation errors:');
|
||||
expect(e.message).toContain('NG2001');
|
||||
expect(e.message).toContain('component is missing a template');
|
||||
}
|
||||
});
|
||||
|
||||
it('should not fail but log an error with formatted diagnostics when an error diagnostic is produced, if targetEntryPoint is not provided and errorOnFailedEntryPoint is false',
|
||||
() => {
|
||||
loadTestFiles([
|
||||
{
|
||||
name: _('/node_modules/fatal-error/package.json'),
|
||||
contents:
|
||||
'{"name": "fatal-error", "es2015": "./index.js", "typings": "./index.d.ts"}',
|
||||
},
|
||||
{name: _('/node_modules/fatal-error/index.metadata.json'), contents: 'DUMMY DATA'},
|
||||
{
|
||||
name: _('/node_modules/fatal-error/index.js'),
|
||||
contents: `
|
||||
import {Component} from '@angular/core';
|
||||
export class FatalError {}
|
||||
FatalError.decorators = [
|
||||
{type: Component, args: [{selector: 'fatal-error'}]}
|
||||
];`,
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/fatal-error/index.d.ts'),
|
||||
contents: `export declare class FatalError {}`,
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/dependent/package.json'),
|
||||
contents: '{"name": "dependent", "es2015": "./index.js", "typings": "./index.d.ts"}',
|
||||
},
|
||||
{name: _('/node_modules/dependent/index.metadata.json'), contents: 'DUMMY DATA'},
|
||||
{
|
||||
name: _('/node_modules/dependent/index.js'),
|
||||
contents: `
|
||||
import {Component} from '@angular/core';
|
||||
import {FatalError} from 'fatal-error';
|
||||
export class Dependent {}
|
||||
Dependent.decorators = [
|
||||
{type: Component, args: [{selector: 'dependent', template: ''}]}
|
||||
];`,
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/dependent/index.d.ts'),
|
||||
contents: `export declare class Dependent {}`,
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/independent/package.json'),
|
||||
contents:
|
||||
'{"name": "independent", "es2015": "./index.js", "typings": "./index.d.ts"}',
|
||||
},
|
||||
{name: _('/node_modules/independent/index.metadata.json'), contents: 'DUMMY DATA'},
|
||||
{
|
||||
name: _('/node_modules/independent/index.js'),
|
||||
contents: `
|
||||
import {Component} from '@angular/core';
|
||||
export class Independent {}
|
||||
Independent.decorators = [
|
||||
{type: Component, args: [{selector: 'independent', template: ''}]}
|
||||
];`,
|
||||
},
|
||||
{
|
||||
name: _('/node_modules/independent/index.d.ts'),
|
||||
contents: `export declare class Independent {}`,
|
||||
},
|
||||
]);
|
||||
|
||||
const logger = new MockLogger();
|
||||
mainNgcc({
|
||||
basePath: '/node_modules',
|
||||
propertiesToConsider: ['es2015'],
|
||||
errorOnFailedEntryPoint: false, logger,
|
||||
});
|
||||
expect(logger.logs.error.length).toEqual(1);
|
||||
const message = logger.logs.error[0][0];
|
||||
expect(message).toContain(
|
||||
'Failed to compile entry-point fatal-error (es2015 as esm2015) due to compilation errors:');
|
||||
expect(message).toContain('NG2001');
|
||||
expect(message).toContain('component is missing a template');
|
||||
|
||||
expect(hasBeenProcessed(loadPackage('fatal-error', _('/node_modules')), 'es2015'))
|
||||
.toBe(false);
|
||||
expect(hasBeenProcessed(loadPackage('dependent', _('/node_modules')), 'es2015'))
|
||||
.toBe(false);
|
||||
expect(hasBeenProcessed(loadPackage('independent', _('/node_modules')), 'es2015'))
|
||||
.toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('logger', () => {
|
||||
|
Reference in New Issue
Block a user