diff --git a/packages/compiler-cli/ngcc/src/dependencies/dependency_host.ts b/packages/compiler-cli/ngcc/src/dependencies/dependency_host.ts index d71beca388393..e086d71772b6c 100644 --- a/packages/compiler-cli/ngcc/src/dependencies/dependency_host.ts +++ b/packages/compiler-cli/ngcc/src/dependencies/dependency_host.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import {AbsoluteFsPath, FileSystem, PathSegment} from '../../../src/ngtsc/file_system'; +import {EntryPoint} from '../packages/entry_point'; import {resolveFileWithPostfixes} from '../utils'; import {ModuleResolver} from './module_resolver'; @@ -21,6 +22,11 @@ export interface DependencyInfo { deepImports: Set; } +export interface EntryPointWithDependencies { + entryPoint: EntryPoint; + depInfo: DependencyInfo; +} + export function createDependencyInfo(): DependencyInfo { return {dependencies: new Set(), missing: new Set(), deepImports: new Set()}; } diff --git a/packages/compiler-cli/ngcc/src/dependencies/dependency_resolver.ts b/packages/compiler-cli/ngcc/src/dependencies/dependency_resolver.ts index 9570ddcf4ab9e..06e544791d098 100644 --- a/packages/compiler-cli/ngcc/src/dependencies/dependency_resolver.ts +++ b/packages/compiler-cli/ngcc/src/dependencies/dependency_resolver.ts @@ -14,7 +14,7 @@ import {NgccConfiguration} from '../packages/configuration'; import {EntryPoint, EntryPointFormat, getEntryPointFormat, SUPPORTED_FORMAT_PROPERTIES} from '../packages/entry_point'; import {PartiallyOrderedList} from '../utils'; -import {createDependencyInfo, DependencyHost, DependencyInfo} from './dependency_host'; +import {createDependencyInfo, DependencyHost, EntryPointWithDependencies} from './dependency_host'; const builtinNodeJsModules = new Set(require('module').builtinModules); @@ -94,7 +94,7 @@ export class DependencyResolver { * @param target If provided, only return entry-points depended on by this entry-point. * @returns the result of sorting the entry points by dependency. */ - sortEntryPointsByDependency(entryPoints: EntryPoint[], target?: EntryPoint): + sortEntryPointsByDependency(entryPoints: EntryPointWithDependencies[], target?: EntryPoint): SortedEntryPointsInfo { const {invalidEntryPoints, ignoredDependencies, graph} = this.computeDependencyGraph(entryPoints); @@ -120,18 +120,21 @@ 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}'.`); + getEntryPointWithDependencies(entryPoint: EntryPoint): EntryPointWithDependencies { + const dependencies = createDependencyInfo(); + if (entryPoint.compiledByAngular) { + // Only bother to compute dependencies of entry-points that have been compiled by Angular + 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}'.`); + } + host.collectDependencies(formatInfo.path, dependencies); + this.typingsHost.collectDependencies(entryPoint.typings, dependencies); } - const depInfo = createDependencyInfo(); - host.collectDependencies(formatInfo.path, depInfo); - this.typingsHost.collectDependencies(entryPoint.typings, depInfo); - return depInfo; + return {entryPoint, depInfo: dependencies}; } /** @@ -140,20 +143,18 @@ export class DependencyResolver { * The graph only holds entry-points that ngcc cares about and whose dependencies * (direct and transitive) all exist. */ - private computeDependencyGraph(entryPoints: EntryPoint[]): DependencyGraph { + private computeDependencyGraph(entryPoints: EntryPointWithDependencies[]): DependencyGraph { const invalidEntryPoints: InvalidEntryPoint[] = []; const ignoredDependencies: IgnoredDependency[] = []; const graph = new DepGraph(); - const angularEntryPoints = entryPoints.filter(entryPoint => entryPoint.compiledByAngular); + const angularEntryPoints = entryPoints.filter(e => e.entryPoint.compiledByAngular); // Add the Angular compiled entry points to the graph as nodes - angularEntryPoints.forEach(entryPoint => graph.addNode(entryPoint.path, entryPoint)); + angularEntryPoints.forEach(e => graph.addNode(e.entryPoint.path, e.entryPoint)); // Now add the dependencies between them - angularEntryPoints.forEach(entryPoint => { - const {dependencies, missing, deepImports} = this.getEntryPointDependencies(entryPoint); - + angularEntryPoints.forEach(({entryPoint, depInfo: {dependencies, missing, deepImports}}) => { const missingDependencies = Array.from(missing).filter(dep => !builtinNodeJsModules.has(dep)); if (missingDependencies.length > 0 && !entryPoint.ignoreMissingDependencies) { diff --git a/packages/compiler-cli/ngcc/src/entry_point_finder/directory_walker_entry_point_finder.ts b/packages/compiler-cli/ngcc/src/entry_point_finder/directory_walker_entry_point_finder.ts index 4e238a576e285..c4b7843231406 100644 --- a/packages/compiler-cli/ngcc/src/entry_point_finder/directory_walker_entry_point_finder.ts +++ b/packages/compiler-cli/ngcc/src/entry_point_finder/directory_walker_entry_point_finder.ts @@ -6,10 +6,11 @@ * found in the LICENSE file at https://angular.io/license */ import {AbsoluteFsPath, FileSystem, PathSegment} from '../../../src/ngtsc/file_system'; +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 {getEntryPointInfo, INCOMPATIBLE_ENTRY_POINT, NO_ENTRY_POINT} from '../packages/entry_point'; import {EntryPointManifest} from '../packages/entry_point_manifest'; import {PathMappings} from '../utils'; import {NGCC_DIRECTORY} from '../writing/new_entry_point_file_writer'; @@ -32,11 +33,11 @@ export class DirectoryWalkerEntryPointFinder implements EntryPointFinder { * all package entry-points. */ findEntryPoints(): SortedEntryPointsInfo { - const unsortedEntryPoints: EntryPoint[] = []; + const unsortedEntryPoints: EntryPointWithDependencies[] = []; for (const basePath of this.basePaths) { const entryPoints = this.entryPointManifest.readEntryPointsUsingManifest(basePath) || this.walkBasePathForPackages(basePath); - unsortedEntryPoints.push(...entryPoints); + entryPoints.forEach(e => unsortedEntryPoints.push(e)); } return this.resolver.sortEntryPointsByDependency(unsortedEntryPoints); } @@ -47,10 +48,10 @@ export class DirectoryWalkerEntryPointFinder implements EntryPointFinder { * @param basePath The path at which to start the search * @returns an array of `EntryPoint`s that were found within `basePath`. */ - walkBasePathForPackages(basePath: AbsoluteFsPath): EntryPoint[] { + walkBasePathForPackages(basePath: AbsoluteFsPath): EntryPointWithDependencies[] { this.logger.debug( `No manifest found for ${basePath} so walking the directories for entry-points.`); - const entryPoints: EntryPoint[] = trackDuration( + const entryPoints = trackDuration( () => this.walkDirectoryForPackages(basePath), duration => this.logger.debug(`Walking ${basePath} for entry-points took ${duration}s.`)); this.entryPointManifest.writeEntryPointManifest(basePath, entryPoints); @@ -64,7 +65,7 @@ export class DirectoryWalkerEntryPointFinder implements EntryPointFinder { * @param sourceDirectory An absolute path to the root directory where searching begins. * @returns an array of `EntryPoint`s that were found within `sourceDirectory`. */ - walkDirectoryForPackages(sourceDirectory: AbsoluteFsPath): EntryPoint[] { + walkDirectoryForPackages(sourceDirectory: AbsoluteFsPath): EntryPointWithDependencies[] { // Try to get a primary entry point from this directory const primaryEntryPoint = getEntryPointInfo(this.fs, this.config, this.logger, sourceDirectory, sourceDirectory); @@ -76,15 +77,15 @@ export class DirectoryWalkerEntryPointFinder implements EntryPointFinder { return []; } - const entryPoints: EntryPoint[] = []; + const entryPoints: EntryPointWithDependencies[] = []; if (primaryEntryPoint !== NO_ENTRY_POINT) { - entryPoints.push(primaryEntryPoint); + entryPoints.push(this.resolver.getEntryPointWithDependencies(primaryEntryPoint)); this.collectSecondaryEntryPoints( entryPoints, sourceDirectory, sourceDirectory, this.fs.readdir(sourceDirectory)); // Also check for any nested node_modules in this package but only if at least one of the // entry-points was compiled by Angular. - if (entryPoints.some(e => e.compiledByAngular)) { + if (entryPoints.some(e => e.entryPoint.compiledByAngular)) { const nestedNodeModulesPath = this.fs.join(sourceDirectory, 'node_modules'); if (this.fs.exists(nestedNodeModulesPath)) { entryPoints.push(...this.walkDirectoryForPackages(nestedNodeModulesPath)); @@ -125,8 +126,8 @@ export class DirectoryWalkerEntryPointFinder implements EntryPointFinder { * @param paths The paths contained in the current `directory`. */ private collectSecondaryEntryPoints( - entryPoints: EntryPoint[], packagePath: AbsoluteFsPath, directory: AbsoluteFsPath, - paths: PathSegment[]): void { + entryPoints: EntryPointWithDependencies[], packagePath: AbsoluteFsPath, + directory: AbsoluteFsPath, paths: PathSegment[]): void { for (const path of paths) { if (isIgnorablePath(path)) { // Ignore hidden files, node_modules and ngcc directory @@ -153,7 +154,7 @@ export class DirectoryWalkerEntryPointFinder implements EntryPointFinder { const subEntryPoint = getEntryPointInfo(this.fs, this.config, this.logger, packagePath, possibleEntryPointPath); if (subEntryPoint !== NO_ENTRY_POINT && subEntryPoint !== INCOMPATIBLE_ENTRY_POINT) { - entryPoints.push(subEntryPoint); + entryPoints.push(this.resolver.getEntryPointWithDependencies(subEntryPoint)); isEntryPoint = true; } diff --git a/packages/compiler-cli/ngcc/src/entry_point_finder/targeted_entry_point_finder.ts b/packages/compiler-cli/ngcc/src/entry_point_finder/targeted_entry_point_finder.ts index 3258561805a3c..0a5ef883ebdeb 100644 --- a/packages/compiler-cli/ngcc/src/entry_point_finder/targeted_entry_point_finder.ts +++ b/packages/compiler-cli/ngcc/src/entry_point_finder/targeted_entry_point_finder.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import {AbsoluteFsPath, FileSystem, join, PathSegment, relative, relativeFrom} from '../../../src/ngtsc/file_system'; +import {EntryPointWithDependencies} from '../dependencies/dependency_host'; import {DependencyResolver, SortedEntryPointsInfo} from '../dependencies/dependency_resolver'; import {Logger} from '../logging/logger'; import {hasBeenProcessed} from '../packages/build_marker'; @@ -25,7 +26,7 @@ import {getBasePaths} from './utils'; */ export class TargetedEntryPointFinder implements EntryPointFinder { private unprocessedPaths: AbsoluteFsPath[] = []; - private unsortedEntryPoints = new Map(); + private unsortedEntryPoints = new Map(); private basePaths = getBasePaths(this.logger, this.basePath, this.pathMappings); constructor( @@ -40,7 +41,7 @@ export class TargetedEntryPointFinder implements EntryPointFinder { } const targetEntryPoint = this.unsortedEntryPoints.get(this.targetPath); const entryPoints = this.resolver.sortEntryPointsByDependency( - Array.from(this.unsortedEntryPoints.values()), targetEntryPoint); + Array.from(this.unsortedEntryPoints.values()), targetEntryPoint?.entryPoint); const invalidTarget = entryPoints.invalidEntryPoints.find(i => i.entryPoint.path === this.targetPath); @@ -82,9 +83,9 @@ export class TargetedEntryPointFinder implements EntryPointFinder { if (entryPoint === null || !entryPoint.compiledByAngular) { return; } - this.unsortedEntryPoints.set(entryPoint.path, entryPoint); - const deps = this.resolver.getEntryPointDependencies(entryPoint); - deps.dependencies.forEach(dep => { + const entryPointWithDeps = this.resolver.getEntryPointWithDependencies(entryPoint); + this.unsortedEntryPoints.set(entryPoint.path, entryPointWithDeps); + entryPointWithDeps.depInfo.dependencies.forEach(dep => { if (!this.unsortedEntryPoints.has(dep)) { this.unprocessedPaths.push(dep); } diff --git a/packages/compiler-cli/ngcc/src/main.ts b/packages/compiler-cli/ngcc/src/main.ts index e0565b4c5b865..1f63a029bbbf7 100644 --- a/packages/compiler-cli/ngcc/src/main.ts +++ b/packages/compiler-cli/ngcc/src/main.ts @@ -44,7 +44,6 @@ import {NgccConfiguration} from './packages/configuration'; import {EntryPoint, EntryPointJsonProperty, EntryPointPackageJson, getEntryPointFormat, SUPPORTED_FORMAT_PROPERTIES} from './packages/entry_point'; import {makeEntryPointBundle} from './packages/entry_point_bundle'; import {EntryPointManifest, InvalidatingEntryPointManifest} from './packages/entry_point_manifest'; -import {Transformer} from './packages/transformer'; import {PathMappings} from './utils'; import {cleanOutdatedPackages} from './writing/cleaning/package_cleaner'; import {FileWriter} from './writing/file_writer'; @@ -315,6 +314,7 @@ export function mainNgcc({ const createCompileFn: CreateCompileFn = onTaskCompleted => { const fileWriter = getFileWriter( fileSystem, logger, pkgJsonUpdater, createNewEntryPointFormats, errorOnFailedEntryPoint); + const {Transformer} = require('./packages/transformer'); const transformer = new Transformer(fileSystem, logger, tsConfig); return (task: Task) => { diff --git a/packages/compiler-cli/ngcc/src/packages/entry_point_manifest.ts b/packages/compiler-cli/ngcc/src/packages/entry_point_manifest.ts index 60d78df97e670..9321ad594001c 100644 --- a/packages/compiler-cli/ngcc/src/packages/entry_point_manifest.ts +++ b/packages/compiler-cli/ngcc/src/packages/entry_point_manifest.ts @@ -7,12 +7,13 @@ */ import {createHash} from 'crypto'; -import {AbsoluteFsPath, FileSystem} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, FileSystem, PathSegment} from '../../../src/ngtsc/file_system'; +import {EntryPointWithDependencies} from '../dependencies/dependency_host'; import {Logger} from '../logging/logger'; import {NGCC_VERSION} from './build_marker'; import {NgccConfiguration} from './configuration'; -import {EntryPoint, getEntryPointInfo, INCOMPATIBLE_ENTRY_POINT, NO_ENTRY_POINT} from './entry_point'; +import {getEntryPointInfo, INCOMPATIBLE_ENTRY_POINT, NO_ENTRY_POINT} from './entry_point'; /** * Manages reading and writing a manifest file that contains a list of all the entry-points that @@ -40,7 +41,7 @@ export class EntryPointManifest { * @returns an array of entry-point information for all entry-points found below the given * `basePath` or `null` if the manifest was out of date. */ - readEntryPointsUsingManifest(basePath: AbsoluteFsPath): EntryPoint[]|null { + readEntryPointsUsingManifest(basePath: AbsoluteFsPath): EntryPointWithDependencies[]|null { try { if (this.fs.basename(basePath) !== 'node_modules') { return null; @@ -67,16 +68,26 @@ export class EntryPointManifest { basePath} so loading entry-point information directly.`); const startTime = Date.now(); - const entryPoints: EntryPoint[] = []; - for (const [packagePath, entryPointPath] of entryPointPaths) { - const result = - getEntryPointInfo(this.fs, this.config, this.logger, packagePath, entryPointPath); + const entryPoints: EntryPointWithDependencies[] = []; + for (const + [packagePath, entryPointPath, dependencyPaths = [], missingPaths = [], + deepImportPaths = []] of entryPointPaths) { + 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) { throw new Error(`The entry-point manifest at ${ manifestPath} contained an invalid pair of package paths: [${packagePath}, ${ entryPointPath}]`); } else { - entryPoints.push(result); + entryPoints.push({ + entryPoint: result, + depInfo: { + dependencies: new Set(dependencyPaths), + missing: new Set(missingPaths), + deepImports: new Set(deepImportPaths), + } + }); } } const duration = Math.round((Date.now() - startTime) / 100) / 10; @@ -99,7 +110,8 @@ export class EntryPointManifest { * @param basePath The path where the manifest file is to be written. * @param entryPoints A collection of entry-points to record in the manifest. */ - writeEntryPointManifest(basePath: AbsoluteFsPath, entryPoints: EntryPoint[]): void { + writeEntryPointManifest(basePath: AbsoluteFsPath, entryPoints: EntryPointWithDependencies[]): + void { if (this.fs.basename(basePath) !== 'node_modules') { return; } @@ -112,7 +124,27 @@ export class EntryPointManifest { ngccVersion: NGCC_VERSION, configFileHash: this.config.hash, lockFileHash: lockFileHash, - entryPointPaths: entryPoints.map(entryPoint => [entryPoint.package, entryPoint.path]), + entryPointPaths: entryPoints.map(e => { + const entryPointPaths: EntryPointPaths = [ + this.fs.relative(basePath, e.entryPoint.package), + this.fs.relative(basePath, e.entryPoint.path), + ]; + // Only add depInfo arrays if needed. + if (e.depInfo.dependencies.size > 0) { + entryPointPaths[2] = Array.from(e.depInfo.dependencies); + } else if (e.depInfo.missing.size > 0 || e.depInfo.deepImports.size > 0) { + entryPointPaths[2] = []; + } + if (e.depInfo.missing.size > 0) { + entryPointPaths[3] = Array.from(e.depInfo.missing); + } else if (e.depInfo.deepImports.size > 0) { + entryPointPaths[3] = []; + } + if (e.depInfo.deepImports.size > 0) { + entryPointPaths[4] = Array.from(e.depInfo.deepImports); + } + return entryPointPaths; + }), }; this.fs.writeFile(this.getEntryPointManifestPath(basePath), JSON.stringify(manifest)); } @@ -139,15 +171,23 @@ export class EntryPointManifest { * current manifest file. * * It always returns `null` from the `readEntryPointsUsingManifest()` method, which forces a new - * manifest to be created, which will overwrite the current file when `writeEntryPointManifest()` is - * called. + * manifest to be created, which will overwrite the current file when `writeEntryPointManifest()` + * is called. */ export class InvalidatingEntryPointManifest extends EntryPointManifest { - readEntryPointsUsingManifest(basePath: AbsoluteFsPath): EntryPoint[]|null { + readEntryPointsUsingManifest(_basePath: AbsoluteFsPath): EntryPointWithDependencies[]|null { return null; } } +export type EntryPointPaths = [ + string, + string, + Array?, + Array?, + Array?, +]; + /** * The JSON format of the manifest file that is written to disk. */ @@ -155,5 +195,5 @@ export interface EntryPointManifestFile { ngccVersion: string; configFileHash: string; lockFileHash: string; - entryPointPaths: Array<[AbsoluteFsPath, AbsoluteFsPath]>; + entryPointPaths: EntryPointPaths[]; } diff --git a/packages/compiler-cli/ngcc/test/dependencies/dependency_resolver_spec.ts b/packages/compiler-cli/ngcc/test/dependencies/dependency_resolver_spec.ts index 0fffdbcf9071f..d418618d191e6 100644 --- a/packages/compiler-cli/ngcc/test/dependencies/dependency_resolver_spec.ts +++ b/packages/compiler-cli/ngcc/test/dependencies/dependency_resolver_spec.ts @@ -10,7 +10,7 @@ import {DepGraph} from 'dependency-graph'; import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, relativeFrom} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; -import {DependencyInfo} from '../../src/dependencies/dependency_host'; +import {DependencyInfo, EntryPointWithDependencies} from '../../src/dependencies/dependency_host'; import {DependencyResolver, SortedEntryPointsInfo} from '../../src/dependencies/dependency_resolver'; import {DtsDependencyHost} from '../../src/dependencies/dts_dependency_host'; import {EsmDependencyHost} from '../../src/dependencies/esm_dependency_host'; @@ -131,7 +131,8 @@ runInEachFileSystem(() => { .and.callFake(createFakeComputeDependencies(dependencies)); spyOn(dtsHost, 'collectDependencies') .and.callFake(createFakeComputeDependencies(dtsDependencies)); - const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]); + const result = resolver.sortEntryPointsByDependency( + getEntryPointsWithDeps(resolver, [fifth, first, fourth, second, third])); expect(result.entryPoints).toEqual([fifth, fourth, third, second, first]); }); @@ -144,7 +145,8 @@ runInEachFileSystem(() => { [_('/first/index.d.ts')]: {resolved: [], missing: [_('/missing')]}, [_('/second/sub/index.d.ts')]: {resolved: [], missing: []}, })); - const result = resolver.sortEntryPointsByDependency([first, second]); + const result = + resolver.sortEntryPointsByDependency(getEntryPointsWithDeps(resolver, [first, second])); expect(result.entryPoints).toEqual([second]); expect(result.invalidEntryPoints).toEqual([ {entryPoint: first, missingDependencies: [_('/missing')]}, @@ -163,7 +165,8 @@ runInEachFileSystem(() => { [_('/third/index.d.ts')]: {resolved: [], missing: []}, })); // Note that we will process `first` before `second`, which has the missing dependency. - const result = resolver.sortEntryPointsByDependency([first, second, third]); + const result = resolver.sortEntryPointsByDependency( + getEntryPointsWithDeps(resolver, [first, second, third])); expect(result.entryPoints).toEqual([third]); expect(result.invalidEntryPoints).toEqual([ {entryPoint: second, missingDependencies: [_('/missing')]}, @@ -183,7 +186,8 @@ runInEachFileSystem(() => { [_('/third/index.d.ts')]: {resolved: [], missing: []}, })); // Note that we will process `first` after `second`, which has the missing dependency. - const result = resolver.sortEntryPointsByDependency([second, first, third]); + const result = resolver.sortEntryPointsByDependency( + getEntryPointsWithDeps(resolver, [second, first, third])); expect(result.entryPoints).toEqual([third]); expect(result.invalidEntryPoints).toEqual([ {entryPoint: second, missingDependencies: [_('/missing')]}, @@ -202,7 +206,8 @@ runInEachFileSystem(() => { [_('/sixth/index.d.ts')]: {resolved: [], missing: [_('/missing')]}, })); // Note that we will process `first` after `second`, which has the missing dependency. - const result = resolver.sortEntryPointsByDependency([sixthIgnoreMissing, first]); + const result = resolver.sortEntryPointsByDependency( + getEntryPointsWithDeps(resolver, [sixthIgnoreMissing, first])); expect(result.entryPoints).toEqual([sixthIgnoreMissing, first]); expect(result.invalidEntryPoints).toEqual([]); }); @@ -218,7 +223,8 @@ runInEachFileSystem(() => { [_('/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( + getEntryPointsWithDeps(resolver, [first, second, sixthIgnoreMissing])); // sixth has no missing dependencies, but it has _invalid_ dependencies, so it's not // compiled. expect(result.entryPoints).toEqual([]); @@ -235,7 +241,8 @@ runInEachFileSystem(() => { [_('/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( + getEntryPointsWithDeps(resolver, [first, second, third])); expect(result.entryPoints).toEqual([]); expect(result.invalidEntryPoints).toEqual([ {entryPoint: first, missingDependencies: [_('/missing1')]}, @@ -251,7 +258,8 @@ runInEachFileSystem(() => { spyOn(dtsHost, 'collectDependencies').and.callFake(createFakeComputeDependencies({ [_('/first/index.d.ts')]: {resolved: [], missing: []}, })); - const result = resolver.sortEntryPointsByDependency([first]); + const result = + resolver.sortEntryPointsByDependency(getEntryPointsWithDeps(resolver, [first])); expect(result.entryPoints).toEqual([first]); expect(logger.logs.warn).toEqual([[ `Entry point 'first' contains deep imports into '${ @@ -290,7 +298,8 @@ runInEachFileSystem(() => { typings: _('/project/node_modules/test-package/index.d.ts'), } as EntryPoint; - const result = resolver.sortEntryPointsByDependency([testEntryPoint]); + const result = resolver.sortEntryPointsByDependency( + getEntryPointsWithDeps(resolver, [testEntryPoint])); expect(result.entryPoints).toEqual([testEntryPoint]); expect(logger.logs.warn).toEqual([[ `Entry point 'test-package' contains deep imports into '${ @@ -299,14 +308,15 @@ runInEachFileSystem(() => { }); it('should error if the entry point does not have a suitable format', () => { - expect(() => resolver.sortEntryPointsByDependency([ + expect(() => resolver.sortEntryPointsByDependency(getEntryPointsWithDeps(resolver, [ {path: '/first', packageJson: {}, compiledByAngular: true} as EntryPoint - ])).toThrowError(`There is no appropriate source code format in '/first' entry-point.`); + ]))).toThrowError(`There is no appropriate source code format in '/first' entry-point.`); }); it('should error if there is no appropriate DependencyHost for the given formats', () => { resolver = new DependencyResolver(fs, new MockLogger(), config, {esm2015: host}, host); - expect(() => resolver.sortEntryPointsByDependency([first])) + expect( + () => resolver.sortEntryPointsByDependency(getEntryPointsWithDeps(resolver, [first]))) .toThrowError( `Could not find a suitable format for computing dependencies of entry-point: '${ first.path}'.`); @@ -317,7 +327,8 @@ runInEachFileSystem(() => { .and.callFake(createFakeComputeDependencies(dependencies)); spyOn(dtsHost, 'collectDependencies') .and.callFake(createFakeComputeDependencies(dtsDependencies)); - const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]); + const result = resolver.sortEntryPointsByDependency( + getEntryPointsWithDeps(resolver, [fifth, first, fourth, second, third])); expect(result.ignoredDependencies).toEqual([ {entryPoint: first, dependencyPath: _('/ignored-1')}, {entryPoint: third, dependencyPath: _('/ignored-2')}, @@ -329,7 +340,8 @@ runInEachFileSystem(() => { .and.callFake(createFakeComputeDependencies(dependencies)); spyOn(dtsHost, 'collectDependencies') .and.callFake(createFakeComputeDependencies(dtsDependencies)); - const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]); + const result = resolver.sortEntryPointsByDependency( + getEntryPointsWithDeps(resolver, [fifth, first, fourth, second, third])); expect(result.graph).toEqual(jasmine.any(DepGraph)); expect(result.graph.size()).toBe(5); @@ -341,7 +353,7 @@ runInEachFileSystem(() => { .and.callFake(createFakeComputeDependencies(dependencies)); spyOn(dtsHost, 'collectDependencies') .and.callFake(createFakeComputeDependencies(dtsDependencies)); - const entryPoints = [fifth, first, fourth, second, third]; + const entryPoints = getEntryPointsWithDeps(resolver, [fifth, first, fourth, second, third]); let sorted: SortedEntryPointsInfo; sorted = resolver.sortEntryPointsByDependency(entryPoints, first); @@ -363,7 +375,7 @@ runInEachFileSystem(() => { spyOn(dtsHost, 'collectDependencies').and.callFake(createFakeComputeDependencies({ [_('/first/index.d.ts')]: {resolved: [], missing: [_('/missing')]}, })); - const entryPoints = [first]; + const entryPoints = getEntryPointsWithDeps(resolver, [first]); let sorted: SortedEntryPointsInfo; sorted = resolver.sortEntryPointsByDependency(entryPoints, first); @@ -379,7 +391,7 @@ runInEachFileSystem(() => { spyOn(dtsHost, 'collectDependencies').and.callFake(createFakeComputeDependencies({ [_('/first/index.d.ts')]: {resolved: [], missing: ['fs']}, })); - const entryPoints = [first]; + const entryPoints = getEntryPointsWithDeps(resolver, [first]); let sorted: SortedEntryPointsInfo; sorted = resolver.sortEntryPointsByDependency(entryPoints, first); @@ -400,7 +412,8 @@ runInEachFileSystem(() => { .and.callFake(createFakeComputeDependencies(dependencies)); spyOn(dtsHost, 'collectDependencies') .and.callFake(createFakeComputeDependencies(dtsDependencies)); - const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]); + const result = resolver.sortEntryPointsByDependency( + getEntryPointsWithDeps(resolver, [fifth, first, fourth, second, third])); expect(result.entryPoints).toEqual([fifth, fourth, third, second, first]); expect(esm5Host.collectDependencies) @@ -449,7 +462,7 @@ runInEachFileSystem(() => { [_('/second/sub/index.d.ts')]: {resolved: [], missing: [_('/missing2')]}, [_('/third/index.d.ts')]: {resolved: [second.path], missing: []}, })); - const entryPoints = [first, second, third]; + const entryPoints = getEntryPointsWithDeps(resolver, [first, second, third]); const sorted = resolver.sortEntryPointsByDependency(entryPoints); expect(sorted.entryPoints).toEqual([first]); expect(sorted.invalidEntryPoints).toEqual([ @@ -469,6 +482,11 @@ runInEachFileSystem(() => { return {dependencies, missing, deepImports}; }; } + + function getEntryPointsWithDeps( + resolver: DependencyResolver, entryPoints: EntryPoint[]): EntryPointWithDependencies[] { + return entryPoints.map(entryPoint => resolver.getEntryPointWithDependencies(entryPoint)); + } }); }); }); diff --git a/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts b/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts index cab8b9852d9a1..a4c6ae2d31e3e 100644 --- a/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts +++ b/packages/compiler-cli/ngcc/test/integration/ngcc_spec.ts @@ -1079,7 +1079,7 @@ runInEachFileSystem(() => { // Populate the manifest file mainNgcc( {basePath: '/node_modules', propertiesToConsider: ['esm5'], logger: new MockLogger()}); - // Check that common/testings ES5 was processed + // Check that common/testing ES5 was processed let commonTesting = JSON.parse(fs.readFile(_('/node_modules/@angular/common/testing/package.json'))); expect(hasBeenProcessed(commonTesting, 'esm5')).toBe(true); @@ -1087,8 +1087,8 @@ runInEachFileSystem(() => { // Modify the manifest to test that is has no effect let manifest: EntryPointManifestFile = JSON.parse(fs.readFile(_('/node_modules/__ngcc_entry_points__.json'))); - manifest.entryPointPaths = manifest.entryPointPaths.filter( - paths => paths[1] !== _('/node_modules/@angular/common/testing')); + manifest.entryPointPaths = + manifest.entryPointPaths.filter(paths => paths[1] !== '@angular/common/testing'); fs.writeFile(_('/node_modules/__ngcc_entry_points__.json'), JSON.stringify(manifest)); // Now run ngcc again ignoring this manifest but trying to process ES2015, which are not yet // processed. @@ -1107,7 +1107,12 @@ runInEachFileSystem(() => { // had removed earlier. manifest = JSON.parse(fs.readFile(_('/node_modules/__ngcc_entry_points__.json'))); expect(manifest.entryPointPaths).toContain([ - _('/node_modules/@angular/common'), _('/node_modules/@angular/common/testing') + '@angular/common', + '@angular/common/testing', + [ + _('/node_modules/@angular/core'), _('/node_modules/@angular/common'), + _('/node_modules/rxjs') + ], ]); }); }); diff --git a/packages/compiler-cli/ngcc/test/packages/entry_point_manifest_spec.ts b/packages/compiler-cli/ngcc/test/packages/entry_point_manifest_spec.ts index 12debe0965011..afed5fe6b134d 100644 --- a/packages/compiler-cli/ngcc/test/packages/entry_point_manifest_spec.ts +++ b/packages/compiler-cli/ngcc/test/packages/entry_point_manifest_spec.ts @@ -7,12 +7,12 @@ */ import {createHash} from 'crypto'; -import {absoluteFrom, FileSystem, getFileSystem} from '../../../src/ngtsc/file_system'; +import {absoluteFrom, FileSystem, getFileSystem, relativeFrom} from '../../../src/ngtsc/file_system'; import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing'; import {loadTestFiles} from '../../../test/helpers'; +import {EntryPointWithDependencies} from '../../src/dependencies/dependency_host'; import {NGCC_VERSION} from '../../src/packages/build_marker'; import {NgccConfiguration} from '../../src/packages/configuration'; -import {EntryPoint} from '../../src/packages/entry_point'; import {EntryPointManifest, EntryPointManifestFile} from '../../src/packages/entry_point_manifest'; import {MockLogger} from '../helpers/mock_logger'; @@ -129,22 +129,48 @@ runInEachFileSystem(() => { ]); manifestFile.entryPointPaths.push([ _Abs('/project/node_modules/some_package'), - _Abs('/project/node_modules/some_package/valid_entry_point') + _Abs('/project/node_modules/some_package/valid_entry_point'), + [ + _Abs('/project/node_modules/other_package_1'), + _Abs('/project/node_modules/other_package_2'), + ], + [ + _Abs('/project/node_modules/missing_1'), + relativeFrom('missing_2'), + ], + [ + _Abs('/project/node_modules/deep/import/path'), + ], ]); fs.writeFile( _Abs('/project/node_modules/__ngcc_entry_points__.json'), JSON.stringify(manifestFile)); const entryPoints = manifest.readEntryPointsUsingManifest(_Abs('/project/node_modules')); expect(entryPoints).toEqual([{ - name: 'some_package/valid_entry_point', - packageJson: jasmine.any(Object), - package: _Abs('/project/node_modules/some_package'), - path: _Abs('/project/node_modules/some_package/valid_entry_point'), - typings: - _Abs('/project/node_modules/some_package/valid_entry_point/valid_entry_point.d.ts'), - compiledByAngular: true, - ignoreMissingDependencies: false, - generateDeepReexports: false, - } as any]); + entryPoint: { + name: 'some_package/valid_entry_point', + packageJson: jasmine.any(Object), + package: _Abs('/project/node_modules/some_package'), + path: _Abs('/project/node_modules/some_package/valid_entry_point'), + typings: + _Abs('/project/node_modules/some_package/valid_entry_point/valid_entry_point.d.ts'), + compiledByAngular: true, + ignoreMissingDependencies: false, + generateDeepReexports: false, + } as any, + depInfo: { + dependencies: new Set([ + _Abs('/project/node_modules/other_package_1'), + _Abs('/project/node_modules/other_package_2'), + ]), + missing: new Set([ + _Abs('/project/node_modules/missing_1'), + relativeFrom('missing_2'), + ]), + deepImports: new Set([ + _Abs('/project/node_modules/deep/import/path'), + ]) + } + }]); }); it('should return null if any of the entry-points are not valid', () => { @@ -152,7 +178,7 @@ runInEachFileSystem(() => { fs.writeFile(_Abs('/project/yarn.lock'), 'LOCK FILE CONTENTS'); manifestFile.entryPointPaths.push([ _Abs('/project/node_modules/some_package'), - _Abs('/project/node_modules/some_package/valid_entry_point') + _Abs('/project/node_modules/some_package/valid_entry_point'), [], [], [] ]); fs.writeFile( _Abs('/project/node_modules/__ngcc_entry_points__.json'), JSON.stringify(manifestFile)); @@ -223,25 +249,59 @@ runInEachFileSystem(() => { it('should write the package path and entry-point path of each entry-point provided', () => { fs.writeFile(_Abs('/project/package-lock.json'), 'LOCK FILE CONTENTS'); - const entryPoint1 = { - package: _Abs('/project/node_modules/package-1/'), - path: _Abs('/project/node_modules/package-1/'), - } as unknown as EntryPoint; - const entryPoint2 = { - package: _Abs('/project/node_modules/package-2/'), - path: _Abs('/project/node_modules/package-2/entry-point'), - } as unknown as EntryPoint; + const entryPoint1: EntryPointWithDependencies = { + entryPoint: { + package: _Abs('/project/node_modules/package-1/'), + path: _Abs('/project/node_modules/package-1/'), + } as any, + depInfo: { + dependencies: new Set([ + _Abs('/project/node_modules/other_package_1'), + _Abs('/project/node_modules/other_package_2'), + ]), + missing: new Set(), + deepImports: new Set() + } + }; + const entryPoint2: EntryPointWithDependencies = { + entryPoint: { + package: _Abs('/project/node_modules/package-2/'), + path: _Abs('/project/node_modules/package-2/entry-point'), + } as any, + depInfo: { + dependencies: new Set(), + missing: new Set([ + _Abs('/project/node_modules/missing_1'), + relativeFrom('missing_2'), + ]), + deepImports: new Set([ + _Abs('/project/node_modules/deep/import/path'), + ]) + } + }; manifest.writeEntryPointManifest(_Abs('/project/node_modules'), [entryPoint1, entryPoint2]); const file: EntryPointManifestFile = JSON.parse(fs.readFile(_Abs('/project/node_modules/__ngcc_entry_points__.json'))); expect(file.entryPointPaths).toEqual([ [ - _Abs('/project/node_modules/package-1/'), - _Abs('/project/node_modules/package-1/'), + 'package-1', + 'package-1', + [ + _Abs('/project/node_modules/other_package_1'), + _Abs('/project/node_modules/other_package_2'), + ], ], [ - _Abs('/project/node_modules/package-2/'), - _Abs('/project/node_modules/package-2/entry-point'), + 'package-2', + 'package-2/entry-point', + [], + [ + _Abs('/project/node_modules/missing_1'), + relativeFrom('missing_2'), + ], + [ + _Abs('/project/node_modules/deep/import/path'), + ], ] ]); });