fix(ngcc): correctly get config for sub-entry-points when primary entry-point is ignored (#37040)

Previously, when an entry-point was ignored via an ngcc config, ngcc
would scan sub-directories for sub-entry-points, but would not use the
correct `packagePath`. For example, if `@angular/common` was ignored, it
would look at `@angular/common/http` but incorrectly use
`.../@angular/common/http` as the `packagePath` (instead of
`.../@angular/common`). As a result, it would not retrieve the correct
ngcc config for the actual package.

This commit fixes it by ensuring the correct `packagePath` is used, even
if the primary entry-point corresponding to that path is ignored. In
order to do this, a new return value for `getEntryPointInfo()` is added:
`IGNORED_ENTRY_POINT`. This is used to differentiate between directories
that correspond to no or an incompatible entry-point and those that
correspond to an entry-point that could otherwise be valid but is
explicitly ignored. Consumers of `getEntryPointInfo()` can then use this
info to discard ignored entry-points, but still use the correct
`packagePath` when scanning their sub-directories for secondary
entry-points.

PR Close #37040
This commit is contained in:
George Kalpakas
2020-06-08 22:04:25 +03:00
committed by Misko Hevery
parent a32579ae5b
commit bf57776b59
11 changed files with 193 additions and 49 deletions

View File

@ -10,7 +10,7 @@ import {EntryPointWithDependencies} from '../dependencies/dependency_host';
import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver';
import {Logger} from '../logging/logger';
import {NgccConfiguration} from '../packages/configuration';
import {getEntryPointInfo, INCOMPATIBLE_ENTRY_POINT, NO_ENTRY_POINT} from '../packages/entry_point';
import {getEntryPointInfo, IGNORED_ENTRY_POINT, INCOMPATIBLE_ENTRY_POINT, isEntryPoint, NO_ENTRY_POINT} from '../packages/entry_point';
import {EntryPointManifest} from '../packages/entry_point_manifest';
import {PathMappings} from '../path_mappings';
import {NGCC_DIRECTORY} from '../writing/new_entry_point_file_writer';
@ -79,7 +79,9 @@ export class DirectoryWalkerEntryPointFinder implements EntryPointFinder {
const entryPoints: EntryPointWithDependencies[] = [];
if (primaryEntryPoint !== NO_ENTRY_POINT) {
entryPoints.push(this.resolver.getEntryPointWithDependencies(primaryEntryPoint));
if (primaryEntryPoint !== IGNORED_ENTRY_POINT) {
entryPoints.push(this.resolver.getEntryPointWithDependencies(primaryEntryPoint));
}
this.collectSecondaryEntryPoints(
entryPoints, sourceDirectory, sourceDirectory, this.fs.readdir(sourceDirectory));
@ -148,14 +150,12 @@ export class DirectoryWalkerEntryPointFinder implements EntryPointFinder {
}
// If the path is a JS file then strip its extension and see if we can match an
// entry-point.
// entry-point (even if it is an ignored one).
const possibleEntryPointPath = isDirectory ? absolutePath : stripJsExtension(absolutePath);
let isEntryPoint = false;
const subEntryPoint =
getEntryPointInfo(this.fs, this.config, this.logger, packagePath, possibleEntryPointPath);
if (subEntryPoint !== NO_ENTRY_POINT && subEntryPoint !== INCOMPATIBLE_ENTRY_POINT) {
if (isEntryPoint(subEntryPoint)) {
entryPoints.push(this.resolver.getEntryPointWithDependencies(subEntryPoint));
isEntryPoint = true;
}
if (!isDirectory) {
@ -163,9 +163,11 @@ export class DirectoryWalkerEntryPointFinder implements EntryPointFinder {
continue;
}
// This directory may contain entry-points of its own.
// 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 (!isEntryPoint &&
if (canContainEntryPoints &&
childPaths.some(
childPath => childPath.endsWith('.js') &&
this.fs.stat(this.fs.resolve(absolutePath, childPath)).isFile())) {

View File

@ -11,7 +11,7 @@ import {EntryPointWithDependencies} from '../dependencies/dependency_host';
import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver';
import {Logger} from '../logging/logger';
import {NgccConfiguration} from '../packages/configuration';
import {EntryPoint, getEntryPointInfo, INCOMPATIBLE_ENTRY_POINT, NO_ENTRY_POINT} from '../packages/entry_point';
import {EntryPoint, getEntryPointInfo, isEntryPoint} from '../packages/entry_point';
import {PathMappings} from '../path_mappings';
import {EntryPointFinder} from './interface';
@ -61,10 +61,8 @@ export abstract class TracingEntryPointFinder implements EntryPointFinder {
const packagePath = this.computePackagePath(entryPointPath);
const entryPoint =
getEntryPointInfo(this.fs, this.config, this.logger, packagePath, entryPointPath);
if (entryPoint === NO_ENTRY_POINT || entryPoint === INCOMPATIBLE_ENTRY_POINT) {
return null;
}
return entryPoint;
return isEntryPoint(entryPoint) ? entryPoint : null;
}
private processNextPath(): void {

View File

@ -80,12 +80,16 @@ export const SUPPORTED_FORMAT_PROPERTIES: EntryPointJsonProperty[] =
/**
* The path does not represent an entry-point:
* * there is no package.json at the path and there is no config to force an entry-point
* * or the entrypoint is `ignored` by a config.
* The path does not represent an entry-point, i.e. there is no package.json at the path and there
* is no config to force an entry-point.
*/
export const NO_ENTRY_POINT = 'no-entry-point';
/**
* The path represents an entry-point that is `ignored` by an ngcc config.
*/
export const IGNORED_ENTRY_POINT = 'ignored-entry-point';
/**
* The path has a package.json, but it is not a valid entry-point for ngcc processing.
*/
@ -100,7 +104,8 @@ export const INCOMPATIBLE_ENTRY_POINT = 'incompatible-entry-point';
* * INCOMPATIBLE_ENTRY_POINT - the path was a non-processable entry-point that should be searched
* for sub-entry-points
*/
export type GetEntryPointResult = EntryPoint|typeof INCOMPATIBLE_ENTRY_POINT|typeof NO_ENTRY_POINT;
export type GetEntryPointResult =
EntryPoint|typeof IGNORED_ENTRY_POINT|typeof INCOMPATIBLE_ENTRY_POINT|typeof NO_ENTRY_POINT;
/**
@ -109,11 +114,12 @@ export type GetEntryPointResult = EntryPoint|typeof INCOMPATIBLE_ENTRY_POINT|typ
* @param packagePath the absolute path to the containing npm package
* @param entryPointPath the absolute path to the potential entry-point.
* @returns
* - An entry-point if it is valid.
* - An entry-point if it is valid and not ignored.
* - `NO_ENTRY_POINT` when there is no package.json at the path and there is no config to force an
* entry-point or the entrypoint is `ignored`.
* - `INCOMPATIBLE_ENTRY_POINT` there is a package.json but it is not a valid Angular compiled
* entry-point.
* entry-point,
* - `IGNORED_ENTRY_POINT` when the entry-point is ignored by an ngcc config.
* - `INCOMPATIBLE_ENTRY_POINT` when there is a package.json but it is not a valid Angular compiled
* entry-point.
*/
export function getEntryPointInfo(
fs: FileSystem, config: NgccConfiguration, logger: Logger, packagePath: AbsoluteFsPath,
@ -131,7 +137,7 @@ export function getEntryPointInfo(
if (hasConfig && entryPointConfig.ignore === true) {
// Explicitly ignored
return NO_ENTRY_POINT;
return IGNORED_ENTRY_POINT;
}
const loadedEntryPointPackageJson = loadEntryPointPackage(fs, logger, packageJsonPath, hasConfig);
@ -174,6 +180,11 @@ export function getEntryPointInfo(
return entryPointInfo;
}
export function isEntryPoint(result: GetEntryPointResult): result is EntryPoint {
return result !== NO_ENTRY_POINT && result !== INCOMPATIBLE_ENTRY_POINT &&
result !== IGNORED_ENTRY_POINT;
}
/**
* Convert a package.json property into an entry-point format.
*

View File

@ -13,7 +13,7 @@ import {Logger} from '../logging/logger';
import {NGCC_VERSION} from './build_marker';
import {NgccConfiguration} from './configuration';
import {getEntryPointInfo, INCOMPATIBLE_ENTRY_POINT, NO_ENTRY_POINT} from './entry_point';
import {getEntryPointInfo, isEntryPoint} from './entry_point';
/**
* Manages reading and writing a manifest file that contains a list of all the entry-points that
@ -75,7 +75,7 @@ export class EntryPointManifest {
const result = getEntryPointInfo(
this.fs, this.config, this.logger, this.fs.resolve(basePath, packagePath),
this.fs.resolve(basePath, entryPointPath));
if (result === NO_ENTRY_POINT || result === INCOMPATIBLE_ENTRY_POINT) {
if (!isEntryPoint(result)) {
throw new Error(`The entry-point manifest at ${
manifestPath} contained an invalid pair of package paths: [${packagePath}, ${
entryPointPath}]`);