perf(ngcc): use EntryPointManifest to speed up noop ProgramBaseEntryPointFinder (#37665)

Previously the `ProgramBasedEntryPointFinder` was parsing all the
entry-points referenced by the program for dependencies even if all the
entry-points had been processed already.

Now this entry-point finder will re-use the `EntryPointManifest` to load
the entry-point dependencies when possible which avoids having to parse
them all again, on every invocation of ngcc.

Previously the `EntryPointManifest` was only used in the
`DirectoryWalkerEntryPointFinder`, which also contained the logic for
computing the contents of the manifest. This logic has been factored out
into an `EntryPointCollector` class. Both the `ProgramBasedEntryPointFinder`
and `DirectoryWalkerEntryPointFinder` now use the `EntryPointManifest` and
the `EntryPointCollector`.

The result of this change is that there is a small cost on the first run of
ngcc to compute and store the manifest - the processing takes 102% of the
processing time before this PR. But on subsequent runs there is a
significant benefit on subsequent runs - the processing takes around 50%
of the processing time before this PR.

PR Close #37665
This commit is contained in:
Pete Bacon Darwin
2020-06-22 13:12:56 +01:00
committed by Andrew Kushnir
parent 290bc7334d
commit 9318e23e64
9 changed files with 509 additions and 343 deletions

View File

@ -14,6 +14,7 @@ import {DtsDependencyHost} from '../../src/dependencies/dts_dependency_host';
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 {EntryPointCollector} from '../../src/entry_point_finder/entry_point_collector';
import {NgccConfiguration, ProcessedNgccPackageConfig} from '../../src/packages/configuration';
import {EntryPoint} from '../../src/packages/entry_point';
import {EntryPointManifest, EntryPointManifestFile} from '../../src/packages/entry_point_manifest';
@ -25,6 +26,7 @@ runInEachFileSystem(() => {
let resolver: DependencyResolver;
let logger: MockLogger;
let config: NgccConfiguration;
let collector: EntryPointCollector;
let manifest: EntryPointManifest;
let _Abs: typeof absoluteFrom;
@ -37,6 +39,7 @@ runInEachFileSystem(() => {
config = new NgccConfiguration(fs, _Abs('/'));
resolver = new DependencyResolver(fs, logger, config, {esm2015: srcHost}, dtsHost);
manifest = new EntryPointManifest(fs, config, logger);
collector = new EntryPointCollector(fs, config, logger, resolver);
});
describe('findEntryPoints()', () => {
@ -50,7 +53,7 @@ runInEachFileSystem(() => {
...createPackage(fs.resolve(basePath, 'common'), 'testing', ['common']),
]);
const finder = new DirectoryWalkerEntryPointFinder(
fs, config, logger, resolver, manifest, basePath, undefined);
logger, resolver, collector, manifest, basePath, undefined);
const {entryPoints} = finder.findEntryPoints();
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
['common', 'common'],
@ -71,7 +74,7 @@ runInEachFileSystem(() => {
...createPackage(fs.resolve(basePath, '@angular/common'), 'testing', ['@angular/common']),
]);
const finder = new DirectoryWalkerEntryPointFinder(
fs, config, logger, resolver, manifest, basePath, undefined);
logger, resolver, collector, manifest, basePath, undefined);
const {entryPoints} = finder.findEntryPoints();
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
['@angular/common', '@angular/common'],
@ -84,7 +87,7 @@ runInEachFileSystem(() => {
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, manifest, _Abs('/no_packages/node_modules'), undefined);
logger, resolver, collector, manifest, _Abs('/no_packages/node_modules'), undefined);
const {entryPoints} = finder.findEntryPoints();
expect(entryPoints).toEqual([]);
});
@ -97,7 +100,7 @@ runInEachFileSystem(() => {
},
]);
const finder = new DirectoryWalkerEntryPointFinder(
fs, config, logger, resolver, manifest, _Abs('/no_valid_entry_points/node_modules'),
logger, resolver, collector, manifest, _Abs('/no_valid_entry_points/node_modules'),
undefined);
const {entryPoints} = finder.findEntryPoints();
expect(entryPoints).toEqual([]);
@ -106,7 +109,7 @@ runInEachFileSystem(() => {
it('should not include ignored entry-points', () => {
const basePath = _Abs('/project/node_modules');
const finder = new DirectoryWalkerEntryPointFinder(
fs, config, logger, resolver, manifest, basePath, undefined);
logger, resolver, collector, manifest, basePath, undefined);
loadTestFiles(createPackage(basePath, 'some-package'));
spyOn(config, 'getPackageConfig')
@ -124,7 +127,7 @@ runInEachFileSystem(() => {
it('should look for sub-entry-points even if a containing entry-point is ignored', () => {
const basePath = _Abs('/project/node_modules');
const finder = new DirectoryWalkerEntryPointFinder(
fs, config, logger, resolver, manifest, basePath, undefined);
logger, resolver, collector, manifest, basePath, undefined);
loadTestFiles([
...createPackage(basePath, 'some-package'),
@ -161,7 +164,7 @@ runInEachFileSystem(() => {
spyOn(manifest, 'readEntryPointsUsingManifest').and.callThrough();
spyOn(manifest, 'writeEntryPointManifest').and.callThrough();
const finder = new DirectoryWalkerEntryPointFinder(
fs, config, logger, resolver, manifest, basePath, undefined);
logger, resolver, collector, manifest, basePath, undefined);
finder.findEntryPoints();
expect(manifest.readEntryPointsUsingManifest).toHaveBeenCalled();
expect(manifest.writeEntryPointManifest).toHaveBeenCalled();
@ -182,7 +185,7 @@ runInEachFileSystem(() => {
spyOn(manifest, 'readEntryPointsUsingManifest').and.callThrough();
spyOn(manifest, 'writeEntryPointManifest').and.callThrough();
const finder = new DirectoryWalkerEntryPointFinder(
fs, config, logger, resolver, manifest, basePath, undefined);
logger, resolver, collector, manifest, basePath, undefined);
finder.findEntryPoints();
expect(manifest.readEntryPointsUsingManifest).toHaveBeenCalled();
expect(manifest.writeEntryPointManifest).toHaveBeenCalled();
@ -200,7 +203,7 @@ runInEachFileSystem(() => {
{name: _Abs('/sub_entry_points/yarn.lock'), contents: 'MOCK LOCK FILE'},
]);
const finder = new DirectoryWalkerEntryPointFinder(
fs, config, logger, resolver, manifest, basePath, undefined);
logger, resolver, collector, manifest, basePath, undefined);
// Prime the manifest by calling findEntryPoints() once.
finder.findEntryPoints();
@ -229,8 +232,7 @@ runInEachFileSystem(() => {
...createPackage(_Abs('/dotted_folders/node_modules/'), '.common'),
]);
const finder = new DirectoryWalkerEntryPointFinder(
fs, config, logger, resolver, manifest, _Abs('/dotted_folders/node_modules'),
undefined);
logger, resolver, collector, manifest, _Abs('/dotted_folders/node_modules'), undefined);
const {entryPoints} = finder.findEntryPoints();
expect(entryPoints).toEqual([]);
});
@ -241,7 +243,7 @@ runInEachFileSystem(() => {
_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, manifest, _Abs('/symlinked_folders/node_modules'),
logger, resolver, collector, manifest, _Abs('/symlinked_folders/node_modules'),
undefined);
const {entryPoints} = finder.findEntryPoints();
expect(entryPoints).toEqual([]);
@ -253,7 +255,7 @@ runInEachFileSystem(() => {
...createPackage(_Abs('/nested_node_modules/node_modules/outer/node_modules'), 'inner'),
]);
const finder = new DirectoryWalkerEntryPointFinder(
fs, config, logger, resolver, manifest, _Abs('/nested_node_modules/node_modules'),
logger, resolver, collector, manifest, _Abs('/nested_node_modules/node_modules'),
undefined);
const {entryPoints} = finder.findEntryPoints();
// Note that the `inner` entry-point is not part of the `outer` package
@ -272,9 +274,9 @@ runInEachFileSystem(() => {
]);
const finder = new DirectoryWalkerEntryPointFinder(
fs, config, logger, resolver, manifest, _Abs('/nested_node_modules/node_modules'),
logger, resolver, collector, manifest, _Abs('/nested_node_modules/node_modules'),
undefined);
const spy = spyOn(finder, 'walkDirectoryForPackages').and.callThrough();
const spy = spyOn(collector, 'walkDirectoryForPackages').and.callThrough();
const {entryPoints} = finder.findEntryPoints();
expect(spy.calls.allArgs()).toEqual([
[_Abs(basePath)],
@ -295,8 +297,8 @@ runInEachFileSystem(() => {
]);
const finder = new DirectoryWalkerEntryPointFinder(
fs, config, logger, resolver, manifest, basePath, undefined);
const spy = spyOn(finder, 'walkDirectoryForPackages').and.callThrough();
logger, resolver, collector, manifest, basePath, undefined);
const spy = spyOn(collector, 'walkDirectoryForPackages').and.callThrough();
const {entryPoints} = finder.findEntryPoints();
expect(spy.calls.allArgs()).toEqual([
[_Abs(basePath)],
@ -319,8 +321,8 @@ runInEachFileSystem(() => {
]);
const finder = new DirectoryWalkerEntryPointFinder(
fs, config, logger, resolver, manifest, basePath, undefined);
const spy = spyOn(finder, 'walkDirectoryForPackages').and.callThrough();
logger, resolver, collector, manifest, basePath, undefined);
const spy = spyOn(collector, 'walkDirectoryForPackages').and.callThrough();
const {entryPoints} = finder.findEntryPoints();
expect(spy.calls.allArgs()).toEqual([
[_Abs(basePath)],
@ -339,7 +341,7 @@ runInEachFileSystem(() => {
...createPackage(fs.resolve(basePath, 'package/container'), 'entry-point-1'),
]);
const finder = new DirectoryWalkerEntryPointFinder(
fs, config, logger, resolver, manifest, basePath, undefined);
logger, resolver, collector, manifest, basePath, undefined);
const {entryPoints} = finder.findEntryPoints();
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
['package', 'package'],
@ -367,8 +369,9 @@ runInEachFileSystem(() => {
const srcHost = new EsmDependencyHost(fs, new ModuleResolver(fs, pathMappings));
const dtsHost = new DtsDependencyHost(fs, pathMappings);
resolver = new DependencyResolver(fs, logger, config, {esm2015: srcHost}, dtsHost);
collector = new EntryPointCollector(fs, config, logger, resolver);
const finder = new DirectoryWalkerEntryPointFinder(
fs, config, logger, resolver, manifest, basePath, pathMappings);
logger, resolver, collector, manifest, basePath, pathMappings);
const {entryPoints} = finder.findEntryPoints();
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
['pkg1', 'pkg1'],
@ -396,7 +399,7 @@ runInEachFileSystem(() => {
const dtsHost = new DtsDependencyHost(fs, pathMappings);
resolver = new DependencyResolver(fs, logger, config, {esm2015: srcHost}, dtsHost);
const finder = new DirectoryWalkerEntryPointFinder(
fs, config, logger, resolver, manifest, basePath, pathMappings);
logger, resolver, collector, manifest, basePath, pathMappings);
const {entryPoints} = finder.findEntryPoints();
expect(dumpEntryPointPaths(basePath, entryPoints)).toEqual([
['test', 'test'],

View File

@ -15,6 +15,7 @@ import {DependencyResolver} from '../../src/dependencies/dependency_resolver';
import {DtsDependencyHost} from '../../src/dependencies/dts_dependency_host';
import {EsmDependencyHost} from '../../src/dependencies/esm_dependency_host';
import {ModuleResolver} from '../../src/dependencies/module_resolver';
import {EntryPointCollector} from '../../src/entry_point_finder/entry_point_collector';
import {ProgramBasedEntryPointFinder} from '../../src/entry_point_finder/program_based_entry_point_finder';
import {NgccConfiguration} from '../../src/packages/configuration';
import {EntryPoint} from '../../src/packages/entry_point';
@ -68,9 +69,10 @@ runInEachFileSystem(() => {
const dtsHost = new DtsDependencyHost(fs);
const config = new NgccConfiguration(fs, projectPath);
const resolver = new DependencyResolver(fs, logger, config, {esm2015: srcHost}, dtsHost);
const collector = new EntryPointCollector(fs, config, logger, resolver);
const manifest = new EntryPointManifest(fs, config, logger);
return new ProgramBasedEntryPointFinder(
fs, config, logger, resolver, basePath, tsConfig, projectPath);
fs, config, logger, resolver, collector, manifest, basePath, tsConfig, projectPath);
}
function createProgram(projectPath: AbsoluteFsPath): TestFile[] {