perf(ivy): ngcc - only find dependencies when targeting a single entry-point (#30525)
Previously, ngcc had to walk the entire `node_modules` tree looking for entry-points, even if it only needed to process a single target entry-point and its dependencies. This added up to a few seconds to each execution of ngcc, which is noticeable when being run via the CLI integration. Now, if an entry-point target is provided, only that target and its entry-points are considered rather than the whole folder tree. PR Close #30525
This commit is contained in:
parent
7f2330a968
commit
a581773887
|
@ -10,7 +10,7 @@ import {DepGraph} from 'dependency-graph';
|
|||
import {AbsoluteFsPath, FileSystem, resolve} from '../../../src/ngtsc/file_system';
|
||||
import {Logger} from '../logging/logger';
|
||||
import {EntryPoint, EntryPointFormat, EntryPointJsonProperty, getEntryPointFormat} from '../packages/entry_point';
|
||||
import {DependencyHost} from './dependency_host';
|
||||
import {DependencyHost, DependencyInfo} from './dependency_host';
|
||||
|
||||
/**
|
||||
* Holds information about entry points that are removed because
|
||||
|
@ -98,6 +98,16 @@ export class DependencyResolver {
|
|||
};
|
||||
}
|
||||
|
||||
getEntryPointDependencies(entryPoint: EntryPoint): DependencyInfo {
|
||||
const formatInfo = this.getEntryPointFormatInfo(entryPoint);
|
||||
const host = this.hosts[formatInfo.format];
|
||||
if (!host) {
|
||||
throw new Error(
|
||||
`Could not find a suitable format for computing dependencies of entry-point: '${entryPoint.path}'.`);
|
||||
}
|
||||
return host.findDependencies(formatInfo.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a dependency graph of the given entry-points.
|
||||
*
|
||||
|
@ -116,13 +126,7 @@ export class DependencyResolver {
|
|||
|
||||
// Now add the dependencies between them
|
||||
angularEntryPoints.forEach(entryPoint => {
|
||||
const formatInfo = this.getEntryPointFormatInfo(entryPoint);
|
||||
const host = this.hosts[formatInfo.format];
|
||||
if (!host) {
|
||||
throw new Error(
|
||||
`Could not find a suitable format for computing dependencies of entry-point: '${entryPoint.path}'.`);
|
||||
}
|
||||
const {dependencies, missing, deepImports} = host.findDependencies(formatInfo.path);
|
||||
const {dependencies, missing, deepImports} = this.getEntryPointDependencies(entryPoint);
|
||||
|
||||
if (missing.size > 0) {
|
||||
// This entry point has dependencies that are missing
|
||||
|
|
|
@ -8,61 +8,31 @@
|
|||
import {AbsoluteFsPath, FileSystem, join, resolve} from '../../../src/ngtsc/file_system';
|
||||
import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver';
|
||||
import {Logger} from '../logging/logger';
|
||||
import {NgccConfiguration} from '../packages/configuration';
|
||||
import {EntryPoint, getEntryPointInfo} from '../packages/entry_point';
|
||||
import {PathMappings} from '../utils';
|
||||
import {NgccConfiguration} from './configuration';
|
||||
import {EntryPoint, getEntryPointInfo} from './entry_point';
|
||||
import {EntryPointFinder} from './interface';
|
||||
import {getBasePaths} from './utils';
|
||||
|
||||
export class EntryPointFinder {
|
||||
/**
|
||||
* An EntryPointFinder that searches for all entry-points that can be found given a `basePath` and
|
||||
* `pathMappings`.
|
||||
*/
|
||||
export class DirectoryWalkerEntryPointFinder implements EntryPointFinder {
|
||||
private basePaths = getBasePaths(this.sourceDirectory, this.pathMappings);
|
||||
constructor(
|
||||
private fs: FileSystem, private config: NgccConfiguration, private logger: Logger,
|
||||
private resolver: DependencyResolver) {}
|
||||
private resolver: DependencyResolver, private sourceDirectory: AbsoluteFsPath,
|
||||
private pathMappings: PathMappings|undefined) {}
|
||||
/**
|
||||
* Search the given directory, and sub-directories, for Angular package entry points.
|
||||
* @param sourceDirectory An absolute path to the directory to search for entry points.
|
||||
* Search the `sourceDirectory`, and sub-directories, using `pathMappings` as necessary, to find
|
||||
* all package entry-points.
|
||||
*/
|
||||
findEntryPoints(
|
||||
sourceDirectory: AbsoluteFsPath, targetEntryPointPath?: AbsoluteFsPath,
|
||||
pathMappings?: PathMappings): SortedEntryPointsInfo {
|
||||
const basePaths = this.getBasePaths(sourceDirectory, pathMappings);
|
||||
const unsortedEntryPoints = basePaths.reduce<EntryPoint[]>(
|
||||
findEntryPoints(): SortedEntryPointsInfo {
|
||||
const unsortedEntryPoints = this.basePaths.reduce<EntryPoint[]>(
|
||||
(entryPoints, basePath) => entryPoints.concat(this.walkDirectoryForEntryPoints(basePath)),
|
||||
[]);
|
||||
const targetEntryPoint = targetEntryPointPath ?
|
||||
unsortedEntryPoints.find(entryPoint => entryPoint.path === targetEntryPointPath) :
|
||||
undefined;
|
||||
return this.resolver.sortEntryPointsByDependency(unsortedEntryPoints, targetEntryPoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract all the base-paths that we need to search for entry-points.
|
||||
*
|
||||
* This always contains the standard base-path (`sourceDirectory`).
|
||||
* But it also parses the `paths` mappings object to guess additional base-paths.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* ```
|
||||
* getBasePaths('/node_modules', {baseUrl: '/dist', paths: {'*': ['lib/*', 'lib/generated/*']}})
|
||||
* > ['/node_modules', '/dist/lib']
|
||||
* ```
|
||||
*
|
||||
* Notice that `'/dist'` is not included as there is no `'*'` path,
|
||||
* and `'/dist/lib/generated'` is not included as it is covered by `'/dist/lib'`.
|
||||
*
|
||||
* @param sourceDirectory The standard base-path (e.g. node_modules).
|
||||
* @param pathMappings Path mapping configuration, from which to extract additional base-paths.
|
||||
*/
|
||||
private getBasePaths(sourceDirectory: AbsoluteFsPath, pathMappings?: PathMappings):
|
||||
AbsoluteFsPath[] {
|
||||
const basePaths = [sourceDirectory];
|
||||
if (pathMappings) {
|
||||
const baseUrl = resolve(pathMappings.baseUrl);
|
||||
values(pathMappings.paths).forEach(paths => paths.forEach(path => {
|
||||
basePaths.push(join(baseUrl, extractPathPrefix(path)));
|
||||
}));
|
||||
}
|
||||
basePaths.sort(); // Get the paths in order with the shorter ones first.
|
||||
return basePaths.filter(removeDeeperPaths);
|
||||
return this.resolver.sortEntryPointsByDependency(unsortedEntryPoints);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -168,38 +138,6 @@ export class EntryPointFinder {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract everything in the `path` up to the first `*`.
|
||||
* @param path The path to parse.
|
||||
* @returns The extracted prefix.
|
||||
*/
|
||||
function extractPathPrefix(path: string) {
|
||||
return path.split('*', 1)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* A filter function that removes paths that are already covered by higher paths.
|
||||
*
|
||||
* @param value The current path.
|
||||
* @param index The index of the current path.
|
||||
* @param array The array of paths (sorted alphabetically).
|
||||
* @returns true if this path is not already covered by a previous path.
|
||||
*/
|
||||
function removeDeeperPaths(value: AbsoluteFsPath, index: number, array: AbsoluteFsPath[]) {
|
||||
for (let i = 0; i < index; i++) {
|
||||
if (value.startsWith(array[i])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract all the values (not keys) from an object.
|
||||
* @param obj The object to process.
|
||||
*/
|
||||
function values<T>(obj: {[key: string]: T}): T[] {
|
||||
return Object.keys(obj).map(key => obj[key]);
|
||||
}
|
||||
|
||||
function stripJsExtension<T extends string>(filePath: T): T {
|
||||
return filePath.replace(/\.js$/, '') as T;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {SortedEntryPointsInfo} from '../dependencies/dependency_resolver';
|
||||
|
||||
export interface EntryPointFinder {
|
||||
/**
|
||||
* Search for Angular package entry-points.
|
||||
*/
|
||||
findEntryPoints(): SortedEntryPointsInfo;
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {AbsoluteFsPath, FileSystem, PathSegment, join, relative, relativeFrom} from '../../../src/ngtsc/file_system';
|
||||
import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver';
|
||||
import {Logger} from '../logging/logger';
|
||||
import {NgccConfiguration} from '../packages/configuration';
|
||||
import {EntryPoint, getEntryPointInfo} from '../packages/entry_point';
|
||||
import {PathMappings} from '../utils';
|
||||
import {EntryPointFinder} from './interface';
|
||||
import {getBasePaths} from './utils';
|
||||
|
||||
/**
|
||||
* An EntryPointFinder that starts from a target entry-point and only finds
|
||||
* entry-points that are dependencies of the target.
|
||||
*
|
||||
* 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, EntryPoint>();
|
||||
private basePaths = getBasePaths(this.basePath, this.pathMappings);
|
||||
|
||||
constructor(
|
||||
private fs: FileSystem, private config: NgccConfiguration, private logger: Logger,
|
||||
private resolver: DependencyResolver, private basePath: AbsoluteFsPath,
|
||||
private targetPath: AbsoluteFsPath, private pathMappings: PathMappings|undefined) {}
|
||||
|
||||
findEntryPoints(): SortedEntryPointsInfo {
|
||||
this.unprocessedPaths = [this.targetPath];
|
||||
while (this.unprocessedPaths.length > 0) {
|
||||
this.processNextPath();
|
||||
}
|
||||
const targetEntryPoint = this.unsortedEntryPoints.get(this.targetPath);
|
||||
return this.resolver.sortEntryPointsByDependency(
|
||||
Array.from(this.unsortedEntryPoints.values()), targetEntryPoint);
|
||||
}
|
||||
|
||||
private processNextPath(): void {
|
||||
const path = this.unprocessedPaths.shift() !;
|
||||
const entryPoint = this.getEntryPoint(path);
|
||||
if (entryPoint !== null) {
|
||||
this.unsortedEntryPoints.set(entryPoint.path, entryPoint);
|
||||
const deps = this.resolver.getEntryPointDependencies(entryPoint);
|
||||
deps.dependencies.forEach(dep => {
|
||||
if (!this.unsortedEntryPoints.has(dep)) {
|
||||
this.unprocessedPaths.push(dep);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private getEntryPoint(entryPointPath: AbsoluteFsPath): EntryPoint|null {
|
||||
const packagePath = this.computePackagePath(entryPointPath);
|
||||
return getEntryPointInfo(this.fs, this.config, this.logger, packagePath, entryPointPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search down to the `entryPointPath` from each `basePath` for the first `package.json` that we
|
||||
* come to. This is the path to the entry-point's containing package. For example if `basePath` 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 `basePath`. E.g. if `basePath` 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 computePackagePath(entryPointPath: AbsoluteFsPath): AbsoluteFsPath {
|
||||
for (const basePath of this.basePaths) {
|
||||
if (entryPointPath.startsWith(basePath)) {
|
||||
let packagePath = basePath;
|
||||
const segments = this.splitPath(relative(basePath, entryPointPath));
|
||||
|
||||
// Start the search at the last nested `node_modules` folder if the relative
|
||||
// `entryPointPath` contains one or more.
|
||||
let nodeModulesIndex = segments.lastIndexOf(relativeFrom('node_modules'));
|
||||
while (nodeModulesIndex >= 0) {
|
||||
packagePath = join(packagePath, segments.shift() !);
|
||||
nodeModulesIndex--;
|
||||
}
|
||||
|
||||
// Note that we skip the first `packagePath` and start looking from the first folder below
|
||||
// it because that will be the `node_modules` folder.
|
||||
for (const segment of segments) {
|
||||
packagePath = join(packagePath, segment);
|
||||
if (this.fs.exists(join(packagePath, 'package.json'))) {
|
||||
return packagePath;
|
||||
}
|
||||
}
|
||||
|
||||
// If we got here then we couldn't find a `packagePath` for the current `basePath` but since
|
||||
// `basePath`s are guaranteed not to be a sub-directory each other then no other `basePath`
|
||||
// will match either.
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If we get here then none of the `basePaths` matched the `entryPointPath`, which
|
||||
// is somewhat unexpected and means that this entry-point lives completely outside
|
||||
// any of the `basePaths`.
|
||||
// All we can do is assume that his 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {AbsoluteFsPath, join, resolve} from '../../../src/ngtsc/file_system';
|
||||
import {PathMappings} from '../utils';
|
||||
|
||||
/**
|
||||
* Extract all the base-paths that we need to search for entry-points.
|
||||
*
|
||||
* This always contains the standard base-path (`sourceDirectory`).
|
||||
* But it also parses the `paths` mappings object to guess additional base-paths.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* ```
|
||||
* getBasePaths('/node_modules', {baseUrl: '/dist', paths: {'*': ['lib/*', 'lib/generated/*']}})
|
||||
* > ['/node_modules', '/dist/lib']
|
||||
* ```
|
||||
*
|
||||
* Notice that `'/dist'` is not included as there is no `'*'` path,
|
||||
* and `'/dist/lib/generated'` is not included as it is covered by `'/dist/lib'`.
|
||||
*
|
||||
* @param sourceDirectory The standard base-path (e.g. node_modules).
|
||||
* @param pathMappings Path mapping configuration, from which to extract additional base-paths.
|
||||
*/
|
||||
export function getBasePaths(
|
||||
sourceDirectory: AbsoluteFsPath, pathMappings: PathMappings | undefined): AbsoluteFsPath[] {
|
||||
const basePaths = [sourceDirectory];
|
||||
if (pathMappings) {
|
||||
const baseUrl = resolve(pathMappings.baseUrl);
|
||||
Object.values(pathMappings.paths).forEach(paths => paths.forEach(path => {
|
||||
basePaths.push(join(baseUrl, extractPathPrefix(path)));
|
||||
}));
|
||||
}
|
||||
basePaths.sort(); // Get the paths in order with the shorter ones first.
|
||||
return basePaths.filter(removeDeeperPaths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract everything in the `path` up to the first `*`.
|
||||
* @param path The path to parse.
|
||||
* @returns The extracted prefix.
|
||||
*/
|
||||
function extractPathPrefix(path: string) {
|
||||
return path.split('*', 1)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* A filter function that removes paths that are already covered by higher paths.
|
||||
*
|
||||
* @param value The current path.
|
||||
* @param index The index of the current path.
|
||||
* @param array The array of paths (sorted alphabetically).
|
||||
* @returns true if this path is not already covered by a previous path.
|
||||
*/
|
||||
function removeDeeperPaths(value: AbsoluteFsPath, index: number, array: AbsoluteFsPath[]) {
|
||||
for (let i = 0; i < index; i++) {
|
||||
if (value.startsWith(array[i])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -7,17 +7,18 @@
|
|||
*/
|
||||
import {AbsoluteFsPath, FileSystem, absoluteFrom, dirname, getFileSystem, resolve} from '../../src/ngtsc/file_system';
|
||||
import {CommonJsDependencyHost} from './dependencies/commonjs_dependency_host';
|
||||
import {DependencyResolver} from './dependencies/dependency_resolver';
|
||||
import {DependencyResolver, InvalidEntryPoint, SortedEntryPointsInfo} from './dependencies/dependency_resolver';
|
||||
import {EsmDependencyHost} from './dependencies/esm_dependency_host';
|
||||
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 {TargetedEntryPointFinder} from './entry_point_finder/targeted_entry_point_finder';
|
||||
import {ConsoleLogger, LogLevel} from './logging/console_logger';
|
||||
import {Logger} from './logging/logger';
|
||||
import {hasBeenProcessed, markAsProcessed} from './packages/build_marker';
|
||||
import {NgccConfiguration} from './packages/configuration';
|
||||
import {EntryPointFormat, EntryPointJsonProperty, SUPPORTED_FORMAT_PROPERTIES, getEntryPointFormat} from './packages/entry_point';
|
||||
import {EntryPoint, EntryPointFormat, EntryPointJsonProperty, SUPPORTED_FORMAT_PROPERTIES, getEntryPointFormat} from './packages/entry_point';
|
||||
import {makeEntryPointBundle} from './packages/entry_point_bundle';
|
||||
import {EntryPointFinder} from './packages/entry_point_finder';
|
||||
import {Transformer} from './packages/transformer';
|
||||
import {PathMappings} from './utils';
|
||||
import {FileWriter} from './writing/file_writer';
|
||||
|
@ -93,36 +94,12 @@ export function mainNgcc(
|
|||
umd: umdDependencyHost,
|
||||
commonjs: commonJsDependencyHost
|
||||
});
|
||||
const config = new NgccConfiguration(fileSystem, dirname(absoluteFrom(basePath)));
|
||||
const finder = new EntryPointFinder(fileSystem, config, logger, resolver);
|
||||
const absBasePath = absoluteFrom(basePath);
|
||||
const config = new NgccConfiguration(fileSystem, dirname(absBasePath));
|
||||
const fileWriter = getFileWriter(fileSystem, createNewEntryPointFormats);
|
||||
|
||||
const absoluteTargetEntryPointPath =
|
||||
targetEntryPointPath ? resolve(basePath, targetEntryPointPath) : undefined;
|
||||
|
||||
if (absoluteTargetEntryPointPath &&
|
||||
hasProcessedTargetEntryPoint(
|
||||
fileSystem, absoluteTargetEntryPointPath, propertiesToConsider, compileAllFormats)) {
|
||||
logger.debug('The target entry-point has already been processed');
|
||||
return;
|
||||
}
|
||||
|
||||
const {entryPoints, invalidEntryPoints} =
|
||||
finder.findEntryPoints(absoluteFrom(basePath), absoluteTargetEntryPointPath, pathMappings);
|
||||
|
||||
invalidEntryPoints.forEach(invalidEntryPoint => {
|
||||
logger.debug(
|
||||
`Invalid entry-point ${invalidEntryPoint.entryPoint.path}.`,
|
||||
`It is missing required dependencies:\n` +
|
||||
invalidEntryPoint.missingDependencies.map(dep => ` - ${dep}`).join('\n'));
|
||||
});
|
||||
|
||||
if (absoluteTargetEntryPointPath && entryPoints.length === 0) {
|
||||
markNonAngularPackageAsProcessed(
|
||||
fileSystem, absoluteTargetEntryPointPath, propertiesToConsider);
|
||||
return;
|
||||
}
|
||||
|
||||
const entryPoints = getEntryPoints(
|
||||
fileSystem, config, logger, resolver, absBasePath, targetEntryPointPath, pathMappings,
|
||||
propertiesToConsider, compileAllFormats);
|
||||
for (const entryPoint of entryPoints) {
|
||||
// Are we compiling the Angular core?
|
||||
const isCore = entryPoint.name === '@angular/core';
|
||||
|
@ -188,6 +165,47 @@ function getFileWriter(fs: FileSystem, createNewEntryPointFormats: boolean): Fil
|
|||
return createNewEntryPointFormats ? new NewEntryPointFileWriter(fs) : new InPlaceFileWriter(fs);
|
||||
}
|
||||
|
||||
function getEntryPoints(
|
||||
fs: FileSystem, config: NgccConfiguration, logger: Logger, resolver: DependencyResolver,
|
||||
basePath: AbsoluteFsPath, targetEntryPointPath: string | undefined,
|
||||
pathMappings: PathMappings | undefined, propertiesToConsider: string[],
|
||||
compileAllFormats: boolean): EntryPoint[] {
|
||||
const {entryPoints, invalidEntryPoints} = (targetEntryPointPath !== undefined) ?
|
||||
getTargetedEntryPoints(
|
||||
fs, config, logger, resolver, basePath, targetEntryPointPath, propertiesToConsider,
|
||||
compileAllFormats, pathMappings) :
|
||||
getAllEntryPoints(fs, config, logger, resolver, basePath, pathMappings);
|
||||
logInvalidEntryPoints(logger, invalidEntryPoints);
|
||||
return entryPoints;
|
||||
}
|
||||
|
||||
function getTargetedEntryPoints(
|
||||
fs: FileSystem, config: NgccConfiguration, logger: Logger, resolver: DependencyResolver,
|
||||
basePath: AbsoluteFsPath, targetEntryPointPath: string, propertiesToConsider: string[],
|
||||
compileAllFormats: boolean, pathMappings: PathMappings | undefined): SortedEntryPointsInfo {
|
||||
const absoluteTargetEntryPointPath = resolve(basePath, targetEntryPointPath);
|
||||
if (hasProcessedTargetEntryPoint(
|
||||
fs, absoluteTargetEntryPointPath, propertiesToConsider, compileAllFormats)) {
|
||||
logger.debug('The target entry-point has already been processed');
|
||||
return {entryPoints: [], invalidEntryPoints: [], ignoredDependencies: []};
|
||||
}
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, basePath, absoluteTargetEntryPointPath, pathMappings);
|
||||
const entryPointInfo = finder.findEntryPoints();
|
||||
if (entryPointInfo.entryPoints.length === 0) {
|
||||
markNonAngularPackageAsProcessed(fs, absoluteTargetEntryPointPath, propertiesToConsider);
|
||||
}
|
||||
return entryPointInfo;
|
||||
}
|
||||
|
||||
function getAllEntryPoints(
|
||||
fs: FileSystem, config: NgccConfiguration, logger: Logger, resolver: DependencyResolver,
|
||||
basePath: AbsoluteFsPath, pathMappings: PathMappings | undefined): SortedEntryPointsInfo {
|
||||
const finder =
|
||||
new DirectoryWalkerEntryPointFinder(fs, config, logger, resolver, basePath, pathMappings);
|
||||
return finder.findEntryPoints();
|
||||
}
|
||||
|
||||
function hasProcessedTargetEntryPoint(
|
||||
fs: FileSystem, targetPath: AbsoluteFsPath, propertiesToConsider: string[],
|
||||
compileAllFormats: boolean) {
|
||||
|
@ -233,3 +251,12 @@ function markNonAngularPackageAsProcessed(
|
|||
markAsProcessed(fs, packageJson, packageJsonPath, formatProperty as EntryPointJsonProperty);
|
||||
});
|
||||
}
|
||||
|
||||
function logInvalidEntryPoints(logger: Logger, invalidEntryPoints: InvalidEntryPoint[]): void {
|
||||
invalidEntryPoints.forEach(invalidEntryPoint => {
|
||||
logger.debug(
|
||||
`Invalid entry-point ${invalidEntryPoint.entryPoint.path}.`,
|
||||
`It is missing required dependencies:\n` +
|
||||
invalidEntryPoint.missingDependencies.map(dep => ` - ${dep}`).join('\n'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,196 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {AbsoluteFsPath, FileSystem, absoluteFrom, getFileSystem, relative} from '../../../src/ngtsc/file_system';
|
||||
import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
|
||||
import {loadTestFiles} from '../../../test/helpers';
|
||||
import {DependencyResolver} from '../../src/dependencies/dependency_resolver';
|
||||
import {EsmDependencyHost} from '../../src/dependencies/esm_dependency_host';
|
||||
import {ModuleResolver} from '../../src/dependencies/module_resolver';
|
||||
import {DirectoryWalkerEntryPointFinder} from '../../src/entry_point_finder/directory_walker_entry_point_finder';
|
||||
import {NgccConfiguration} from '../../src/packages/configuration';
|
||||
import {EntryPoint} from '../../src/packages/entry_point';
|
||||
import {PathMappings} from '../../src/utils';
|
||||
import {MockLogger} from '../helpers/mock_logger';
|
||||
|
||||
runInEachFileSystem(() => {
|
||||
describe('DirectoryWalkerEntryPointFinder', () => {
|
||||
let fs: FileSystem;
|
||||
let resolver: DependencyResolver;
|
||||
let logger: MockLogger;
|
||||
let config: NgccConfiguration;
|
||||
let _Abs: typeof absoluteFrom;
|
||||
|
||||
beforeEach(() => {
|
||||
fs = getFileSystem();
|
||||
_Abs = absoluteFrom;
|
||||
logger = new MockLogger();
|
||||
resolver = new DependencyResolver(
|
||||
fs, logger, {esm2015: new EsmDependencyHost(fs, new ModuleResolver(fs))});
|
||||
config = new NgccConfiguration(fs, _Abs('/'));
|
||||
});
|
||||
|
||||
describe('findEntryPoints()', () => {
|
||||
it('should find sub-entry-points within a package', () => {
|
||||
const basePath = _Abs('/sub_entry_points/node_modules');
|
||||
loadTestFiles([
|
||||
...createPackage(basePath, 'common'),
|
||||
...createPackage(fs.resolve(basePath, 'common'), 'http', ['common']),
|
||||
...createPackage(
|
||||
fs.resolve(basePath, 'common/http'), 'testing', ['common/http', 'common/testing']),
|
||||
...createPackage(fs.resolve(basePath, 'common'), 'testing', ['common']),
|
||||
]);
|
||||
const finder =
|
||||
new DirectoryWalkerEntryPointFinder(fs, config, logger, resolver, basePath, undefined);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
||||
['common', 'common'],
|
||||
['common', 'common/http'],
|
||||
['common', 'common/testing'],
|
||||
['common', 'common/http/testing'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should find packages inside a namespace', () => {
|
||||
const basePath = _Abs('/namespaced/node_modules');
|
||||
loadTestFiles([
|
||||
...createPackage(fs.resolve(basePath, '@angular'), 'common'),
|
||||
...createPackage(fs.resolve(basePath, '@angular/common'), 'http', ['@angular/common']),
|
||||
...createPackage(
|
||||
fs.resolve(basePath, '@angular/common/http'), 'testing',
|
||||
['@angular/common/http', '@angular/common/testing']),
|
||||
...createPackage(fs.resolve(basePath, '@angular/common'), 'testing', ['@angular/common']),
|
||||
]);
|
||||
const finder =
|
||||
new DirectoryWalkerEntryPointFinder(fs, config, logger, resolver, basePath, undefined);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
||||
['@angular/common', '@angular/common'],
|
||||
['@angular/common', '@angular/common/http'],
|
||||
['@angular/common', '@angular/common/testing'],
|
||||
['@angular/common', '@angular/common/http/testing'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return an empty array if there are no packages', () => {
|
||||
fs.ensureDir(_Abs('/no_packages/node_modules/should_not_be_found'));
|
||||
const finder = new DirectoryWalkerEntryPointFinder(
|
||||
fs, config, logger, resolver, _Abs('/no_packages/node_modules'), undefined);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return an empty array if there are no valid entry-points', () => {
|
||||
loadTestFiles([
|
||||
{
|
||||
name: _Abs('/no_valid_entry_points/node_modules/some_package/package.json'),
|
||||
contents: '{}'
|
||||
},
|
||||
]);
|
||||
const finder = new DirectoryWalkerEntryPointFinder(
|
||||
fs, config, logger, resolver, _Abs('/no_valid_entry_points/node_modules'), undefined);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should ignore folders starting with .', () => {
|
||||
loadTestFiles([
|
||||
...createPackage(_Abs('/dotted_folders/node_modules/'), '.common'),
|
||||
]);
|
||||
const finder = new DirectoryWalkerEntryPointFinder(
|
||||
fs, config, logger, resolver, _Abs('/dotted_folders/node_modules'), undefined);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should ignore folders that are symlinked', () => {
|
||||
fs.ensureDir(_Abs('/symlinked_folders/node_modules'));
|
||||
fs.symlink(
|
||||
_Abs('/external/node_modules/common'), _Abs('/symlinked_folders/node_modules/common'));
|
||||
loadTestFiles(createPackage(_Abs('/external/node_modules'), 'common'));
|
||||
const finder = new DirectoryWalkerEntryPointFinder(
|
||||
fs, config, logger, resolver, _Abs('/symlinked_folders/node_modules'), undefined);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle nested node_modules folders', () => {
|
||||
loadTestFiles([
|
||||
...createPackage(_Abs('/nested_node_modules/node_modules'), 'outer', ['inner']),
|
||||
...createPackage(_Abs('/nested_node_modules/node_modules/outer/node_modules'), 'inner'),
|
||||
]);
|
||||
const finder = new DirectoryWalkerEntryPointFinder(
|
||||
fs, config, logger, resolver, _Abs('/nested_node_modules/node_modules'), undefined);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
// Note that the `inner` entry-point is not part of the `outer` package
|
||||
expect(dumpEntryPointPaths(_Abs('/nested_node_modules/node_modules'), entryPoints))
|
||||
.toEqual([
|
||||
['outer/node_modules/inner', 'outer/node_modules/inner'],
|
||||
['outer', 'outer'],
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
it('should handle dependencies via pathMappings', () => {
|
||||
const basePath = _Abs('/path_mapped/node_modules');
|
||||
const pathMappings: PathMappings = {
|
||||
baseUrl: '/path_mapped/dist',
|
||||
paths: {
|
||||
'@x/*': ['*'],
|
||||
'@y/*/test': ['lib/*/test'],
|
||||
}
|
||||
};
|
||||
loadTestFiles([
|
||||
...createPackage(
|
||||
_Abs('/path_mapped/node_modules'), 'test', ['pkg1', '@x/pkg2', '@y/pkg3/test']),
|
||||
...createPackage(_Abs('/path_mapped/node_modules'), 'pkg1'),
|
||||
...createPackage(_Abs('/path_mapped/dist'), 'pkg2', ['pkg4']),
|
||||
...createPackage(_Abs('/path_mapped/dist/pkg2/node_modules'), 'pkg4'),
|
||||
...createPackage(_Abs('/path_mapped/dist/lib/pkg3'), 'test'),
|
||||
]);
|
||||
resolver = new DependencyResolver(
|
||||
fs, logger, {esm2015: new EsmDependencyHost(fs, new ModuleResolver(fs, pathMappings))});
|
||||
const finder = new DirectoryWalkerEntryPointFinder(
|
||||
fs, config, logger, resolver, basePath, pathMappings);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
||||
['pkg1', 'pkg1'],
|
||||
['../dist/pkg2/node_modules/pkg4', '../dist/pkg2/node_modules/pkg4'],
|
||||
['../dist/pkg2', '../dist/pkg2'],
|
||||
['../dist/lib/pkg3/test', '../dist/lib/pkg3/test'],
|
||||
['test', 'test'],
|
||||
]);
|
||||
});
|
||||
|
||||
function createPackage(
|
||||
basePath: AbsoluteFsPath, packageName: string, deps: string[] = []): TestFile[] {
|
||||
return [
|
||||
{
|
||||
name: _Abs(`${basePath}/${packageName}/package.json`),
|
||||
contents: JSON.stringify({
|
||||
typings: `./${packageName}.d.ts`,
|
||||
fesm2015: `./fesm2015/${packageName}.js`,
|
||||
})
|
||||
},
|
||||
{
|
||||
name: _Abs(`${basePath}/${packageName}/${packageName}.metadata.json`),
|
||||
contents: 'metadata info'
|
||||
},
|
||||
{
|
||||
name: _Abs(`${basePath}/${packageName}/fesm2015/${packageName}.js`),
|
||||
contents: deps.map((dep, i) => `import * as i${i} from '${dep}';`).join('\n'),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function dumpEntryPointPaths(
|
||||
basePath: AbsoluteFsPath, entryPoints: EntryPoint[]): [string, string][] {
|
||||
return entryPoints.map(x => [relative(basePath, x.package), relative(basePath, x.path)]);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,201 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {AbsoluteFsPath, FileSystem, absoluteFrom, getFileSystem, relative} from '../../../src/ngtsc/file_system';
|
||||
import {TestFile, runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
|
||||
import {loadTestFiles} from '../../../test/helpers';
|
||||
import {DependencyResolver} from '../../src/dependencies/dependency_resolver';
|
||||
import {EsmDependencyHost} from '../../src/dependencies/esm_dependency_host';
|
||||
import {ModuleResolver} from '../../src/dependencies/module_resolver';
|
||||
import {TargetedEntryPointFinder} from '../../src/entry_point_finder/targeted_entry_point_finder';
|
||||
import {NgccConfiguration} from '../../src/packages/configuration';
|
||||
import {EntryPoint} from '../../src/packages/entry_point';
|
||||
import {PathMappings} from '../../src/utils';
|
||||
import {MockLogger} from '../helpers/mock_logger';
|
||||
|
||||
runInEachFileSystem(() => {
|
||||
describe('TargetedEntryPointFinder', () => {
|
||||
let fs: FileSystem;
|
||||
let resolver: DependencyResolver;
|
||||
let logger: MockLogger;
|
||||
let config: NgccConfiguration;
|
||||
let _Abs: typeof absoluteFrom;
|
||||
|
||||
beforeEach(() => {
|
||||
fs = getFileSystem();
|
||||
_Abs = absoluteFrom;
|
||||
logger = new MockLogger();
|
||||
resolver = new DependencyResolver(
|
||||
fs, logger, {esm2015: new EsmDependencyHost(fs, new ModuleResolver(fs))});
|
||||
config = new NgccConfiguration(fs, _Abs('/'));
|
||||
});
|
||||
|
||||
describe('findEntryPoints()', () => {
|
||||
it('should find a single entry-point with no dependencies', () => {
|
||||
const basePath = _Abs('/sub_entry_points/node_modules');
|
||||
const targetPath = _Abs('/sub_entry_points/node_modules/common');
|
||||
loadTestFiles([
|
||||
...createPackage(fs.resolve(basePath, ''), 'common'),
|
||||
...createPackage(fs.resolve(basePath, 'common'), 'http', ['@angular/common']),
|
||||
...createPackage(
|
||||
fs.resolve(basePath, 'common/http'), 'testing',
|
||||
['@angular/common/http', '@angular/common/testing']),
|
||||
...createPackage(fs.resolve(basePath, 'common'), 'testing', ['@angular/common']),
|
||||
]);
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, _Abs('/sub_entry_points/node_modules'), targetPath,
|
||||
undefined);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
||||
['common', 'common'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should find dependencies of secondary entry-points within a package', () => {
|
||||
const basePath = _Abs('/sub_entry_points/node_modules');
|
||||
const targetPath = _Abs('/sub_entry_points/node_modules/common/http/testing');
|
||||
loadTestFiles([
|
||||
...createPackage(fs.resolve(basePath, ''), 'common'),
|
||||
...createPackage(fs.resolve(basePath, 'common'), 'http', ['common']),
|
||||
...createPackage(
|
||||
fs.resolve(basePath, 'common/http'), 'testing', ['common/http', 'common/testing']),
|
||||
...createPackage(fs.resolve(basePath, 'common'), 'testing', ['common']),
|
||||
]);
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, _Abs('/sub_entry_points/node_modules'), targetPath,
|
||||
undefined);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
||||
['common', 'common'],
|
||||
['common', 'common/http'],
|
||||
['common', 'common/testing'],
|
||||
['common', 'common/http/testing'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should find dependencies inside a namespace', () => {
|
||||
const basePath = _Abs('/namespaced/node_modules');
|
||||
const targetPath = _Abs('/namespaced/node_modules/@angular/common/http');
|
||||
loadTestFiles([
|
||||
...createPackage(fs.resolve(basePath, '@angular'), 'common'),
|
||||
...createPackage(fs.resolve(basePath, '@angular/common'), 'http', ['@angular/common']),
|
||||
...createPackage(
|
||||
fs.resolve(basePath, '@angular/common/http'), 'testing',
|
||||
['@angular/common/http', '@angular/common/testing']),
|
||||
...createPackage(fs.resolve(basePath, '@angular/common'), 'testing', ['@angular/common']),
|
||||
]);
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, _Abs('/namespaced/node_modules'), targetPath, undefined);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
||||
['@angular/common', '@angular/common'],
|
||||
['@angular/common', '@angular/common/http'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return an empty array if the target path is not an entry-point', () => {
|
||||
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);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return an empty array if the target path is not an Angular entry-point', () => {
|
||||
const targetPath = _Abs('/no_valid_entry_points/node_modules/some_package');
|
||||
loadTestFiles([
|
||||
{
|
||||
name: _Abs('/no_valid_entry_points/node_modules/some_package/package.json'),
|
||||
contents: '{}'
|
||||
},
|
||||
]);
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, _Abs('/no_valid_entry_points/node_modules'), targetPath,
|
||||
undefined);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle nested node_modules folders', () => {
|
||||
const targetPath = _Abs('/nested_node_modules/node_modules/outer');
|
||||
loadTestFiles([
|
||||
...createPackage(_Abs('/nested_node_modules/node_modules'), 'outer', ['inner']),
|
||||
...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);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(dumpEntryPointPaths(_Abs('/nested_node_modules/node_modules'), entryPoints))
|
||||
.toEqual([
|
||||
['outer/node_modules/inner', 'outer/node_modules/inner'],
|
||||
['outer', 'outer'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle dependencies via pathMappings', () => {
|
||||
const basePath = _Abs('/path_mapped/node_modules');
|
||||
const targetPath = _Abs('/path_mapped/node_modules/test');
|
||||
const pathMappings: PathMappings = {
|
||||
baseUrl: '/path_mapped/dist',
|
||||
paths: {
|
||||
'@x/*': ['*'],
|
||||
'@y/*/test': ['lib/*/test'],
|
||||
}
|
||||
};
|
||||
loadTestFiles([
|
||||
...createPackage(
|
||||
_Abs('/path_mapped/node_modules'), 'test', ['pkg1', '@x/pkg2', '@y/pkg3/test']),
|
||||
...createPackage(_Abs('/path_mapped/node_modules'), 'pkg1'),
|
||||
...createPackage(_Abs('/path_mapped/dist'), 'pkg2', ['pkg4']),
|
||||
...createPackage(_Abs('/path_mapped/dist/pkg2/node_modules'), 'pkg4'),
|
||||
...createPackage(_Abs('/path_mapped/dist/lib/pkg3'), 'test'),
|
||||
]);
|
||||
resolver = new DependencyResolver(
|
||||
fs, logger, {esm2015: new EsmDependencyHost(fs, new ModuleResolver(fs, pathMappings))});
|
||||
const finder = new TargetedEntryPointFinder(
|
||||
fs, config, logger, resolver, basePath, targetPath, pathMappings);
|
||||
const {entryPoints} = finder.findEntryPoints();
|
||||
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
|
||||
['pkg1', 'pkg1'],
|
||||
['../dist/pkg2/node_modules/pkg4', '../dist/pkg2/node_modules/pkg4'],
|
||||
['../dist/pkg2', '../dist/pkg2'],
|
||||
['../dist/lib/pkg3/test', '../dist/lib/pkg3/test'],
|
||||
['test', 'test'],
|
||||
]);
|
||||
});
|
||||
|
||||
function createPackage(
|
||||
basePath: AbsoluteFsPath, packageName: string, deps: string[] = []): TestFile[] {
|
||||
return [
|
||||
{
|
||||
name: _Abs(`${basePath}/${packageName}/package.json`),
|
||||
contents: JSON.stringify({
|
||||
typings: `./${packageName}.d.ts`,
|
||||
fesm2015: `./fesm2015/${packageName}.js`,
|
||||
})
|
||||
},
|
||||
{
|
||||
name: _Abs(`${basePath}/${packageName}/${packageName}.metadata.json`),
|
||||
contents: 'metadata info'
|
||||
},
|
||||
{
|
||||
name: _Abs(`${basePath}/${packageName}/fesm2015/${packageName}.js`),
|
||||
contents: deps.map((dep, i) => `import * as i${i} from '${dep}';`).join('\n'),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function dumpEntryPointPaths(
|
||||
basePath: AbsoluteFsPath, entryPoints: EntryPoint[]): [string, string][] {
|
||||
return entryPoints.map(x => [relative(basePath, x.package), relative(basePath, x.path)]);
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,212 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {absoluteFrom, getFileSystem} from '../../../src/ngtsc/file_system';
|
||||
import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
|
||||
import {loadTestFiles} from '../../../test/helpers';
|
||||
import {DependencyResolver} from '../../src/dependencies/dependency_resolver';
|
||||
import {EsmDependencyHost} from '../../src/dependencies/esm_dependency_host';
|
||||
import {ModuleResolver} from '../../src/dependencies/module_resolver';
|
||||
import {NgccConfiguration} from '../../src/packages/configuration';
|
||||
import {EntryPoint} from '../../src/packages/entry_point';
|
||||
import {EntryPointFinder} from '../../src/packages/entry_point_finder';
|
||||
import {MockLogger} from '../helpers/mock_logger';
|
||||
|
||||
runInEachFileSystem(() => {
|
||||
|
||||
describe('findEntryPoints()', () => {
|
||||
let resolver: DependencyResolver;
|
||||
let finder: EntryPointFinder;
|
||||
let _: typeof absoluteFrom;
|
||||
|
||||
beforeEach(() => {
|
||||
const fs = getFileSystem();
|
||||
_ = absoluteFrom;
|
||||
setupMockFileSystem();
|
||||
resolver = new DependencyResolver(
|
||||
fs, new MockLogger(), {esm2015: new EsmDependencyHost(fs, new ModuleResolver(fs))});
|
||||
spyOn(resolver, 'sortEntryPointsByDependency').and.callFake((entryPoints: EntryPoint[]) => {
|
||||
return {entryPoints, ignoredEntryPoints: [], ignoredDependencies: []};
|
||||
});
|
||||
finder =
|
||||
new EntryPointFinder(fs, new NgccConfiguration(fs, _('/')), new MockLogger(), resolver);
|
||||
});
|
||||
|
||||
it('should find sub-entry-points within a package', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/sub_entry_points'));
|
||||
const entryPointPaths = entryPoints.map(x => [x.package, x.path]);
|
||||
expect(entryPointPaths).toEqual([
|
||||
[_('/sub_entry_points/common'), _('/sub_entry_points/common')],
|
||||
[_('/sub_entry_points/common'), _('/sub_entry_points/common/http')],
|
||||
[_('/sub_entry_points/common'), _('/sub_entry_points/common/http/testing')],
|
||||
[_('/sub_entry_points/common'), _('/sub_entry_points/common/testing')],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should find packages inside a namespace', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/namespaced'));
|
||||
const entryPointPaths = entryPoints.map(x => [x.package, x.path]);
|
||||
expect(entryPointPaths).toEqual([
|
||||
[_('/namespaced/@angular/common'), _('/namespaced/@angular/common')],
|
||||
[_('/namespaced/@angular/common'), _('/namespaced/@angular/common/http')],
|
||||
[_('/namespaced/@angular/common'), _('/namespaced/@angular/common/http/testing')],
|
||||
[_('/namespaced/@angular/common'), _('/namespaced/@angular/common/testing')],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should find entry-points via `pathMappings', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(
|
||||
_('/pathMappings/node_modules'), undefined,
|
||||
{baseUrl: _('/pathMappings'), paths: {'my-lib': ['dist/my-lib']}});
|
||||
const entryPointPaths = entryPoints.map(x => [x.package, x.path]);
|
||||
expect(entryPointPaths).toEqual([
|
||||
[_('/pathMappings/dist/my-lib'), _('/pathMappings/dist/my-lib')],
|
||||
[_('/pathMappings/dist/my-lib'), _('/pathMappings/dist/my-lib/sub-lib')],
|
||||
[
|
||||
_('/pathMappings/node_modules/@angular/common'),
|
||||
_('/pathMappings/node_modules/@angular/common')
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return an empty array if there are no packages', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/no_packages'));
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return an empty array if there are no valid entry-points', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/no_valid_entry_points'));
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should ignore folders starting with .', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/dotted_folders'));
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should ignore folders that are symlinked', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/symlinked_folders'));
|
||||
expect(entryPoints).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle nested node_modules folders', () => {
|
||||
const {entryPoints} = finder.findEntryPoints(_('/nested_node_modules'));
|
||||
const entryPointPaths = entryPoints.map(x => [x.package, x.path]);
|
||||
expect(entryPointPaths).toEqual([
|
||||
[_('/nested_node_modules/outer'), _('/nested_node_modules/outer')],
|
||||
// Note that the inner entry point does not get included as part of the outer package
|
||||
[
|
||||
_('/nested_node_modules/outer/node_modules/inner'),
|
||||
_('/nested_node_modules/outer/node_modules/inner'),
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
function setupMockFileSystem(): void {
|
||||
loadTestFiles([
|
||||
{name: _('/sub_entry_points/common/package.json'), contents: createPackageJson('common')},
|
||||
{name: _('/sub_entry_points/common/common.metadata.json'), contents: 'metadata info'},
|
||||
{
|
||||
name: _('/sub_entry_points/common/http/package.json'),
|
||||
contents: createPackageJson('http')
|
||||
},
|
||||
{name: _('/sub_entry_points/common/http/http.metadata.json'), contents: 'metadata info'},
|
||||
{
|
||||
name: _('/sub_entry_points/common/http/testing/package.json'),
|
||||
contents: createPackageJson('testing')
|
||||
},
|
||||
{
|
||||
name: _('/sub_entry_points/common/http/testing/testing.metadata.json'),
|
||||
contents: 'metadata info'
|
||||
},
|
||||
{
|
||||
name: _('/sub_entry_points/common/testing/package.json'),
|
||||
contents: createPackageJson('testing')
|
||||
},
|
||||
{
|
||||
name: _('/sub_entry_points/common/testing/testing.metadata.json'),
|
||||
contents: 'metadata info'
|
||||
},
|
||||
{name: _('/pathMappings/dist/my-lib/package.json'), contents: createPackageJson('my-lib')},
|
||||
{name: _('/pathMappings/dist/my-lib/my-lib.metadata.json'), contents: 'metadata info'},
|
||||
{
|
||||
name: _('/pathMappings/dist/my-lib/sub-lib/package.json'),
|
||||
contents: createPackageJson('sub-lib')
|
||||
},
|
||||
{
|
||||
name: _('/pathMappings/dist/my-lib/sub-lib/sub-lib.metadata.json'),
|
||||
contents: 'metadata info'
|
||||
},
|
||||
{
|
||||
name: _('/pathMappings/node_modules/@angular/common/package.json'),
|
||||
contents: createPackageJson('common')
|
||||
},
|
||||
{
|
||||
name: _('/pathMappings/node_modules/@angular/common/common.metadata.json'),
|
||||
contents: 'metadata info'
|
||||
},
|
||||
{
|
||||
name: _('/namespaced/@angular/common/package.json'),
|
||||
contents: createPackageJson('common')
|
||||
},
|
||||
{name: _('/namespaced/@angular/common/common.metadata.json'), contents: 'metadata info'},
|
||||
{
|
||||
name: _('/namespaced/@angular/common/http/package.json'),
|
||||
contents: createPackageJson('http')
|
||||
},
|
||||
{name: _('/namespaced/@angular/common/http/http.metadata.json'), contents: 'metadata info'},
|
||||
{
|
||||
name: _('/namespaced/@angular/common/http/testing/package.json'),
|
||||
contents: createPackageJson('testing')
|
||||
},
|
||||
{
|
||||
name: _('/namespaced/@angular/common/http/testing/testing.metadata.json'),
|
||||
contents: 'metadata info'
|
||||
},
|
||||
{
|
||||
name: _('/namespaced/@angular/common/testing/package.json'),
|
||||
contents: createPackageJson('testing')
|
||||
},
|
||||
{
|
||||
name: _('/namespaced/@angular/common/testing/testing.metadata.json'),
|
||||
contents: 'metadata info'
|
||||
},
|
||||
{name: _('/no_valid_entry_points/some_package/package.json'), contents: '{}'},
|
||||
{name: _('/dotted_folders/.common/package.json'), contents: createPackageJson('common')},
|
||||
{name: _('/dotted_folders/.common/common.metadata.json'), contents: 'metadata info'},
|
||||
{name: _('/nested_node_modules/outer/package.json'), contents: createPackageJson('outer')},
|
||||
{name: _('/nested_node_modules/outer/outer.metadata.json'), contents: 'metadata info'},
|
||||
{
|
||||
name: _('/nested_node_modules/outer/node_modules/inner/package.json'),
|
||||
contents: createPackageJson('inner')
|
||||
},
|
||||
{
|
||||
name: _('/nested_node_modules/outer/node_modules/inner/inner.metadata.json'),
|
||||
contents: 'metadata info'
|
||||
},
|
||||
]);
|
||||
const fs = getFileSystem();
|
||||
|
||||
fs.ensureDir(_('/no_packages/should_not_be_found'));
|
||||
|
||||
fs.ensureDir(_('/symlinked_folders'));
|
||||
fs.symlink(_('/sub_entry_points/common'), _('/symlinked_folders/common'));
|
||||
}
|
||||
});
|
||||
|
||||
function createPackageJson(packageName: string): string {
|
||||
const packageJson: any = {
|
||||
typings: `./${packageName}.d.ts`,
|
||||
fesm2015: `./fesm2015/${packageName}.js`,
|
||||
esm2015: `./esm2015/${packageName}.js`,
|
||||
fesm5: `./fesm2015/${packageName}.js`,
|
||||
esm5: `./esm2015/${packageName}.js`,
|
||||
main: `./bundles/${packageName}.umd.js`,
|
||||
};
|
||||
return JSON.stringify(packageJson);
|
||||
}
|
||||
});
|
|
@ -6,7 +6,8 @@
|
|||
"stripInternal": false,
|
||||
"target": "es2015",
|
||||
"lib": [
|
||||
"es2015"
|
||||
"es2015",
|
||||
"es2017.object",
|
||||
],
|
||||
"baseUrl": ".",
|
||||
"rootDir": ".",
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
"compilerOptions": {
|
||||
"target": "es2015",
|
||||
"lib": [
|
||||
"es2015"
|
||||
"es2015",
|
||||
"es2017.object",
|
||||
],
|
||||
"strict": true,
|
||||
"types": [
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
},
|
||||
"rootDir": ".",
|
||||
"inlineSourceMap": true,
|
||||
"lib": ["es5", "dom", "es2015.promise", "es2015.collection", "es2015.iterable", "es2015.core"],
|
||||
"lib": ["es5", "dom", "es2015.promise", "es2015.collection", "es2015.iterable", "es2015.core", "es2017.object"],
|
||||
"skipDefaultLibCheck": true,
|
||||
"skipLibCheck": true,
|
||||
"target": "es5",
|
||||
|
|
Loading…
Reference in New Issue