Previously, when `ngcc` encountered an entry-point with a format-path that pointed to a non-existing or empty file it would throw an error and stop processing the remaining tasks. In the past, we used to ignore such format-paths and continue processing the rest of the tasks ([see code][1]). This was changed to a hard failure in 2954d1b5ca212548c5c4634a30ce773cfe3b9410. Looking at the code history, the reason for changing the behavior was an (incorrect) assumption that the condition could not fail. This assumption failed to take into account the case where a 3rd-party library has an invalid format-path in its `package.json`. This is an issue with the library, but it should not prevent `ngcc` from processing other packages/entry-points/formats. This commit fixes this by reporting the task as failed but not throwing an error, thus allowing `ngcc` to continue processing other tasks. [1]: https://github.com/angular/angular/blob/3077c9a1f89c5bd75fb96c16e/packages/compiler-cli/ngcc/src/main.ts#L124 Fixes #40965 PR Close #40985
92 lines
4.0 KiB
TypeScript
92 lines
4.0 KiB
TypeScript
|
|
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* 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 * as ts from 'typescript';
|
|
|
|
import {replaceTsWithNgInErrors} from '../../../src/ngtsc/diagnostics';
|
|
import {FileSystem} from '../../../src/ngtsc/file_system';
|
|
import {Logger} from '../../../src/ngtsc/logging';
|
|
import {ParsedConfiguration} from '../../../src/perform_compile';
|
|
import {getEntryPointFormat} from '../packages/entry_point';
|
|
import {makeEntryPointBundle} from '../packages/entry_point_bundle';
|
|
import {createModuleResolutionCache, SharedFileCache} from '../packages/source_file_cache';
|
|
import {PathMappings} from '../path_mappings';
|
|
import {FileWriter} from '../writing/file_writer';
|
|
|
|
import {CreateCompileFn} from './api';
|
|
import {Task, TaskProcessingOutcome} from './tasks/api';
|
|
|
|
/**
|
|
* The function for creating the `compile()` function.
|
|
*/
|
|
export function getCreateCompileFn(
|
|
fileSystem: FileSystem, logger: Logger, fileWriter: FileWriter,
|
|
enableI18nLegacyMessageIdFormat: boolean, tsConfig: ParsedConfiguration|null,
|
|
pathMappings: PathMappings|undefined): CreateCompileFn {
|
|
return (beforeWritingFiles, onTaskCompleted) => {
|
|
const {Transformer} = require('../packages/transformer');
|
|
const transformer = new Transformer(fileSystem, logger, tsConfig);
|
|
const sharedFileCache = new SharedFileCache(fileSystem);
|
|
const moduleResolutionCache = createModuleResolutionCache(fileSystem);
|
|
|
|
return (task: Task) => {
|
|
const {entryPoint, formatProperty, formatPropertiesToMarkAsProcessed, processDts} = task;
|
|
|
|
const isCore = entryPoint.name === '@angular/core'; // Are we compiling the Angular core?
|
|
const packageJson = entryPoint.packageJson;
|
|
const formatPath = packageJson[formatProperty];
|
|
const format = getEntryPointFormat(fileSystem, entryPoint, formatProperty);
|
|
|
|
// All properties listed in `propertiesToProcess` are guaranteed to point to a format-path
|
|
// (i.e. they are defined in `entryPoint.packageJson`). Furthermore, they are also guaranteed
|
|
// to be among `SUPPORTED_FORMAT_PROPERTIES`.
|
|
// Based on the above, `formatPath` should always be defined and `getEntryPointFormat()`
|
|
// should always return a format here (and not `undefined`) unless `formatPath` points to a
|
|
// missing or empty file.
|
|
if (!formatPath || !format) {
|
|
onTaskCompleted(
|
|
task, TaskProcessingOutcome.Failed,
|
|
`property \`${formatProperty}\` pointing to a missing or empty file: ${formatPath}`);
|
|
return;
|
|
}
|
|
|
|
logger.info(`Compiling ${entryPoint.name} : ${formatProperty} as ${format}`);
|
|
|
|
const bundle = makeEntryPointBundle(
|
|
fileSystem, entryPoint, sharedFileCache, moduleResolutionCache, formatPath, isCore,
|
|
format, processDts, pathMappings, true, enableI18nLegacyMessageIdFormat);
|
|
|
|
const result = transformer.transform(bundle);
|
|
if (result.success) {
|
|
if (result.diagnostics.length > 0) {
|
|
logger.warn(replaceTsWithNgInErrors(
|
|
ts.formatDiagnosticsWithColorAndContext(result.diagnostics, bundle.src.host)));
|
|
}
|
|
|
|
const writeBundle = () => {
|
|
fileWriter.writeBundle(
|
|
bundle, result.transformedFiles, formatPropertiesToMarkAsProcessed);
|
|
|
|
logger.debug(` Successfully compiled ${entryPoint.name} : ${formatProperty}`);
|
|
onTaskCompleted(task, TaskProcessingOutcome.Processed, null);
|
|
};
|
|
|
|
const beforeWritingResult = beforeWritingFiles(result.transformedFiles);
|
|
|
|
return (beforeWritingResult instanceof Promise) ?
|
|
beforeWritingResult.then(writeBundle) as ReturnType<typeof beforeWritingFiles>:
|
|
writeBundle();
|
|
} else {
|
|
const errors = replaceTsWithNgInErrors(
|
|
ts.formatDiagnosticsWithColorAndContext(result.diagnostics, bundle.src.host));
|
|
onTaskCompleted(task, TaskProcessingOutcome.Failed, `compilation errors:\n${errors}`);
|
|
}
|
|
};
|
|
};
|
|
}
|