2018-07-16 08:49:56 +01:00
|
|
|
/**
|
|
|
|
* @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
|
|
|
|
*/
|
2019-08-29 18:47:54 +03:00
|
|
|
|
|
|
|
/// <reference types="node" />
|
|
|
|
|
|
|
|
import {DepGraph} from 'dependency-graph';
|
|
|
|
import * as os from 'os';
|
2019-08-04 19:20:38 +02:00
|
|
|
import * as ts from 'typescript';
|
|
|
|
|
2019-11-23 19:17:16 +01:00
|
|
|
import {replaceTsWithNgInErrors} from '../../src/ngtsc/diagnostics';
|
2019-05-21 15:23:24 +01:00
|
|
|
import {AbsoluteFsPath, FileSystem, absoluteFrom, dirname, getFileSystem, resolve} from '../../src/ngtsc/file_system';
|
2019-08-04 18:04:03 +03:00
|
|
|
|
2019-04-29 18:51:52 +01:00
|
|
|
import {CommonJsDependencyHost} from './dependencies/commonjs_dependency_host';
|
2019-08-27 17:36:25 +03:00
|
|
|
import {DependencyResolver, InvalidEntryPoint, PartiallyOrderedEntryPoints, SortedEntryPointsInfo} from './dependencies/dependency_resolver';
|
2019-04-28 20:47:57 +01:00
|
|
|
import {EsmDependencyHost} from './dependencies/esm_dependency_host';
|
|
|
|
import {ModuleResolver} from './dependencies/module_resolver';
|
2019-04-28 20:48:35 +01:00
|
|
|
import {UmdDependencyHost} from './dependencies/umd_dependency_host';
|
2019-05-16 08:53:19 +01:00
|
|
|
import {DirectoryWalkerEntryPointFinder} from './entry_point_finder/directory_walker_entry_point_finder';
|
|
|
|
import {TargetedEntryPointFinder} from './entry_point_finder/targeted_entry_point_finder';
|
2019-08-29 18:47:54 +03:00
|
|
|
import {AnalyzeEntryPointsFn, CreateCompileFn, Executor, PartiallyOrderedTasks, Task, TaskProcessingOutcome, TaskQueue} from './execution/api';
|
|
|
|
import {ClusterExecutor} from './execution/cluster/executor';
|
|
|
|
import {ClusterPackageJsonUpdater} from './execution/cluster/package_json_updater';
|
refactor(ngcc): add support for asynchronous execution (#32427)
Previously, `ngcc`'s programmatic API would run and complete
synchronously. This was necessary for specific usecases (such as how the
`@angular/cli` invokes `ngcc` as part of the TypeScript module
resolution process), but not for others (e.g. running `ivy-ngcc` as a
`postinstall` script).
This commit adds a new option (`async`) that enables turning on
asynchronous execution. I.e. it signals that the caller is OK with the
function call to complete asynchronously, which allows `ngcc` to
potentially run in a more efficient mode.
Currently, there is no difference in the way tasks are executed in sync
vs async mode, but this change sets the ground for adding new execution
options (that require asynchronous operation), such as processing tasks
in parallel on multiple processes.
NOTE:
When using the programmatic API, the default value for `async` is
`false`, thus retaining backwards compatibility.
When running `ngcc` from the command line (i.e. via the `ivy-ngcc`
script), it runs in async mode (to be able to take advantage of future
optimizations), but that is transparent to the caller.
PR Close #32427
2019-08-19 22:58:22 +03:00
|
|
|
import {AsyncSingleProcessExecutor, SingleProcessExecutor} from './execution/single_process_executor';
|
2019-08-29 18:47:54 +03:00
|
|
|
import {ParallelTaskQueue} from './execution/task_selection/parallel_task_queue';
|
2019-08-27 17:36:25 +03:00
|
|
|
import {SerialTaskQueue} from './execution/task_selection/serial_task_queue';
|
2019-03-29 10:13:14 +00:00
|
|
|
import {ConsoleLogger, LogLevel} from './logging/console_logger';
|
|
|
|
import {Logger} from './logging/logger';
|
2019-03-20 13:47:59 +00:00
|
|
|
import {hasBeenProcessed, markAsProcessed} from './packages/build_marker';
|
2019-05-21 15:23:24 +01:00
|
|
|
import {NgccConfiguration} from './packages/configuration';
|
2019-08-29 18:47:54 +03:00
|
|
|
import {EntryPoint, EntryPointJsonProperty, EntryPointPackageJson, SUPPORTED_FORMAT_PROPERTIES, getEntryPointFormat} from './packages/entry_point';
|
2018-11-25 21:40:25 +00:00
|
|
|
import {makeEntryPointBundle} from './packages/entry_point_bundle';
|
2018-08-09 15:59:10 +01:00
|
|
|
import {Transformer} from './packages/transformer';
|
2019-12-19 22:43:13 +00:00
|
|
|
import {PathMappings, createDtsDependencyHost} from './utils';
|
2019-03-20 13:47:59 +00:00
|
|
|
import {FileWriter} from './writing/file_writer';
|
|
|
|
import {InPlaceFileWriter} from './writing/in_place_file_writer';
|
2019-03-20 13:47:59 +00:00
|
|
|
import {NewEntryPointFileWriter} from './writing/new_entry_point_file_writer';
|
2019-08-12 18:15:24 +03:00
|
|
|
import {DirectPackageJsonUpdater, PackageJsonUpdater} from './writing/package_json_updater';
|
2018-07-16 08:49:56 +01:00
|
|
|
|
2019-08-29 18:47:54 +03:00
|
|
|
|
2019-03-20 13:47:58 +00:00
|
|
|
/**
|
refactor(ngcc): add support for asynchronous execution (#32427)
Previously, `ngcc`'s programmatic API would run and complete
synchronously. This was necessary for specific usecases (such as how the
`@angular/cli` invokes `ngcc` as part of the TypeScript module
resolution process), but not for others (e.g. running `ivy-ngcc` as a
`postinstall` script).
This commit adds a new option (`async`) that enables turning on
asynchronous execution. I.e. it signals that the caller is OK with the
function call to complete asynchronously, which allows `ngcc` to
potentially run in a more efficient mode.
Currently, there is no difference in the way tasks are executed in sync
vs async mode, but this change sets the ground for adding new execution
options (that require asynchronous operation), such as processing tasks
in parallel on multiple processes.
NOTE:
When using the programmatic API, the default value for `async` is
`false`, thus retaining backwards compatibility.
When running `ngcc` from the command line (i.e. via the `ivy-ngcc`
script), it runs in async mode (to be able to take advantage of future
optimizations), but that is transparent to the caller.
PR Close #32427
2019-08-19 22:58:22 +03:00
|
|
|
* The options to configure the ngcc compiler for synchronous execution.
|
2019-03-20 13:47:58 +00:00
|
|
|
*/
|
refactor(ngcc): add support for asynchronous execution (#32427)
Previously, `ngcc`'s programmatic API would run and complete
synchronously. This was necessary for specific usecases (such as how the
`@angular/cli` invokes `ngcc` as part of the TypeScript module
resolution process), but not for others (e.g. running `ivy-ngcc` as a
`postinstall` script).
This commit adds a new option (`async`) that enables turning on
asynchronous execution. I.e. it signals that the caller is OK with the
function call to complete asynchronously, which allows `ngcc` to
potentially run in a more efficient mode.
Currently, there is no difference in the way tasks are executed in sync
vs async mode, but this change sets the ground for adding new execution
options (that require asynchronous operation), such as processing tasks
in parallel on multiple processes.
NOTE:
When using the programmatic API, the default value for `async` is
`false`, thus retaining backwards compatibility.
When running `ngcc` from the command line (i.e. via the `ivy-ngcc`
script), it runs in async mode (to be able to take advantage of future
optimizations), but that is transparent to the caller.
PR Close #32427
2019-08-19 22:58:22 +03:00
|
|
|
export interface SyncNgccOptions {
|
2019-03-20 13:47:59 +00:00
|
|
|
/** The absolute path to the `node_modules` folder that contains the packages to process. */
|
|
|
|
basePath: string;
|
refactor(ngcc): add support for asynchronous execution (#32427)
Previously, `ngcc`'s programmatic API would run and complete
synchronously. This was necessary for specific usecases (such as how the
`@angular/cli` invokes `ngcc` as part of the TypeScript module
resolution process), but not for others (e.g. running `ivy-ngcc` as a
`postinstall` script).
This commit adds a new option (`async`) that enables turning on
asynchronous execution. I.e. it signals that the caller is OK with the
function call to complete asynchronously, which allows `ngcc` to
potentially run in a more efficient mode.
Currently, there is no difference in the way tasks are executed in sync
vs async mode, but this change sets the ground for adding new execution
options (that require asynchronous operation), such as processing tasks
in parallel on multiple processes.
NOTE:
When using the programmatic API, the default value for `async` is
`false`, thus retaining backwards compatibility.
When running `ngcc` from the command line (i.e. via the `ivy-ngcc`
script), it runs in async mode (to be able to take advantage of future
optimizations), but that is transparent to the caller.
PR Close #32427
2019-08-19 22:58:22 +03:00
|
|
|
|
2019-03-20 13:47:58 +00:00
|
|
|
/**
|
2019-05-02 15:59:03 +02:00
|
|
|
* The path to the primary package to be processed. If not absolute then it must be relative to
|
|
|
|
* `basePath`.
|
2019-03-20 13:47:59 +00:00
|
|
|
*
|
|
|
|
* All its dependencies will need to be processed too.
|
2019-03-20 13:47:58 +00:00
|
|
|
*/
|
2019-03-20 13:47:59 +00:00
|
|
|
targetEntryPointPath?: string;
|
refactor(ngcc): add support for asynchronous execution (#32427)
Previously, `ngcc`'s programmatic API would run and complete
synchronously. This was necessary for specific usecases (such as how the
`@angular/cli` invokes `ngcc` as part of the TypeScript module
resolution process), but not for others (e.g. running `ivy-ngcc` as a
`postinstall` script).
This commit adds a new option (`async`) that enables turning on
asynchronous execution. I.e. it signals that the caller is OK with the
function call to complete asynchronously, which allows `ngcc` to
potentially run in a more efficient mode.
Currently, there is no difference in the way tasks are executed in sync
vs async mode, but this change sets the ground for adding new execution
options (that require asynchronous operation), such as processing tasks
in parallel on multiple processes.
NOTE:
When using the programmatic API, the default value for `async` is
`false`, thus retaining backwards compatibility.
When running `ngcc` from the command line (i.e. via the `ivy-ngcc`
script), it runs in async mode (to be able to take advantage of future
optimizations), but that is transparent to the caller.
PR Close #32427
2019-08-19 22:58:22 +03:00
|
|
|
|
2019-03-20 13:47:58 +00:00
|
|
|
/**
|
2019-03-20 13:47:59 +00:00
|
|
|
* Which entry-point properties in the package.json to consider when processing an entry-point.
|
|
|
|
* Each property should hold a path to the particular bundle format for the entry-point.
|
|
|
|
* Defaults to all the properties in the package.json.
|
2019-03-20 13:47:58 +00:00
|
|
|
*/
|
2019-03-20 13:47:59 +00:00
|
|
|
propertiesToConsider?: string[];
|
refactor(ngcc): add support for asynchronous execution (#32427)
Previously, `ngcc`'s programmatic API would run and complete
synchronously. This was necessary for specific usecases (such as how the
`@angular/cli` invokes `ngcc` as part of the TypeScript module
resolution process), but not for others (e.g. running `ivy-ngcc` as a
`postinstall` script).
This commit adds a new option (`async`) that enables turning on
asynchronous execution. I.e. it signals that the caller is OK with the
function call to complete asynchronously, which allows `ngcc` to
potentially run in a more efficient mode.
Currently, there is no difference in the way tasks are executed in sync
vs async mode, but this change sets the ground for adding new execution
options (that require asynchronous operation), such as processing tasks
in parallel on multiple processes.
NOTE:
When using the programmatic API, the default value for `async` is
`false`, thus retaining backwards compatibility.
When running `ngcc` from the command line (i.e. via the `ivy-ngcc`
script), it runs in async mode (to be able to take advantage of future
optimizations), but that is transparent to the caller.
PR Close #32427
2019-08-19 22:58:22 +03:00
|
|
|
|
2019-03-20 13:47:58 +00:00
|
|
|
/**
|
2019-03-20 13:47:59 +00:00
|
|
|
* Whether to process all formats specified by (`propertiesToConsider`) or to stop processing
|
|
|
|
* this entry-point at the first matching format. Defaults to `true`.
|
2019-03-20 13:47:58 +00:00
|
|
|
*/
|
|
|
|
compileAllFormats?: boolean;
|
refactor(ngcc): add support for asynchronous execution (#32427)
Previously, `ngcc`'s programmatic API would run and complete
synchronously. This was necessary for specific usecases (such as how the
`@angular/cli` invokes `ngcc` as part of the TypeScript module
resolution process), but not for others (e.g. running `ivy-ngcc` as a
`postinstall` script).
This commit adds a new option (`async`) that enables turning on
asynchronous execution. I.e. it signals that the caller is OK with the
function call to complete asynchronously, which allows `ngcc` to
potentially run in a more efficient mode.
Currently, there is no difference in the way tasks are executed in sync
vs async mode, but this change sets the ground for adding new execution
options (that require asynchronous operation), such as processing tasks
in parallel on multiple processes.
NOTE:
When using the programmatic API, the default value for `async` is
`false`, thus retaining backwards compatibility.
When running `ngcc` from the command line (i.e. via the `ivy-ngcc`
script), it runs in async mode (to be able to take advantage of future
optimizations), but that is transparent to the caller.
PR Close #32427
2019-08-19 22:58:22 +03:00
|
|
|
|
2019-03-20 13:47:59 +00:00
|
|
|
/**
|
|
|
|
* Whether to create new entry-points bundles rather than overwriting the original files.
|
|
|
|
*/
|
|
|
|
createNewEntryPointFormats?: boolean;
|
refactor(ngcc): add support for asynchronous execution (#32427)
Previously, `ngcc`'s programmatic API would run and complete
synchronously. This was necessary for specific usecases (such as how the
`@angular/cli` invokes `ngcc` as part of the TypeScript module
resolution process), but not for others (e.g. running `ivy-ngcc` as a
`postinstall` script).
This commit adds a new option (`async`) that enables turning on
asynchronous execution. I.e. it signals that the caller is OK with the
function call to complete asynchronously, which allows `ngcc` to
potentially run in a more efficient mode.
Currently, there is no difference in the way tasks are executed in sync
vs async mode, but this change sets the ground for adding new execution
options (that require asynchronous operation), such as processing tasks
in parallel on multiple processes.
NOTE:
When using the programmatic API, the default value for `async` is
`false`, thus retaining backwards compatibility.
When running `ngcc` from the command line (i.e. via the `ivy-ngcc`
script), it runs in async mode (to be able to take advantage of future
optimizations), but that is transparent to the caller.
PR Close #32427
2019-08-19 22:58:22 +03:00
|
|
|
|
2019-03-29 10:13:14 +00:00
|
|
|
/**
|
|
|
|
* Provide a logger that will be called with log messages.
|
|
|
|
*/
|
|
|
|
logger?: Logger;
|
refactor(ngcc): add support for asynchronous execution (#32427)
Previously, `ngcc`'s programmatic API would run and complete
synchronously. This was necessary for specific usecases (such as how the
`@angular/cli` invokes `ngcc` as part of the TypeScript module
resolution process), but not for others (e.g. running `ivy-ngcc` as a
`postinstall` script).
This commit adds a new option (`async`) that enables turning on
asynchronous execution. I.e. it signals that the caller is OK with the
function call to complete asynchronously, which allows `ngcc` to
potentially run in a more efficient mode.
Currently, there is no difference in the way tasks are executed in sync
vs async mode, but this change sets the ground for adding new execution
options (that require asynchronous operation), such as processing tasks
in parallel on multiple processes.
NOTE:
When using the programmatic API, the default value for `async` is
`false`, thus retaining backwards compatibility.
When running `ngcc` from the command line (i.e. via the `ivy-ngcc`
script), it runs in async mode (to be able to take advantage of future
optimizations), but that is transparent to the caller.
PR Close #32427
2019-08-19 22:58:22 +03:00
|
|
|
|
2019-04-28 20:47:57 +01:00
|
|
|
/**
|
|
|
|
* Paths mapping configuration (`paths` and `baseUrl`), as found in `ts.CompilerOptions`.
|
|
|
|
* These are used to resolve paths to locally built Angular libraries.
|
|
|
|
*/
|
|
|
|
pathMappings?: PathMappings;
|
refactor(ngcc): add support for asynchronous execution (#32427)
Previously, `ngcc`'s programmatic API would run and complete
synchronously. This was necessary for specific usecases (such as how the
`@angular/cli` invokes `ngcc` as part of the TypeScript module
resolution process), but not for others (e.g. running `ivy-ngcc` as a
`postinstall` script).
This commit adds a new option (`async`) that enables turning on
asynchronous execution. I.e. it signals that the caller is OK with the
function call to complete asynchronously, which allows `ngcc` to
potentially run in a more efficient mode.
Currently, there is no difference in the way tasks are executed in sync
vs async mode, but this change sets the ground for adding new execution
options (that require asynchronous operation), such as processing tasks
in parallel on multiple processes.
NOTE:
When using the programmatic API, the default value for `async` is
`false`, thus retaining backwards compatibility.
When running `ngcc` from the command line (i.e. via the `ivy-ngcc`
script), it runs in async mode (to be able to take advantage of future
optimizations), but that is transparent to the caller.
PR Close #32427
2019-08-19 22:58:22 +03:00
|
|
|
|
2019-05-22 15:01:33 +01:00
|
|
|
/**
|
|
|
|
* Provide a file-system service that will be used by ngcc for all file interactions.
|
|
|
|
*/
|
|
|
|
fileSystem?: FileSystem;
|
refactor(ngcc): add support for asynchronous execution (#32427)
Previously, `ngcc`'s programmatic API would run and complete
synchronously. This was necessary for specific usecases (such as how the
`@angular/cli` invokes `ngcc` as part of the TypeScript module
resolution process), but not for others (e.g. running `ivy-ngcc` as a
`postinstall` script).
This commit adds a new option (`async`) that enables turning on
asynchronous execution. I.e. it signals that the caller is OK with the
function call to complete asynchronously, which allows `ngcc` to
potentially run in a more efficient mode.
Currently, there is no difference in the way tasks are executed in sync
vs async mode, but this change sets the ground for adding new execution
options (that require asynchronous operation), such as processing tasks
in parallel on multiple processes.
NOTE:
When using the programmatic API, the default value for `async` is
`false`, thus retaining backwards compatibility.
When running `ngcc` from the command line (i.e. via the `ivy-ngcc`
script), it runs in async mode (to be able to take advantage of future
optimizations), but that is transparent to the caller.
PR Close #32427
2019-08-19 22:58:22 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether the compilation should run and return asynchronously. Allowing asynchronous execution
|
|
|
|
* may speed up the compilation by utilizing multiple CPU cores (if available).
|
|
|
|
*
|
|
|
|
* Default: `false` (i.e. run synchronously)
|
|
|
|
*/
|
|
|
|
async?: false;
|
2019-12-03 08:36:38 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Render `$localize` messages with legacy format ids.
|
|
|
|
*
|
|
|
|
* The default value is `true`. Only set this to `false` if you do not want legacy message ids to
|
|
|
|
* be rendered. For example, if you are not using legacy message ids in your translation files
|
|
|
|
* AND are not doing compile-time inlining of translations, in which case the extra message ids
|
|
|
|
* would add unwanted size to the final source bundle.
|
|
|
|
*
|
|
|
|
* It is safe to leave this set to true if you are doing compile-time inlining because the extra
|
|
|
|
* legacy message ids will all be stripped during translation.
|
|
|
|
*/
|
|
|
|
enableI18nLegacyMessageIdFormat?: boolean;
|
2019-03-20 13:47:58 +00:00
|
|
|
}
|
2018-08-09 15:59:10 +01:00
|
|
|
|
refactor(ngcc): add support for asynchronous execution (#32427)
Previously, `ngcc`'s programmatic API would run and complete
synchronously. This was necessary for specific usecases (such as how the
`@angular/cli` invokes `ngcc` as part of the TypeScript module
resolution process), but not for others (e.g. running `ivy-ngcc` as a
`postinstall` script).
This commit adds a new option (`async`) that enables turning on
asynchronous execution. I.e. it signals that the caller is OK with the
function call to complete asynchronously, which allows `ngcc` to
potentially run in a more efficient mode.
Currently, there is no difference in the way tasks are executed in sync
vs async mode, but this change sets the ground for adding new execution
options (that require asynchronous operation), such as processing tasks
in parallel on multiple processes.
NOTE:
When using the programmatic API, the default value for `async` is
`false`, thus retaining backwards compatibility.
When running `ngcc` from the command line (i.e. via the `ivy-ngcc`
script), it runs in async mode (to be able to take advantage of future
optimizations), but that is transparent to the caller.
PR Close #32427
2019-08-19 22:58:22 +03:00
|
|
|
/**
|
|
|
|
* The options to configure the ngcc compiler for asynchronous execution.
|
|
|
|
*/
|
|
|
|
export type AsyncNgccOptions = Omit<SyncNgccOptions, 'async'>& {async: true};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The options to configure the ngcc compiler.
|
|
|
|
*/
|
|
|
|
export type NgccOptions = AsyncNgccOptions | SyncNgccOptions;
|
|
|
|
|
2019-08-29 18:47:54 +03:00
|
|
|
const EMPTY_GRAPH = new DepGraph<EntryPoint>();
|
|
|
|
|
2019-03-20 13:47:58 +00:00
|
|
|
/**
|
|
|
|
* This is the main entry-point into ngcc (aNGular Compatibility Compiler).
|
|
|
|
*
|
|
|
|
* You can call this function to process one or more npm packages, to ensure
|
|
|
|
* that they are compatible with the ivy compiler (ngtsc).
|
|
|
|
*
|
|
|
|
* @param options The options telling ngcc what to compile and how.
|
|
|
|
*/
|
refactor(ngcc): add support for asynchronous execution (#32427)
Previously, `ngcc`'s programmatic API would run and complete
synchronously. This was necessary for specific usecases (such as how the
`@angular/cli` invokes `ngcc` as part of the TypeScript module
resolution process), but not for others (e.g. running `ivy-ngcc` as a
`postinstall` script).
This commit adds a new option (`async`) that enables turning on
asynchronous execution. I.e. it signals that the caller is OK with the
function call to complete asynchronously, which allows `ngcc` to
potentially run in a more efficient mode.
Currently, there is no difference in the way tasks are executed in sync
vs async mode, but this change sets the ground for adding new execution
options (that require asynchronous operation), such as processing tasks
in parallel on multiple processes.
NOTE:
When using the programmatic API, the default value for `async` is
`false`, thus retaining backwards compatibility.
When running `ngcc` from the command line (i.e. via the `ivy-ngcc`
script), it runs in async mode (to be able to take advantage of future
optimizations), but that is transparent to the caller.
PR Close #32427
2019-08-19 22:58:22 +03:00
|
|
|
export function mainNgcc(options: AsyncNgccOptions): Promise<void>;
|
|
|
|
export function mainNgcc(options: SyncNgccOptions): void;
|
2019-04-28 20:47:57 +01:00
|
|
|
export function mainNgcc(
|
|
|
|
{basePath, targetEntryPointPath, propertiesToConsider = SUPPORTED_FORMAT_PROPERTIES,
|
|
|
|
compileAllFormats = true, createNewEntryPointFormats = false,
|
2019-12-03 08:36:38 +00:00
|
|
|
logger = new ConsoleLogger(LogLevel.info), pathMappings, async = false,
|
|
|
|
enableI18nLegacyMessageIdFormat = true}: NgccOptions): void|Promise<void> {
|
2019-08-29 18:47:54 +03:00
|
|
|
// Execute in parallel, if async execution is acceptable and there are more than 1 CPU cores.
|
|
|
|
const inParallel = async && (os.cpus().length > 1);
|
|
|
|
|
|
|
|
// Instantiate common utilities that are always used.
|
|
|
|
// NOTE: Avoid eagerly instantiating anything that might not be used when running sync/async or in
|
|
|
|
// master/worker process.
|
2019-05-22 15:01:33 +01:00
|
|
|
const fileSystem = getFileSystem();
|
2019-08-29 18:47:54 +03:00
|
|
|
// NOTE: To avoid file corruption, ensure that each `ngcc` invocation only creates _one_ instance
|
|
|
|
// of `PackageJsonUpdater` that actually writes to disk (across all processes).
|
|
|
|
// This is hard to enforce automatically, when running on multiple processes, so needs to be
|
|
|
|
// enforced manually.
|
|
|
|
const pkgJsonUpdater = getPackageJsonUpdater(inParallel, fileSystem);
|
2019-08-07 22:46:35 +03:00
|
|
|
|
|
|
|
// The function for performing the analysis.
|
2019-08-09 15:01:57 +03:00
|
|
|
const analyzeEntryPoints: AnalyzeEntryPointsFn = () => {
|
2019-08-29 18:51:02 +03:00
|
|
|
logger.debug('Analyzing entry-points...');
|
|
|
|
const startTime = Date.now();
|
|
|
|
|
2019-08-07 22:46:35 +03:00
|
|
|
const supportedPropertiesToConsider = ensureSupportedProperties(propertiesToConsider);
|
|
|
|
|
|
|
|
const moduleResolver = new ModuleResolver(fileSystem, pathMappings);
|
|
|
|
const esmDependencyHost = new EsmDependencyHost(fileSystem, moduleResolver);
|
|
|
|
const umdDependencyHost = new UmdDependencyHost(fileSystem, moduleResolver);
|
|
|
|
const commonJsDependencyHost = new CommonJsDependencyHost(fileSystem, moduleResolver);
|
2019-12-19 22:43:13 +00:00
|
|
|
const dtsDependencyHost = createDtsDependencyHost(fileSystem, pathMappings);
|
|
|
|
const dependencyResolver = new DependencyResolver(
|
|
|
|
fileSystem, logger, {
|
|
|
|
esm5: esmDependencyHost,
|
|
|
|
esm2015: esmDependencyHost,
|
|
|
|
umd: umdDependencyHost,
|
|
|
|
commonjs: commonJsDependencyHost
|
|
|
|
},
|
|
|
|
dtsDependencyHost);
|
2019-08-07 22:46:35 +03:00
|
|
|
|
|
|
|
const absBasePath = absoluteFrom(basePath);
|
|
|
|
const config = new NgccConfiguration(fileSystem, dirname(absBasePath));
|
2019-08-29 18:47:54 +03:00
|
|
|
const {entryPoints, graph} = getEntryPoints(
|
2019-08-12 18:15:24 +03:00
|
|
|
fileSystem, pkgJsonUpdater, logger, dependencyResolver, config, absBasePath,
|
|
|
|
targetEntryPointPath, pathMappings, supportedPropertiesToConsider, compileAllFormats);
|
2019-08-07 22:46:35 +03:00
|
|
|
|
refactor(ngcc): take advantage of early knowledge about format property processability (#32427)
In the past, a task's processability didn't use to be known in advance.
It was possible that a task would be created and added to the queue
during the analysis phase and then later (during the compilation phase)
it would be found out that the task (i.e. the associated format
property) was not processable.
As a result, certain checks had to be delayed, until a task's processing
had started or even until all tasks had been processed. Examples of
checks that had to be delayed are:
- Whether a task can be skipped due to `compileAllFormats: false`.
- Whether there were entry-points for which no format at all was
successfully processed.
It turns out that (as made clear by the refactoring in 9537b2ff8), once
a task starts being processed it is expected to either complete
successfully (with the associated format being processed) or throw an
error (in which case the process will exit). In other words, a task's
processability is known in advance.
This commit takes advantage of this fact by moving certain checks
earlier in the process (e.g. in the analysis phase instead of the
compilation phase), which in turn allows avoiding some unnecessary work.
More specifically:
- When `compileAllFormats` is `false`, tasks are created _only_ for the
first suitable format property for each entry-point, since the rest of
the tasks would have been skipped during the compilation phase anyway.
This has the following advantages:
1. It avoids the slight overhead of generating extraneous tasks and
then starting to process them (before realizing they should be
skipped).
2. In a potential future parallel execution mode, unnecessary tasks
might start being processed at the same time as the first (useful)
task, even if their output would be later discarded, wasting
resources. Alternatively, extra logic would have to be added to
prevent this from happening. The change in this commit avoids these
issues.
- When an entry-point is not processable, an error will be thrown
upfront without having to wait for other tasks to be processed before
failing.
PR Close #32427
2019-08-29 01:33:15 +03:00
|
|
|
const unprocessableEntryPointPaths: string[] = [];
|
2019-08-27 17:36:25 +03:00
|
|
|
// The tasks are partially ordered by virtue of the entry-points being partially ordered too.
|
|
|
|
const tasks: PartiallyOrderedTasks = [] as any;
|
2019-08-07 22:46:35 +03:00
|
|
|
|
|
|
|
for (const entryPoint of entryPoints) {
|
|
|
|
const packageJson = entryPoint.packageJson;
|
2019-08-29 15:23:05 +01:00
|
|
|
const hasProcessedTypings = hasBeenProcessed(packageJson, 'typings', entryPoint.path);
|
2019-08-09 15:01:57 +03:00
|
|
|
const {propertiesToProcess, equivalentPropertiesMap} =
|
refactor(ngcc): take advantage of early knowledge about format property processability (#32427)
In the past, a task's processability didn't use to be known in advance.
It was possible that a task would be created and added to the queue
during the analysis phase and then later (during the compilation phase)
it would be found out that the task (i.e. the associated format
property) was not processable.
As a result, certain checks had to be delayed, until a task's processing
had started or even until all tasks had been processed. Examples of
checks that had to be delayed are:
- Whether a task can be skipped due to `compileAllFormats: false`.
- Whether there were entry-points for which no format at all was
successfully processed.
It turns out that (as made clear by the refactoring in 9537b2ff8), once
a task starts being processed it is expected to either complete
successfully (with the associated format being processed) or throw an
error (in which case the process will exit). In other words, a task's
processability is known in advance.
This commit takes advantage of this fact by moving certain checks
earlier in the process (e.g. in the analysis phase instead of the
compilation phase), which in turn allows avoiding some unnecessary work.
More specifically:
- When `compileAllFormats` is `false`, tasks are created _only_ for the
first suitable format property for each entry-point, since the rest of
the tasks would have been skipped during the compilation phase anyway.
This has the following advantages:
1. It avoids the slight overhead of generating extraneous tasks and
then starting to process them (before realizing they should be
skipped).
2. In a potential future parallel execution mode, unnecessary tasks
might start being processed at the same time as the first (useful)
task, even if their output would be later discarded, wasting
resources. Alternatively, extra logic would have to be added to
prevent this from happening. The change in this commit avoids these
issues.
- When an entry-point is not processable, an error will be thrown
upfront without having to wait for other tasks to be processed before
failing.
PR Close #32427
2019-08-29 01:33:15 +03:00
|
|
|
getPropertiesToProcess(packageJson, supportedPropertiesToConsider, compileAllFormats);
|
2019-08-07 22:46:35 +03:00
|
|
|
let processDts = !hasProcessedTypings;
|
|
|
|
|
refactor(ngcc): take advantage of early knowledge about format property processability (#32427)
In the past, a task's processability didn't use to be known in advance.
It was possible that a task would be created and added to the queue
during the analysis phase and then later (during the compilation phase)
it would be found out that the task (i.e. the associated format
property) was not processable.
As a result, certain checks had to be delayed, until a task's processing
had started or even until all tasks had been processed. Examples of
checks that had to be delayed are:
- Whether a task can be skipped due to `compileAllFormats: false`.
- Whether there were entry-points for which no format at all was
successfully processed.
It turns out that (as made clear by the refactoring in 9537b2ff8), once
a task starts being processed it is expected to either complete
successfully (with the associated format being processed) or throw an
error (in which case the process will exit). In other words, a task's
processability is known in advance.
This commit takes advantage of this fact by moving certain checks
earlier in the process (e.g. in the analysis phase instead of the
compilation phase), which in turn allows avoiding some unnecessary work.
More specifically:
- When `compileAllFormats` is `false`, tasks are created _only_ for the
first suitable format property for each entry-point, since the rest of
the tasks would have been skipped during the compilation phase anyway.
This has the following advantages:
1. It avoids the slight overhead of generating extraneous tasks and
then starting to process them (before realizing they should be
skipped).
2. In a potential future parallel execution mode, unnecessary tasks
might start being processed at the same time as the first (useful)
task, even if their output would be later discarded, wasting
resources. Alternatively, extra logic would have to be added to
prevent this from happening. The change in this commit avoids these
issues.
- When an entry-point is not processable, an error will be thrown
upfront without having to wait for other tasks to be processed before
failing.
PR Close #32427
2019-08-29 01:33:15 +03:00
|
|
|
if (propertiesToProcess.length === 0) {
|
|
|
|
// This entry-point is unprocessable (i.e. there is no format property that is of interest
|
|
|
|
// and can be processed). This will result in an error, but continue looping over
|
|
|
|
// entry-points in order to collect all unprocessable ones and display a more informative
|
|
|
|
// error.
|
|
|
|
unprocessableEntryPointPaths.push(entryPoint.path);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-08-07 22:46:35 +03:00
|
|
|
for (const formatProperty of propertiesToProcess) {
|
2019-08-09 15:01:57 +03:00
|
|
|
const formatPropertiesToMarkAsProcessed = equivalentPropertiesMap.get(formatProperty) !;
|
2019-08-08 03:23:46 +03:00
|
|
|
tasks.push({entryPoint, formatProperty, formatPropertiesToMarkAsProcessed, processDts});
|
2019-08-07 22:46:35 +03:00
|
|
|
|
|
|
|
// Only process typings for the first property (if not already processed).
|
|
|
|
processDts = false;
|
|
|
|
}
|
refactor(ngcc): take advantage of early knowledge about format property processability (#32427)
In the past, a task's processability didn't use to be known in advance.
It was possible that a task would be created and added to the queue
during the analysis phase and then later (during the compilation phase)
it would be found out that the task (i.e. the associated format
property) was not processable.
As a result, certain checks had to be delayed, until a task's processing
had started or even until all tasks had been processed. Examples of
checks that had to be delayed are:
- Whether a task can be skipped due to `compileAllFormats: false`.
- Whether there were entry-points for which no format at all was
successfully processed.
It turns out that (as made clear by the refactoring in 9537b2ff8), once
a task starts being processed it is expected to either complete
successfully (with the associated format being processed) or throw an
error (in which case the process will exit). In other words, a task's
processability is known in advance.
This commit takes advantage of this fact by moving certain checks
earlier in the process (e.g. in the analysis phase instead of the
compilation phase), which in turn allows avoiding some unnecessary work.
More specifically:
- When `compileAllFormats` is `false`, tasks are created _only_ for the
first suitable format property for each entry-point, since the rest of
the tasks would have been skipped during the compilation phase anyway.
This has the following advantages:
1. It avoids the slight overhead of generating extraneous tasks and
then starting to process them (before realizing they should be
skipped).
2. In a potential future parallel execution mode, unnecessary tasks
might start being processed at the same time as the first (useful)
task, even if their output would be later discarded, wasting
resources. Alternatively, extra logic would have to be added to
prevent this from happening. The change in this commit avoids these
issues.
- When an entry-point is not processable, an error will be thrown
upfront without having to wait for other tasks to be processed before
failing.
PR Close #32427
2019-08-29 01:33:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check for entry-points for which we could not process any format at all.
|
|
|
|
if (unprocessableEntryPointPaths.length > 0) {
|
|
|
|
throw new Error(
|
|
|
|
'Unable to process any formats for the following entry-points (tried ' +
|
|
|
|
`${propertiesToConsider.join(', ')}): ` +
|
|
|
|
unprocessableEntryPointPaths.map(path => `\n - ${path}`).join(''));
|
2019-08-07 22:46:35 +03:00
|
|
|
}
|
|
|
|
|
2019-08-29 18:51:02 +03:00
|
|
|
const duration = Math.round((Date.now() - startTime) / 1000);
|
|
|
|
logger.debug(
|
|
|
|
`Analyzed ${entryPoints.length} entry-points in ${duration}s. ` +
|
|
|
|
`(Total tasks: ${tasks.length})`);
|
|
|
|
|
2019-08-29 18:47:54 +03:00
|
|
|
return getTaskQueue(inParallel, tasks, graph);
|
2019-08-07 22:46:35 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
// The function for creating the `compile()` function.
|
|
|
|
const createCompileFn: CreateCompileFn = onTaskCompleted => {
|
2019-08-12 18:15:24 +03:00
|
|
|
const fileWriter = getFileWriter(fileSystem, pkgJsonUpdater, createNewEntryPointFormats);
|
2019-08-07 22:46:35 +03:00
|
|
|
const transformer = new Transformer(fileSystem, logger);
|
|
|
|
|
|
|
|
return (task: Task) => {
|
2019-08-08 03:23:46 +03:00
|
|
|
const {entryPoint, formatProperty, formatPropertiesToMarkAsProcessed, processDts} = task;
|
2019-08-07 22:46:35 +03:00
|
|
|
|
|
|
|
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);
|
2018-12-04 22:18:00 +01:00
|
|
|
|
2019-08-07 18:35:22 +03:00
|
|
|
// All properties listed in `propertiesToProcess` are guaranteed to point to a format-path
|
fix(ngcc): ignore format properties that exist but are undefined (#32205)
Previously, `ngcc` assumed that if a format property was defined in
`package.json` it would point to a valid format-path (i.e. a file that
is an entry-point for a specific format). This is generally the case,
except if a format property is set to a non-string value (such as
`package.json`) - either directly in the `package.json` (which is unusual)
or in ngcc.config.js (which is a valid usecase, when one wants a
format property to be ignored by `ngcc`).
For example, the following config file would cause `ngcc` to throw:
```
module.exports = {
packages: {
'test-package': {
entryPoints: {
'.': {
override: {
fesm2015: undefined,
},
},
},
},
},
};
```
This commit fixes it by ensuring that only format properties whose value
is a string are considered by `ngcc`.
For reference, this regression was introduced in #32052.
Fixes #32188
PR Close #32205
2019-08-20 09:43:08 +02:00
|
|
|
// (i.e. they are defined in `entryPoint.packageJson`). Furthermore, they are also guaranteed
|
|
|
|
// to be among `SUPPORTED_FORMAT_PROPERTIES`.
|
2019-08-07 18:35:22 +03:00
|
|
|
// Based on the above, `formatPath` should always be defined and `getEntryPointFormat()`
|
|
|
|
// should always return a format here (and not `undefined`).
|
|
|
|
if (!formatPath || !format) {
|
|
|
|
// This should never happen.
|
|
|
|
throw new Error(
|
2019-08-07 22:46:35 +03:00
|
|
|
`Invariant violated: No format-path or format for ${entryPoint.path} : ` +
|
|
|
|
`${formatProperty} (formatPath: ${formatPath} | format: ${format})`);
|
2019-08-07 18:35:22 +03:00
|
|
|
}
|
2019-03-20 13:47:58 +00:00
|
|
|
|
2019-08-07 22:46:35 +03:00
|
|
|
// The format-path which the property maps to is already processed - nothing to do.
|
2019-08-29 15:23:05 +01:00
|
|
|
if (hasBeenProcessed(packageJson, formatProperty, entryPoint.path)) {
|
2019-08-07 22:46:35 +03:00
|
|
|
logger.debug(`Skipping ${entryPoint.name} : ${formatProperty} (already compiled).`);
|
|
|
|
onTaskCompleted(task, TaskProcessingOutcome.AlreadyProcessed);
|
|
|
|
return;
|
2019-03-20 13:47:58 +00:00
|
|
|
}
|
2019-03-20 13:47:58 +00:00
|
|
|
|
2019-08-07 18:35:22 +03:00
|
|
|
const bundle = makeEntryPointBundle(
|
2019-12-03 08:36:38 +00:00
|
|
|
fileSystem, entryPoint, formatPath, isCore, format, processDts, pathMappings, true,
|
2019-12-03 08:36:38 +00:00
|
|
|
enableI18nLegacyMessageIdFormat);
|
2019-08-07 22:46:35 +03:00
|
|
|
|
|
|
|
logger.info(`Compiling ${entryPoint.name} : ${formatProperty} as ${format}`);
|
2019-04-02 11:51:39 +01:00
|
|
|
|
2019-08-04 19:20:38 +02:00
|
|
|
const result = transformer.transform(bundle);
|
|
|
|
if (result.success) {
|
|
|
|
if (result.diagnostics.length > 0) {
|
2019-11-23 19:17:16 +01:00
|
|
|
logger.warn(replaceTsWithNgInErrors(
|
|
|
|
ts.formatDiagnosticsWithColorAndContext(result.diagnostics, bundle.src.host)));
|
2019-08-04 19:20:38 +02:00
|
|
|
}
|
|
|
|
fileWriter.writeBundle(bundle, result.transformedFiles, formatPropertiesToMarkAsProcessed);
|
|
|
|
} else {
|
2019-11-23 19:17:16 +01:00
|
|
|
const errors = replaceTsWithNgInErrors(
|
|
|
|
ts.formatDiagnosticsWithColorAndContext(result.diagnostics, bundle.src.host));
|
2019-08-04 19:20:38 +02:00
|
|
|
throw new Error(
|
2019-12-18 14:03:05 +00:00
|
|
|
`Failed to compile entry-point ${entryPoint.name} (${formatProperty} as ${format}) due to compilation errors:\n${errors}`);
|
2019-08-04 19:20:38 +02:00
|
|
|
}
|
2019-08-05 23:12:58 +03:00
|
|
|
|
2019-12-05 21:02:57 +02:00
|
|
|
logger.debug(` Successfully compiled ${entryPoint.name} : ${formatProperty}`);
|
|
|
|
|
2019-08-07 22:46:35 +03:00
|
|
|
onTaskCompleted(task, TaskProcessingOutcome.Processed);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2019-08-19 17:10:09 +03:00
|
|
|
// The executor for actually planning and getting the work done.
|
2019-08-29 18:47:54 +03:00
|
|
|
const executor = getExecutor(async, inParallel, logger, pkgJsonUpdater);
|
2019-08-07 22:46:35 +03:00
|
|
|
|
refactor(ngcc): take advantage of early knowledge about format property processability (#32427)
In the past, a task's processability didn't use to be known in advance.
It was possible that a task would be created and added to the queue
during the analysis phase and then later (during the compilation phase)
it would be found out that the task (i.e. the associated format
property) was not processable.
As a result, certain checks had to be delayed, until a task's processing
had started or even until all tasks had been processed. Examples of
checks that had to be delayed are:
- Whether a task can be skipped due to `compileAllFormats: false`.
- Whether there were entry-points for which no format at all was
successfully processed.
It turns out that (as made clear by the refactoring in 9537b2ff8), once
a task starts being processed it is expected to either complete
successfully (with the associated format being processed) or throw an
error (in which case the process will exit). In other words, a task's
processability is known in advance.
This commit takes advantage of this fact by moving certain checks
earlier in the process (e.g. in the analysis phase instead of the
compilation phase), which in turn allows avoiding some unnecessary work.
More specifically:
- When `compileAllFormats` is `false`, tasks are created _only_ for the
first suitable format property for each entry-point, since the rest of
the tasks would have been skipped during the compilation phase anyway.
This has the following advantages:
1. It avoids the slight overhead of generating extraneous tasks and
then starting to process them (before realizing they should be
skipped).
2. In a potential future parallel execution mode, unnecessary tasks
might start being processed at the same time as the first (useful)
task, even if their output would be later discarded, wasting
resources. Alternatively, extra logic would have to be added to
prevent this from happening. The change in this commit avoids these
issues.
- When an entry-point is not processable, an error will be thrown
upfront without having to wait for other tasks to be processed before
failing.
PR Close #32427
2019-08-29 01:33:15 +03:00
|
|
|
return executor.execute(analyzeEntryPoints, createCompileFn);
|
2019-08-06 00:53:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function ensureSupportedProperties(properties: string[]): EntryPointJsonProperty[] {
|
|
|
|
// Short-circuit the case where `properties` has fallen back to the default value:
|
|
|
|
// `SUPPORTED_FORMAT_PROPERTIES`
|
|
|
|
if (properties === SUPPORTED_FORMAT_PROPERTIES) return SUPPORTED_FORMAT_PROPERTIES;
|
|
|
|
|
|
|
|
const supportedProperties: EntryPointJsonProperty[] = [];
|
|
|
|
|
|
|
|
for (const prop of properties as EntryPointJsonProperty[]) {
|
|
|
|
if (SUPPORTED_FORMAT_PROPERTIES.indexOf(prop) !== -1) {
|
|
|
|
supportedProperties.push(prop);
|
2019-03-20 13:47:58 +00:00
|
|
|
}
|
2019-06-06 20:22:32 +01:00
|
|
|
}
|
2019-08-06 00:53:38 +03:00
|
|
|
|
|
|
|
if (supportedProperties.length === 0) {
|
|
|
|
throw new Error(
|
|
|
|
`No supported format property to consider among [${properties.join(', ')}]. ` +
|
|
|
|
`Supported properties: ${SUPPORTED_FORMAT_PROPERTIES.join(', ')}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
return supportedProperties;
|
2018-08-09 13:54:20 +01:00
|
|
|
}
|
2019-03-20 13:47:59 +00:00
|
|
|
|
2019-08-29 18:47:54 +03:00
|
|
|
function getPackageJsonUpdater(inParallel: boolean, fs: FileSystem): PackageJsonUpdater {
|
|
|
|
const directPkgJsonUpdater = new DirectPackageJsonUpdater(fs);
|
|
|
|
return inParallel ? new ClusterPackageJsonUpdater(directPkgJsonUpdater) : directPkgJsonUpdater;
|
|
|
|
}
|
|
|
|
|
2019-08-12 18:15:24 +03:00
|
|
|
function getFileWriter(
|
|
|
|
fs: FileSystem, pkgJsonUpdater: PackageJsonUpdater,
|
|
|
|
createNewEntryPointFormats: boolean): FileWriter {
|
|
|
|
return createNewEntryPointFormats ? new NewEntryPointFileWriter(fs, pkgJsonUpdater) :
|
|
|
|
new InPlaceFileWriter(fs);
|
2019-03-20 13:47:59 +00:00
|
|
|
}
|
2019-04-06 15:35:40 +01:00
|
|
|
|
2019-08-29 18:47:54 +03:00
|
|
|
function getTaskQueue(
|
|
|
|
inParallel: boolean, tasks: PartiallyOrderedTasks, graph: DepGraph<EntryPoint>): TaskQueue {
|
|
|
|
return inParallel ? new ParallelTaskQueue(tasks, graph) : new SerialTaskQueue(tasks);
|
|
|
|
}
|
|
|
|
|
|
|
|
function getExecutor(
|
|
|
|
async: boolean, inParallel: boolean, logger: Logger,
|
|
|
|
pkgJsonUpdater: PackageJsonUpdater): Executor {
|
|
|
|
if (inParallel) {
|
|
|
|
// Execute in parallel (which implies async).
|
|
|
|
// Use up to 8 CPU cores for workers, always reserving one for master.
|
|
|
|
const workerCount = Math.min(8, os.cpus().length - 1);
|
|
|
|
return new ClusterExecutor(workerCount, logger, pkgJsonUpdater);
|
refactor(ngcc): add support for asynchronous execution (#32427)
Previously, `ngcc`'s programmatic API would run and complete
synchronously. This was necessary for specific usecases (such as how the
`@angular/cli` invokes `ngcc` as part of the TypeScript module
resolution process), but not for others (e.g. running `ivy-ngcc` as a
`postinstall` script).
This commit adds a new option (`async`) that enables turning on
asynchronous execution. I.e. it signals that the caller is OK with the
function call to complete asynchronously, which allows `ngcc` to
potentially run in a more efficient mode.
Currently, there is no difference in the way tasks are executed in sync
vs async mode, but this change sets the ground for adding new execution
options (that require asynchronous operation), such as processing tasks
in parallel on multiple processes.
NOTE:
When using the programmatic API, the default value for `async` is
`false`, thus retaining backwards compatibility.
When running `ngcc` from the command line (i.e. via the `ivy-ngcc`
script), it runs in async mode (to be able to take advantage of future
optimizations), but that is transparent to the caller.
PR Close #32427
2019-08-19 22:58:22 +03:00
|
|
|
} else {
|
2019-08-29 18:47:54 +03:00
|
|
|
// Execute serially, on a single thread (either sync or async).
|
|
|
|
return async ? new AsyncSingleProcessExecutor(logger, pkgJsonUpdater) :
|
|
|
|
new SingleProcessExecutor(logger, pkgJsonUpdater);
|
refactor(ngcc): add support for asynchronous execution (#32427)
Previously, `ngcc`'s programmatic API would run and complete
synchronously. This was necessary for specific usecases (such as how the
`@angular/cli` invokes `ngcc` as part of the TypeScript module
resolution process), but not for others (e.g. running `ivy-ngcc` as a
`postinstall` script).
This commit adds a new option (`async`) that enables turning on
asynchronous execution. I.e. it signals that the caller is OK with the
function call to complete asynchronously, which allows `ngcc` to
potentially run in a more efficient mode.
Currently, there is no difference in the way tasks are executed in sync
vs async mode, but this change sets the ground for adding new execution
options (that require asynchronous operation), such as processing tasks
in parallel on multiple processes.
NOTE:
When using the programmatic API, the default value for `async` is
`false`, thus retaining backwards compatibility.
When running `ngcc` from the command line (i.e. via the `ivy-ngcc`
script), it runs in async mode (to be able to take advantage of future
optimizations), but that is transparent to the caller.
PR Close #32427
2019-08-19 22:58:22 +03:00
|
|
|
}
|
2019-08-19 17:10:09 +03:00
|
|
|
}
|
|
|
|
|
2019-05-16 08:53:19 +01:00
|
|
|
function getEntryPoints(
|
2019-08-12 18:15:24 +03:00
|
|
|
fs: FileSystem, pkgJsonUpdater: PackageJsonUpdater, logger: Logger,
|
|
|
|
resolver: DependencyResolver, config: NgccConfiguration, basePath: AbsoluteFsPath,
|
|
|
|
targetEntryPointPath: string | undefined, pathMappings: PathMappings | undefined,
|
2019-08-29 18:47:54 +03:00
|
|
|
propertiesToConsider: string[], compileAllFormats: boolean):
|
|
|
|
{entryPoints: PartiallyOrderedEntryPoints, graph: DepGraph<EntryPoint>} {
|
|
|
|
const {entryPoints, invalidEntryPoints, graph} = (targetEntryPointPath !== undefined) ?
|
2019-05-16 08:53:19 +01:00
|
|
|
getTargetedEntryPoints(
|
2019-08-12 18:15:24 +03:00
|
|
|
fs, pkgJsonUpdater, logger, resolver, config, basePath, targetEntryPointPath,
|
|
|
|
propertiesToConsider, compileAllFormats, pathMappings) :
|
2019-05-16 08:53:19 +01:00
|
|
|
getAllEntryPoints(fs, config, logger, resolver, basePath, pathMappings);
|
|
|
|
logInvalidEntryPoints(logger, invalidEntryPoints);
|
2019-08-29 18:47:54 +03:00
|
|
|
return {entryPoints, graph};
|
2019-05-16 08:53:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function getTargetedEntryPoints(
|
2019-08-12 18:15:24 +03:00
|
|
|
fs: FileSystem, pkgJsonUpdater: PackageJsonUpdater, logger: Logger,
|
|
|
|
resolver: DependencyResolver, config: NgccConfiguration, basePath: AbsoluteFsPath,
|
|
|
|
targetEntryPointPath: string, propertiesToConsider: string[], compileAllFormats: boolean,
|
|
|
|
pathMappings: PathMappings | undefined): SortedEntryPointsInfo {
|
2019-05-16 08:53:19 +01:00
|
|
|
const absoluteTargetEntryPointPath = resolve(basePath, targetEntryPointPath);
|
|
|
|
if (hasProcessedTargetEntryPoint(
|
|
|
|
fs, absoluteTargetEntryPointPath, propertiesToConsider, compileAllFormats)) {
|
|
|
|
logger.debug('The target entry-point has already been processed');
|
2019-08-27 17:36:25 +03:00
|
|
|
return {
|
|
|
|
entryPoints: [] as unknown as PartiallyOrderedEntryPoints,
|
|
|
|
invalidEntryPoints: [],
|
|
|
|
ignoredDependencies: [],
|
2019-08-29 18:47:54 +03:00
|
|
|
graph: EMPTY_GRAPH,
|
2019-08-27 17:36:25 +03:00
|
|
|
};
|
2019-05-16 08:53:19 +01:00
|
|
|
}
|
|
|
|
const finder = new TargetedEntryPointFinder(
|
|
|
|
fs, config, logger, resolver, basePath, absoluteTargetEntryPointPath, pathMappings);
|
|
|
|
const entryPointInfo = finder.findEntryPoints();
|
2019-07-31 12:54:12 +01:00
|
|
|
const invalidTarget = entryPointInfo.invalidEntryPoints.find(
|
|
|
|
i => i.entryPoint.path === absoluteTargetEntryPointPath);
|
|
|
|
if (invalidTarget !== undefined) {
|
|
|
|
throw new Error(
|
|
|
|
`The target entry-point "${invalidTarget.entryPoint.name}" has missing dependencies:\n` +
|
2019-10-14 13:00:24 +03:00
|
|
|
invalidTarget.missingDependencies.map(dep => ` - ${dep}\n`).join(''));
|
2019-07-31 12:54:12 +01:00
|
|
|
}
|
2019-05-16 08:53:19 +01:00
|
|
|
if (entryPointInfo.entryPoints.length === 0) {
|
2019-08-12 18:15:24 +03:00
|
|
|
markNonAngularPackageAsProcessed(fs, pkgJsonUpdater, absoluteTargetEntryPointPath);
|
2019-05-16 08:53:19 +01:00
|
|
|
}
|
|
|
|
return entryPointInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getAllEntryPoints(
|
|
|
|
fs: FileSystem, config: NgccConfiguration, logger: Logger, resolver: DependencyResolver,
|
|
|
|
basePath: AbsoluteFsPath, pathMappings: PathMappings | undefined): SortedEntryPointsInfo {
|
|
|
|
const finder =
|
|
|
|
new DirectoryWalkerEntryPointFinder(fs, config, logger, resolver, basePath, pathMappings);
|
|
|
|
return finder.findEntryPoints();
|
|
|
|
}
|
|
|
|
|
2019-04-06 15:35:40 +01:00
|
|
|
function hasProcessedTargetEntryPoint(
|
2019-04-28 20:47:57 +01:00
|
|
|
fs: FileSystem, targetPath: AbsoluteFsPath, propertiesToConsider: string[],
|
|
|
|
compileAllFormats: boolean) {
|
2019-06-06 20:22:32 +01:00
|
|
|
const packageJsonPath = resolve(targetPath, 'package.json');
|
2019-05-21 15:23:24 +01:00
|
|
|
// It might be that this target is configured in which case its package.json might not exist.
|
|
|
|
if (!fs.exists(packageJsonPath)) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-04-28 20:47:57 +01:00
|
|
|
const packageJson = JSON.parse(fs.readFile(packageJsonPath));
|
2019-04-06 15:35:40 +01:00
|
|
|
|
|
|
|
for (const property of propertiesToConsider) {
|
|
|
|
if (packageJson[property]) {
|
|
|
|
// Here is a property that should be processed
|
2019-08-29 15:23:05 +01:00
|
|
|
if (hasBeenProcessed(packageJson, property as EntryPointJsonProperty, targetPath)) {
|
2019-04-06 15:35:40 +01:00
|
|
|
if (!compileAllFormats) {
|
|
|
|
// It has been processed and we only need one, so we are done.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// It has not been processed but we need all of them, so we are done.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Either all formats need to be compiled and there were none that were unprocessed,
|
|
|
|
// Or only the one matching format needs to be compiled but there was at least one matching
|
|
|
|
// property before the first processed format that was unprocessed.
|
|
|
|
return true;
|
|
|
|
}
|
2019-04-28 20:47:56 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* If we get here, then the requested entry-point did not contain anything compiled by
|
|
|
|
* the old Angular compiler. Therefore there is nothing for ngcc to do.
|
|
|
|
* So mark all formats in this entry-point as processed so that clients of ngcc can avoid
|
|
|
|
* triggering ngcc for this entry-point in the future.
|
|
|
|
*/
|
2019-08-12 18:15:24 +03:00
|
|
|
function markNonAngularPackageAsProcessed(
|
|
|
|
fs: FileSystem, pkgJsonUpdater: PackageJsonUpdater, path: AbsoluteFsPath) {
|
2019-06-06 20:22:32 +01:00
|
|
|
const packageJsonPath = resolve(path, 'package.json');
|
2019-04-28 20:47:57 +01:00
|
|
|
const packageJson = JSON.parse(fs.readFile(packageJsonPath));
|
2019-08-04 17:51:37 +03:00
|
|
|
|
|
|
|
// Note: We are marking all supported properties as processed, even if they don't exist in the
|
|
|
|
// `package.json` file. While this is redundant, it is also harmless.
|
2019-08-12 18:15:24 +03:00
|
|
|
markAsProcessed(pkgJsonUpdater, packageJson, packageJsonPath, SUPPORTED_FORMAT_PROPERTIES);
|
2019-04-28 20:47:56 +01:00
|
|
|
}
|
2019-05-16 08:53:19 +01:00
|
|
|
|
|
|
|
function logInvalidEntryPoints(logger: Logger, invalidEntryPoints: InvalidEntryPoint[]): void {
|
|
|
|
invalidEntryPoints.forEach(invalidEntryPoint => {
|
|
|
|
logger.debug(
|
|
|
|
`Invalid entry-point ${invalidEntryPoint.entryPoint.path}.`,
|
|
|
|
`It is missing required dependencies:\n` +
|
|
|
|
invalidEntryPoint.missingDependencies.map(dep => ` - ${dep}`).join('\n'));
|
|
|
|
});
|
|
|
|
}
|
2019-08-05 13:36:51 +03:00
|
|
|
|
2019-08-07 18:35:22 +03:00
|
|
|
/**
|
|
|
|
* This function computes and returns the following:
|
|
|
|
* - `propertiesToProcess`: An (ordered) list of properties that exist and need to be processed,
|
2019-08-09 15:01:57 +03:00
|
|
|
* based on the provided `propertiesToConsider`, the properties in `package.json` and their
|
2019-08-07 18:35:22 +03:00
|
|
|
* corresponding format-paths. NOTE: Only one property per format-path needs to be processed.
|
2019-08-09 15:01:57 +03:00
|
|
|
* - `equivalentPropertiesMap`: A mapping from each property in `propertiesToProcess` to the list of
|
|
|
|
* other format properties in `package.json` that need to be marked as processed as soon as the
|
|
|
|
* former has been processed.
|
2019-08-07 18:35:22 +03:00
|
|
|
*/
|
2019-08-09 15:01:57 +03:00
|
|
|
function getPropertiesToProcess(
|
refactor(ngcc): take advantage of early knowledge about format property processability (#32427)
In the past, a task's processability didn't use to be known in advance.
It was possible that a task would be created and added to the queue
during the analysis phase and then later (during the compilation phase)
it would be found out that the task (i.e. the associated format
property) was not processable.
As a result, certain checks had to be delayed, until a task's processing
had started or even until all tasks had been processed. Examples of
checks that had to be delayed are:
- Whether a task can be skipped due to `compileAllFormats: false`.
- Whether there were entry-points for which no format at all was
successfully processed.
It turns out that (as made clear by the refactoring in 9537b2ff8), once
a task starts being processed it is expected to either complete
successfully (with the associated format being processed) or throw an
error (in which case the process will exit). In other words, a task's
processability is known in advance.
This commit takes advantage of this fact by moving certain checks
earlier in the process (e.g. in the analysis phase instead of the
compilation phase), which in turn allows avoiding some unnecessary work.
More specifically:
- When `compileAllFormats` is `false`, tasks are created _only_ for the
first suitable format property for each entry-point, since the rest of
the tasks would have been skipped during the compilation phase anyway.
This has the following advantages:
1. It avoids the slight overhead of generating extraneous tasks and
then starting to process them (before realizing they should be
skipped).
2. In a potential future parallel execution mode, unnecessary tasks
might start being processed at the same time as the first (useful)
task, even if their output would be later discarded, wasting
resources. Alternatively, extra logic would have to be added to
prevent this from happening. The change in this commit avoids these
issues.
- When an entry-point is not processable, an error will be thrown
upfront without having to wait for other tasks to be processed before
failing.
PR Close #32427
2019-08-29 01:33:15 +03:00
|
|
|
packageJson: EntryPointPackageJson, propertiesToConsider: EntryPointJsonProperty[],
|
|
|
|
compileAllFormats: boolean): {
|
2019-08-07 18:35:22 +03:00
|
|
|
propertiesToProcess: EntryPointJsonProperty[];
|
2019-08-09 15:01:57 +03:00
|
|
|
equivalentPropertiesMap: Map<EntryPointJsonProperty, EntryPointJsonProperty[]>;
|
2019-08-07 18:35:22 +03:00
|
|
|
} {
|
|
|
|
const formatPathsToConsider = new Set<string>();
|
|
|
|
|
|
|
|
const propertiesToProcess: EntryPointJsonProperty[] = [];
|
|
|
|
for (const prop of propertiesToConsider) {
|
fix(ngcc): ignore format properties that exist but are undefined (#32205)
Previously, `ngcc` assumed that if a format property was defined in
`package.json` it would point to a valid format-path (i.e. a file that
is an entry-point for a specific format). This is generally the case,
except if a format property is set to a non-string value (such as
`package.json`) - either directly in the `package.json` (which is unusual)
or in ngcc.config.js (which is a valid usecase, when one wants a
format property to be ignored by `ngcc`).
For example, the following config file would cause `ngcc` to throw:
```
module.exports = {
packages: {
'test-package': {
entryPoints: {
'.': {
override: {
fesm2015: undefined,
},
},
},
},
},
};
```
This commit fixes it by ensuring that only format properties whose value
is a string are considered by `ngcc`.
For reference, this regression was introduced in #32052.
Fixes #32188
PR Close #32205
2019-08-20 09:43:08 +02:00
|
|
|
const formatPath = packageJson[prop];
|
2019-08-07 18:35:22 +03:00
|
|
|
|
fix(ngcc): ignore format properties that exist but are undefined (#32205)
Previously, `ngcc` assumed that if a format property was defined in
`package.json` it would point to a valid format-path (i.e. a file that
is an entry-point for a specific format). This is generally the case,
except if a format property is set to a non-string value (such as
`package.json`) - either directly in the `package.json` (which is unusual)
or in ngcc.config.js (which is a valid usecase, when one wants a
format property to be ignored by `ngcc`).
For example, the following config file would cause `ngcc` to throw:
```
module.exports = {
packages: {
'test-package': {
entryPoints: {
'.': {
override: {
fesm2015: undefined,
},
},
},
},
},
};
```
This commit fixes it by ensuring that only format properties whose value
is a string are considered by `ngcc`.
For reference, this regression was introduced in #32052.
Fixes #32188
PR Close #32205
2019-08-20 09:43:08 +02:00
|
|
|
// Ignore properties that are not defined in `package.json`.
|
|
|
|
if (typeof formatPath !== 'string') continue;
|
2019-08-07 18:35:22 +03:00
|
|
|
|
|
|
|
// Ignore properties that map to the same format-path as a preceding property.
|
|
|
|
if (formatPathsToConsider.has(formatPath)) continue;
|
|
|
|
|
|
|
|
// Process this property, because it is the first one to map to this format-path.
|
|
|
|
formatPathsToConsider.add(formatPath);
|
|
|
|
propertiesToProcess.push(prop);
|
refactor(ngcc): take advantage of early knowledge about format property processability (#32427)
In the past, a task's processability didn't use to be known in advance.
It was possible that a task would be created and added to the queue
during the analysis phase and then later (during the compilation phase)
it would be found out that the task (i.e. the associated format
property) was not processable.
As a result, certain checks had to be delayed, until a task's processing
had started or even until all tasks had been processed. Examples of
checks that had to be delayed are:
- Whether a task can be skipped due to `compileAllFormats: false`.
- Whether there were entry-points for which no format at all was
successfully processed.
It turns out that (as made clear by the refactoring in 9537b2ff8), once
a task starts being processed it is expected to either complete
successfully (with the associated format being processed) or throw an
error (in which case the process will exit). In other words, a task's
processability is known in advance.
This commit takes advantage of this fact by moving certain checks
earlier in the process (e.g. in the analysis phase instead of the
compilation phase), which in turn allows avoiding some unnecessary work.
More specifically:
- When `compileAllFormats` is `false`, tasks are created _only_ for the
first suitable format property for each entry-point, since the rest of
the tasks would have been skipped during the compilation phase anyway.
This has the following advantages:
1. It avoids the slight overhead of generating extraneous tasks and
then starting to process them (before realizing they should be
skipped).
2. In a potential future parallel execution mode, unnecessary tasks
might start being processed at the same time as the first (useful)
task, even if their output would be later discarded, wasting
resources. Alternatively, extra logic would have to be added to
prevent this from happening. The change in this commit avoids these
issues.
- When an entry-point is not processable, an error will be thrown
upfront without having to wait for other tasks to be processed before
failing.
PR Close #32427
2019-08-29 01:33:15 +03:00
|
|
|
|
|
|
|
// If we only need one format processed, there is no need to process any more properties.
|
|
|
|
if (!compileAllFormats) break;
|
2019-08-07 18:35:22 +03:00
|
|
|
}
|
2019-08-05 13:36:51 +03:00
|
|
|
|
2019-08-07 18:35:22 +03:00
|
|
|
const formatPathToProperties: {[formatPath: string]: EntryPointJsonProperty[]} = {};
|
2019-08-05 13:36:51 +03:00
|
|
|
for (const prop of SUPPORTED_FORMAT_PROPERTIES) {
|
fix(ngcc): ignore format properties that exist but are undefined (#32205)
Previously, `ngcc` assumed that if a format property was defined in
`package.json` it would point to a valid format-path (i.e. a file that
is an entry-point for a specific format). This is generally the case,
except if a format property is set to a non-string value (such as
`package.json`) - either directly in the `package.json` (which is unusual)
or in ngcc.config.js (which is a valid usecase, when one wants a
format property to be ignored by `ngcc`).
For example, the following config file would cause `ngcc` to throw:
```
module.exports = {
packages: {
'test-package': {
entryPoints: {
'.': {
override: {
fesm2015: undefined,
},
},
},
},
},
};
```
This commit fixes it by ensuring that only format properties whose value
is a string are considered by `ngcc`.
For reference, this regression was introduced in #32052.
Fixes #32188
PR Close #32205
2019-08-20 09:43:08 +02:00
|
|
|
const formatPath = packageJson[prop];
|
2019-08-07 18:35:22 +03:00
|
|
|
|
fix(ngcc): ignore format properties that exist but are undefined (#32205)
Previously, `ngcc` assumed that if a format property was defined in
`package.json` it would point to a valid format-path (i.e. a file that
is an entry-point for a specific format). This is generally the case,
except if a format property is set to a non-string value (such as
`package.json`) - either directly in the `package.json` (which is unusual)
or in ngcc.config.js (which is a valid usecase, when one wants a
format property to be ignored by `ngcc`).
For example, the following config file would cause `ngcc` to throw:
```
module.exports = {
packages: {
'test-package': {
entryPoints: {
'.': {
override: {
fesm2015: undefined,
},
},
},
},
},
};
```
This commit fixes it by ensuring that only format properties whose value
is a string are considered by `ngcc`.
For reference, this regression was introduced in #32052.
Fixes #32188
PR Close #32205
2019-08-20 09:43:08 +02:00
|
|
|
// Ignore properties that are not defined in `package.json`.
|
|
|
|
if (typeof formatPath !== 'string') continue;
|
2019-08-07 18:35:22 +03:00
|
|
|
|
|
|
|
// Ignore properties that do not map to a format-path that will be considered.
|
|
|
|
if (!formatPathsToConsider.has(formatPath)) continue;
|
|
|
|
|
|
|
|
// Add this property to the map.
|
|
|
|
const list = formatPathToProperties[formatPath] || (formatPathToProperties[formatPath] = []);
|
|
|
|
list.push(prop);
|
|
|
|
}
|
|
|
|
|
2019-08-09 15:01:57 +03:00
|
|
|
const equivalentPropertiesMap = new Map<EntryPointJsonProperty, EntryPointJsonProperty[]>();
|
2019-08-07 18:35:22 +03:00
|
|
|
for (const prop of propertiesToConsider) {
|
|
|
|
const formatPath = packageJson[prop] !;
|
2019-08-09 15:01:57 +03:00
|
|
|
const equivalentProperties = formatPathToProperties[formatPath];
|
|
|
|
equivalentPropertiesMap.set(prop, equivalentProperties);
|
2019-08-05 13:36:51 +03:00
|
|
|
}
|
|
|
|
|
2019-08-09 15:01:57 +03:00
|
|
|
return {propertiesToProcess, equivalentPropertiesMap};
|
2019-08-05 13:36:51 +03:00
|
|
|
}
|