feat(ngcc): implement a program-based entry-point finder (#37075)
This finder is designed to only process entry-points that are reachable by the program defined by a tsconfig.json file. It is triggered by calling `mainNgcc()` with the `findEntryPointsFromTsConfigProgram` option set to true. It is ignored if a `targetEntryPointPath` has been provided as well. It is triggered from the command line by adding the `--use-program-dependencies` option, which is also ignored if the `--target` option has been provided. Using this option can speed up processing in cases where there is a large number of dependencies installed but only a small proportion of the entry-points are actually imported into the application. PR Close #37075
This commit is contained in:
parent
07a8016118
commit
57411c85b9
|
@ -36,7 +36,14 @@ export function parseCommandLineOptions(args: string[]): NgccOptions {
|
|||
alias: 'target',
|
||||
describe:
|
||||
'A relative path (from the `source` path) to a single entry-point to process (plus its dependencies).\n' +
|
||||
'If this property is provided then `error-on-failed-entry-point` is forced to true',
|
||||
'If this property is provided then `error-on-failed-entry-point` is forced to true.\n' +
|
||||
'This option overrides the `--use-program-dependencies` option.',
|
||||
})
|
||||
.option('use-program-dependencies', {
|
||||
type: 'boolean',
|
||||
describe:
|
||||
'If this property is provided then the entry-points to process are parsed from the program defined by the loaded tsconfig.json. See `--tsconfig`.\n' +
|
||||
'This option is overridden by the `--target` option.',
|
||||
})
|
||||
.option('first-only', {
|
||||
describe:
|
||||
|
@ -116,6 +123,7 @@ export function parseCommandLineOptions(args: string[]): NgccOptions {
|
|||
const enableI18nLegacyMessageIdFormat = options['legacy-message-ids'];
|
||||
const invalidateEntryPointManifest = options['invalidate-entry-point-manifest'];
|
||||
const errorOnFailedEntryPoint = options['error-on-failed-entry-point'];
|
||||
const findEntryPointsFromTsConfigProgram = options['use-program-dependencies'];
|
||||
// yargs is not so great at mixed string+boolean types, so we have to test tsconfig against a
|
||||
// string "false" to capture the `tsconfig=false` option.
|
||||
// And we have to convert the option to a string to handle `no-tsconfig`, which will be `false`.
|
||||
|
@ -134,6 +142,7 @@ export function parseCommandLineOptions(args: string[]): NgccOptions {
|
|||
async: options['async'],
|
||||
invalidateEntryPointManifest,
|
||||
errorOnFailedEntryPoint,
|
||||
tsConfigPath
|
||||
tsConfigPath,
|
||||
findEntryPointsFromTsConfigProgram,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* @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 {AbsoluteFsPath, FileSystem} from '../../../src/ngtsc/file_system';
|
||||
import {ParsedConfiguration} from '../../../src/perform_compile';
|
||||
|
||||
import {createDependencyInfo} from '../dependencies/dependency_host';
|
||||
import {DependencyResolver} from '../dependencies/dependency_resolver';
|
||||
import {EsmDependencyHost} from '../dependencies/esm_dependency_host';
|
||||
import {ModuleResolver} from '../dependencies/module_resolver';
|
||||
import {Logger} from '../logging/logger';
|
||||
import {NgccConfiguration} from '../packages/configuration';
|
||||
import {getPathMappingsFromTsConfig} from '../path_mappings';
|
||||
|
||||
import {TracingEntryPointFinder} from './tracing_entry_point_finder';
|
||||
|
||||
/**
|
||||
* An EntryPointFinder that starts from the files in the program defined by the given tsconfig.json
|
||||
* and only returns entry-points that are dependencies of these files.
|
||||
*
|
||||
* This is faster than searching the entire file-system for all the entry-points,
|
||||
* and is used primarily by the CLI integration.
|
||||
*/
|
||||
export class ProgramBasedEntryPointFinder extends TracingEntryPointFinder {
|
||||
constructor(
|
||||
fs: FileSystem, config: NgccConfiguration, logger: Logger, resolver: DependencyResolver,
|
||||
basePath: AbsoluteFsPath, private tsConfig: ParsedConfiguration,
|
||||
projectPath: AbsoluteFsPath) {
|
||||
super(
|
||||
fs, config, logger, resolver, basePath, getPathMappingsFromTsConfig(tsConfig, projectPath));
|
||||
}
|
||||
|
||||
protected getInitialEntryPointPaths(): AbsoluteFsPath[] {
|
||||
const moduleResolver = new ModuleResolver(this.fs, this.pathMappings, ['', '.ts', '/index.ts']);
|
||||
const host = new EsmDependencyHost(this.fs, moduleResolver);
|
||||
const dependencies = createDependencyInfo();
|
||||
this.logger.debug(
|
||||
`Using the program from ${this.tsConfig.project} to seed the entry-point finding.`);
|
||||
this.logger.debug(
|
||||
`Collecting dependencies from the following files:` +
|
||||
this.tsConfig.rootNames.map(file => `\n- ${file}`));
|
||||
this.tsConfig.rootNames.forEach(rootName => {
|
||||
host.collectDependencies(this.fs.resolve(rootName), dependencies);
|
||||
});
|
||||
return Array.from(dependencies.dependencies);
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ import {EntryPoint, EntryPointJsonProperty, getEntryPointInfo, INCOMPATIBLE_ENTR
|
|||
import {PathMappings} from '../path_mappings';
|
||||
|
||||
import {EntryPointFinder} from './interface';
|
||||
import {TracingEntryPointFinder} from './tracing_entry_point_finder';
|
||||
import {getBasePaths} from './utils';
|
||||
|
||||
/**
|
||||
|
@ -24,30 +25,16 @@ import {getBasePaths} from './utils';
|
|||
* This is faster than searching the entire file-system for all the entry-points,
|
||||
* and is used primarily by the CLI integration.
|
||||
*/
|
||||
export class TargetedEntryPointFinder implements EntryPointFinder {
|
||||
private unprocessedPaths: AbsoluteFsPath[] = [];
|
||||
private unsortedEntryPoints = new Map<AbsoluteFsPath, EntryPointWithDependencies>();
|
||||
private basePaths: AbsoluteFsPath[]|null = null;
|
||||
private getBasePaths() {
|
||||
if (this.basePaths === null) {
|
||||
this.basePaths = getBasePaths(this.logger, this.basePath, this.pathMappings);
|
||||
}
|
||||
return this.basePaths;
|
||||
}
|
||||
|
||||
export class TargetedEntryPointFinder extends TracingEntryPointFinder {
|
||||
constructor(
|
||||
private fs: FileSystem, private config: NgccConfiguration, private logger: Logger,
|
||||
private resolver: DependencyResolver, private basePath: AbsoluteFsPath,
|
||||
private targetPath: AbsoluteFsPath, private pathMappings: PathMappings|undefined) {}
|
||||
fs: FileSystem, config: NgccConfiguration, logger: Logger, resolver: DependencyResolver,
|
||||
basePath: AbsoluteFsPath, pathMappings: PathMappings|undefined,
|
||||
private targetPath: AbsoluteFsPath) {
|
||||
super(fs, config, logger, resolver, basePath, pathMappings);
|
||||
}
|
||||
|
||||
findEntryPoints(): SortedEntryPointsInfo {
|
||||
this.unprocessedPaths = [this.targetPath];
|
||||
while (this.unprocessedPaths.length > 0) {
|
||||
this.processNextPath();
|
||||
}
|
||||
const targetEntryPoint = this.unsortedEntryPoints.get(this.targetPath);
|
||||
const entryPoints = this.resolver.sortEntryPointsByDependency(
|
||||
Array.from(this.unsortedEntryPoints.values()), targetEntryPoint?.entryPoint);
|
||||
const entryPoints = super.findEntryPoints();
|
||||
|
||||
const invalidTarget =
|
||||
entryPoints.invalidEntryPoints.find(i => i.entryPoint.path === this.targetPath);
|
||||
|
@ -83,149 +70,7 @@ export class TargetedEntryPointFinder implements EntryPointFinder {
|
|||
return false;
|
||||
}
|
||||
|
||||
private processNextPath(): void {
|
||||
const path = this.unprocessedPaths.shift()!;
|
||||
const entryPoint = this.getEntryPoint(path);
|
||||
if (entryPoint === null || !entryPoint.compiledByAngular) {
|
||||
return;
|
||||
}
|
||||
const entryPointWithDeps = this.resolver.getEntryPointWithDependencies(entryPoint);
|
||||
this.unsortedEntryPoints.set(entryPoint.path, entryPointWithDeps);
|
||||
entryPointWithDeps.depInfo.dependencies.forEach(dep => {
|
||||
if (!this.unsortedEntryPoints.has(dep)) {
|
||||
this.unprocessedPaths.push(dep);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private getEntryPoint(entryPointPath: AbsoluteFsPath): EntryPoint|null {
|
||||
const packagePath = this.computePackagePath(entryPointPath);
|
||||
const entryPoint =
|
||||
getEntryPointInfo(this.fs, this.config, this.logger, packagePath, entryPointPath);
|
||||
if (entryPoint === NO_ENTRY_POINT || entryPoint === INCOMPATIBLE_ENTRY_POINT) {
|
||||
return null;
|
||||
}
|
||||
return entryPoint;
|
||||
}
|
||||
|
||||
private computePackagePath(entryPointPath: AbsoluteFsPath): AbsoluteFsPath {
|
||||
// First try the main basePath, to avoid having to compute the other basePaths from the paths
|
||||
// mappings, which can be computationally intensive.
|
||||
if (entryPointPath.startsWith(this.basePath)) {
|
||||
const packagePath = this.computePackagePathFromContainingPath(entryPointPath, this.basePath);
|
||||
if (packagePath !== null) {
|
||||
return packagePath;
|
||||
}
|
||||
}
|
||||
|
||||
// The main `basePath` didn't work out so now we try the `basePaths` computed from the paths
|
||||
// mappings in `tsconfig.json`.
|
||||
for (const basePath of this.getBasePaths()) {
|
||||
if (entryPointPath.startsWith(basePath)) {
|
||||
const packagePath = this.computePackagePathFromContainingPath(entryPointPath, basePath);
|
||||
if (packagePath !== null) {
|
||||
return packagePath;
|
||||
}
|
||||
// If we got here then we couldn't find a `packagePath` for the current `basePath`.
|
||||
// Since `basePath`s are guaranteed not to be a sub-directory of each other then no other
|
||||
// `basePath` will match either.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, if we couldn't find a `packagePath` using `basePaths` then try to find the nearest
|
||||
// `node_modules` that contains the `entryPointPath`, if there is one, and use it as a
|
||||
// `basePath`.
|
||||
return this.computePackagePathFromNearestNodeModules(entryPointPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search down to the `entryPointPath` from the `containingPath` for the first `package.json` that
|
||||
* we come to. This is the path to the entry-point's containing package. For example if
|
||||
* `containingPath` is `/a/b/c` and `entryPointPath` is `/a/b/c/d/e` and there exists
|
||||
* `/a/b/c/d/package.json` and `/a/b/c/d/e/package.json`, then we will return `/a/b/c/d`.
|
||||
*
|
||||
* To account for nested `node_modules` we actually start the search at the last `node_modules` in
|
||||
* the `entryPointPath` that is below the `containingPath`. E.g. if `containingPath` is `/a/b/c`
|
||||
* and `entryPointPath` is `/a/b/c/d/node_modules/x/y/z`, we start the search at
|
||||
* `/a/b/c/d/node_modules`.
|
||||
*/
|
||||
private computePackagePathFromContainingPath(
|
||||
entryPointPath: AbsoluteFsPath, containingPath: AbsoluteFsPath): AbsoluteFsPath|null {
|
||||
let packagePath = containingPath;
|
||||
const segments = this.splitPath(relative(containingPath, entryPointPath));
|
||||
let nodeModulesIndex = segments.lastIndexOf(relativeFrom('node_modules'));
|
||||
|
||||
// If there are no `node_modules` in the relative path between the `basePath` and the
|
||||
// `entryPointPath` then just try the `basePath` as the `packagePath`.
|
||||
// (This can be the case with path-mapped entry-points.)
|
||||
if (nodeModulesIndex === -1) {
|
||||
if (this.fs.exists(join(packagePath, 'package.json'))) {
|
||||
return packagePath;
|
||||
}
|
||||
}
|
||||
|
||||
// Start the search at the deepest nested `node_modules` folder that is below the `basePath`
|
||||
// but above the `entryPointPath`, if there are any.
|
||||
while (nodeModulesIndex >= 0) {
|
||||
packagePath = join(packagePath, segments.shift()!);
|
||||
nodeModulesIndex--;
|
||||
}
|
||||
|
||||
// Note that we start at the folder below the current candidate `packagePath` because the
|
||||
// initial candidate `packagePath` is either a `node_modules` folder or the `basePath` with
|
||||
// no `package.json`.
|
||||
for (const segment of segments) {
|
||||
packagePath = join(packagePath, segment);
|
||||
if (this.fs.exists(join(packagePath, 'package.json'))) {
|
||||
return packagePath;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search up the directory tree from the `entryPointPath` looking for a `node_modules` directory
|
||||
* that we can use as a potential starting point for computing the package path.
|
||||
*/
|
||||
private computePackagePathFromNearestNodeModules(entryPointPath: AbsoluteFsPath): AbsoluteFsPath {
|
||||
let packagePath = entryPointPath;
|
||||
let scopedPackagePath = packagePath;
|
||||
let containerPath = this.fs.dirname(packagePath);
|
||||
while (!this.fs.isRoot(containerPath) && !containerPath.endsWith('node_modules')) {
|
||||
scopedPackagePath = packagePath;
|
||||
packagePath = containerPath;
|
||||
containerPath = this.fs.dirname(containerPath);
|
||||
}
|
||||
|
||||
if (this.fs.exists(join(packagePath, 'package.json'))) {
|
||||
// The directory directly below `node_modules` is a package - use it
|
||||
return packagePath;
|
||||
} else if (
|
||||
this.fs.basename(packagePath).startsWith('@') &&
|
||||
this.fs.exists(join(scopedPackagePath, 'package.json'))) {
|
||||
// The directory directly below the `node_modules` is a scope and the directory directly
|
||||
// below that is a scoped package - use it
|
||||
return scopedPackagePath;
|
||||
} else {
|
||||
// If we get here then none of the `basePaths` contained the `entryPointPath` and the
|
||||
// `entryPointPath` contains no `node_modules` that contains a package or a scoped
|
||||
// package. All we can do is assume that this entry-point is a primary entry-point to a
|
||||
// package.
|
||||
return entryPointPath;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the given `path` into path segments using an FS independent algorithm.
|
||||
* @param path The path to split.
|
||||
*/
|
||||
private splitPath(path: PathSegment) {
|
||||
const segments = [];
|
||||
while (path !== '.') {
|
||||
segments.unshift(this.fs.basename(path));
|
||||
path = this.fs.dirname(path);
|
||||
}
|
||||
return segments;
|
||||
protected getInitialEntryPointPaths(): AbsoluteFsPath[] {
|
||||
return [this.targetPath];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,206 @@
|
|||
/**
|
||||
* @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 {AbsoluteFsPath, FileSystem, join, PathSegment, relative, relativeFrom} from '../../../src/ngtsc/file_system';
|
||||
|
||||
import {EntryPointWithDependencies} from '../dependencies/dependency_host';
|
||||
import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver';
|
||||
import {Logger} from '../logging/logger';
|
||||
import {NgccConfiguration} from '../packages/configuration';
|
||||
import {EntryPoint, getEntryPointInfo, INCOMPATIBLE_ENTRY_POINT, NO_ENTRY_POINT} from '../packages/entry_point';
|
||||
import {PathMappings} from '../path_mappings';
|
||||
|
||||
import {EntryPointFinder} from './interface';
|
||||
import {getBasePaths} from './utils';
|
||||
|
||||
/**
|
||||
* An EntryPointFinder that starts from a set of initial files and only returns entry-points that
|
||||
* are dependencies of these files.
|
||||
*
|
||||
* This is faster than searching the entire file-system for all the entry-points,
|
||||
* and is used primarily by the CLI integration.
|
||||
*
|
||||
* There are two concrete implementation of this class.
|
||||
*
|
||||
* * `TargetEntryPointFinder` - is given a single entry-point as the initial entry-point
|
||||
* * `ProgramBasedEntryPointFinder` - computes the initial entry-points from program files given by
|
||||
* a `tsconfig.json` file.
|
||||
*/
|
||||
export abstract class TracingEntryPointFinder implements EntryPointFinder {
|
||||
protected unprocessedPaths: AbsoluteFsPath[] = [];
|
||||
protected unsortedEntryPoints = new Map<AbsoluteFsPath, EntryPointWithDependencies>();
|
||||
private basePaths: AbsoluteFsPath[]|null = null;
|
||||
|
||||
constructor(
|
||||
protected fs: FileSystem, protected config: NgccConfiguration, protected logger: Logger,
|
||||
protected resolver: DependencyResolver, protected basePath: AbsoluteFsPath,
|
||||
protected pathMappings: PathMappings|undefined) {}
|
||||
|
||||
protected getBasePaths() {
|
||||
if (this.basePaths === null) {
|
||||
this.basePaths = getBasePaths(this.logger, this.basePath, this.pathMappings);
|
||||
}
|
||||
return this.basePaths;
|
||||
}
|
||||
|
||||
findEntryPoints(): SortedEntryPointsInfo {
|
||||
this.unprocessedPaths = this.getInitialEntryPointPaths();
|
||||
while (this.unprocessedPaths.length > 0) {
|
||||
this.processNextPath();
|
||||
}
|
||||
return this.resolver.sortEntryPointsByDependency(Array.from(this.unsortedEntryPoints.values()));
|
||||
}
|
||||
|
||||
protected abstract getInitialEntryPointPaths(): AbsoluteFsPath[];
|
||||
|
||||
protected getEntryPoint(entryPointPath: AbsoluteFsPath): EntryPoint|null {
|
||||
const packagePath = this.computePackagePath(entryPointPath);
|
||||
const entryPoint =
|
||||
getEntryPointInfo(this.fs, this.config, this.logger, packagePath, entryPointPath);
|
||||
if (entryPoint === NO_ENTRY_POINT || entryPoint === INCOMPATIBLE_ENTRY_POINT) {
|
||||
return null;
|
||||
}
|
||||
return entryPoint;
|
||||
}
|
||||
|
||||
private processNextPath(): void {
|
||||
const path = this.unprocessedPaths.shift()!;
|
||||
const entryPoint = this.getEntryPoint(path);
|
||||
if (entryPoint === null || !entryPoint.compiledByAngular) {
|
||||
return;
|
||||
}
|
||||
const entryPointWithDeps = this.resolver.getEntryPointWithDependencies(entryPoint);
|
||||
this.unsortedEntryPoints.set(entryPoint.path, entryPointWithDeps);
|
||||
entryPointWithDeps.depInfo.dependencies.forEach(dep => {
|
||||
if (!this.unsortedEntryPoints.has(dep)) {
|
||||
this.unprocessedPaths.push(dep);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private computePackagePath(entryPointPath: AbsoluteFsPath): AbsoluteFsPath {
|
||||
// First try the main basePath, to avoid having to compute the other basePaths from the paths
|
||||
// mappings, which can be computationally intensive.
|
||||
if (entryPointPath.startsWith(this.basePath)) {
|
||||
const packagePath = this.computePackagePathFromContainingPath(entryPointPath, this.basePath);
|
||||
if (packagePath !== null) {
|
||||
return packagePath;
|
||||
}
|
||||
}
|
||||
|
||||
// The main `basePath` didn't work out so now we try the `basePaths` computed from the paths
|
||||
// mappings in `tsconfig.json`.
|
||||
for (const basePath of this.getBasePaths()) {
|
||||
if (entryPointPath.startsWith(basePath)) {
|
||||
const packagePath = this.computePackagePathFromContainingPath(entryPointPath, basePath);
|
||||
if (packagePath !== null) {
|
||||
return packagePath;
|
||||
}
|
||||
// If we got here then we couldn't find a `packagePath` for the current `basePath`.
|
||||
// Since `basePath`s are guaranteed not to be a sub-directory of each other then no other
|
||||
// `basePath` will match either.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, if we couldn't find a `packagePath` using `basePaths` then try to find the nearest
|
||||
// `node_modules` that contains the `entryPointPath`, if there is one, and use it as a
|
||||
// `basePath`.
|
||||
return this.computePackagePathFromNearestNodeModules(entryPointPath);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Search down to the `entryPointPath` from the `containingPath` for the first `package.json` that
|
||||
* we come to. This is the path to the entry-point's containing package. For example if
|
||||
* `containingPath` is `/a/b/c` and `entryPointPath` is `/a/b/c/d/e` and there exists
|
||||
* `/a/b/c/d/package.json` and `/a/b/c/d/e/package.json`, then we will return `/a/b/c/d`.
|
||||
*
|
||||
* To account for nested `node_modules` we actually start the search at the last `node_modules` in
|
||||
* the `entryPointPath` that is below the `containingPath`. E.g. if `containingPath` is `/a/b/c`
|
||||
* and `entryPointPath` is `/a/b/c/d/node_modules/x/y/z`, we start the search at
|
||||
* `/a/b/c/d/node_modules`.
|
||||
*/
|
||||
private computePackagePathFromContainingPath(
|
||||
entryPointPath: AbsoluteFsPath, containingPath: AbsoluteFsPath): AbsoluteFsPath|null {
|
||||
let packagePath = containingPath;
|
||||
const segments = this.splitPath(relative(containingPath, entryPointPath));
|
||||
let nodeModulesIndex = segments.lastIndexOf(relativeFrom('node_modules'));
|
||||
|
||||
// If there are no `node_modules` in the relative path between the `basePath` and the
|
||||
// `entryPointPath` then just try the `basePath` as the `packagePath`.
|
||||
// (This can be the case with path-mapped entry-points.)
|
||||
if (nodeModulesIndex === -1) {
|
||||
if (this.fs.exists(join(packagePath, 'package.json'))) {
|
||||
return packagePath;
|
||||
}
|
||||
}
|
||||
|
||||
// Start the search at the deepest nested `node_modules` folder that is below the `basePath`
|
||||
// but above the `entryPointPath`, if there are any.
|
||||
while (nodeModulesIndex >= 0) {
|
||||
packagePath = join(packagePath, segments.shift()!);
|
||||
nodeModulesIndex--;
|
||||
}
|
||||
|
||||
// Note that we start at the folder below the current candidate `packagePath` because the
|
||||
// initial candidate `packagePath` is either a `node_modules` folder or the `basePath` with
|
||||
// no `package.json`.
|
||||
for (const segment of segments) {
|
||||
packagePath = join(packagePath, segment);
|
||||
if (this.fs.exists(join(packagePath, 'package.json'))) {
|
||||
return packagePath;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search up the directory tree from the `entryPointPath` looking for a `node_modules` directory
|
||||
* that we can use as a potential starting point for computing the package path.
|
||||
*/
|
||||
private computePackagePathFromNearestNodeModules(entryPointPath: AbsoluteFsPath): AbsoluteFsPath {
|
||||
let packagePath = entryPointPath;
|
||||
let scopedPackagePath = packagePath;
|
||||
let containerPath = this.fs.dirname(packagePath);
|
||||
while (!this.fs.isRoot(containerPath) && !containerPath.endsWith('node_modules')) {
|
||||
scopedPackagePath = packagePath;
|
||||
packagePath = containerPath;
|
||||
containerPath = this.fs.dirname(containerPath);
|
||||
}
|
||||
|
||||
if (this.fs.exists(join(packagePath, 'package.json'))) {
|
||||
// The directory directly below `node_modules` is a package - use it
|
||||
return packagePath;
|
||||
} else if (
|
||||
this.fs.basename(packagePath).startsWith('@') &&
|
||||
this.fs.exists(join(scopedPackagePath, 'package.json'))) {
|
||||
// The directory directly below the `node_modules` is a scope and the directory directly
|
||||
// below that is a scoped package - use it
|
||||
return scopedPackagePath;
|
||||
} else {
|
||||
// If we get here then none of the `basePaths` contained the `entryPointPath` and the
|
||||
// `entryPointPath` contains no `node_modules` that contains a package or a scoped
|
||||
// package. All we can do is assume that this entry-point is a primary entry-point to a
|
||||
// package.
|
||||
return entryPointPath;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the given `path` into path segments using an FS independent algorithm.
|
||||
* @param path The path to split.
|
||||
*/
|
||||
private splitPath(path: PathSegment) {
|
||||
const segments = [];
|
||||
while (path !== '.') {
|
||||
segments.unshift(this.fs.basename(path));
|
||||
path = this.fs.dirname(path);
|
||||
}
|
||||
return segments;
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@
|
|||
import * as os from 'os';
|
||||
|
||||
import {AbsoluteFsPath, FileSystem, resolve} from '../../src/ngtsc/file_system';
|
||||
import {ParsedConfiguration} from '../../src/perform_compile';
|
||||
|
||||
import {CommonJsDependencyHost} from './dependencies/commonjs_dependency_host';
|
||||
import {DependencyResolver} from './dependencies/dependency_resolver';
|
||||
|
@ -20,6 +21,7 @@ import {ModuleResolver} from './dependencies/module_resolver';
|
|||
import {UmdDependencyHost} from './dependencies/umd_dependency_host';
|
||||
import {DirectoryWalkerEntryPointFinder} from './entry_point_finder/directory_walker_entry_point_finder';
|
||||
import {EntryPointFinder} from './entry_point_finder/interface';
|
||||
import {ProgramBasedEntryPointFinder} from './entry_point_finder/program_based_entry_point_finder';
|
||||
import {TargetedEntryPointFinder} from './entry_point_finder/targeted_entry_point_finder';
|
||||
import {getAnalyzeEntryPointsFn} from './execution/analyze_entry_points';
|
||||
import {Executor} from './execution/api';
|
||||
|
@ -81,7 +83,8 @@ export function mainNgcc(options: AsyncNgccOptions|SyncNgccOptions): void|Promis
|
|||
targetEntryPointPath !== undefined ? resolve(basePath, targetEntryPointPath) : null;
|
||||
const finder = getEntryPointFinder(
|
||||
fileSystem, logger, dependencyResolver, config, entryPointManifest, absBasePath,
|
||||
absoluteTargetEntryPointPath, pathMappings);
|
||||
absoluteTargetEntryPointPath, pathMappings,
|
||||
options.findEntryPointsFromTsConfigProgram ? tsConfig : null, projectPath);
|
||||
if (finder instanceof TargetedEntryPointFinder &&
|
||||
!finder.targetNeedsProcessingOrCleaning(supportedPropertiesToConsider, compileAllFormats)) {
|
||||
logger.debug('The target entry-point has already been processed');
|
||||
|
@ -195,13 +198,15 @@ function getDependencyResolver(
|
|||
function getEntryPointFinder(
|
||||
fs: FileSystem, logger: Logger, resolver: DependencyResolver, config: NgccConfiguration,
|
||||
entryPointManifest: EntryPointManifest, basePath: AbsoluteFsPath,
|
||||
absoluteTargetEntryPointPath: AbsoluteFsPath|null,
|
||||
pathMappings: PathMappings|undefined): EntryPointFinder {
|
||||
absoluteTargetEntryPointPath: AbsoluteFsPath|null, pathMappings: PathMappings|undefined,
|
||||
tsConfig: ParsedConfiguration|null, projectPath: AbsoluteFsPath): EntryPointFinder {
|
||||
if (absoluteTargetEntryPointPath !== null) {
|
||||
return new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, basePath, absoluteTargetEntryPointPath, pathMappings);
|
||||
} else {
|
||||
fs, config, logger, resolver, basePath, pathMappings, absoluteTargetEntryPointPath);
|
||||
} else if (tsConfig !== null) {
|
||||
return new ProgramBasedEntryPointFinder(
|
||||
fs, config, logger, resolver, basePath, tsConfig, projectPath);
|
||||
}
|
||||
return new DirectoryWalkerEntryPointFinder(
|
||||
fs, config, logger, resolver, entryPointManifest, basePath, pathMappings);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,6 +124,14 @@ export interface SyncNgccOptions {
|
|||
* If `null`, ngcc will not attempt to load any TS config file at all.
|
||||
*/
|
||||
tsConfigPath?: string|null;
|
||||
|
||||
/**
|
||||
* Use the program defined in the loaded tsconfig.json (if available - see
|
||||
* `tsConfigPath` option) to identify the entry-points that should be processed.
|
||||
* If this is set to `true` then only the entry-points reachable from the given
|
||||
* program (and their dependencies) will be processed.
|
||||
*/
|
||||
findEntryPointsFromTsConfigProgram?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -136,7 +144,8 @@ export type AsyncNgccOptions = Omit<SyncNgccOptions, 'async'>&{async: true};
|
|||
*/
|
||||
export type NgccOptions = AsyncNgccOptions|SyncNgccOptions;
|
||||
|
||||
export type OptionalNgccOptionKeys = 'targetEntryPointPath'|'tsConfigPath'|'pathMappings';
|
||||
export type OptionalNgccOptionKeys =
|
||||
'targetEntryPointPath'|'tsConfigPath'|'pathMappings'|'findEntryPointsFromTsConfigProgram';
|
||||
export type RequiredNgccOptions = Required<Omit<NgccOptions, OptionalNgccOptionKeys>>;
|
||||
export type OptionalNgccOptions = Pick<NgccOptions, OptionalNgccOptionKeys>;
|
||||
export type SharedSetup = {
|
||||
|
|
|
@ -12,6 +12,7 @@ ts_library(
|
|||
),
|
||||
deps = [
|
||||
"//packages/compiler",
|
||||
"//packages/compiler-cli",
|
||||
"//packages/compiler-cli/ngcc",
|
||||
"//packages/compiler-cli/ngcc/test/helpers",
|
||||
"//packages/compiler-cli/src/ngtsc/diagnostics",
|
||||
|
|
|
@ -257,6 +257,16 @@ runInEachFileSystem(() => {
|
|||
.toEqual(new ResolvedExternalModule(_('/dist/package-4/secondary-entry-point')));
|
||||
});
|
||||
});
|
||||
|
||||
describe('with mapped path relative paths', () => {
|
||||
it('should resolve to a relative file if found via a paths mapping', () => {
|
||||
const resolver = new ModuleResolver(
|
||||
getFileSystem(), {baseUrl: '/', paths: {'mapped/*': ['libs/local-package/*']}});
|
||||
|
||||
expect(resolver.resolveModuleImport('mapped/x', _('/libs/local-package/index.js')))
|
||||
.toEqual(new ResolvedRelativeModule(_('/libs/local-package/x.js')));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
/**
|
||||
* @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 {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, relative} from '../../../src/ngtsc/file_system';
|
||||
import {runInEachFileSystem, TestFile} from '../../../src/ngtsc/file_system/testing';
|
||||
import {readConfiguration} from '../../../src/perform_compile';
|
||||
import {loadTestFiles} from '../../../test/helpers';
|
||||
import {DependencyResolver} from '../../src/dependencies/dependency_resolver';
|
||||
import {DtsDependencyHost} from '../../src/dependencies/dts_dependency_host';
|
||||
import {EsmDependencyHost} from '../../src/dependencies/esm_dependency_host';
|
||||
import {ModuleResolver} from '../../src/dependencies/module_resolver';
|
||||
import {ProgramBasedEntryPointFinder} from '../../src/entry_point_finder/program_based_entry_point_finder';
|
||||
import {NgccConfiguration} from '../../src/packages/configuration';
|
||||
import {EntryPoint} from '../../src/packages/entry_point';
|
||||
import {EntryPointManifest} from '../../src/packages/entry_point_manifest';
|
||||
import {MockLogger} from '../helpers/mock_logger';
|
||||
|
||||
runInEachFileSystem(() => {
|
||||
describe('ProgramBasedEntryPointFinder', () => {
|
||||
let fs: FileSystem;
|
||||
let _Abs: typeof absoluteFrom;
|
||||
let projectPath: AbsoluteFsPath;
|
||||
let basePath: AbsoluteFsPath;
|
||||
let angularNamespacePath: AbsoluteFsPath;
|
||||
|
||||
beforeEach(() => {
|
||||
fs = getFileSystem();
|
||||
_Abs = absoluteFrom;
|
||||
projectPath = _Abs('/sub_entry_points');
|
||||
basePath = _Abs('/sub_entry_points/node_modules');
|
||||
angularNamespacePath = fs.resolve(basePath, '@angular');
|
||||
});
|
||||
|
||||
describe('findEntryPoints()', () => {
|
||||
it('should find entry-points imported into the program', () => {
|
||||
loadTestFiles([
|
||||
...createProgram(projectPath),
|
||||
...createPackage(angularNamespacePath, 'core'),
|
||||
...createPackage(angularNamespacePath, 'common'),
|
||||
...createPackage(fs.resolve(angularNamespacePath, 'common'), 'http', ['@angular/common']),
|
||||
...createPackage(
|
||||
fs.resolve(angularNamespacePath, 'common/http'), 'testing',
|
||||
['common/http', 'common/testing']),
|
||||
...createPackage(
|
||||
fs.resolve(angularNamespacePath, 'common'), 'testing', ['@angular/common']),
|
||||
]);
|
||||
const finder = createFinder();
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
||||
['@angular/core', '@angular/core'],
|
||||
['@angular/common', '@angular/common'],
|
||||
['@angular/common', '@angular/common/http'],
|
||||
]);
|
||||
});
|
||||
|
||||
function createFinder(): ProgramBasedEntryPointFinder {
|
||||
const tsConfig = readConfiguration(`${projectPath}/tsconfig.json`);
|
||||
const baseUrl = fs.resolve(projectPath, tsConfig.options.basePath!);
|
||||
const paths = tsConfig.options.paths!;
|
||||
|
||||
const logger = new MockLogger();
|
||||
const srcHost = new EsmDependencyHost(fs, new ModuleResolver(fs, {baseUrl, paths}));
|
||||
const dtsHost = new DtsDependencyHost(fs);
|
||||
const config = new NgccConfiguration(fs, projectPath);
|
||||
const resolver = new DependencyResolver(fs, logger, config, {esm2015: srcHost}, dtsHost);
|
||||
const manifest = new EntryPointManifest(fs, config, logger);
|
||||
return new ProgramBasedEntryPointFinder(
|
||||
fs, config, logger, resolver, basePath, tsConfig, projectPath);
|
||||
}
|
||||
|
||||
function createProgram(projectPath: AbsoluteFsPath): TestFile[] {
|
||||
return [
|
||||
{
|
||||
name: _Abs(`${projectPath}/package.json`),
|
||||
contents: '',
|
||||
},
|
||||
{
|
||||
name: _Abs(`${projectPath}/tsconfig.json`),
|
||||
contents: `{
|
||||
"files": [
|
||||
"src/main.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"lib/*": ["lib/*"]
|
||||
}
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: _Abs(`${projectPath}/src/main.ts`),
|
||||
contents: `
|
||||
import {AppComponent} from './app.component';
|
||||
import * from './app.module';
|
||||
import * from 'lib/service';
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: _Abs(`${projectPath}/src/app.component.ts`),
|
||||
contents: `
|
||||
import * as core from '@angular/core';
|
||||
export class AppComponent {}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: _Abs(`${projectPath}/src/app.module.ts`),
|
||||
contents: `
|
||||
import {NgModule} from '@angular/core';
|
||||
import * as common from '@angular/common';
|
||||
import {AppComponent} from './app.component';
|
||||
export class AppModule {}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: _Abs(`${projectPath}/lib/service/index.ts`),
|
||||
contents: `
|
||||
import * as http from '@angular/common/http';
|
||||
export class Service {}
|
||||
`,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function createPackage(
|
||||
basePath: AbsoluteFsPath, packageName: string, deps: string[] = [],
|
||||
isCompiledByAngular = true): TestFile[] {
|
||||
const files: TestFile[] = [
|
||||
{
|
||||
name: _Abs(`${basePath}/${packageName}/package.json`),
|
||||
contents: JSON.stringify({
|
||||
typings: `./${packageName}.d.ts`,
|
||||
fesm2015: `./fesm2015/${packageName}.js`,
|
||||
})
|
||||
},
|
||||
{
|
||||
name: _Abs(`${basePath}/${packageName}/${packageName}.d.ts`),
|
||||
contents: deps.map((dep, i) => `import * as i${i} from '${dep}';`).join('\n'),
|
||||
},
|
||||
{
|
||||
name: _Abs(`${basePath}/${packageName}/fesm2015/${packageName}.js`),
|
||||
contents: deps.map((dep, i) => `import * as i${i} from '${dep}';`).join('\n'),
|
||||
},
|
||||
];
|
||||
|
||||
if (isCompiledByAngular) {
|
||||
files.push({
|
||||
name: _Abs(`${basePath}/${packageName}/${packageName}.metadata.json`),
|
||||
contents: 'metadata info'
|
||||
});
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
function dumpEntryPointPaths(
|
||||
basePath: AbsoluteFsPath, entryPoints: EntryPoint[]): [string, string][] {
|
||||
return entryPoints.map(x => [relative(basePath, x.package), relative(basePath, x.path)]);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
|
@ -50,8 +50,8 @@ runInEachFileSystem(() => {
|
|||
...createPackage(fs.resolve(basePath, 'common'), 'testing', ['@angular/common']),
|
||||
]);
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, _Abs('/sub_entry_points/node_modules'), targetPath,
|
||||
undefined);
|
||||
fs, config, logger, resolver, _Abs('/sub_entry_points/node_modules'), undefined,
|
||||
targetPath);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
||||
['common', 'common'],
|
||||
|
@ -69,8 +69,8 @@ runInEachFileSystem(() => {
|
|||
...createPackage(fs.resolve(basePath, 'common'), 'testing', ['common']),
|
||||
]);
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, _Abs('/sub_entry_points/node_modules'), targetPath,
|
||||
undefined);
|
||||
fs, config, logger, resolver, _Abs('/sub_entry_points/node_modules'), undefined,
|
||||
targetPath);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
||||
['common', 'common'],
|
||||
|
@ -92,7 +92,7 @@ runInEachFileSystem(() => {
|
|||
...createPackage(fs.resolve(basePath, '@angular/common'), 'testing', ['@angular/common']),
|
||||
]);
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, _Abs('/namespaced/node_modules'), targetPath, undefined);
|
||||
fs, config, logger, resolver, _Abs('/namespaced/node_modules'), undefined, targetPath);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
||||
['@angular/common', '@angular/common'],
|
||||
|
@ -104,7 +104,7 @@ runInEachFileSystem(() => {
|
|||
const targetPath = _Abs('/no_packages/node_modules/should_not_be_found');
|
||||
fs.ensureDir(_Abs('/no_packages/node_modules/should_not_be_found'));
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, _Abs('/no_packages/node_modules'), targetPath, undefined);
|
||||
fs, config, logger, resolver, _Abs('/no_packages/node_modules'), undefined, targetPath);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
@ -118,8 +118,8 @@ runInEachFileSystem(() => {
|
|||
},
|
||||
]);
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, _Abs('/no_valid_entry_points/node_modules'), targetPath,
|
||||
undefined);
|
||||
fs, config, logger, resolver, _Abs('/no_valid_entry_points/node_modules'), undefined,
|
||||
targetPath);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
@ -143,8 +143,8 @@ runInEachFileSystem(() => {
|
|||
},
|
||||
]);
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, _Abs('/no_valid_entry_points/node_modules'),
|
||||
targetPath, undefined);
|
||||
fs, config, logger, resolver, _Abs('/no_valid_entry_points/node_modules'), undefined,
|
||||
targetPath);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
@ -156,8 +156,8 @@ runInEachFileSystem(() => {
|
|||
...createPackage(_Abs('/nested_node_modules/node_modules/outer/node_modules'), 'inner'),
|
||||
]);
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, _Abs('/nested_node_modules/node_modules'), targetPath,
|
||||
undefined);
|
||||
fs, config, logger, resolver, _Abs('/nested_node_modules/node_modules'), undefined,
|
||||
targetPath);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(dumpEntryPointPaths(_Abs('/nested_node_modules/node_modules'), entryPoints))
|
||||
.toEqual([
|
||||
|
@ -175,7 +175,7 @@ runInEachFileSystem(() => {
|
|||
...createPackage(_Abs('/nested_node_modules/node_modules/package'), 'entry-point'),
|
||||
]);
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, basePath, targetPath, undefined);
|
||||
fs, config, logger, resolver, basePath, undefined, targetPath);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(dumpEntryPointPaths(_Abs('/nested_node_modules'), entryPoints)).toEqual([
|
||||
['node_modules/package', 'node_modules/package/entry-point'],
|
||||
|
@ -200,7 +200,7 @@ runInEachFileSystem(() => {
|
|||
// entry-point info for the `lib3/entry-point` dependency.
|
||||
const targetPath = _Abs('/project/node_modules/lib1/node_modules/lib2');
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, basePath, targetPath, undefined);
|
||||
fs, config, logger, resolver, basePath, undefined, targetPath);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(dumpEntryPointPaths(_Abs('/project/node_modules'), entryPoints)).toEqual([
|
||||
['lib3', 'lib3/entry-point'],
|
||||
|
@ -232,7 +232,7 @@ runInEachFileSystem(() => {
|
|||
// entry-point info for the `lib3/entry-point` dependency.
|
||||
const targetPath = _Abs('/project/node_modules/lib1/node_modules/lib2');
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, basePath, targetPath, undefined);
|
||||
fs, config, logger, resolver, basePath, undefined, targetPath);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(dumpEntryPointPaths(_Abs('/project/node_modules'), entryPoints)).toEqual([
|
||||
['@scope/lib3/node_modules/lib4', '@scope/lib3/node_modules/lib4/entry-point'],
|
||||
|
@ -266,7 +266,7 @@ runInEachFileSystem(() => {
|
|||
const dtsHost = new DtsDependencyHost(fs, pathMappings);
|
||||
resolver = new DependencyResolver(fs, logger, config, {esm2015: srcHost}, dtsHost);
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, basePath, targetPath, pathMappings);
|
||||
fs, config, logger, resolver, basePath, pathMappings, targetPath);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
||||
['pkg1', 'pkg1'],
|
||||
|
@ -294,7 +294,7 @@ runInEachFileSystem(() => {
|
|||
const dtsHost = new DtsDependencyHost(fs, pathMappings);
|
||||
resolver = new DependencyResolver(fs, logger, config, {esm2015: srcHost}, dtsHost);
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, basePath, targetPath, pathMappings);
|
||||
fs, config, logger, resolver, basePath, pathMappings, targetPath);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(entryPoints.length).toEqual(1);
|
||||
const entryPoint = entryPoints[0];
|
||||
|
@ -327,7 +327,7 @@ runInEachFileSystem(() => {
|
|||
const dtsHost = new DtsDependencyHost(fs, pathMappings);
|
||||
resolver = new DependencyResolver(fs, logger, config, {esm2015: srcHost}, dtsHost);
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, basePath, targetPath, pathMappings);
|
||||
fs, config, logger, resolver, basePath, pathMappings, targetPath);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
||||
['../dist/my-lib/a', '../dist/my-lib/a'],
|
||||
|
@ -354,7 +354,7 @@ runInEachFileSystem(() => {
|
|||
const dtsHost = new DtsDependencyHost(fs, pathMappings);
|
||||
resolver = new DependencyResolver(fs, logger, config, {esm2015: srcHost}, dtsHost);
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, basePath, targetPath, pathMappings);
|
||||
fs, config, logger, resolver, basePath, pathMappings, targetPath);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
||||
['test', 'test'],
|
||||
|
@ -372,7 +372,7 @@ runInEachFileSystem(() => {
|
|||
const targetPath = _Abs('/no_packages/node_modules/should_not_be_found');
|
||||
fs.ensureDir(targetPath);
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, _Abs('/no_packages/node_modules'), targetPath, undefined);
|
||||
fs, config, logger, resolver, _Abs('/no_packages/node_modules'), undefined, targetPath);
|
||||
expect(finder.targetNeedsProcessingOrCleaning(['fesm2015'], true)).toBe(false);
|
||||
});
|
||||
|
||||
|
@ -385,8 +385,8 @@ runInEachFileSystem(() => {
|
|||
},
|
||||
]);
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, _Abs('/no_valid_entry_points/node_modules'), targetPath,
|
||||
undefined);
|
||||
fs, config, logger, resolver, _Abs('/no_valid_entry_points/node_modules'), undefined,
|
||||
targetPath);
|
||||
expect(finder.targetNeedsProcessingOrCleaning(['fesm2015'], true)).toBe(false);
|
||||
});
|
||||
|
||||
|
@ -408,8 +408,8 @@ runInEachFileSystem(() => {
|
|||
},
|
||||
]);
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, _Abs('/no_valid_entry_points/node_modules'), targetPath,
|
||||
undefined);
|
||||
fs, config, logger, resolver, _Abs('/no_valid_entry_points/node_modules'), undefined,
|
||||
targetPath);
|
||||
expect(finder.targetNeedsProcessingOrCleaning(['fesm2015'], true)).toBe(false);
|
||||
});
|
||||
|
||||
|
@ -431,8 +431,8 @@ runInEachFileSystem(() => {
|
|||
},
|
||||
]);
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, _Abs('/no_valid_entry_points/node_modules'),
|
||||
targetPath, undefined);
|
||||
fs, config, logger, resolver, _Abs('/no_valid_entry_points/node_modules'), undefined,
|
||||
targetPath);
|
||||
expect(finder.targetNeedsProcessingOrCleaning(['fesm2015'], true)).toBe(false);
|
||||
});
|
||||
|
||||
|
@ -448,7 +448,7 @@ runInEachFileSystem(() => {
|
|||
...createPackage(fs.resolve(basePath, 'common'), 'testing', ['common']),
|
||||
]);
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, basePath, targetPath, undefined);
|
||||
fs, config, logger, resolver, basePath, undefined, targetPath);
|
||||
expect(finder.targetNeedsProcessingOrCleaning(['fesm2015', 'esm5'], true)).toBe(true);
|
||||
});
|
||||
|
||||
|
@ -474,7 +474,7 @@ runInEachFileSystem(() => {
|
|||
fs.writeFile(packageJsonPath, JSON.stringify(packageJson));
|
||||
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, basePath, targetPath, undefined);
|
||||
fs, config, logger, resolver, basePath, undefined, targetPath);
|
||||
expect(finder.targetNeedsProcessingOrCleaning(['fesm2015', 'esm5'], true)).toBe(true);
|
||||
});
|
||||
|
||||
|
@ -500,7 +500,7 @@ runInEachFileSystem(() => {
|
|||
fs.writeFile(packageJsonPath, JSON.stringify(packageJson));
|
||||
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, basePath, targetPath, undefined);
|
||||
fs, config, logger, resolver, basePath, undefined, targetPath);
|
||||
expect(finder.targetNeedsProcessingOrCleaning(['fesm2015', 'esm5'], true)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
@ -518,7 +518,7 @@ runInEachFileSystem(() => {
|
|||
]);
|
||||
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, basePath, targetPath, undefined);
|
||||
fs, config, logger, resolver, basePath, undefined, targetPath);
|
||||
expect(finder.targetNeedsProcessingOrCleaning(['fesm2015', 'esm5'], false)).toBe(true);
|
||||
});
|
||||
|
||||
|
@ -544,7 +544,7 @@ runInEachFileSystem(() => {
|
|||
fs.writeFile(packageJsonPath, JSON.stringify(packageJson));
|
||||
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, basePath, targetPath, undefined);
|
||||
fs, config, logger, resolver, basePath, undefined, targetPath);
|
||||
expect(finder.targetNeedsProcessingOrCleaning(['fesm2015', 'esm5'], false)).toBe(true);
|
||||
});
|
||||
|
||||
|
@ -570,7 +570,7 @@ runInEachFileSystem(() => {
|
|||
fs.writeFile(packageJsonPath, JSON.stringify(packageJson));
|
||||
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, basePath, targetPath, undefined);
|
||||
fs, config, logger, resolver, basePath, undefined, targetPath);
|
||||
expect(finder.targetNeedsProcessingOrCleaning(['fesm2015', 'esm5'], false))
|
||||
.toBe(false);
|
||||
});
|
||||
|
|
|
@ -32,6 +32,14 @@ runInEachFileSystem(() => {
|
|||
let _: typeof absoluteFrom;
|
||||
let fs: FileSystem;
|
||||
let pkgJsonUpdater: PackageJsonUpdater;
|
||||
const STANDARD_MARKERS = {
|
||||
main: '0.0.0-PLACEHOLDER',
|
||||
module: '0.0.0-PLACEHOLDER',
|
||||
es2015: '0.0.0-PLACEHOLDER',
|
||||
esm2015: '0.0.0-PLACEHOLDER',
|
||||
fesm2015: '0.0.0-PLACEHOLDER',
|
||||
typings: '0.0.0-PLACEHOLDER',
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
_ = absoluteFrom;
|
||||
|
@ -644,15 +652,6 @@ runInEachFileSystem(() => {
|
|||
|
||||
describe('with targetEntryPointPath', () => {
|
||||
it('should only compile the given package entry-point (and its dependencies).', () => {
|
||||
const STANDARD_MARKERS = {
|
||||
main: '0.0.0-PLACEHOLDER',
|
||||
module: '0.0.0-PLACEHOLDER',
|
||||
es2015: '0.0.0-PLACEHOLDER',
|
||||
esm2015: '0.0.0-PLACEHOLDER',
|
||||
fesm2015: '0.0.0-PLACEHOLDER',
|
||||
typings: '0.0.0-PLACEHOLDER',
|
||||
};
|
||||
|
||||
mainNgcc({basePath: '/node_modules', targetEntryPointPath: '@angular/common/http/testing'});
|
||||
expect(loadPackage('@angular/common/http/testing').__processed_by_ivy_ngcc__)
|
||||
.toEqual(STANDARD_MARKERS);
|
||||
|
@ -818,6 +817,27 @@ runInEachFileSystem(() => {
|
|||
pkgJsonUpdater, targetPackage, targetPackageJsonPath, ['typings', ...properties]);
|
||||
}
|
||||
|
||||
describe('with findEntryPointsFromTsConfigProgram', () => {
|
||||
it('should only compile the package entry-points (and their dependencies) reachable from the program in tsconfig.json.',
|
||||
() => {
|
||||
mainNgcc({basePath: '/node_modules', findEntryPointsFromTsConfigProgram: true});
|
||||
// * `common/testing` is a dependency of `./y`, so is compiled.
|
||||
expect(loadPackage('@angular/common/testing').__processed_by_ivy_ngcc__)
|
||||
.toEqual(STANDARD_MARKERS);
|
||||
// * `common/http` is a dependency of `./x`, so is compiled.
|
||||
expect(loadPackage('@angular/common/http').__processed_by_ivy_ngcc__)
|
||||
.toEqual(STANDARD_MARKERS);
|
||||
// * `core` is a dependency of `common/http`, so is compiled.
|
||||
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toEqual(STANDARD_MARKERS);
|
||||
// * `common` is a private (only in .js not .d.ts) dependency so is compiled.
|
||||
expect(loadPackage('@angular/common').__processed_by_ivy_ngcc__)
|
||||
.toEqual(STANDARD_MARKERS);
|
||||
// * `common/http/testing` is not a dependency of the program so is not compiled.
|
||||
expect(loadPackage('@angular/common/http/testing').__processed_by_ivy_ngcc__)
|
||||
.toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('should clean up outdated artifacts', () => {
|
||||
compileIntoFlatEs2015Package('test-package', {
|
||||
'index.ts': `
|
||||
|
@ -2113,6 +2133,17 @@ runInEachFileSystem(() => {
|
|||
},
|
||||
{name: _('/node_modules/invalid-package/index.metadata.json'), contents: 'DUMMY DATA'},
|
||||
]);
|
||||
|
||||
// A sample application that imports entry-points
|
||||
loadTestFiles([
|
||||
{name: _('/tsconfig.json'), contents: '{"files": ["src/index.ts"]}'},
|
||||
{name: _('/src/index.ts'), contents: `import {X} from './x';\nimport {Y} from './y';`},
|
||||
{name: _('/src/x.ts'), contents: `import '@angular/common/http';\nexport class X {}`},
|
||||
{
|
||||
name: _('/src/y.ts'),
|
||||
contents: `import * as t from '@angular/common/testing';\n export class Y {}`
|
||||
},
|
||||
]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue