perf(ngcc): only compute basePaths in TargetedEntryPointFinder when needed (#36881)
Previously the `basePaths` were computed when the finder was instantiated. This was a waste of effort in the case that the targeted entry-point is already processed. This change makes the computation of `basePaths` lazy, so that the work is only done if they are actually needed. Fixes #36874 PR Close #36881
This commit is contained in:
parent
db4c59dad9
commit
ec6b9cc17d
|
@ -27,7 +27,13 @@ import {getBasePaths} from './utils';
|
||||||
export class TargetedEntryPointFinder implements EntryPointFinder {
|
export class TargetedEntryPointFinder implements EntryPointFinder {
|
||||||
private unprocessedPaths: AbsoluteFsPath[] = [];
|
private unprocessedPaths: AbsoluteFsPath[] = [];
|
||||||
private unsortedEntryPoints = new Map<AbsoluteFsPath, EntryPointWithDependencies>();
|
private unsortedEntryPoints = new Map<AbsoluteFsPath, EntryPointWithDependencies>();
|
||||||
private basePaths = getBasePaths(this.logger, this.basePath, this.pathMappings);
|
private basePaths: AbsoluteFsPath[]|null = null;
|
||||||
|
private getBasePaths() {
|
||||||
|
if (this.basePaths === null) {
|
||||||
|
this.basePaths = getBasePaths(this.logger, this.basePath, this.pathMappings);
|
||||||
|
}
|
||||||
|
return this.basePaths;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private fs: FileSystem, private config: NgccConfiguration, private logger: Logger,
|
private fs: FileSystem, private config: NgccConfiguration, private logger: Logger,
|
||||||
|
@ -102,22 +108,52 @@ export class TargetedEntryPointFinder implements EntryPointFinder {
|
||||||
return entryPoint;
|
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 each `basePath` for the first `package.json` that we
|
* Search down to the `entryPointPath` from the `containingPath` for the first `package.json` that
|
||||||
* come to. This is the path to the entry-point's containing package. For example if `basePath` is
|
* we come to. This is the path to the entry-point's containing package. For example if
|
||||||
* `/a/b/c` and `entryPointPath` is `/a/b/c/d/e` and there exists `/a/b/c/d/package.json` and
|
* `containingPath` is `/a/b/c` and `entryPointPath` is `/a/b/c/d/e` and there exists
|
||||||
* `/a/b/c/d/e/package.json`, then we will return `/a/b/c/d`.
|
* `/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
|
* To account for nested `node_modules` we actually start the search at the last `node_modules` in
|
||||||
* the `entryPointPath` that is below the `basePath`. E.g. if `basePath` is `/a/b/c` and
|
* the `entryPointPath` that is below the `containingPath`. E.g. if `containingPath` is `/a/b/c`
|
||||||
* `entryPointPath` is `/a/b/c/d/node_modules/x/y/z`, we start the search at
|
* and `entryPointPath` is `/a/b/c/d/node_modules/x/y/z`, we start the search at
|
||||||
* `/a/b/c/d/node_modules`.
|
* `/a/b/c/d/node_modules`.
|
||||||
*/
|
*/
|
||||||
private computePackagePath(entryPointPath: AbsoluteFsPath): AbsoluteFsPath {
|
private computePackagePathFromContainingPath(
|
||||||
for (const basePath of this.basePaths) {
|
entryPointPath: AbsoluteFsPath, containingPath: AbsoluteFsPath): AbsoluteFsPath|null {
|
||||||
if (entryPointPath.startsWith(basePath)) {
|
let packagePath = containingPath;
|
||||||
let packagePath = basePath;
|
const segments = this.splitPath(relative(containingPath, entryPointPath));
|
||||||
const segments = this.splitPath(relative(basePath, entryPointPath));
|
|
||||||
let nodeModulesIndex = segments.lastIndexOf(relativeFrom('node_modules'));
|
let nodeModulesIndex = segments.lastIndexOf(relativeFrom('node_modules'));
|
||||||
|
|
||||||
// If there are no `node_modules` in the relative path between the `basePath` and the
|
// If there are no `node_modules` in the relative path between the `basePath` and the
|
||||||
|
@ -145,16 +181,14 @@ export class TargetedEntryPointFinder implements EntryPointFinder {
|
||||||
return packagePath;
|
return packagePath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We couldn't find a `packagePath` using `basePaths` so try to find the nearest `node_modules`
|
/**
|
||||||
// that contains the `entryPointPath`, if there is one, and use it as a `basePath`.
|
* 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 packagePath = entryPointPath;
|
||||||
let scopedPackagePath = packagePath;
|
let scopedPackagePath = packagePath;
|
||||||
let containerPath = this.fs.dirname(packagePath);
|
let containerPath = this.fs.dirname(packagePath);
|
||||||
|
|
Loading…
Reference in New Issue