fix(ngcc): do not lock if the target is not compiled by Angular (#35057)

To support parallel CLI builds we instruct developers to pre-process
their node_modules via ngcc at the command line.

Despite doing this ngcc was still trying to set a lock when it was being
triggered by the CLI for packages that are not going to be processed,
since they are not compiled by Angular for instance.

This commit checks whether a target package needs to be compiled
at all before attempting to set the lock.

Fixes #35000

PR Close #35057
This commit is contained in:
Pete Bacon Darwin 2020-01-30 14:29:15 +00:00 committed by Miško Hevery
parent a3c7ab99b7
commit 3d4067a464
4 changed files with 359 additions and 133 deletions

View File

@ -8,8 +8,9 @@
import {AbsoluteFsPath, FileSystem, PathSegment, join, relative, relativeFrom} from '../../../src/ngtsc/file_system'; import {AbsoluteFsPath, FileSystem, PathSegment, join, relative, relativeFrom} from '../../../src/ngtsc/file_system';
import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver'; import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver';
import {Logger} from '../logging/logger'; import {Logger} from '../logging/logger';
import {hasBeenProcessed} from '../packages/build_marker';
import {NgccConfiguration} from '../packages/configuration'; import {NgccConfiguration} from '../packages/configuration';
import {EntryPoint, getEntryPointInfo} from '../packages/entry_point'; import {EntryPoint, EntryPointJsonProperty, getEntryPointInfo} from '../packages/entry_point';
import {PathMappings} from '../utils'; import {PathMappings} from '../utils';
import {EntryPointFinder} from './interface'; import {EntryPointFinder} from './interface';
import {getBasePaths} from './utils'; import {getBasePaths} from './utils';
@ -37,8 +38,41 @@ export class TargetedEntryPointFinder implements EntryPointFinder {
this.processNextPath(); this.processNextPath();
} }
const targetEntryPoint = this.unsortedEntryPoints.get(this.targetPath); const targetEntryPoint = this.unsortedEntryPoints.get(this.targetPath);
return this.resolver.sortEntryPointsByDependency( const entryPoints = this.resolver.sortEntryPointsByDependency(
Array.from(this.unsortedEntryPoints.values()), targetEntryPoint); Array.from(this.unsortedEntryPoints.values()), targetEntryPoint);
const invalidTarget =
entryPoints.invalidEntryPoints.find(i => i.entryPoint.path === this.targetPath);
if (invalidTarget !== undefined) {
throw new Error(
`The target entry-point "${invalidTarget.entryPoint.name}" has missing dependencies:\n` +
invalidTarget.missingDependencies.map(dep => ` - ${dep}\n`).join(''));
}
return entryPoints;
}
targetNeedsProcessingOrCleaning(
propertiesToConsider: EntryPointJsonProperty[], compileAllFormats: boolean): boolean {
const entryPoint = this.getEntryPoint(this.targetPath);
if (entryPoint === null || !entryPoint.compiledByAngular) {
return false;
}
for (const property of propertiesToConsider) {
if (entryPoint.packageJson[property]) {
// Here is a property that should be processed.
if (!hasBeenProcessed(entryPoint.packageJson, property)) {
return true;
}
if (!compileAllFormats) {
// This property has been processed, and we only need one.
return false;
}
}
}
// All `propertiesToConsider` that appear in this entry-point have been processed.
// In other words, there were no properties that need processing.
return false;
} }
private processNextPath(): void { private processNextPath(): void {

View File

@ -16,12 +16,13 @@ import {replaceTsWithNgInErrors} from '../../src/ngtsc/diagnostics';
import {AbsoluteFsPath, FileSystem, absoluteFrom, dirname, getFileSystem, resolve} from '../../src/ngtsc/file_system'; import {AbsoluteFsPath, FileSystem, absoluteFrom, dirname, getFileSystem, resolve} from '../../src/ngtsc/file_system';
import {CommonJsDependencyHost} from './dependencies/commonjs_dependency_host'; import {CommonJsDependencyHost} from './dependencies/commonjs_dependency_host';
import {DependencyResolver, InvalidEntryPoint, PartiallyOrderedEntryPoints, SortedEntryPointsInfo} from './dependencies/dependency_resolver'; import {DependencyResolver, InvalidEntryPoint} from './dependencies/dependency_resolver';
import {DtsDependencyHost} from './dependencies/dts_dependency_host'; import {DtsDependencyHost} from './dependencies/dts_dependency_host';
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 {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 {EntryPointFinder} from './entry_point_finder/interface';
import {TargetedEntryPointFinder} from './entry_point_finder/targeted_entry_point_finder'; import {TargetedEntryPointFinder} from './entry_point_finder/targeted_entry_point_finder';
import {AnalyzeEntryPointsFn, CreateCompileFn, Executor, PartiallyOrderedTasks, Task, TaskProcessingOutcome, TaskQueue} from './execution/api'; import {AnalyzeEntryPointsFn, CreateCompileFn, Executor, PartiallyOrderedTasks, Task, TaskProcessingOutcome, TaskQueue} from './execution/api';
import {ClusterExecutor} from './execution/cluster/executor'; import {ClusterExecutor} from './execution/cluster/executor';
@ -147,16 +148,19 @@ export function mainNgcc(
// NOTE: Avoid eagerly instantiating anything that might not be used when running sync/async or in // NOTE: Avoid eagerly instantiating anything that might not be used when running sync/async or in
// master/worker process. // master/worker process.
const fileSystem = getFileSystem(); const fileSystem = getFileSystem();
const absBasePath = absoluteFrom(basePath);
const config = new NgccConfiguration(fileSystem, dirname(absBasePath));
const dependencyResolver = getDependencyResolver(fileSystem, logger, pathMappings);
// Bail out early if the work is already done. // Bail out early if the work is already done.
const supportedPropertiesToConsider = ensureSupportedProperties(propertiesToConsider); const supportedPropertiesToConsider = ensureSupportedProperties(propertiesToConsider);
const absoluteTargetEntryPointPath = const absoluteTargetEntryPointPath =
targetEntryPointPath !== undefined ? resolve(basePath, targetEntryPointPath) : undefined; targetEntryPointPath !== undefined ? resolve(basePath, targetEntryPointPath) : null;
if (absoluteTargetEntryPointPath !== undefined && const finder = getEntryPointFinder(
hasProcessedTargetEntryPoint( fileSystem, logger, dependencyResolver, config, absBasePath, absoluteTargetEntryPointPath,
fileSystem, absoluteTargetEntryPointPath, supportedPropertiesToConsider, pathMappings);
compileAllFormats)) { if (finder instanceof TargetedEntryPointFinder &&
!finder.targetNeedsProcessingOrCleaning(supportedPropertiesToConsider, compileAllFormats)) {
logger.debug('The target entry-point has already been processed'); logger.debug('The target entry-point has already been processed');
return; return;
} }
@ -172,34 +176,15 @@ export function mainNgcc(
logger.debug('Analyzing entry-points...'); logger.debug('Analyzing entry-points...');
const startTime = Date.now(); const startTime = Date.now();
const moduleResolver = new ModuleResolver(fileSystem, pathMappings); let entryPointInfo = finder.findEntryPoints();
const esmDependencyHost = new EsmDependencyHost(fileSystem, moduleResolver);
const umdDependencyHost = new UmdDependencyHost(fileSystem, moduleResolver);
const commonJsDependencyHost = new CommonJsDependencyHost(fileSystem, moduleResolver);
const dtsDependencyHost = new DtsDependencyHost(fileSystem, pathMappings);
const dependencyResolver = new DependencyResolver(
fileSystem, logger, {
esm5: esmDependencyHost,
esm2015: esmDependencyHost,
umd: umdDependencyHost,
commonjs: commonJsDependencyHost
},
dtsDependencyHost);
const absBasePath = absoluteFrom(basePath);
const config = new NgccConfiguration(fileSystem, dirname(absBasePath));
let entryPointInfo = getEntryPoints(
fileSystem, pkgJsonUpdater, logger, dependencyResolver, config, absBasePath,
absoluteTargetEntryPointPath, pathMappings);
const cleaned = cleanOutdatedPackages(fileSystem, entryPointInfo.entryPoints); const cleaned = cleanOutdatedPackages(fileSystem, entryPointInfo.entryPoints);
if (cleaned) { if (cleaned) {
// If we had to clean up one or more packages then we must read in the entry-points again. // If we had to clean up one or more packages then we must read in the entry-points again.
entryPointInfo = getEntryPoints( entryPointInfo = finder.findEntryPoints();
fileSystem, pkgJsonUpdater, logger, dependencyResolver, config, absBasePath,
absoluteTargetEntryPointPath, pathMappings);
} }
const {entryPoints, graph} = entryPointInfo;
const {entryPoints, invalidEntryPoints, graph} = entryPointInfo;
logInvalidEntryPoints(logger, invalidEntryPoints);
const unprocessableEntryPointPaths: string[] = []; const unprocessableEntryPointPaths: string[] = [];
// The tasks are partially ordered by virtue of the entry-points being partially ordered too. // The tasks are partially ordered by virtue of the entry-points being partially ordered too.
@ -364,78 +349,36 @@ function getExecutor(
} }
} }
function getEntryPoints( function getDependencyResolver(
fs: FileSystem, pkgJsonUpdater: PackageJsonUpdater, logger: Logger, fileSystem: FileSystem, logger: Logger,
resolver: DependencyResolver, config: NgccConfiguration, basePath: AbsoluteFsPath, pathMappings: PathMappings | undefined): DependencyResolver {
targetEntryPointPath: AbsoluteFsPath | undefined, pathMappings: PathMappings | const moduleResolver = new ModuleResolver(fileSystem, pathMappings);
undefined): {entryPoints: PartiallyOrderedEntryPoints, graph: DepGraph<EntryPoint>} { const esmDependencyHost = new EsmDependencyHost(fileSystem, moduleResolver);
const {entryPoints, invalidEntryPoints, graph} = (targetEntryPointPath !== undefined) ? const umdDependencyHost = new UmdDependencyHost(fileSystem, moduleResolver);
getTargetedEntryPoints( const commonJsDependencyHost = new CommonJsDependencyHost(fileSystem, moduleResolver);
fs, pkgJsonUpdater, logger, resolver, config, basePath, targetEntryPointPath, const dtsDependencyHost = new DtsDependencyHost(fileSystem, pathMappings);
pathMappings) : return new DependencyResolver(
getAllEntryPoints(fs, config, logger, resolver, basePath, pathMappings); fileSystem, logger, {
logInvalidEntryPoints(logger, invalidEntryPoints); esm5: esmDependencyHost,
return {entryPoints, graph}; esm2015: esmDependencyHost,
umd: umdDependencyHost,
commonjs: commonJsDependencyHost
},
dtsDependencyHost);
} }
function getTargetedEntryPoints( function getEntryPointFinder(
fs: FileSystem, pkgJsonUpdater: PackageJsonUpdater, logger: Logger, fs: FileSystem, logger: Logger, resolver: DependencyResolver, config: NgccConfiguration,
resolver: DependencyResolver, config: NgccConfiguration, basePath: AbsoluteFsPath, basePath: AbsoluteFsPath, absoluteTargetEntryPointPath: AbsoluteFsPath | null,
absoluteTargetEntryPointPath: AbsoluteFsPath, pathMappings: PathMappings | undefined): EntryPointFinder {
pathMappings: PathMappings | undefined): SortedEntryPointsInfo { if (absoluteTargetEntryPointPath !== null) {
const finder = new TargetedEntryPointFinder( return new TargetedEntryPointFinder(
fs, config, logger, resolver, basePath, absoluteTargetEntryPointPath, pathMappings); fs, config, logger, resolver, basePath, absoluteTargetEntryPointPath, pathMappings);
const entryPointInfo = finder.findEntryPoints();
const invalidTarget = entryPointInfo.invalidEntryPoints.find(
i => i.entryPoint.path === absoluteTargetEntryPointPath);
if (invalidTarget !== undefined) {
throw new Error(
`The target entry-point "${invalidTarget.entryPoint.name}" has missing dependencies:\n` +
invalidTarget.missingDependencies.map(dep => ` - ${dep}\n`).join(''));
}
if (entryPointInfo.entryPoints.length === 0) {
markNonAngularPackageAsProcessed(fs, pkgJsonUpdater, absoluteTargetEntryPointPath);
}
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) {
const packageJsonPath = resolve(targetPath, 'package.json');
// It might be that this target is configured in which case its package.json might not exist.
if (!fs.exists(packageJsonPath)) {
return false;
}
const packageJson = JSON.parse(fs.readFile(packageJsonPath));
for (const property of propertiesToConsider) {
if (packageJson[property]) {
// Here is a property that should be processed
if (hasBeenProcessed(packageJson, property as EntryPointJsonProperty)) {
if (!compileAllFormats) {
// It has been processed and we only need one, so we are done.
return true;
}
} else { } else {
// It has not been processed but we need all of them, so we are done. return new DirectoryWalkerEntryPointFinder(
return false; fs, config, logger, resolver, basePath, pathMappings);
} }
} }
}
// Either all formats need to be compiled and there were none that were unprocessed,
// Or only the one matching format needs to be compiled but there was at least one matching
// property before the first processed format that was unprocessed.
return true;
}
/** /**
* If we get here, then the requested entry-point did not contain anything compiled by * If we get here, then the requested entry-point did not contain anything compiled by

View File

@ -13,6 +13,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 {TargetedEntryPointFinder} from '../../src/entry_point_finder/targeted_entry_point_finder'; import {TargetedEntryPointFinder} from '../../src/entry_point_finder/targeted_entry_point_finder';
import {NGCC_VERSION} from '../../src/packages/build_marker';
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';
import {PathMappings} from '../../src/utils'; import {PathMappings} from '../../src/utils';
@ -227,6 +228,223 @@ runInEachFileSystem(() => {
]); ]);
}); });
function dumpEntryPointPaths(
basePath: AbsoluteFsPath, entryPoints: EntryPoint[]): [string, string][] {
return entryPoints.map(x => [relative(basePath, x.package), relative(basePath, x.path)]);
}
});
describe('targetNeedsProcessingOrCleaning()', () => {
it('should return false if there is no entry-point', () => {
const targetPath = _Abs('/no_packages/node_modules/should_not_be_found');
fs.ensureDir(targetPath);
const finder = new TargetedEntryPointFinder(
fs, config, logger, resolver, _Abs('/no_packages/node_modules'), targetPath, undefined);
expect(finder.targetNeedsProcessingOrCleaning(['fesm2015'], true)).toBe(false);
});
it('should return false if the target path is not a valid 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);
expect(finder.targetNeedsProcessingOrCleaning(['fesm2015'], true)).toBe(false);
});
it('should false if the target path has no typings', () => {
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: '{"fesm2015": "./index.js"}'
},
{
name:
_Abs('/no_valid_entry_points/node_modules/some_package/some_package.metadata.json'),
contents: 'metadata info'
},
{
name: _Abs('/no_valid_entry_points/node_modules/some_package/index.js'),
contents: 'export class MyClass {}'
},
]);
const finder = new TargetedEntryPointFinder(
fs, config, logger, resolver, _Abs('/no_valid_entry_points/node_modules'), targetPath,
undefined);
expect(finder.targetNeedsProcessingOrCleaning(['fesm2015'], true)).toBe(false);
});
it('should false if the target path is not compiled by Angular - i.e has no metadata file',
() => {
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: '{"typings": "./index.d.ts", "fesm2015": "./index.js"}'
},
{
name: _Abs('/no_valid_entry_points/node_modules/some_package/index.d.ts'),
contents: 'export declare class MyClass {}'
},
{
name: _Abs('/no_valid_entry_points/node_modules/some_package/index.js'),
contents: 'export class MyClass {}'
},
]);
const finder = new TargetedEntryPointFinder(
fs, config, logger, resolver, _Abs('/no_valid_entry_points/node_modules'),
targetPath, undefined);
expect(finder.targetNeedsProcessingOrCleaning(['fesm2015'], true)).toBe(false);
});
describe('[compileAllFormats: true]', () => {
it('should return true if none of the properties to consider have been processed', () => {
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, basePath, targetPath, undefined);
expect(finder.targetNeedsProcessingOrCleaning(['fesm2015', 'esm5'], true)).toBe(true);
});
it('should return true if at least one of the properties to consider has not been processed',
() => {
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']),
]);
// Add a build marker to the package.json
const packageJsonPath = _Abs(`${targetPath}/package.json`);
const packageJson = JSON.parse(fs.readFile(packageJsonPath));
packageJson.__processed_by_ivy_ngcc__ = {
esm5: NGCC_VERSION,
};
fs.writeFile(packageJsonPath, JSON.stringify(packageJson));
const finder = new TargetedEntryPointFinder(
fs, config, logger, resolver, basePath, targetPath, undefined);
expect(finder.targetNeedsProcessingOrCleaning(['fesm2015', 'esm5'], true)).toBe(true);
});
it('should return false if all of the properties to consider have been processed', () => {
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']),
]);
// Add build markers to the package.json
const packageJsonPath = _Abs(`${targetPath}/package.json`);
const packageJson = JSON.parse(fs.readFile(packageJsonPath));
packageJson.__processed_by_ivy_ngcc__ = {
fesm2015: NGCC_VERSION,
esm5: NGCC_VERSION,
main: NGCC_VERSION,
};
fs.writeFile(packageJsonPath, JSON.stringify(packageJson));
const finder = new TargetedEntryPointFinder(
fs, config, logger, resolver, basePath, targetPath, undefined);
expect(finder.targetNeedsProcessingOrCleaning(['fesm2015', 'esm5'], true)).toBe(false);
});
});
describe('[compileAllFormats: false]', () => {
it('should return true if none of the properties to consider have been processed', () => {
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, basePath, targetPath, undefined);
expect(finder.targetNeedsProcessingOrCleaning(['fesm2015', 'esm5'], false)).toBe(true);
});
it('should return true if the first of the properties to consider that is in the package.json has not been processed',
() => {
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']),
]);
// Add build markers to the package.json
const packageJsonPath = _Abs(`${targetPath}/package.json`);
const packageJson = JSON.parse(fs.readFile(packageJsonPath));
packageJson.__processed_by_ivy_ngcc__ = {
esm5: NGCC_VERSION,
};
fs.writeFile(packageJsonPath, JSON.stringify(packageJson));
const finder = new TargetedEntryPointFinder(
fs, config, logger, resolver, basePath, targetPath, undefined);
expect(finder.targetNeedsProcessingOrCleaning(['fesm2015', 'esm5'], false)).toBe(true);
});
it('should return false if the first of the properties to consider (that actually appear in the package.json) has been processed',
() => {
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']),
]);
// Add build markers to the package.json
const packageJsonPath = _Abs(`${targetPath}/package.json`);
const packageJson = JSON.parse(fs.readFile(packageJsonPath));
packageJson.__processed_by_ivy_ngcc__ = {
fesm2015: NGCC_VERSION,
};
fs.writeFile(packageJsonPath, JSON.stringify(packageJson));
const finder = new TargetedEntryPointFinder(
fs, config, logger, resolver, basePath, targetPath, undefined);
expect(finder.targetNeedsProcessingOrCleaning(['fesm2015', 'esm5'], false))
.toBe(false);
});
});
});
function createPackage( function createPackage(
basePath: AbsoluteFsPath, packageName: string, deps: string[] = []): TestFile[] { basePath: AbsoluteFsPath, packageName: string, deps: string[] = []): TestFile[] {
return [ return [
@ -235,6 +453,8 @@ runInEachFileSystem(() => {
contents: JSON.stringify({ contents: JSON.stringify({
typings: `./${packageName}.d.ts`, typings: `./${packageName}.d.ts`,
fesm2015: `./fesm2015/${packageName}.js`, fesm2015: `./fesm2015/${packageName}.js`,
esm5: `./esm5/${packageName}.js`,
main: `./common/${packageName}.js`,
}) })
}, },
{ {
@ -245,14 +465,15 @@ runInEachFileSystem(() => {
name: _Abs(`${basePath}/${packageName}/fesm2015/${packageName}.js`), name: _Abs(`${basePath}/${packageName}/fesm2015/${packageName}.js`),
contents: deps.map((dep, i) => `import * as i${i} from '${dep}';`).join('\n'), contents: deps.map((dep, i) => `import * as i${i} from '${dep}';`).join('\n'),
}, },
{
name: _Abs(`${basePath}/${packageName}/esm5/${packageName}.js`),
contents: deps.map((dep, i) => `import * as i${i} from '${dep}';`).join('\n'),
},
{
name: _Abs(`${basePath}/${packageName}/commonjs/${packageName}.js`),
contents: deps.map((dep, i) => `var i${i} = require('${dep}');`).join('\n'),
},
]; ];
} }
function dumpEntryPointPaths(
basePath: AbsoluteFsPath, entryPoints: EntryPoint[]): [string, string][] {
return entryPoints.map(x => [relative(basePath, x.package), relative(basePath, x.path)]);
}
});
}); });
}); });

View File

@ -480,22 +480,50 @@ runInEachFileSystem(() => {
expect(loadPackage('@angular/common/testing').__processed_by_ivy_ngcc__).toBeUndefined(); expect(loadPackage('@angular/common/testing').__processed_by_ivy_ngcc__).toBeUndefined();
}); });
it('should mark a non-Angular package target as processed', () => { it('should not mark a non-Angular package as processed if it is the target', () => {
mainNgcc({basePath: '/node_modules', targetEntryPointPath: 'test-package'}); mainNgcc({basePath: '/node_modules', targetEntryPointPath: 'test-package'});
// `test-package` has no Angular but is marked as processed. // * `test-package` has no Angular and is not marked as processed.
expect(loadPackage('test-package').__processed_by_ivy_ngcc__).toEqual({ expect(loadPackage('test-package').__processed_by_ivy_ngcc__).toBeUndefined();
es2015: '0.0.0-PLACEHOLDER',
esm2015: '0.0.0-PLACEHOLDER', // * `core` is a dependency of `test-package`, but it is also not processed, since
esm5: '0.0.0-PLACEHOLDER', // `test-package` was not processed.
fesm2015: '0.0.0-PLACEHOLDER', expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toBeUndefined();
fesm5: '0.0.0-PLACEHOLDER',
main: '0.0.0-PLACEHOLDER',
module: '0.0.0-PLACEHOLDER',
}); });
// * `core` is a dependency of `test-package`, but it is not processed, since test-package it('should not mark a non-Angular package as processed if it is a dependency', () => {
// was not processed. // `test-package-user` is a valid Angular package that depends upon `test-package`.
loadTestFiles([
{
name: _('/node_modules/test-package-user/package.json'),
contents:
'{"name": "test-package-user", "es2015": "./index.js", "typings": "./index.d.ts"}'
},
{
name: _('/node_modules/test-package-user/index.js'),
contents: 'import * as x from \'test-package\';'
},
{
name: _('/node_modules/test-package-user/index.d.ts'),
contents: 'import * as x from \'test-package\';'
},
{name: _('/node_modules/test-package-user/index.metadata.json'), contents: 'DUMMY DATA'},
]);
mainNgcc({basePath: '/node_modules', targetEntryPointPath: 'test-package-user'});
// * `test-package-user` is processed because it is compiled by Angular
expect(loadPackage('test-package-user').__processed_by_ivy_ngcc__).toEqual({
es2015: '0.0.0-PLACEHOLDER',
typings: '0.0.0-PLACEHOLDER',
});
// * `test-package` is a dependency of `test-package-user` but has not been compiled by
// Angular, and so is not marked as processed
expect(loadPackage('test-package').__processed_by_ivy_ngcc__).toBeUndefined();
// * `core` is a dependency of `test-package`, but it is not processed, because
// `test-package` was not processed.
expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toBeUndefined(); expect(loadPackage('@angular/core').__processed_by_ivy_ngcc__).toBeUndefined();
}); });