fix(ngcc): capture entry-point dependencies from typings as well as source (#34494)

ngcc computes a dependency graph of entry-points to ensure that
entry-points are processed in the correct order. Previously only the imports
in source files were analysed to determine the dependencies for each
entry-point.

This is not sufficient when an entry-point has a "type-only" dependency
 - for example only importing an interface from another entry-point.
In this case the "type-only" import does not appear in the
source code. It only appears in the typings files. This can cause a
dependency to be missed on the entry-point.

This commit fixes this by additionally processing the imports in the
typings program, as well as the source program.

Note that these missing dependencies could cause unexpected flakes when
running ngcc in async mode on multiple processes due to the way that
ngcc caches files when they are first read from disk.

Fixes #34411

// FW-1781

PR Close #34494
This commit is contained in:
Pete Bacon Darwin 2019-12-19 22:43:13 +00:00 committed by Alex Rickabaugh
parent 69950e3888
commit 4f42de9704
7 changed files with 140 additions and 28 deletions

View File

@ -82,7 +82,8 @@ export interface SortedEntryPointsInfo extends DependencyDiagnostics {
export class DependencyResolver { export class DependencyResolver {
constructor( constructor(
private fs: FileSystem, private logger: Logger, private fs: FileSystem, private logger: Logger,
private hosts: Partial<Record<EntryPointFormat, DependencyHost>>) {} private hosts: Partial<Record<EntryPointFormat, DependencyHost>>,
private typingsHost: DependencyHost) {}
/** /**
* Sort the array of entry points so that the dependant entry points always come later than * Sort the array of entry points so that the dependant entry points always come later than
* their dependencies in the array. * their dependencies in the array.
@ -125,6 +126,7 @@ export class DependencyResolver {
} }
const depInfo = createDependencyInfo(); const depInfo = createDependencyInfo();
host.collectDependencies(formatInfo.path, depInfo); host.collectDependencies(formatInfo.path, depInfo);
this.typingsHost.collectDependencies(entryPoint.typings, depInfo);
return depInfo; return depInfo;
} }

View File

@ -35,7 +35,7 @@ import {NgccConfiguration} from './packages/configuration';
import {EntryPoint, EntryPointJsonProperty, EntryPointPackageJson, SUPPORTED_FORMAT_PROPERTIES, getEntryPointFormat} from './packages/entry_point'; import {EntryPoint, EntryPointJsonProperty, EntryPointPackageJson, SUPPORTED_FORMAT_PROPERTIES, getEntryPointFormat} from './packages/entry_point';
import {makeEntryPointBundle} from './packages/entry_point_bundle'; import {makeEntryPointBundle} from './packages/entry_point_bundle';
import {Transformer} from './packages/transformer'; import {Transformer} from './packages/transformer';
import {PathMappings} from './utils'; import {PathMappings, createDtsDependencyHost} from './utils';
import {FileWriter} from './writing/file_writer'; import {FileWriter} from './writing/file_writer';
import {InPlaceFileWriter} from './writing/in_place_file_writer'; import {InPlaceFileWriter} from './writing/in_place_file_writer';
import {NewEntryPointFileWriter} from './writing/new_entry_point_file_writer'; import {NewEntryPointFileWriter} from './writing/new_entry_point_file_writer';
@ -164,12 +164,15 @@ export function mainNgcc(
const esmDependencyHost = new EsmDependencyHost(fileSystem, moduleResolver); const esmDependencyHost = new EsmDependencyHost(fileSystem, moduleResolver);
const umdDependencyHost = new UmdDependencyHost(fileSystem, moduleResolver); const umdDependencyHost = new UmdDependencyHost(fileSystem, moduleResolver);
const commonJsDependencyHost = new CommonJsDependencyHost(fileSystem, moduleResolver); const commonJsDependencyHost = new CommonJsDependencyHost(fileSystem, moduleResolver);
const dependencyResolver = new DependencyResolver(fileSystem, logger, { const dtsDependencyHost = createDtsDependencyHost(fileSystem, pathMappings);
const dependencyResolver = new DependencyResolver(
fileSystem, logger, {
esm5: esmDependencyHost, esm5: esmDependencyHost,
esm2015: esmDependencyHost, esm2015: esmDependencyHost,
umd: umdDependencyHost, umd: umdDependencyHost,
commonjs: commonJsDependencyHost commonjs: commonJsDependencyHost
}); },
dtsDependencyHost);
const absBasePath = absoluteFrom(basePath); const absBasePath = absoluteFrom(basePath);
const config = new NgccConfiguration(fileSystem, dirname(absBasePath)); const config = new NgccConfiguration(fileSystem, dirname(absBasePath));

View File

@ -7,6 +7,8 @@
*/ */
import * as ts from 'typescript'; import * as ts from 'typescript';
import {AbsoluteFsPath, FileSystem, absoluteFrom} from '../../src/ngtsc/file_system'; import {AbsoluteFsPath, FileSystem, absoluteFrom} from '../../src/ngtsc/file_system';
import {EsmDependencyHost} from './dependencies/esm_dependency_host';
import {ModuleResolver} from './dependencies/module_resolver';
/** /**
* A list (`Array`) of partially ordered `T` items. * A list (`Array`) of partially ordered `T` items.
@ -119,3 +121,8 @@ export function stripDollarSuffix(value: string): string {
export function stripExtension(fileName: string): string { export function stripExtension(fileName: string): string {
return fileName.replace(/\..+$/, ''); return fileName.replace(/\..+$/, '');
} }
export function createDtsDependencyHost(fileSystem: FileSystem, pathMappings?: PathMappings) {
return new EsmDependencyHost(
fileSystem, new ModuleResolver(fileSystem, pathMappings, ['', '.d.ts', '/index.d.ts']));
}

View File

@ -15,6 +15,7 @@ import {DependencyResolver, SortedEntryPointsInfo} from '../../src/dependencies/
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 {EntryPoint} from '../../src/packages/entry_point'; import {EntryPoint} from '../../src/packages/entry_point';
import {createDtsDependencyHost} from '../../src/utils';
import {MockLogger} from '../helpers/mock_logger'; import {MockLogger} from '../helpers/mock_logger';
@ -26,6 +27,7 @@ runInEachFileSystem(() => {
describe('DependencyResolver', () => { describe('DependencyResolver', () => {
let _: typeof absoluteFrom; let _: typeof absoluteFrom;
let host: EsmDependencyHost; let host: EsmDependencyHost;
let dtsHost: EsmDependencyHost;
let resolver: DependencyResolver; let resolver: DependencyResolver;
let fs: FileSystem; let fs: FileSystem;
let moduleResolver: ModuleResolver; let moduleResolver: ModuleResolver;
@ -35,7 +37,8 @@ runInEachFileSystem(() => {
fs = getFileSystem(); fs = getFileSystem();
moduleResolver = new ModuleResolver(fs); moduleResolver = new ModuleResolver(fs);
host = new EsmDependencyHost(fs, moduleResolver); host = new EsmDependencyHost(fs, moduleResolver);
resolver = new DependencyResolver(fs, new MockLogger(), {esm5: host, esm2015: host}); dtsHost = createDtsDependencyHost(fs);
resolver = new DependencyResolver(fs, new MockLogger(), {esm5: host, esm2015: host}, dtsHost);
}); });
describe('sortEntryPointsByDependency()', () => { describe('sortEntryPointsByDependency()', () => {
@ -46,6 +49,7 @@ runInEachFileSystem(() => {
let fifth: EntryPoint; let fifth: EntryPoint;
let sixthIgnoreMissing: EntryPoint; let sixthIgnoreMissing: EntryPoint;
let dependencies: DepMap; let dependencies: DepMap;
let dtsDependencies: DepMap;
beforeEach(() => { beforeEach(() => {
first = { first = {
@ -53,30 +57,35 @@ runInEachFileSystem(() => {
packageJson: {esm5: './index.js'}, packageJson: {esm5: './index.js'},
compiledByAngular: true, compiledByAngular: true,
ignoreMissingDependencies: false, ignoreMissingDependencies: false,
typings: _('/first/index.d.ts'),
} as EntryPoint; } as EntryPoint;
second = { second = {
path: _('/second'), path: _('/second'),
packageJson: {esm2015: './sub/index.js'}, packageJson: {esm2015: './sub/index.js'},
compiledByAngular: true, compiledByAngular: true,
ignoreMissingDependencies: false, ignoreMissingDependencies: false,
typings: _('/second/sub/index.d.ts'),
} as EntryPoint; } as EntryPoint;
third = { third = {
path: _('/third'), path: _('/third'),
packageJson: {fesm5: './index.js'}, packageJson: {fesm5: './index.js'},
compiledByAngular: true, compiledByAngular: true,
ignoreMissingDependencies: false, ignoreMissingDependencies: false,
typings: _('/third/index.d.ts'),
} as EntryPoint; } as EntryPoint;
fourth = { fourth = {
path: _('/fourth'), path: _('/fourth'),
packageJson: {fesm2015: './sub2/index.js'}, packageJson: {fesm2015: './sub2/index.js'},
compiledByAngular: true, compiledByAngular: true,
ignoreMissingDependencies: false, ignoreMissingDependencies: false,
typings: _('/fourth/sub2/index.d.ts'),
} as EntryPoint; } as EntryPoint;
fifth = { fifth = {
path: _('/fifth'), path: _('/fifth'),
packageJson: {module: './index.js'}, packageJson: {module: './index.js'},
compiledByAngular: true, compiledByAngular: true,
ignoreMissingDependencies: false, ignoreMissingDependencies: false,
typings: _('/fifth/index.d.ts'),
} as EntryPoint; } as EntryPoint;
sixthIgnoreMissing = { sixthIgnoreMissing = {
@ -84,6 +93,7 @@ runInEachFileSystem(() => {
packageJson: {module: './index.js'}, packageJson: {module: './index.js'},
compiledByAngular: true, compiledByAngular: true,
ignoreMissingDependencies: true, ignoreMissingDependencies: true,
typings: _('/sixth/index.d.ts'),
} as EntryPoint; } as EntryPoint;
dependencies = { dependencies = {
@ -94,11 +104,21 @@ runInEachFileSystem(() => {
[_('/fourth/sub2/index.js')]: {resolved: [fifth.path], missing: []}, [_('/fourth/sub2/index.js')]: {resolved: [fifth.path], missing: []},
[_('/fifth/index.js')]: {resolved: [], missing: []}, [_('/fifth/index.js')]: {resolved: [], missing: []},
}; };
dtsDependencies = {
[_('/first/index.d.ts')]:
{resolved: [second.path, third.path, _('/ignored-1')], missing: []},
[_('/second/sub/index.d.ts')]: {resolved: [third.path, fifth.path], missing: []},
[_('/third/index.d.ts')]: {resolved: [fourth.path, _('/ignored-2')], missing: []},
[_('/fourth/sub2/index.d.ts')]: {resolved: [fifth.path], missing: []},
[_('/fifth/index.d.ts')]: {resolved: [], missing: []},
};
}); });
it('should order the entry points by their dependency on each other', () => { it('should order the entry points by their dependency on each other', () => {
spyOn(host, 'collectDependencies') spyOn(host, 'collectDependencies')
.and.callFake(createFakeComputeDependencies(dependencies)); .and.callFake(createFakeComputeDependencies(dependencies));
spyOn(dtsHost, 'collectDependencies')
.and.callFake(createFakeComputeDependencies(dtsDependencies));
const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]); const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]);
expect(result.entryPoints).toEqual([fifth, fourth, third, second, first]); expect(result.entryPoints).toEqual([fifth, fourth, third, second, first]);
}); });
@ -108,6 +128,10 @@ runInEachFileSystem(() => {
[_('/first/index.js')]: {resolved: [], missing: [_('/missing')]}, [_('/first/index.js')]: {resolved: [], missing: [_('/missing')]},
[_('/second/sub/index.js')]: {resolved: [], missing: []}, [_('/second/sub/index.js')]: {resolved: [], missing: []},
})); }));
spyOn(dtsHost, 'collectDependencies').and.callFake(createFakeComputeDependencies({
[_('/first/index.d.ts')]: {resolved: [], missing: [_('/missing')]},
[_('/second/sub/index.d.ts')]: {resolved: [], missing: []},
}));
const result = resolver.sortEntryPointsByDependency([first, second]); const result = resolver.sortEntryPointsByDependency([first, second]);
expect(result.entryPoints).toEqual([second]); expect(result.entryPoints).toEqual([second]);
expect(result.invalidEntryPoints).toEqual([ expect(result.invalidEntryPoints).toEqual([
@ -121,6 +145,11 @@ runInEachFileSystem(() => {
[_('/second/sub/index.js')]: {resolved: [], missing: [_('/missing')]}, [_('/second/sub/index.js')]: {resolved: [], missing: [_('/missing')]},
[_('/third/index.js')]: {resolved: [], missing: []}, [_('/third/index.js')]: {resolved: [], missing: []},
})); }));
spyOn(dtsHost, 'collectDependencies').and.callFake(createFakeComputeDependencies({
[_('/first/index.d.ts')]: {resolved: [second.path, third.path], missing: []},
[_('/second/sub/index.d.ts')]: {resolved: [], missing: [_('/missing')]},
[_('/third/index.d.ts')]: {resolved: [], missing: []},
}));
// Note that we will process `first` before `second`, which has the missing dependency. // Note that we will process `first` before `second`, which has the missing dependency.
const result = resolver.sortEntryPointsByDependency([first, second, third]); const result = resolver.sortEntryPointsByDependency([first, second, third]);
expect(result.entryPoints).toEqual([third]); expect(result.entryPoints).toEqual([third]);
@ -136,6 +165,11 @@ runInEachFileSystem(() => {
[_('/second/sub/index.js')]: {resolved: [], missing: [_('/missing')]}, [_('/second/sub/index.js')]: {resolved: [], missing: [_('/missing')]},
[_('/third/index.js')]: {resolved: [], missing: []}, [_('/third/index.js')]: {resolved: [], missing: []},
})); }));
spyOn(dtsHost, 'collectDependencies').and.callFake(createFakeComputeDependencies({
[_('/first/index.d.ts')]: {resolved: [second.path, third.path], missing: []},
[_('/second/sub/index.d.ts')]: {resolved: [], missing: [_('/missing')]},
[_('/third/index.d.ts')]: {resolved: [], missing: []},
}));
// Note that we will process `first` after `second`, which has the missing dependency. // Note that we will process `first` after `second`, which has the missing dependency.
const result = resolver.sortEntryPointsByDependency([second, first, third]); const result = resolver.sortEntryPointsByDependency([second, first, third]);
expect(result.entryPoints).toEqual([third]); expect(result.entryPoints).toEqual([third]);
@ -151,6 +185,10 @@ runInEachFileSystem(() => {
[_('/first/index.js')]: {resolved: [sixthIgnoreMissing.path], missing: []}, [_('/first/index.js')]: {resolved: [sixthIgnoreMissing.path], missing: []},
[_('/sixth/index.js')]: {resolved: [], missing: [_('/missing')]}, [_('/sixth/index.js')]: {resolved: [], missing: [_('/missing')]},
})); }));
spyOn(dtsHost, 'collectDependencies').and.callFake(createFakeComputeDependencies({
[_('/first/index.d.ts')]: {resolved: [sixthIgnoreMissing.path], missing: []},
[_('/sixth/index.d.ts')]: {resolved: [], missing: [_('/missing')]},
}));
// Note that we will process `first` after `second`, which has the missing dependency. // Note that we will process `first` after `second`, which has the missing dependency.
const result = resolver.sortEntryPointsByDependency([sixthIgnoreMissing, first]); const result = resolver.sortEntryPointsByDependency([sixthIgnoreMissing, first]);
expect(result.entryPoints).toEqual([sixthIgnoreMissing, first]); expect(result.entryPoints).toEqual([sixthIgnoreMissing, first]);
@ -163,6 +201,11 @@ runInEachFileSystem(() => {
[_('/second/sub/index.js')]: {resolved: [first.path], missing: []}, [_('/second/sub/index.js')]: {resolved: [first.path], missing: []},
[_('/sixth/index.js')]: {resolved: [second.path], missing: []}, [_('/sixth/index.js')]: {resolved: [second.path], missing: []},
})); }));
spyOn(dtsHost, 'collectDependencies').and.callFake(createFakeComputeDependencies({
[_('/first/index.d.ts')]: {resolved: [], missing: [_('/missing')]},
[_('/second/sub/index.d.ts')]: {resolved: [first.path], missing: []},
[_('/sixth/index.d.ts')]: {resolved: [second.path], missing: []},
}));
const result = resolver.sortEntryPointsByDependency([first, second, sixthIgnoreMissing]); const result = resolver.sortEntryPointsByDependency([first, second, sixthIgnoreMissing]);
// sixth has no missing dependencies, but it has _invalid_ dependencies, so it's not // sixth has no missing dependencies, but it has _invalid_ dependencies, so it's not
// compiled. // compiled.
@ -175,6 +218,11 @@ runInEachFileSystem(() => {
[_('/second/sub/index.js')]: {resolved: [], missing: [_('/missing2')]}, [_('/second/sub/index.js')]: {resolved: [], missing: [_('/missing2')]},
[_('/third/index.js')]: {resolved: [first.path, second.path], missing: []}, [_('/third/index.js')]: {resolved: [first.path, second.path], missing: []},
})); }));
spyOn(dtsHost, 'collectDependencies').and.callFake(createFakeComputeDependencies({
[_('/first/index.d.ts')]: {resolved: [], missing: [_('/missing1')]},
[_('/second/sub/index.d.ts')]: {resolved: [], missing: [_('/missing2')]},
[_('/third/index.d.ts')]: {resolved: [first.path, second.path], missing: []},
}));
const result = resolver.sortEntryPointsByDependency([first, second, third]); const result = resolver.sortEntryPointsByDependency([first, second, third]);
expect(result.entryPoints).toEqual([]); expect(result.entryPoints).toEqual([]);
expect(result.invalidEntryPoints).toEqual([ expect(result.invalidEntryPoints).toEqual([
@ -191,7 +239,7 @@ runInEachFileSystem(() => {
}); });
it('should error if there is no appropriate DependencyHost for the given formats', () => { it('should error if there is no appropriate DependencyHost for the given formats', () => {
resolver = new DependencyResolver(fs, new MockLogger(), {esm2015: host}); resolver = new DependencyResolver(fs, new MockLogger(), {esm2015: host}, host);
expect(() => resolver.sortEntryPointsByDependency([first])) expect(() => resolver.sortEntryPointsByDependency([first]))
.toThrowError( .toThrowError(
`Could not find a suitable format for computing dependencies of entry-point: '${first.path}'.`); `Could not find a suitable format for computing dependencies of entry-point: '${first.path}'.`);
@ -200,6 +248,8 @@ runInEachFileSystem(() => {
it('should capture any dependencies that were ignored', () => { it('should capture any dependencies that were ignored', () => {
spyOn(host, 'collectDependencies') spyOn(host, 'collectDependencies')
.and.callFake(createFakeComputeDependencies(dependencies)); .and.callFake(createFakeComputeDependencies(dependencies));
spyOn(dtsHost, 'collectDependencies')
.and.callFake(createFakeComputeDependencies(dtsDependencies));
const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]); const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]);
expect(result.ignoredDependencies).toEqual([ expect(result.ignoredDependencies).toEqual([
{entryPoint: first, dependencyPath: _('/ignored-1')}, {entryPoint: first, dependencyPath: _('/ignored-1')},
@ -210,6 +260,8 @@ runInEachFileSystem(() => {
it('should return the computed dependency graph', () => { it('should return the computed dependency graph', () => {
spyOn(host, 'collectDependencies') spyOn(host, 'collectDependencies')
.and.callFake(createFakeComputeDependencies(dependencies)); .and.callFake(createFakeComputeDependencies(dependencies));
spyOn(dtsHost, 'collectDependencies')
.and.callFake(createFakeComputeDependencies(dtsDependencies));
const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]); const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]);
expect(result.graph).toEqual(jasmine.any(DepGraph)); expect(result.graph).toEqual(jasmine.any(DepGraph));
@ -220,6 +272,8 @@ runInEachFileSystem(() => {
it('should only return dependencies of the target, if provided', () => { it('should only return dependencies of the target, if provided', () => {
spyOn(host, 'collectDependencies') spyOn(host, 'collectDependencies')
.and.callFake(createFakeComputeDependencies(dependencies)); .and.callFake(createFakeComputeDependencies(dependencies));
spyOn(dtsHost, 'collectDependencies')
.and.callFake(createFakeComputeDependencies(dtsDependencies));
const entryPoints = [fifth, first, fourth, second, third]; const entryPoints = [fifth, first, fourth, second, third];
let sorted: SortedEntryPointsInfo; let sorted: SortedEntryPointsInfo;
@ -239,6 +293,9 @@ runInEachFileSystem(() => {
spyOn(host, 'collectDependencies').and.callFake(createFakeComputeDependencies({ spyOn(host, 'collectDependencies').and.callFake(createFakeComputeDependencies({
[_('/first/index.js')]: {resolved: [], missing: [_('/missing')]}, [_('/first/index.js')]: {resolved: [], missing: [_('/missing')]},
})); }));
spyOn(dtsHost, 'collectDependencies').and.callFake(createFakeComputeDependencies({
[_('/first/index.d.ts')]: {resolved: [], missing: [_('/missing')]},
}));
const entryPoints = [first]; const entryPoints = [first];
let sorted: SortedEntryPointsInfo; let sorted: SortedEntryPointsInfo;
@ -252,6 +309,9 @@ runInEachFileSystem(() => {
spyOn(host, 'collectDependencies').and.callFake(createFakeComputeDependencies({ spyOn(host, 'collectDependencies').and.callFake(createFakeComputeDependencies({
[_('/first/index.js')]: {resolved: [], missing: ['fs']}, [_('/first/index.js')]: {resolved: [], missing: ['fs']},
})); }));
spyOn(dtsHost, 'collectDependencies').and.callFake(createFakeComputeDependencies({
[_('/first/index.d.ts')]: {resolved: [], missing: ['fs']},
}));
const entryPoints = [first]; const entryPoints = [first];
let sorted: SortedEntryPointsInfo; let sorted: SortedEntryPointsInfo;
@ -264,12 +324,15 @@ runInEachFileSystem(() => {
it('should use the appropriate DependencyHost for each entry-point', () => { it('should use the appropriate DependencyHost for each entry-point', () => {
const esm5Host = new EsmDependencyHost(fs, moduleResolver); const esm5Host = new EsmDependencyHost(fs, moduleResolver);
const esm2015Host = new EsmDependencyHost(fs, moduleResolver); const esm2015Host = new EsmDependencyHost(fs, moduleResolver);
resolver = const dtsHost = createDtsDependencyHost(fs);
new DependencyResolver(fs, new MockLogger(), {esm5: esm5Host, esm2015: esm2015Host}); resolver = new DependencyResolver(
fs, new MockLogger(), {esm5: esm5Host, esm2015: esm2015Host}, dtsHost);
spyOn(esm5Host, 'collectDependencies') spyOn(esm5Host, 'collectDependencies')
.and.callFake(createFakeComputeDependencies(dependencies)); .and.callFake(createFakeComputeDependencies(dependencies));
spyOn(esm2015Host, 'collectDependencies') spyOn(esm2015Host, 'collectDependencies')
.and.callFake(createFakeComputeDependencies(dependencies)); .and.callFake(createFakeComputeDependencies(dependencies));
spyOn(dtsHost, 'collectDependencies')
.and.callFake(createFakeComputeDependencies(dtsDependencies));
const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]); const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]);
expect(result.entryPoints).toEqual([fifth, fourth, third, second, first]); expect(result.entryPoints).toEqual([fifth, fourth, third, second, first]);
@ -295,6 +358,37 @@ runInEachFileSystem(() => {
.toHaveBeenCalledWith(fs.resolve(fourth.path, 'sub2/index.js'), jasmine.any(Object)); .toHaveBeenCalledWith(fs.resolve(fourth.path, 'sub2/index.js'), jasmine.any(Object));
expect(esm2015Host.collectDependencies) expect(esm2015Host.collectDependencies)
.not.toHaveBeenCalledWith(fs.resolve(fifth.path, 'index.js'), jasmine.any(Object)); .not.toHaveBeenCalledWith(fs.resolve(fifth.path, 'index.js'), jasmine.any(Object));
expect(dtsHost.collectDependencies)
.toHaveBeenCalledWith(fs.resolve(first.path, 'index.d.ts'), jasmine.any(Object));
expect(dtsHost.collectDependencies)
.toHaveBeenCalledWith(fs.resolve(second.path, 'sub/index.d.ts'), jasmine.any(Object));
expect(dtsHost.collectDependencies)
.toHaveBeenCalledWith(fs.resolve(third.path, 'index.d.ts'), jasmine.any(Object));
expect(dtsHost.collectDependencies)
.toHaveBeenCalledWith(fs.resolve(fourth.path, 'sub2/index.d.ts'), jasmine.any(Object));
expect(dtsHost.collectDependencies)
.toHaveBeenCalledWith(fs.resolve(fifth.path, 'index.d.ts'), jasmine.any(Object));
});
it('should merge "typings-only" dependencies with source dependencies', () => {
spyOn(host, 'collectDependencies').and.callFake(createFakeComputeDependencies({
[_('/first/index.js')]: {resolved: [], missing: []},
[_('/second/sub/index.js')]: {resolved: [], missing: [_('/missing1')]},
[_('/third/index.js')]: {resolved: [first.path], missing: []},
}));
spyOn(dtsHost, 'collectDependencies').and.callFake(createFakeComputeDependencies({
[_('/first/index.d.ts')]: {resolved: [], missing: []},
[_('/second/sub/index.d.ts')]: {resolved: [], missing: [_('/missing2')]},
[_('/third/index.d.ts')]: {resolved: [second.path], missing: []},
}));
const entryPoints = [first, second, third];
const sorted = resolver.sortEntryPointsByDependency(entryPoints);
expect(sorted.entryPoints).toEqual([first]);
expect(sorted.invalidEntryPoints).toEqual([
{entryPoint: second, missingDependencies: [_('/missing1'), _('/missing2')]},
{entryPoint: third, missingDependencies: [_('/second')]},
]);
}); });
function createFakeComputeDependencies(deps: DepMap) { function createFakeComputeDependencies(deps: DepMap) {

View File

@ -13,6 +13,7 @@ import {loadTestFiles} from '../../../test/helpers';
import {createDependencyInfo} from '../../src/dependencies/dependency_host'; import {createDependencyInfo} from '../../src/dependencies/dependency_host';
import {EsmDependencyHost, hasImportOrReexportStatements, isStringImportOrReexport} from '../../src/dependencies/esm_dependency_host'; import {EsmDependencyHost, hasImportOrReexportStatements, isStringImportOrReexport} from '../../src/dependencies/esm_dependency_host';
import {ModuleResolver} from '../../src/dependencies/module_resolver'; import {ModuleResolver} from '../../src/dependencies/module_resolver';
import {createDtsDependencyHost} from '../../src/utils';
runInEachFileSystem(() => { runInEachFileSystem(() => {
@ -159,8 +160,7 @@ runInEachFileSystem(() => {
expect(jsDeps.missing.has(relativeFrom('./internal-typings'))).toBeTruthy(); expect(jsDeps.missing.has(relativeFrom('./internal-typings'))).toBeTruthy();
// Typings mode will pick up `internal-typings.d.ts` dependency // Typings mode will pick up `internal-typings.d.ts` dependency
const dtsHost = new EsmDependencyHost( const dtsHost = createDtsDependencyHost(fs);
fs, new ModuleResolver(fs, undefined, ['', '.d.ts', 'index.d.ts']));
const dtsDeps = createDependencyInfo(); const dtsDeps = createDependencyInfo();
dtsHost.collectDependencies(_('/external/index.d.ts'), dtsDeps); dtsHost.collectDependencies(_('/external/index.d.ts'), dtsDeps);
expect(dtsDeps.dependencies.size).toEqual(2); expect(dtsDeps.dependencies.size).toEqual(2);

View File

@ -14,7 +14,7 @@ import {ModuleResolver} from '../../src/dependencies/module_resolver';
import {DirectoryWalkerEntryPointFinder} from '../../src/entry_point_finder/directory_walker_entry_point_finder'; import {DirectoryWalkerEntryPointFinder} from '../../src/entry_point_finder/directory_walker_entry_point_finder';
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, createDtsDependencyHost} from '../../src/utils';
import {MockLogger} from '../helpers/mock_logger'; import {MockLogger} from '../helpers/mock_logger';
runInEachFileSystem(() => { runInEachFileSystem(() => {
@ -29,8 +29,9 @@ runInEachFileSystem(() => {
fs = getFileSystem(); fs = getFileSystem();
_Abs = absoluteFrom; _Abs = absoluteFrom;
logger = new MockLogger(); logger = new MockLogger();
resolver = new DependencyResolver( const srcHost = new EsmDependencyHost(fs, new ModuleResolver(fs));
fs, logger, {esm2015: new EsmDependencyHost(fs, new ModuleResolver(fs))}); const dtsHost = createDtsDependencyHost(fs);
resolver = new DependencyResolver(fs, logger, {esm2015: srcHost}, dtsHost);
config = new NgccConfiguration(fs, _Abs('/')); config = new NgccConfiguration(fs, _Abs('/'));
}); });
@ -152,8 +153,9 @@ runInEachFileSystem(() => {
...createPackage(_Abs('/path_mapped/dist/pkg2/node_modules'), 'pkg4'), ...createPackage(_Abs('/path_mapped/dist/pkg2/node_modules'), 'pkg4'),
...createPackage(_Abs('/path_mapped/dist/lib/pkg3'), 'test'), ...createPackage(_Abs('/path_mapped/dist/lib/pkg3'), 'test'),
]); ]);
resolver = new DependencyResolver( const srcHost = new EsmDependencyHost(fs, new ModuleResolver(fs, pathMappings));
fs, logger, {esm2015: new EsmDependencyHost(fs, new ModuleResolver(fs, pathMappings))}); const dtsHost = createDtsDependencyHost(fs, pathMappings);
resolver = new DependencyResolver(fs, logger, {esm2015: srcHost}, dtsHost);
const finder = new DirectoryWalkerEntryPointFinder( const finder = new DirectoryWalkerEntryPointFinder(
fs, config, logger, resolver, basePath, pathMappings); fs, config, logger, resolver, basePath, pathMappings);
const {entryPoints} = finder.findEntryPoints(); const {entryPoints} = finder.findEntryPoints();
@ -179,8 +181,9 @@ runInEachFileSystem(() => {
...createPackage(_Abs('/path_mapped/node_modules'), 'test', []), ...createPackage(_Abs('/path_mapped/node_modules'), 'test', []),
...createPackage(_Abs('/path_mapped/dist'), 'pkg2'), ...createPackage(_Abs('/path_mapped/dist'), 'pkg2'),
]); ]);
resolver = new DependencyResolver( const srcHost = new EsmDependencyHost(fs, new ModuleResolver(fs, pathMappings));
fs, logger, {esm2015: new EsmDependencyHost(fs, new ModuleResolver(fs, pathMappings))}); const dtsHost = createDtsDependencyHost(fs, pathMappings);
resolver = new DependencyResolver(fs, logger, {esm2015: srcHost}, dtsHost);
const finder = new DirectoryWalkerEntryPointFinder( const finder = new DirectoryWalkerEntryPointFinder(
fs, config, logger, resolver, basePath, pathMappings); fs, config, logger, resolver, basePath, pathMappings);
const {entryPoints} = finder.findEntryPoints(); const {entryPoints} = finder.findEntryPoints();

View File

@ -14,7 +14,7 @@ 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 {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, createDtsDependencyHost} from '../../src/utils';
import {MockLogger} from '../helpers/mock_logger'; import {MockLogger} from '../helpers/mock_logger';
runInEachFileSystem(() => { runInEachFileSystem(() => {
@ -29,8 +29,9 @@ runInEachFileSystem(() => {
fs = getFileSystem(); fs = getFileSystem();
_Abs = absoluteFrom; _Abs = absoluteFrom;
logger = new MockLogger(); logger = new MockLogger();
resolver = new DependencyResolver( const srcHost = new EsmDependencyHost(fs, new ModuleResolver(fs));
fs, logger, {esm2015: new EsmDependencyHost(fs, new ModuleResolver(fs))}); const dtsHost = createDtsDependencyHost(fs);
resolver = new DependencyResolver(fs, logger, {esm2015: srcHost}, dtsHost);
config = new NgccConfiguration(fs, _Abs('/')); config = new NgccConfiguration(fs, _Abs('/'));
}); });
@ -184,8 +185,9 @@ runInEachFileSystem(() => {
...createPackage(_Abs('/path_mapped/dist/lib/pkg3'), 'test'), ...createPackage(_Abs('/path_mapped/dist/lib/pkg3'), 'test'),
...createPackage(_Abs('/path_mapped/dist'), 'pkg5'), ...createPackage(_Abs('/path_mapped/dist'), 'pkg5'),
]); ]);
resolver = new DependencyResolver( const srcHost = new EsmDependencyHost(fs, new ModuleResolver(fs, pathMappings));
fs, logger, {esm2015: new EsmDependencyHost(fs, new ModuleResolver(fs, pathMappings))}); const dtsHost = createDtsDependencyHost(fs, pathMappings);
resolver = new DependencyResolver(fs, logger, {esm2015: srcHost}, dtsHost);
const finder = new TargetedEntryPointFinder( const finder = new TargetedEntryPointFinder(
fs, config, logger, resolver, basePath, targetPath, pathMappings); fs, config, logger, resolver, basePath, targetPath, pathMappings);
const {entryPoints} = finder.findEntryPoints(); const {entryPoints} = finder.findEntryPoints();
@ -213,8 +215,9 @@ runInEachFileSystem(() => {
...createPackage(_Abs('/path_mapped/node_modules'), 'test', []), ...createPackage(_Abs('/path_mapped/node_modules'), 'test', []),
...createPackage(_Abs('/path_mapped/dist'), 'pkg2'), ...createPackage(_Abs('/path_mapped/dist'), 'pkg2'),
]); ]);
resolver = new DependencyResolver( const srcHost = new EsmDependencyHost(fs, new ModuleResolver(fs, pathMappings));
fs, logger, {esm2015: new EsmDependencyHost(fs, new ModuleResolver(fs, pathMappings))}); const dtsHost = createDtsDependencyHost(fs, pathMappings);
resolver = new DependencyResolver(fs, logger, {esm2015: srcHost}, dtsHost);
const finder = new TargetedEntryPointFinder( const finder = new TargetedEntryPointFinder(
fs, config, logger, resolver, basePath, targetPath, pathMappings); fs, config, logger, resolver, basePath, targetPath, pathMappings);
const {entryPoints} = finder.findEntryPoints(); const {entryPoints} = finder.findEntryPoints();