refactor(ngcc): abstract work orchestration/execution behind an interface (#32427)

This change does not alter the current behavior, but makes it easier to
introduce new types of `Executors` , for example to do the required work
in parallel (on multiple processes).

Inspired by/Based on @alxhub's prototype: alxhub/angular@cb631bdb1

PR Close #32427
This commit is contained in:
George Kalpakas
2019-08-19 17:10:09 +03:00
committed by Matias Niemelä
parent 3d9dd6df0e
commit 5c213e5474
4 changed files with 129 additions and 55 deletions

View File

@ -20,12 +20,21 @@ export type CompileFn = (task: Task) => void;
/** The type of the function that creates the `CompileFn` function used to process tasks. */
export type CreateCompileFn = (onTaskCompleted: TaskCompletedCallback) => CompileFn;
/** Options related to the orchestration/execution of tasks. */
export interface ExecutionOptions {
compileAllFormats: boolean;
propertiesToConsider: string[];
}
/**
* The type of the function that orchestrates and executes the required work (i.e. analyzes the
* entry-points, processes the resulting tasks, does book-keeping and validates the final outcome).
* A class that orchestrates and executes the required work (i.e. analyzes the entry-points,
* processes the resulting tasks, does book-keeping and validates the final outcome).
*/
export type ExecuteFn =
(analyzeEntryPoints: AnalyzeEntryPointsFn, createCompileFn: CreateCompileFn) => void;
export interface Executor {
execute(
analyzeEntryPoints: AnalyzeEntryPointsFn, createCompileFn: CreateCompileFn,
options: ExecutionOptions): void;
}
/** Represents metadata related to the processing of an entry-point. */
export interface EntryPointProcessingMetadata {

View File

@ -0,0 +1,46 @@
/**
* @license
* Copyright Google Inc. 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 {Logger} from '../logging/logger';
import {PackageJsonUpdater} from '../writing/package_json_updater';
import {AnalyzeEntryPointsFn, CreateCompileFn, ExecutionOptions, Executor} from './api';
import {checkForUnprocessedEntryPoints, onTaskCompleted} from './utils';
/**
* An `Executor` that processes all tasks serially and completes synchronously.
*/
export class SingleProcessExecutor implements Executor {
constructor(private logger: Logger, private pkgJsonUpdater: PackageJsonUpdater) {}
execute(
analyzeEntryPoints: AnalyzeEntryPointsFn, createCompileFn: CreateCompileFn,
options: ExecutionOptions): void {
this.logger.debug(`Running ngcc on ${this.constructor.name}.`);
const {processingMetadataPerEntryPoint, tasks} = analyzeEntryPoints();
const compile = createCompileFn(
(task, outcome) =>
onTaskCompleted(this.pkgJsonUpdater, processingMetadataPerEntryPoint, task, outcome));
// Process all tasks.
for (const task of tasks) {
const processingMeta = processingMetadataPerEntryPoint.get(task.entryPoint.path) !;
// If we only need one format processed and we already have one for the corresponding
// entry-point, skip the task.
if (!options.compileAllFormats && processingMeta.hasAnyProcessedFormat) continue;
compile(task);
}
// Check for entry-points for which we could not process any format at all.
checkForUnprocessedEntryPoints(processingMetadataPerEntryPoint, options.propertiesToConsider);
}
}

View File

@ -0,0 +1,59 @@
/**
* @license
* Copyright Google Inc. 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 {resolve} from '../../../src/ngtsc/file_system';
import {markAsProcessed} from '../packages/build_marker';
import {PackageJsonFormatProperties} from '../packages/entry_point';
import {PackageJsonUpdater} from '../writing/package_json_updater';
import {EntryPointProcessingMetadata, Task, TaskProcessingOutcome} from './api';
/**
* A helper function for checking for unprocessed entry-points (i.e. entry-points for which we could
* not process any format at all).
*/
export const checkForUnprocessedEntryPoints =
(processingMetadataPerEntryPoint: Map<string, EntryPointProcessingMetadata>,
propertiesToConsider: string[]): void => {
const unprocessedEntryPointPaths =
Array.from(processingMetadataPerEntryPoint.entries())
.filter(([, processingMeta]) => !processingMeta.hasAnyProcessedFormat)
.map(([entryPointPath]) => `\n - ${entryPointPath}`)
.join('');
if (unprocessedEntryPointPaths) {
throw new Error(
'Failed to compile any formats for the following entry-points (tried ' +
`${propertiesToConsider.join(', ')}): ${unprocessedEntryPointPaths}`);
}
};
/** A helper function for handling a task's being completed. */
export const onTaskCompleted =
(pkgJsonUpdater: PackageJsonUpdater,
processingMetadataPerEntryPoint: Map<string, EntryPointProcessingMetadata>, task: Task,
outcome: TaskProcessingOutcome, ): void => {
const {entryPoint, formatPropertiesToMarkAsProcessed, processDts} = task;
const processingMeta = processingMetadataPerEntryPoint.get(entryPoint.path) !;
processingMeta.hasAnyProcessedFormat = true;
if (outcome === TaskProcessingOutcome.Processed) {
const packageJsonPath = resolve(entryPoint.path, 'package.json');
const propsToMarkAsProcessed: PackageJsonFormatProperties[] =
[...formatPropertiesToMarkAsProcessed];
if (processDts) {
processingMeta.hasProcessedTypings = true;
propsToMarkAsProcessed.push('typings');
}
markAsProcessed(
pkgJsonUpdater, entryPoint.packageJson, packageJsonPath, propsToMarkAsProcessed);
}
};