perf(ngcc): use EntryPointManifest
to speed up noop ProgramBaseEntryPointFinder
(#37665)
Previously the `ProgramBasedEntryPointFinder` was parsing all the entry-points referenced by the program for dependencies even if all the entry-points had been processed already. Now this entry-point finder will re-use the `EntryPointManifest` to load the entry-point dependencies when possible which avoids having to parse them all again, on every invocation of ngcc. Previously the `EntryPointManifest` was only used in the `DirectoryWalkerEntryPointFinder`, which also contained the logic for computing the contents of the manifest. This logic has been factored out into an `EntryPointCollector` class. Both the `ProgramBasedEntryPointFinder` and `DirectoryWalkerEntryPointFinder` now use the `EntryPointManifest` and the `EntryPointCollector`. The result of this change is that there is a small cost on the first run of ngcc to compute and store the manifest - the processing takes 102% of the processing time before this PR. But on subsequent runs there is a significant benefit on subsequent runs - the processing takes around 50% of the processing time before this PR. PR Close #37665
This commit is contained in:
parent
290bc7334d
commit
9318e23e64
@ -5,16 +5,14 @@
|
|||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {AbsoluteFsPath, FileSystem, PathSegment} from '../../../src/ngtsc/file_system';
|
import {AbsoluteFsPath} from '../../../src/ngtsc/file_system';
|
||||||
import {Logger} from '../../../src/ngtsc/logging';
|
import {Logger} from '../../../src/ngtsc/logging';
|
||||||
import {EntryPointWithDependencies} from '../dependencies/dependency_host';
|
import {EntryPointWithDependencies} from '../dependencies/dependency_host';
|
||||||
import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver';
|
import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver';
|
||||||
import {NgccConfiguration} from '../packages/configuration';
|
|
||||||
import {getEntryPointInfo, IGNORED_ENTRY_POINT, INCOMPATIBLE_ENTRY_POINT, isEntryPoint, NO_ENTRY_POINT} from '../packages/entry_point';
|
|
||||||
import {EntryPointManifest} from '../packages/entry_point_manifest';
|
import {EntryPointManifest} from '../packages/entry_point_manifest';
|
||||||
import {PathMappings} from '../path_mappings';
|
import {PathMappings} from '../path_mappings';
|
||||||
import {NGCC_DIRECTORY} from '../writing/new_entry_point_file_writer';
|
|
||||||
|
|
||||||
|
import {EntryPointCollector} from './entry_point_collector';
|
||||||
import {EntryPointFinder} from './interface';
|
import {EntryPointFinder} from './interface';
|
||||||
import {getBasePaths, trackDuration} from './utils';
|
import {getBasePaths, trackDuration} from './utils';
|
||||||
|
|
||||||
@ -25,9 +23,11 @@ import {getBasePaths, trackDuration} from './utils';
|
|||||||
export class DirectoryWalkerEntryPointFinder implements EntryPointFinder {
|
export class DirectoryWalkerEntryPointFinder implements EntryPointFinder {
|
||||||
private basePaths = getBasePaths(this.logger, this.sourceDirectory, this.pathMappings);
|
private basePaths = getBasePaths(this.logger, this.sourceDirectory, this.pathMappings);
|
||||||
constructor(
|
constructor(
|
||||||
private fs: FileSystem, private config: NgccConfiguration, private logger: Logger,
|
private logger: Logger, private resolver: DependencyResolver,
|
||||||
private resolver: DependencyResolver, private entryPointManifest: EntryPointManifest,
|
private entryPointCollector: EntryPointCollector,
|
||||||
private sourceDirectory: AbsoluteFsPath, private pathMappings: PathMappings|undefined) {}
|
private entryPointManifest: EntryPointManifest, private sourceDirectory: AbsoluteFsPath,
|
||||||
|
private pathMappings: PathMappings|undefined) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search the `sourceDirectory`, and sub-directories, using `pathMappings` as necessary, to find
|
* Search the `sourceDirectory`, and sub-directories, using `pathMappings` as necessary, to find
|
||||||
* all package entry-points.
|
* all package entry-points.
|
||||||
@ -45,145 +45,16 @@ export class DirectoryWalkerEntryPointFinder implements EntryPointFinder {
|
|||||||
/**
|
/**
|
||||||
* Search the `basePath` for possible Angular packages and entry-points.
|
* Search the `basePath` for possible Angular packages and entry-points.
|
||||||
*
|
*
|
||||||
* @param basePath The path at which to start the search
|
* @param basePath The path at which to start the search.
|
||||||
* @returns an array of `EntryPoint`s that were found within `basePath`.
|
* @returns an array of `EntryPoint`s that were found within `basePath`.
|
||||||
*/
|
*/
|
||||||
walkBasePathForPackages(basePath: AbsoluteFsPath): EntryPointWithDependencies[] {
|
walkBasePathForPackages(basePath: AbsoluteFsPath): EntryPointWithDependencies[] {
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`No manifest found for ${basePath} so walking the directories for entry-points.`);
|
`No manifest found for ${basePath} so walking the directories for entry-points.`);
|
||||||
const entryPoints = trackDuration(
|
const entryPoints = trackDuration(
|
||||||
() => this.walkDirectoryForPackages(basePath),
|
() => this.entryPointCollector.walkDirectoryForPackages(basePath),
|
||||||
duration => this.logger.debug(`Walking ${basePath} for entry-points took ${duration}s.`));
|
duration => this.logger.debug(`Walking ${basePath} for entry-points took ${duration}s.`));
|
||||||
this.entryPointManifest.writeEntryPointManifest(basePath, entryPoints);
|
this.entryPointManifest.writeEntryPointManifest(basePath, entryPoints);
|
||||||
return entryPoints;
|
return entryPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Look for Angular packages that need to be compiled, starting at the source directory.
|
|
||||||
* The function will recurse into directories that start with `@...`, e.g. `@angular/...`.
|
|
||||||
*
|
|
||||||
* @param sourceDirectory An absolute path to the root directory where searching begins.
|
|
||||||
* @returns an array of `EntryPoint`s that were found within `sourceDirectory`.
|
|
||||||
*/
|
|
||||||
walkDirectoryForPackages(sourceDirectory: AbsoluteFsPath): EntryPointWithDependencies[] {
|
|
||||||
// Try to get a primary entry point from this directory
|
|
||||||
const primaryEntryPoint =
|
|
||||||
getEntryPointInfo(this.fs, this.config, this.logger, sourceDirectory, sourceDirectory);
|
|
||||||
|
|
||||||
// If there is an entry-point but it is not compatible with ngcc (it has a bad package.json or
|
|
||||||
// invalid typings) then exit. It is unlikely that such an entry point has a dependency on an
|
|
||||||
// Angular library.
|
|
||||||
if (primaryEntryPoint === INCOMPATIBLE_ENTRY_POINT) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const entryPoints: EntryPointWithDependencies[] = [];
|
|
||||||
if (primaryEntryPoint !== NO_ENTRY_POINT) {
|
|
||||||
if (primaryEntryPoint !== IGNORED_ENTRY_POINT) {
|
|
||||||
entryPoints.push(this.resolver.getEntryPointWithDependencies(primaryEntryPoint));
|
|
||||||
}
|
|
||||||
this.collectSecondaryEntryPoints(
|
|
||||||
entryPoints, sourceDirectory, sourceDirectory, this.fs.readdir(sourceDirectory));
|
|
||||||
|
|
||||||
// Also check for any nested node_modules in this package but only if at least one of the
|
|
||||||
// entry-points was compiled by Angular.
|
|
||||||
if (entryPoints.some(e => e.entryPoint.compiledByAngular)) {
|
|
||||||
const nestedNodeModulesPath = this.fs.join(sourceDirectory, 'node_modules');
|
|
||||||
if (this.fs.exists(nestedNodeModulesPath)) {
|
|
||||||
entryPoints.push(...this.walkDirectoryForPackages(nestedNodeModulesPath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return entryPoints;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The `sourceDirectory` was not a package (i.e. there was no package.json)
|
|
||||||
// So search its sub-directories for Angular packages and entry-points
|
|
||||||
for (const path of this.fs.readdir(sourceDirectory)) {
|
|
||||||
if (isIgnorablePath(path)) {
|
|
||||||
// Ignore hidden files, node_modules and ngcc directory
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const absolutePath = this.fs.resolve(sourceDirectory, path);
|
|
||||||
const stat = this.fs.lstat(absolutePath);
|
|
||||||
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
||||||
// Ignore symbolic links and non-directories
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
entryPoints.push(...this.walkDirectoryForPackages(this.fs.join(sourceDirectory, path)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return entryPoints;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search the `directory` looking for any secondary entry-points for a package, adding any that
|
|
||||||
* are found to the `entryPoints` array.
|
|
||||||
*
|
|
||||||
* @param entryPoints An array where we will add any entry-points found in this directory
|
|
||||||
* @param packagePath The absolute path to the package that may contain entry-points
|
|
||||||
* @param directory The current directory being searched
|
|
||||||
* @param paths The paths contained in the current `directory`.
|
|
||||||
*/
|
|
||||||
private collectSecondaryEntryPoints(
|
|
||||||
entryPoints: EntryPointWithDependencies[], packagePath: AbsoluteFsPath,
|
|
||||||
directory: AbsoluteFsPath, paths: PathSegment[]): void {
|
|
||||||
for (const path of paths) {
|
|
||||||
if (isIgnorablePath(path)) {
|
|
||||||
// Ignore hidden files, node_modules and ngcc directory
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const absolutePath = this.fs.resolve(directory, path);
|
|
||||||
const stat = this.fs.lstat(absolutePath);
|
|
||||||
if (stat.isSymbolicLink()) {
|
|
||||||
// Ignore symbolic links
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isDirectory = stat.isDirectory();
|
|
||||||
if (!path.endsWith('.js') && !isDirectory) {
|
|
||||||
// Ignore files that do not end in `.js`
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the path is a JS file then strip its extension and see if we can match an
|
|
||||||
// entry-point (even if it is an ignored one).
|
|
||||||
const possibleEntryPointPath = isDirectory ? absolutePath : stripJsExtension(absolutePath);
|
|
||||||
const subEntryPoint =
|
|
||||||
getEntryPointInfo(this.fs, this.config, this.logger, packagePath, possibleEntryPointPath);
|
|
||||||
if (isEntryPoint(subEntryPoint)) {
|
|
||||||
entryPoints.push(this.resolver.getEntryPointWithDependencies(subEntryPoint));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isDirectory) {
|
|
||||||
// This path is not a directory so we are done.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If not an entry-point itself, this directory may contain entry-points of its own.
|
|
||||||
const canContainEntryPoints =
|
|
||||||
subEntryPoint === NO_ENTRY_POINT || subEntryPoint === INCOMPATIBLE_ENTRY_POINT;
|
|
||||||
const childPaths = this.fs.readdir(absolutePath);
|
|
||||||
if (canContainEntryPoints &&
|
|
||||||
childPaths.some(
|
|
||||||
childPath => childPath.endsWith('.js') &&
|
|
||||||
this.fs.stat(this.fs.resolve(absolutePath, childPath)).isFile())) {
|
|
||||||
// We do not consider non-entry-point directories that contain JS files as they are very
|
|
||||||
// unlikely to be containers for sub-entry-points.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
this.collectSecondaryEntryPoints(entryPoints, packagePath, absolutePath, childPaths);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function stripJsExtension<T extends string>(filePath: T): T {
|
|
||||||
return filePath.replace(/\.js$/, '') as T;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isIgnorablePath(path: PathSegment): boolean {
|
|
||||||
return path.startsWith('.') || path === 'node_modules' || path === NGCC_DIRECTORY;
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,154 @@
|
|||||||
|
/**
|
||||||
|
* @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, PathSegment} from '../../../src/ngtsc/file_system';
|
||||||
|
import {Logger} from '../../../src/ngtsc/logging';
|
||||||
|
|
||||||
|
import {EntryPointWithDependencies} from '../dependencies/dependency_host';
|
||||||
|
import {DependencyResolver} from '../dependencies/dependency_resolver';
|
||||||
|
import {NgccConfiguration} from '../packages/configuration';
|
||||||
|
import {getEntryPointInfo, IGNORED_ENTRY_POINT, INCOMPATIBLE_ENTRY_POINT, isEntryPoint, NO_ENTRY_POINT} from '../packages/entry_point';
|
||||||
|
import {NGCC_DIRECTORY} from '../writing/new_entry_point_file_writer';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that traverses a file-tree, starting at a given path, looking for all entry-points,
|
||||||
|
* also capturing the dependencies of each entry-point that is found.
|
||||||
|
*/
|
||||||
|
export class EntryPointCollector {
|
||||||
|
constructor(
|
||||||
|
private fs: FileSystem, private config: NgccConfiguration, private logger: Logger,
|
||||||
|
private resolver: DependencyResolver) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look for Angular packages that need to be compiled, starting at the source directory.
|
||||||
|
* The function will recurse into directories that start with `@...`, e.g. `@angular/...`.
|
||||||
|
*
|
||||||
|
* @param sourceDirectory An absolute path to the root directory where searching begins.
|
||||||
|
* @returns an array of `EntryPoint`s that were found within `sourceDirectory`.
|
||||||
|
*/
|
||||||
|
walkDirectoryForPackages(sourceDirectory: AbsoluteFsPath): EntryPointWithDependencies[] {
|
||||||
|
// Try to get a primary entry point from this directory
|
||||||
|
const primaryEntryPoint =
|
||||||
|
getEntryPointInfo(this.fs, this.config, this.logger, sourceDirectory, sourceDirectory);
|
||||||
|
|
||||||
|
// If there is an entry-point but it is not compatible with ngcc (it has a bad package.json or
|
||||||
|
// invalid typings) then exit. It is unlikely that such an entry point has a dependency on an
|
||||||
|
// Angular library.
|
||||||
|
if (primaryEntryPoint === INCOMPATIBLE_ENTRY_POINT) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const entryPoints: EntryPointWithDependencies[] = [];
|
||||||
|
if (primaryEntryPoint !== NO_ENTRY_POINT) {
|
||||||
|
if (primaryEntryPoint !== IGNORED_ENTRY_POINT) {
|
||||||
|
entryPoints.push(this.resolver.getEntryPointWithDependencies(primaryEntryPoint));
|
||||||
|
}
|
||||||
|
this.collectSecondaryEntryPoints(
|
||||||
|
entryPoints, sourceDirectory, sourceDirectory, this.fs.readdir(sourceDirectory));
|
||||||
|
|
||||||
|
// Also check for any nested node_modules in this package but only if at least one of the
|
||||||
|
// entry-points was compiled by Angular.
|
||||||
|
if (entryPoints.some(e => e.entryPoint.compiledByAngular)) {
|
||||||
|
const nestedNodeModulesPath = this.fs.join(sourceDirectory, 'node_modules');
|
||||||
|
if (this.fs.exists(nestedNodeModulesPath)) {
|
||||||
|
entryPoints.push(...this.walkDirectoryForPackages(nestedNodeModulesPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entryPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The `sourceDirectory` was not a package (i.e. there was no package.json)
|
||||||
|
// So search its sub-directories for Angular packages and entry-points
|
||||||
|
for (const path of this.fs.readdir(sourceDirectory)) {
|
||||||
|
if (isIgnorablePath(path)) {
|
||||||
|
// Ignore hidden files, node_modules and ngcc directory
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const absolutePath = this.fs.resolve(sourceDirectory, path);
|
||||||
|
const stat = this.fs.lstat(absolutePath);
|
||||||
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
||||||
|
// Ignore symbolic links and non-directories
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
entryPoints.push(...this.walkDirectoryForPackages(this.fs.join(sourceDirectory, path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return entryPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search the `directory` looking for any secondary entry-points for a package, adding any that
|
||||||
|
* are found to the `entryPoints` array.
|
||||||
|
*
|
||||||
|
* @param entryPoints An array where we will add any entry-points found in this directory.
|
||||||
|
* @param packagePath The absolute path to the package that may contain entry-points.
|
||||||
|
* @param directory The current directory being searched.
|
||||||
|
* @param paths The paths contained in the current `directory`.
|
||||||
|
*/
|
||||||
|
private collectSecondaryEntryPoints(
|
||||||
|
entryPoints: EntryPointWithDependencies[], packagePath: AbsoluteFsPath,
|
||||||
|
directory: AbsoluteFsPath, paths: PathSegment[]): void {
|
||||||
|
for (const path of paths) {
|
||||||
|
if (isIgnorablePath(path)) {
|
||||||
|
// Ignore hidden files, node_modules and ngcc directory
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const absolutePath = this.fs.resolve(directory, path);
|
||||||
|
const stat = this.fs.lstat(absolutePath);
|
||||||
|
if (stat.isSymbolicLink()) {
|
||||||
|
// Ignore symbolic links
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isDirectory = stat.isDirectory();
|
||||||
|
if (!path.endsWith('.js') && !isDirectory) {
|
||||||
|
// Ignore files that do not end in `.js`
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the path is a JS file then strip its extension and see if we can match an
|
||||||
|
// entry-point (even if it is an ignored one).
|
||||||
|
const possibleEntryPointPath = isDirectory ? absolutePath : stripJsExtension(absolutePath);
|
||||||
|
const subEntryPoint =
|
||||||
|
getEntryPointInfo(this.fs, this.config, this.logger, packagePath, possibleEntryPointPath);
|
||||||
|
if (isEntryPoint(subEntryPoint)) {
|
||||||
|
entryPoints.push(this.resolver.getEntryPointWithDependencies(subEntryPoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isDirectory) {
|
||||||
|
// This path is not a directory so we are done.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not an entry-point itself, this directory may contain entry-points of its own.
|
||||||
|
const canContainEntryPoints =
|
||||||
|
subEntryPoint === NO_ENTRY_POINT || subEntryPoint === INCOMPATIBLE_ENTRY_POINT;
|
||||||
|
const childPaths = this.fs.readdir(absolutePath);
|
||||||
|
if (canContainEntryPoints &&
|
||||||
|
childPaths.some(
|
||||||
|
childPath => childPath.endsWith('.js') &&
|
||||||
|
this.fs.stat(this.fs.resolve(absolutePath, childPath)).isFile())) {
|
||||||
|
// We do not consider non-entry-point directories that contain JS files as they are very
|
||||||
|
// unlikely to be containers for sub-entry-points.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this.collectSecondaryEntryPoints(entryPoints, packagePath, absolutePath, childPaths);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stripJsExtension<T extends string>(filePath: T): T {
|
||||||
|
return filePath.replace(/\.js$/, '') as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isIgnorablePath(path: PathSegment): boolean {
|
||||||
|
return path.startsWith('.') || path === 'node_modules' || path === NGCC_DIRECTORY;
|
||||||
|
}
|
@ -9,14 +9,17 @@ import {AbsoluteFsPath, FileSystem} from '../../../src/ngtsc/file_system';
|
|||||||
import {Logger} from '../../../src/ngtsc/logging';
|
import {Logger} from '../../../src/ngtsc/logging';
|
||||||
import {ParsedConfiguration} from '../../../src/perform_compile';
|
import {ParsedConfiguration} from '../../../src/perform_compile';
|
||||||
|
|
||||||
import {createDependencyInfo} from '../dependencies/dependency_host';
|
import {createDependencyInfo, EntryPointWithDependencies} from '../dependencies/dependency_host';
|
||||||
import {DependencyResolver} from '../dependencies/dependency_resolver';
|
import {DependencyResolver} from '../dependencies/dependency_resolver';
|
||||||
import {EsmDependencyHost} from '../dependencies/esm_dependency_host';
|
import {EsmDependencyHost} from '../dependencies/esm_dependency_host';
|
||||||
import {ModuleResolver} from '../dependencies/module_resolver';
|
import {ModuleResolver} from '../dependencies/module_resolver';
|
||||||
import {NgccConfiguration} from '../packages/configuration';
|
import {NgccConfiguration} from '../packages/configuration';
|
||||||
|
import {EntryPointManifest} from '../packages/entry_point_manifest';
|
||||||
import {getPathMappingsFromTsConfig} from '../path_mappings';
|
import {getPathMappingsFromTsConfig} from '../path_mappings';
|
||||||
|
|
||||||
|
import {EntryPointCollector} from './entry_point_collector';
|
||||||
import {TracingEntryPointFinder} from './tracing_entry_point_finder';
|
import {TracingEntryPointFinder} from './tracing_entry_point_finder';
|
||||||
|
import {trackDuration} from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An EntryPointFinder that starts from the files in the program defined by the given tsconfig.json
|
* An EntryPointFinder that starts from the files in the program defined by the given tsconfig.json
|
||||||
@ -26,14 +29,21 @@ import {TracingEntryPointFinder} from './tracing_entry_point_finder';
|
|||||||
* and is used primarily by the CLI integration.
|
* and is used primarily by the CLI integration.
|
||||||
*/
|
*/
|
||||||
export class ProgramBasedEntryPointFinder extends TracingEntryPointFinder {
|
export class ProgramBasedEntryPointFinder extends TracingEntryPointFinder {
|
||||||
|
private entryPointsWithDependencies: Map<AbsoluteFsPath, EntryPointWithDependencies>|null = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
fs: FileSystem, config: NgccConfiguration, logger: Logger, resolver: DependencyResolver,
|
fs: FileSystem, config: NgccConfiguration, logger: Logger, resolver: DependencyResolver,
|
||||||
basePath: AbsoluteFsPath, private tsConfig: ParsedConfiguration,
|
private entryPointCollector: EntryPointCollector,
|
||||||
projectPath: AbsoluteFsPath) {
|
private entryPointManifest: EntryPointManifest, basePath: AbsoluteFsPath,
|
||||||
|
private tsConfig: ParsedConfiguration, projectPath: AbsoluteFsPath) {
|
||||||
super(
|
super(
|
||||||
fs, config, logger, resolver, basePath, getPathMappingsFromTsConfig(tsConfig, projectPath));
|
fs, config, logger, resolver, basePath, getPathMappingsFromTsConfig(tsConfig, projectPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array containing the external import paths that were extracted from the source-files
|
||||||
|
* of the program defined by the tsconfig.json.
|
||||||
|
*/
|
||||||
protected getInitialEntryPointPaths(): AbsoluteFsPath[] {
|
protected getInitialEntryPointPaths(): AbsoluteFsPath[] {
|
||||||
const moduleResolver = new ModuleResolver(this.fs, this.pathMappings, ['', '.ts', '/index.ts']);
|
const moduleResolver = new ModuleResolver(this.fs, this.pathMappings, ['', '.ts', '/index.ts']);
|
||||||
const host = new EsmDependencyHost(this.fs, moduleResolver);
|
const host = new EsmDependencyHost(this.fs, moduleResolver);
|
||||||
@ -48,4 +58,64 @@ export class ProgramBasedEntryPointFinder extends TracingEntryPointFinder {
|
|||||||
});
|
});
|
||||||
return Array.from(dependencies.dependencies);
|
return Array.from(dependencies.dependencies);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* For the given `entryPointPath`, compute, or retrieve, the entry-point information, including
|
||||||
|
* paths to other entry-points that this entry-point depends upon.
|
||||||
|
*
|
||||||
|
* In this entry-point finder, we use the `EntryPointManifest` to avoid computing each
|
||||||
|
* entry-point's dependencies in the case that this had been done previously.
|
||||||
|
*
|
||||||
|
* @param entryPointPath the path to the entry-point whose information and dependencies are to be
|
||||||
|
* retrieved or computed.
|
||||||
|
*
|
||||||
|
* @returns the entry-point and its dependencies or `null` if the entry-point is not compiled by
|
||||||
|
* Angular or cannot be determined.
|
||||||
|
*/
|
||||||
|
protected getEntryPointWithDeps(entryPointPath: AbsoluteFsPath): EntryPointWithDependencies|null {
|
||||||
|
const entryPoints = this.findOrLoadEntryPoints();
|
||||||
|
if (!entryPoints.has(entryPointPath)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const entryPointWithDeps = entryPoints.get(entryPointPath)!;
|
||||||
|
if (!entryPointWithDeps.entryPoint.compiledByAngular) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return entryPointWithDeps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Walk the base paths looking for entry-points or load this information from an entry-point
|
||||||
|
* manifest, if available.
|
||||||
|
*/
|
||||||
|
private findOrLoadEntryPoints(): Map<AbsoluteFsPath, EntryPointWithDependencies> {
|
||||||
|
if (this.entryPointsWithDependencies === null) {
|
||||||
|
const entryPointsWithDependencies = this.entryPointsWithDependencies =
|
||||||
|
new Map<AbsoluteFsPath, EntryPointWithDependencies>();
|
||||||
|
for (const basePath of this.getBasePaths()) {
|
||||||
|
const entryPoints = this.entryPointManifest.readEntryPointsUsingManifest(basePath) ||
|
||||||
|
this.walkBasePathForPackages(basePath);
|
||||||
|
for (const e of entryPoints) {
|
||||||
|
entryPointsWithDependencies.set(e.entryPoint.path, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.entryPointsWithDependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search the `basePath` for possible Angular packages and entry-points.
|
||||||
|
*
|
||||||
|
* @param basePath The path at which to start the search.
|
||||||
|
* @returns an array of `EntryPoint`s that were found within `basePath`.
|
||||||
|
*/
|
||||||
|
walkBasePathForPackages(basePath: AbsoluteFsPath): EntryPointWithDependencies[] {
|
||||||
|
this.logger.debug(
|
||||||
|
`No manifest found for ${basePath} so walking the directories for entry-points.`);
|
||||||
|
const entryPoints = trackDuration(
|
||||||
|
() => this.entryPointCollector.walkDirectoryForPackages(basePath),
|
||||||
|
duration => this.logger.debug(`Walking ${basePath} for entry-points took ${duration}s.`));
|
||||||
|
this.entryPointManifest.writeEntryPointManifest(basePath, entryPoints);
|
||||||
|
return entryPoints;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,12 +5,13 @@
|
|||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {AbsoluteFsPath, FileSystem} from '../../../src/ngtsc/file_system';
|
import {AbsoluteFsPath, FileSystem, join, PathSegment, relative, relativeFrom} from '../../../src/ngtsc/file_system';
|
||||||
import {Logger} from '../../../src/ngtsc/logging';
|
import {Logger} from '../../../src/ngtsc/logging';
|
||||||
|
import {EntryPointWithDependencies} from '../dependencies/dependency_host';
|
||||||
import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver';
|
import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver';
|
||||||
import {hasBeenProcessed} from '../packages/build_marker';
|
import {hasBeenProcessed} from '../packages/build_marker';
|
||||||
import {NgccConfiguration} from '../packages/configuration';
|
import {NgccConfiguration} from '../packages/configuration';
|
||||||
import {EntryPointJsonProperty} from '../packages/entry_point';
|
import {EntryPointJsonProperty, getEntryPointInfo, isEntryPoint} from '../packages/entry_point';
|
||||||
import {PathMappings} from '../path_mappings';
|
import {PathMappings} from '../path_mappings';
|
||||||
|
|
||||||
import {TracingEntryPointFinder} from './tracing_entry_point_finder';
|
import {TracingEntryPointFinder} from './tracing_entry_point_finder';
|
||||||
@ -30,6 +31,10 @@ export class TargetedEntryPointFinder extends TracingEntryPointFinder {
|
|||||||
super(fs, config, logger, resolver, basePath, pathMappings);
|
super(fs, config, logger, resolver, basePath, pathMappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for Angular entry-points that can be reached from the entry-point specified by the given
|
||||||
|
* `targetPath`.
|
||||||
|
*/
|
||||||
findEntryPoints(): SortedEntryPointsInfo {
|
findEntryPoints(): SortedEntryPointsInfo {
|
||||||
const entryPoints = super.findEntryPoints();
|
const entryPoints = super.findEntryPoints();
|
||||||
|
|
||||||
@ -43,17 +48,25 @@ export class TargetedEntryPointFinder extends TracingEntryPointFinder {
|
|||||||
return entryPoints;
|
return entryPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the entry-point at the given `targetPath` needs to be processed.
|
||||||
|
*
|
||||||
|
* @param propertiesToConsider the package.json properties that should be considered for
|
||||||
|
* processing.
|
||||||
|
* @param compileAllFormats true if all formats need to be processed, or false if it is enough for
|
||||||
|
* one of the formats covered by the `propertiesToConsider` is processed.
|
||||||
|
*/
|
||||||
targetNeedsProcessingOrCleaning(
|
targetNeedsProcessingOrCleaning(
|
||||||
propertiesToConsider: EntryPointJsonProperty[], compileAllFormats: boolean): boolean {
|
propertiesToConsider: EntryPointJsonProperty[], compileAllFormats: boolean): boolean {
|
||||||
const entryPoint = this.getEntryPoint(this.targetPath);
|
const entryPointWithDeps = this.getEntryPointWithDeps(this.targetPath);
|
||||||
if (entryPoint === null || !entryPoint.compiledByAngular) {
|
if (entryPointWithDeps === null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const property of propertiesToConsider) {
|
for (const property of propertiesToConsider) {
|
||||||
if (entryPoint.packageJson[property]) {
|
if (entryPointWithDeps.entryPoint.packageJson[property]) {
|
||||||
// Here is a property that should be processed.
|
// Here is a property that should be processed.
|
||||||
if (!hasBeenProcessed(entryPoint.packageJson, property)) {
|
if (!hasBeenProcessed(entryPointWithDeps.entryPoint.packageJson, property)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!compileAllFormats) {
|
if (!compileAllFormats) {
|
||||||
@ -67,7 +80,160 @@ export class TargetedEntryPointFinder extends TracingEntryPointFinder {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array containing the `targetPath` from which to start the trace.
|
||||||
|
*/
|
||||||
protected getInitialEntryPointPaths(): AbsoluteFsPath[] {
|
protected getInitialEntryPointPaths(): AbsoluteFsPath[] {
|
||||||
return [this.targetPath];
|
return [this.targetPath];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the given `entryPointPath`, compute, or retrieve, the entry-point information, including
|
||||||
|
* paths to other entry-points that this entry-point depends upon.
|
||||||
|
*
|
||||||
|
* @param entryPointPath the path to the entry-point whose information and dependencies are to be
|
||||||
|
* retrieved or computed.
|
||||||
|
*
|
||||||
|
* @returns the entry-point and its dependencies or `null` if the entry-point is not compiled by
|
||||||
|
* Angular or cannot be determined.
|
||||||
|
*/
|
||||||
|
protected getEntryPointWithDeps(entryPointPath: AbsoluteFsPath): EntryPointWithDependencies|null {
|
||||||
|
const packagePath = this.computePackagePath(entryPointPath);
|
||||||
|
const entryPoint =
|
||||||
|
getEntryPointInfo(this.fs, this.config, this.logger, packagePath, entryPointPath);
|
||||||
|
if (!isEntryPoint(entryPoint) || !entryPoint.compiledByAngular) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.resolver.getEntryPointWithDependencies(entryPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the path to the package that contains the given entry-point.
|
||||||
|
*
|
||||||
|
* In this entry-point finder it is not trivial to find the containing package, since it is
|
||||||
|
* possible that this entry-point is not directly below the directory containing the package.
|
||||||
|
* Moreover, the import path could be affected by path-mapping.
|
||||||
|
*
|
||||||
|
* @param entryPointPath the path to the entry-point, whose package path we want to compute.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
private splitPath(path: PathSegment) {
|
||||||
|
const segments = [];
|
||||||
|
while (path !== '.') {
|
||||||
|
segments.unshift(this.fs.basename(path));
|
||||||
|
path = this.fs.dirname(path);
|
||||||
|
}
|
||||||
|
return segments;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,12 @@
|
|||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {AbsoluteFsPath, FileSystem, join, PathSegment, relative, relativeFrom} from '../../../src/ngtsc/file_system';
|
import {AbsoluteFsPath, FileSystem} from '../../../src/ngtsc/file_system';
|
||||||
import {Logger} from '../../../src/ngtsc/logging';
|
import {Logger} from '../../../src/ngtsc/logging';
|
||||||
|
|
||||||
import {EntryPointWithDependencies} from '../dependencies/dependency_host';
|
import {EntryPointWithDependencies} from '../dependencies/dependency_host';
|
||||||
import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver';
|
import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver';
|
||||||
import {NgccConfiguration} from '../packages/configuration';
|
import {NgccConfiguration} from '../packages/configuration';
|
||||||
import {EntryPoint, getEntryPointInfo, isEntryPoint} from '../packages/entry_point';
|
|
||||||
import {PathMappings} from '../path_mappings';
|
import {PathMappings} from '../path_mappings';
|
||||||
|
|
||||||
import {EntryPointFinder} from './interface';
|
import {EntryPointFinder} from './interface';
|
||||||
@ -21,18 +20,19 @@ import {getBasePaths} from './utils';
|
|||||||
* An EntryPointFinder that starts from a set of initial files and only returns entry-points that
|
* An EntryPointFinder that starts from a set of initial files and only returns entry-points that
|
||||||
* are dependencies of these files.
|
* are dependencies of these files.
|
||||||
*
|
*
|
||||||
* This is faster than searching the entire file-system for all the entry-points,
|
* This is faster than processing all entry-points in the entire file-system, and is used primarily
|
||||||
* and is used primarily by the CLI integration.
|
* by the CLI integration.
|
||||||
*
|
*
|
||||||
* There are two concrete implementations of this class.
|
* There are two concrete implementations of this class.
|
||||||
*
|
*
|
||||||
* * `TargetEntryPointFinder` - is given a single entry-point as the initial entry-point
|
* * `TargetEntryPointFinder` - is given a single entry-point as the initial entry-point. This can
|
||||||
* * `ProgramBasedEntryPointFinder` - computes the initial entry-points from program files given by
|
* be used in the synchronous CLI integration where the build tool has identified an external
|
||||||
* a `tsconfig.json` file.
|
* import to one of the source files being built.
|
||||||
|
* * `ProgramBasedEntryPointFinder` - computes the initial entry-points from the source files
|
||||||
|
* computed from a `tsconfig.json` file. This can be used in the asynchronous CLI integration
|
||||||
|
* where the `tsconfig.json` to be used to do the build is known.
|
||||||
*/
|
*/
|
||||||
export abstract class TracingEntryPointFinder implements EntryPointFinder {
|
export abstract class TracingEntryPointFinder implements EntryPointFinder {
|
||||||
protected unprocessedPaths: AbsoluteFsPath[] = [];
|
|
||||||
protected unsortedEntryPoints = new Map<AbsoluteFsPath, EntryPointWithDependencies>();
|
|
||||||
private basePaths: AbsoluteFsPath[]|null = null;
|
private basePaths: AbsoluteFsPath[]|null = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -40,165 +40,59 @@ export abstract class TracingEntryPointFinder implements EntryPointFinder {
|
|||||||
protected resolver: DependencyResolver, protected basePath: AbsoluteFsPath,
|
protected resolver: DependencyResolver, protected basePath: AbsoluteFsPath,
|
||||||
protected pathMappings: PathMappings|undefined) {}
|
protected pathMappings: PathMappings|undefined) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for Angular package entry-points.
|
||||||
|
*/
|
||||||
|
findEntryPoints(): SortedEntryPointsInfo {
|
||||||
|
const unsortedEntryPoints = new Map<AbsoluteFsPath, EntryPointWithDependencies>();
|
||||||
|
const unprocessedPaths = this.getInitialEntryPointPaths();
|
||||||
|
while (unprocessedPaths.length > 0) {
|
||||||
|
const path = unprocessedPaths.shift()!;
|
||||||
|
const entryPointWithDeps = this.getEntryPointWithDeps(path);
|
||||||
|
if (entryPointWithDeps === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
unsortedEntryPoints.set(entryPointWithDeps.entryPoint.path, entryPointWithDeps);
|
||||||
|
entryPointWithDeps.depInfo.dependencies.forEach(dep => {
|
||||||
|
if (!unsortedEntryPoints.has(dep)) {
|
||||||
|
unprocessedPaths.push(dep);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this.resolver.sortEntryPointsByDependency(Array.from(unsortedEntryPoints.values()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array of entry-point paths from which to start the trace.
|
||||||
|
*/
|
||||||
|
protected abstract getInitialEntryPointPaths(): AbsoluteFsPath[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the given `entryPointPath`, compute, or retrieve, the entry-point information, including
|
||||||
|
* paths to other entry-points that this entry-point depends upon.
|
||||||
|
*
|
||||||
|
* @param entryPointPath the path to the entry-point whose information and dependencies are to be
|
||||||
|
* retrieved or computed.
|
||||||
|
*
|
||||||
|
* @returns the entry-point and its dependencies or `null` if the entry-point is not compiled by
|
||||||
|
* Angular or cannot be determined.
|
||||||
|
*/
|
||||||
|
protected abstract getEntryPointWithDeps(entryPointPath: AbsoluteFsPath):
|
||||||
|
EntryPointWithDependencies|null;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the path-mappings to compute the base-paths that need to be considered when finding
|
||||||
|
* entry-points.
|
||||||
|
*
|
||||||
|
* This processing can be time-consuming if the path-mappings are complex or extensive.
|
||||||
|
* So the result is cached locally once computed.
|
||||||
|
*/
|
||||||
protected getBasePaths() {
|
protected getBasePaths() {
|
||||||
if (this.basePaths === null) {
|
if (this.basePaths === null) {
|
||||||
this.basePaths = getBasePaths(this.logger, this.basePath, this.pathMappings);
|
this.basePaths = getBasePaths(this.logger, this.basePath, this.pathMappings);
|
||||||
}
|
}
|
||||||
return this.basePaths;
|
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);
|
|
||||||
|
|
||||||
return isEntryPoint(entryPoint) ? entryPoint : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -82,8 +82,8 @@ function extractPathPrefix(path: string) {
|
|||||||
/**
|
/**
|
||||||
* Run a task and track how long it takes.
|
* Run a task and track how long it takes.
|
||||||
*
|
*
|
||||||
* @param task The task whose duration we are tracking
|
* @param task The task whose duration we are tracking.
|
||||||
* @param log The function to call with the duration of the task
|
* @param log The function to call with the duration of the task.
|
||||||
* @returns The result of calling `task`.
|
* @returns The result of calling `task`.
|
||||||
*/
|
*/
|
||||||
export function trackDuration<T = void>(task: () => T extends Promise<unknown>? never : T,
|
export function trackDuration<T = void>(task: () => T extends Promise<unknown>? never : T,
|
||||||
@ -136,7 +136,7 @@ function addPath(root: Node, path: AbsoluteFsPath): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flatten the tree of nodes back into an array of absolute paths
|
* Flatten the tree of nodes back into an array of absolute paths.
|
||||||
*/
|
*/
|
||||||
function flattenTree(root: Node): AbsoluteFsPath[] {
|
function flattenTree(root: Node): AbsoluteFsPath[] {
|
||||||
const paths: AbsoluteFsPath[] = [];
|
const paths: AbsoluteFsPath[] = [];
|
||||||
|
@ -21,6 +21,7 @@ import {EsmDependencyHost} from './dependencies/esm_dependency_host';
|
|||||||
import {ModuleResolver} from './dependencies/module_resolver';
|
import {ModuleResolver} from './dependencies/module_resolver';
|
||||||
import {UmdDependencyHost} from './dependencies/umd_dependency_host';
|
import {UmdDependencyHost} from './dependencies/umd_dependency_host';
|
||||||
import {DirectoryWalkerEntryPointFinder} from './entry_point_finder/directory_walker_entry_point_finder';
|
import {DirectoryWalkerEntryPointFinder} from './entry_point_finder/directory_walker_entry_point_finder';
|
||||||
|
import {EntryPointCollector} from './entry_point_finder/entry_point_collector';
|
||||||
import {EntryPointFinder} from './entry_point_finder/interface';
|
import {EntryPointFinder} from './entry_point_finder/interface';
|
||||||
import {ProgramBasedEntryPointFinder} from './entry_point_finder/program_based_entry_point_finder';
|
import {ProgramBasedEntryPointFinder} from './entry_point_finder/program_based_entry_point_finder';
|
||||||
import {TargetedEntryPointFinder} from './entry_point_finder/targeted_entry_point_finder';
|
import {TargetedEntryPointFinder} from './entry_point_finder/targeted_entry_point_finder';
|
||||||
@ -203,10 +204,15 @@ function getEntryPointFinder(
|
|||||||
if (absoluteTargetEntryPointPath !== null) {
|
if (absoluteTargetEntryPointPath !== null) {
|
||||||
return new TargetedEntryPointFinder(
|
return new TargetedEntryPointFinder(
|
||||||
fs, config, logger, resolver, basePath, pathMappings, absoluteTargetEntryPointPath);
|
fs, config, logger, resolver, basePath, pathMappings, absoluteTargetEntryPointPath);
|
||||||
} else if (tsConfig !== null) {
|
} else {
|
||||||
return new ProgramBasedEntryPointFinder(
|
const entryPointCollector = new EntryPointCollector(fs, config, logger, resolver);
|
||||||
fs, config, logger, resolver, basePath, tsConfig, projectPath);
|
if (tsConfig !== null) {
|
||||||
|
return new ProgramBasedEntryPointFinder(
|
||||||
|
fs, config, logger, resolver, entryPointCollector, entryPointManifest, basePath, tsConfig,
|
||||||
|
projectPath);
|
||||||
|
} else {
|
||||||
|
return new DirectoryWalkerEntryPointFinder(
|
||||||
|
logger, resolver, entryPointCollector, entryPointManifest, basePath, pathMappings);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return new DirectoryWalkerEntryPointFinder(
|
|
||||||
fs, config, logger, resolver, entryPointManifest, basePath, pathMappings);
|
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import {DtsDependencyHost} from '../../src/dependencies/dts_dependency_host';
|
|||||||
import {EsmDependencyHost} from '../../src/dependencies/esm_dependency_host';
|
import {EsmDependencyHost} from '../../src/dependencies/esm_dependency_host';
|
||||||
import {ModuleResolver} from '../../src/dependencies/module_resolver';
|
import {ModuleResolver} from '../../src/dependencies/module_resolver';
|
||||||
import {DirectoryWalkerEntryPointFinder} from '../../src/entry_point_finder/directory_walker_entry_point_finder';
|
import {DirectoryWalkerEntryPointFinder} from '../../src/entry_point_finder/directory_walker_entry_point_finder';
|
||||||
|
import {EntryPointCollector} from '../../src/entry_point_finder/entry_point_collector';
|
||||||
import {NgccConfiguration, ProcessedNgccPackageConfig} from '../../src/packages/configuration';
|
import {NgccConfiguration, ProcessedNgccPackageConfig} from '../../src/packages/configuration';
|
||||||
import {EntryPoint} from '../../src/packages/entry_point';
|
import {EntryPoint} from '../../src/packages/entry_point';
|
||||||
import {EntryPointManifest, EntryPointManifestFile} from '../../src/packages/entry_point_manifest';
|
import {EntryPointManifest, EntryPointManifestFile} from '../../src/packages/entry_point_manifest';
|
||||||
@ -25,6 +26,7 @@ runInEachFileSystem(() => {
|
|||||||
let resolver: DependencyResolver;
|
let resolver: DependencyResolver;
|
||||||
let logger: MockLogger;
|
let logger: MockLogger;
|
||||||
let config: NgccConfiguration;
|
let config: NgccConfiguration;
|
||||||
|
let collector: EntryPointCollector;
|
||||||
let manifest: EntryPointManifest;
|
let manifest: EntryPointManifest;
|
||||||
let _Abs: typeof absoluteFrom;
|
let _Abs: typeof absoluteFrom;
|
||||||
|
|
||||||
@ -37,6 +39,7 @@ runInEachFileSystem(() => {
|
|||||||
config = new NgccConfiguration(fs, _Abs('/'));
|
config = new NgccConfiguration(fs, _Abs('/'));
|
||||||
resolver = new DependencyResolver(fs, logger, config, {esm2015: srcHost}, dtsHost);
|
resolver = new DependencyResolver(fs, logger, config, {esm2015: srcHost}, dtsHost);
|
||||||
manifest = new EntryPointManifest(fs, config, logger);
|
manifest = new EntryPointManifest(fs, config, logger);
|
||||||
|
collector = new EntryPointCollector(fs, config, logger, resolver);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('findEntryPoints()', () => {
|
describe('findEntryPoints()', () => {
|
||||||
@ -50,7 +53,7 @@ runInEachFileSystem(() => {
|
|||||||
...createPackage(fs.resolve(basePath, 'common'), 'testing', ['common']),
|
...createPackage(fs.resolve(basePath, 'common'), 'testing', ['common']),
|
||||||
]);
|
]);
|
||||||
const finder = new DirectoryWalkerEntryPointFinder(
|
const finder = new DirectoryWalkerEntryPointFinder(
|
||||||
fs, config, logger, resolver, manifest, basePath, undefined);
|
logger, resolver, collector, manifest, basePath, undefined);
|
||||||
const {entryPoints} = finder.findEntryPoints();
|
const {entryPoints} = finder.findEntryPoints();
|
||||||
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
||||||
['common', 'common'],
|
['common', 'common'],
|
||||||
@ -71,7 +74,7 @@ runInEachFileSystem(() => {
|
|||||||
...createPackage(fs.resolve(basePath, '@angular/common'), 'testing', ['@angular/common']),
|
...createPackage(fs.resolve(basePath, '@angular/common'), 'testing', ['@angular/common']),
|
||||||
]);
|
]);
|
||||||
const finder = new DirectoryWalkerEntryPointFinder(
|
const finder = new DirectoryWalkerEntryPointFinder(
|
||||||
fs, config, logger, resolver, manifest, basePath, undefined);
|
logger, resolver, collector, manifest, basePath, undefined);
|
||||||
const {entryPoints} = finder.findEntryPoints();
|
const {entryPoints} = finder.findEntryPoints();
|
||||||
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
||||||
['@angular/common', '@angular/common'],
|
['@angular/common', '@angular/common'],
|
||||||
@ -84,7 +87,7 @@ runInEachFileSystem(() => {
|
|||||||
it('should return an empty array if there are no packages', () => {
|
it('should return an empty array if there are no packages', () => {
|
||||||
fs.ensureDir(_Abs('/no_packages/node_modules/should_not_be_found'));
|
fs.ensureDir(_Abs('/no_packages/node_modules/should_not_be_found'));
|
||||||
const finder = new DirectoryWalkerEntryPointFinder(
|
const finder = new DirectoryWalkerEntryPointFinder(
|
||||||
fs, config, logger, resolver, manifest, _Abs('/no_packages/node_modules'), undefined);
|
logger, resolver, collector, manifest, _Abs('/no_packages/node_modules'), undefined);
|
||||||
const {entryPoints} = finder.findEntryPoints();
|
const {entryPoints} = finder.findEntryPoints();
|
||||||
expect(entryPoints).toEqual([]);
|
expect(entryPoints).toEqual([]);
|
||||||
});
|
});
|
||||||
@ -97,7 +100,7 @@ runInEachFileSystem(() => {
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
const finder = new DirectoryWalkerEntryPointFinder(
|
const finder = new DirectoryWalkerEntryPointFinder(
|
||||||
fs, config, logger, resolver, manifest, _Abs('/no_valid_entry_points/node_modules'),
|
logger, resolver, collector, manifest, _Abs('/no_valid_entry_points/node_modules'),
|
||||||
undefined);
|
undefined);
|
||||||
const {entryPoints} = finder.findEntryPoints();
|
const {entryPoints} = finder.findEntryPoints();
|
||||||
expect(entryPoints).toEqual([]);
|
expect(entryPoints).toEqual([]);
|
||||||
@ -106,7 +109,7 @@ runInEachFileSystem(() => {
|
|||||||
it('should not include ignored entry-points', () => {
|
it('should not include ignored entry-points', () => {
|
||||||
const basePath = _Abs('/project/node_modules');
|
const basePath = _Abs('/project/node_modules');
|
||||||
const finder = new DirectoryWalkerEntryPointFinder(
|
const finder = new DirectoryWalkerEntryPointFinder(
|
||||||
fs, config, logger, resolver, manifest, basePath, undefined);
|
logger, resolver, collector, manifest, basePath, undefined);
|
||||||
|
|
||||||
loadTestFiles(createPackage(basePath, 'some-package'));
|
loadTestFiles(createPackage(basePath, 'some-package'));
|
||||||
spyOn(config, 'getPackageConfig')
|
spyOn(config, 'getPackageConfig')
|
||||||
@ -124,7 +127,7 @@ runInEachFileSystem(() => {
|
|||||||
it('should look for sub-entry-points even if a containing entry-point is ignored', () => {
|
it('should look for sub-entry-points even if a containing entry-point is ignored', () => {
|
||||||
const basePath = _Abs('/project/node_modules');
|
const basePath = _Abs('/project/node_modules');
|
||||||
const finder = new DirectoryWalkerEntryPointFinder(
|
const finder = new DirectoryWalkerEntryPointFinder(
|
||||||
fs, config, logger, resolver, manifest, basePath, undefined);
|
logger, resolver, collector, manifest, basePath, undefined);
|
||||||
|
|
||||||
loadTestFiles([
|
loadTestFiles([
|
||||||
...createPackage(basePath, 'some-package'),
|
...createPackage(basePath, 'some-package'),
|
||||||
@ -161,7 +164,7 @@ runInEachFileSystem(() => {
|
|||||||
spyOn(manifest, 'readEntryPointsUsingManifest').and.callThrough();
|
spyOn(manifest, 'readEntryPointsUsingManifest').and.callThrough();
|
||||||
spyOn(manifest, 'writeEntryPointManifest').and.callThrough();
|
spyOn(manifest, 'writeEntryPointManifest').and.callThrough();
|
||||||
const finder = new DirectoryWalkerEntryPointFinder(
|
const finder = new DirectoryWalkerEntryPointFinder(
|
||||||
fs, config, logger, resolver, manifest, basePath, undefined);
|
logger, resolver, collector, manifest, basePath, undefined);
|
||||||
finder.findEntryPoints();
|
finder.findEntryPoints();
|
||||||
expect(manifest.readEntryPointsUsingManifest).toHaveBeenCalled();
|
expect(manifest.readEntryPointsUsingManifest).toHaveBeenCalled();
|
||||||
expect(manifest.writeEntryPointManifest).toHaveBeenCalled();
|
expect(manifest.writeEntryPointManifest).toHaveBeenCalled();
|
||||||
@ -182,7 +185,7 @@ runInEachFileSystem(() => {
|
|||||||
spyOn(manifest, 'readEntryPointsUsingManifest').and.callThrough();
|
spyOn(manifest, 'readEntryPointsUsingManifest').and.callThrough();
|
||||||
spyOn(manifest, 'writeEntryPointManifest').and.callThrough();
|
spyOn(manifest, 'writeEntryPointManifest').and.callThrough();
|
||||||
const finder = new DirectoryWalkerEntryPointFinder(
|
const finder = new DirectoryWalkerEntryPointFinder(
|
||||||
fs, config, logger, resolver, manifest, basePath, undefined);
|
logger, resolver, collector, manifest, basePath, undefined);
|
||||||
finder.findEntryPoints();
|
finder.findEntryPoints();
|
||||||
expect(manifest.readEntryPointsUsingManifest).toHaveBeenCalled();
|
expect(manifest.readEntryPointsUsingManifest).toHaveBeenCalled();
|
||||||
expect(manifest.writeEntryPointManifest).toHaveBeenCalled();
|
expect(manifest.writeEntryPointManifest).toHaveBeenCalled();
|
||||||
@ -200,7 +203,7 @@ runInEachFileSystem(() => {
|
|||||||
{name: _Abs('/sub_entry_points/yarn.lock'), contents: 'MOCK LOCK FILE'},
|
{name: _Abs('/sub_entry_points/yarn.lock'), contents: 'MOCK LOCK FILE'},
|
||||||
]);
|
]);
|
||||||
const finder = new DirectoryWalkerEntryPointFinder(
|
const finder = new DirectoryWalkerEntryPointFinder(
|
||||||
fs, config, logger, resolver, manifest, basePath, undefined);
|
logger, resolver, collector, manifest, basePath, undefined);
|
||||||
|
|
||||||
// Prime the manifest by calling findEntryPoints() once.
|
// Prime the manifest by calling findEntryPoints() once.
|
||||||
finder.findEntryPoints();
|
finder.findEntryPoints();
|
||||||
@ -229,8 +232,7 @@ runInEachFileSystem(() => {
|
|||||||
...createPackage(_Abs('/dotted_folders/node_modules/'), '.common'),
|
...createPackage(_Abs('/dotted_folders/node_modules/'), '.common'),
|
||||||
]);
|
]);
|
||||||
const finder = new DirectoryWalkerEntryPointFinder(
|
const finder = new DirectoryWalkerEntryPointFinder(
|
||||||
fs, config, logger, resolver, manifest, _Abs('/dotted_folders/node_modules'),
|
logger, resolver, collector, manifest, _Abs('/dotted_folders/node_modules'), undefined);
|
||||||
undefined);
|
|
||||||
const {entryPoints} = finder.findEntryPoints();
|
const {entryPoints} = finder.findEntryPoints();
|
||||||
expect(entryPoints).toEqual([]);
|
expect(entryPoints).toEqual([]);
|
||||||
});
|
});
|
||||||
@ -241,7 +243,7 @@ runInEachFileSystem(() => {
|
|||||||
_Abs('/external/node_modules/common'), _Abs('/symlinked_folders/node_modules/common'));
|
_Abs('/external/node_modules/common'), _Abs('/symlinked_folders/node_modules/common'));
|
||||||
loadTestFiles(createPackage(_Abs('/external/node_modules'), 'common'));
|
loadTestFiles(createPackage(_Abs('/external/node_modules'), 'common'));
|
||||||
const finder = new DirectoryWalkerEntryPointFinder(
|
const finder = new DirectoryWalkerEntryPointFinder(
|
||||||
fs, config, logger, resolver, manifest, _Abs('/symlinked_folders/node_modules'),
|
logger, resolver, collector, manifest, _Abs('/symlinked_folders/node_modules'),
|
||||||
undefined);
|
undefined);
|
||||||
const {entryPoints} = finder.findEntryPoints();
|
const {entryPoints} = finder.findEntryPoints();
|
||||||
expect(entryPoints).toEqual([]);
|
expect(entryPoints).toEqual([]);
|
||||||
@ -253,7 +255,7 @@ runInEachFileSystem(() => {
|
|||||||
...createPackage(_Abs('/nested_node_modules/node_modules/outer/node_modules'), 'inner'),
|
...createPackage(_Abs('/nested_node_modules/node_modules/outer/node_modules'), 'inner'),
|
||||||
]);
|
]);
|
||||||
const finder = new DirectoryWalkerEntryPointFinder(
|
const finder = new DirectoryWalkerEntryPointFinder(
|
||||||
fs, config, logger, resolver, manifest, _Abs('/nested_node_modules/node_modules'),
|
logger, resolver, collector, manifest, _Abs('/nested_node_modules/node_modules'),
|
||||||
undefined);
|
undefined);
|
||||||
const {entryPoints} = finder.findEntryPoints();
|
const {entryPoints} = finder.findEntryPoints();
|
||||||
// Note that the `inner` entry-point is not part of the `outer` package
|
// Note that the `inner` entry-point is not part of the `outer` package
|
||||||
@ -272,9 +274,9 @@ runInEachFileSystem(() => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const finder = new DirectoryWalkerEntryPointFinder(
|
const finder = new DirectoryWalkerEntryPointFinder(
|
||||||
fs, config, logger, resolver, manifest, _Abs('/nested_node_modules/node_modules'),
|
logger, resolver, collector, manifest, _Abs('/nested_node_modules/node_modules'),
|
||||||
undefined);
|
undefined);
|
||||||
const spy = spyOn(finder, 'walkDirectoryForPackages').and.callThrough();
|
const spy = spyOn(collector, 'walkDirectoryForPackages').and.callThrough();
|
||||||
const {entryPoints} = finder.findEntryPoints();
|
const {entryPoints} = finder.findEntryPoints();
|
||||||
expect(spy.calls.allArgs()).toEqual([
|
expect(spy.calls.allArgs()).toEqual([
|
||||||
[_Abs(basePath)],
|
[_Abs(basePath)],
|
||||||
@ -295,8 +297,8 @@ runInEachFileSystem(() => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const finder = new DirectoryWalkerEntryPointFinder(
|
const finder = new DirectoryWalkerEntryPointFinder(
|
||||||
fs, config, logger, resolver, manifest, basePath, undefined);
|
logger, resolver, collector, manifest, basePath, undefined);
|
||||||
const spy = spyOn(finder, 'walkDirectoryForPackages').and.callThrough();
|
const spy = spyOn(collector, 'walkDirectoryForPackages').and.callThrough();
|
||||||
const {entryPoints} = finder.findEntryPoints();
|
const {entryPoints} = finder.findEntryPoints();
|
||||||
expect(spy.calls.allArgs()).toEqual([
|
expect(spy.calls.allArgs()).toEqual([
|
||||||
[_Abs(basePath)],
|
[_Abs(basePath)],
|
||||||
@ -319,8 +321,8 @@ runInEachFileSystem(() => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const finder = new DirectoryWalkerEntryPointFinder(
|
const finder = new DirectoryWalkerEntryPointFinder(
|
||||||
fs, config, logger, resolver, manifest, basePath, undefined);
|
logger, resolver, collector, manifest, basePath, undefined);
|
||||||
const spy = spyOn(finder, 'walkDirectoryForPackages').and.callThrough();
|
const spy = spyOn(collector, 'walkDirectoryForPackages').and.callThrough();
|
||||||
const {entryPoints} = finder.findEntryPoints();
|
const {entryPoints} = finder.findEntryPoints();
|
||||||
expect(spy.calls.allArgs()).toEqual([
|
expect(spy.calls.allArgs()).toEqual([
|
||||||
[_Abs(basePath)],
|
[_Abs(basePath)],
|
||||||
@ -339,7 +341,7 @@ runInEachFileSystem(() => {
|
|||||||
...createPackage(fs.resolve(basePath, 'package/container'), 'entry-point-1'),
|
...createPackage(fs.resolve(basePath, 'package/container'), 'entry-point-1'),
|
||||||
]);
|
]);
|
||||||
const finder = new DirectoryWalkerEntryPointFinder(
|
const finder = new DirectoryWalkerEntryPointFinder(
|
||||||
fs, config, logger, resolver, manifest, basePath, undefined);
|
logger, resolver, collector, manifest, basePath, undefined);
|
||||||
const {entryPoints} = finder.findEntryPoints();
|
const {entryPoints} = finder.findEntryPoints();
|
||||||
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
||||||
['package', 'package'],
|
['package', 'package'],
|
||||||
@ -367,8 +369,9 @@ runInEachFileSystem(() => {
|
|||||||
const srcHost = new EsmDependencyHost(fs, new ModuleResolver(fs, pathMappings));
|
const srcHost = new EsmDependencyHost(fs, new ModuleResolver(fs, pathMappings));
|
||||||
const dtsHost = new DtsDependencyHost(fs, pathMappings);
|
const dtsHost = new DtsDependencyHost(fs, pathMappings);
|
||||||
resolver = new DependencyResolver(fs, logger, config, {esm2015: srcHost}, dtsHost);
|
resolver = new DependencyResolver(fs, logger, config, {esm2015: srcHost}, dtsHost);
|
||||||
|
collector = new EntryPointCollector(fs, config, logger, resolver);
|
||||||
const finder = new DirectoryWalkerEntryPointFinder(
|
const finder = new DirectoryWalkerEntryPointFinder(
|
||||||
fs, config, logger, resolver, manifest, basePath, pathMappings);
|
logger, resolver, collector, manifest, basePath, pathMappings);
|
||||||
const {entryPoints} = finder.findEntryPoints();
|
const {entryPoints} = finder.findEntryPoints();
|
||||||
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
||||||
['pkg1', 'pkg1'],
|
['pkg1', 'pkg1'],
|
||||||
@ -396,7 +399,7 @@ runInEachFileSystem(() => {
|
|||||||
const dtsHost = new DtsDependencyHost(fs, pathMappings);
|
const dtsHost = new DtsDependencyHost(fs, pathMappings);
|
||||||
resolver = new DependencyResolver(fs, logger, config, {esm2015: srcHost}, dtsHost);
|
resolver = new DependencyResolver(fs, logger, config, {esm2015: srcHost}, dtsHost);
|
||||||
const finder = new DirectoryWalkerEntryPointFinder(
|
const finder = new DirectoryWalkerEntryPointFinder(
|
||||||
fs, config, logger, resolver, manifest, basePath, pathMappings);
|
logger, resolver, collector, manifest, basePath, pathMappings);
|
||||||
const {entryPoints} = finder.findEntryPoints();
|
const {entryPoints} = finder.findEntryPoints();
|
||||||
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
||||||
['test', 'test'],
|
['test', 'test'],
|
||||||
|
@ -15,6 +15,7 @@ import {DependencyResolver} from '../../src/dependencies/dependency_resolver';
|
|||||||
import {DtsDependencyHost} from '../../src/dependencies/dts_dependency_host';
|
import {DtsDependencyHost} from '../../src/dependencies/dts_dependency_host';
|
||||||
import {EsmDependencyHost} from '../../src/dependencies/esm_dependency_host';
|
import {EsmDependencyHost} from '../../src/dependencies/esm_dependency_host';
|
||||||
import {ModuleResolver} from '../../src/dependencies/module_resolver';
|
import {ModuleResolver} from '../../src/dependencies/module_resolver';
|
||||||
|
import {EntryPointCollector} from '../../src/entry_point_finder/entry_point_collector';
|
||||||
import {ProgramBasedEntryPointFinder} from '../../src/entry_point_finder/program_based_entry_point_finder';
|
import {ProgramBasedEntryPointFinder} from '../../src/entry_point_finder/program_based_entry_point_finder';
|
||||||
import {NgccConfiguration} from '../../src/packages/configuration';
|
import {NgccConfiguration} from '../../src/packages/configuration';
|
||||||
import {EntryPoint} from '../../src/packages/entry_point';
|
import {EntryPoint} from '../../src/packages/entry_point';
|
||||||
@ -68,9 +69,10 @@ runInEachFileSystem(() => {
|
|||||||
const dtsHost = new DtsDependencyHost(fs);
|
const dtsHost = new DtsDependencyHost(fs);
|
||||||
const config = new NgccConfiguration(fs, projectPath);
|
const config = new NgccConfiguration(fs, projectPath);
|
||||||
const resolver = new DependencyResolver(fs, logger, config, {esm2015: srcHost}, dtsHost);
|
const resolver = new DependencyResolver(fs, logger, config, {esm2015: srcHost}, dtsHost);
|
||||||
|
const collector = new EntryPointCollector(fs, config, logger, resolver);
|
||||||
const manifest = new EntryPointManifest(fs, config, logger);
|
const manifest = new EntryPointManifest(fs, config, logger);
|
||||||
return new ProgramBasedEntryPointFinder(
|
return new ProgramBasedEntryPointFinder(
|
||||||
fs, config, logger, resolver, basePath, tsConfig, projectPath);
|
fs, config, logger, resolver, collector, manifest, basePath, tsConfig, projectPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createProgram(projectPath: AbsoluteFsPath): TestFile[] {
|
function createProgram(projectPath: AbsoluteFsPath): TestFile[] {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user