From 90007e97ca4705d8b53425e5a3a0b18df97238e1 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Fri, 4 Oct 2019 11:54:33 +0100 Subject: [PATCH] feat(ngcc): support version ranges in project/default configurations (#33008) By appending a version range to the package name, it is now possible to target configuration to specific versions of a package. PR Close #33008 --- package.json | 3 +- packages/compiler-cli/ngcc/BUILD.bazel | 2 + .../ngcc/src/packages/configuration.ts | 151 ++++++++--- .../ngcc/src/packages/entry_point.ts | 21 +- .../ngcc/test/packages/configuration_spec.ts | 243 ++++++++++++------ packages/compiler-cli/package.json | 1 + yarn.lock | 15 +- 7 files changed, 308 insertions(+), 128 deletions(-) diff --git a/package.json b/package.json index 53f345bc69..6472902b26 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "@types/minimist": "^1.2.0", "@types/node": "^10.9.4", "@types/selenium-webdriver": "3.0.7", + "@types/semver": "^6.0.2", "@types/shelljs": "^0.7.8", "@types/systemjs": "0.19.32", "@types/yargs": "^11.1.1", @@ -159,7 +160,7 @@ "mutation-observer": "^1.0.3", "rewire": "2.5.2", "sauce-connect": "https://saucelabs.com/downloads/sc-4.5.1-linux.tar.gz", - "semver": "5.4.1", + "semver": "^6.3.0", "tslint-eslint-rules": "4.1.1", "tslint-no-toplevel-property-access": "0.0.2", "tsutils": "2.27.2", diff --git a/packages/compiler-cli/ngcc/BUILD.bazel b/packages/compiler-cli/ngcc/BUILD.bazel index ba7e40f4eb..d45b894d4c 100644 --- a/packages/compiler-cli/ngcc/BUILD.bazel +++ b/packages/compiler-cli/ngcc/BUILD.bazel @@ -27,10 +27,12 @@ ts_library( "//packages/compiler-cli/src/ngtsc/util", "@npm//@types/convert-source-map", "@npm//@types/node", + "@npm//@types/semver", "@npm//@types/yargs", "@npm//canonical-path", "@npm//dependency-graph", "@npm//magic-string", + "@npm//semver", "@npm//source-map", "@npm//typescript", ], diff --git a/packages/compiler-cli/ngcc/src/packages/configuration.ts b/packages/compiler-cli/ngcc/src/packages/configuration.ts index 612f33a539..87d6274fdc 100644 --- a/packages/compiler-cli/ngcc/src/packages/configuration.ts +++ b/packages/compiler-cli/ngcc/src/packages/configuration.ts @@ -5,6 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +import {satisfies} from 'semver'; import * as vm from 'vm'; import {AbsoluteFsPath, FileSystem, dirname, join, resolve} from '../../../src/ngtsc/file_system'; import {PackageJsonFormatPropertiesMap} from './entry_point'; @@ -12,7 +13,7 @@ import {PackageJsonFormatPropertiesMap} from './entry_point'; /** * The format of a project level configuration file. */ -export interface NgccProjectConfig { packages: {[packagePath: string]: NgccPackageConfig}; } +export interface NgccProjectConfig { packages: {[packagePath: string]: T}; } /** * The format of a package level configuration file. @@ -80,47 +81,96 @@ export const DEFAULT_NGCC_CONFIG: NgccProjectConfig = { } }; +interface VersionedPackageConfig extends NgccPackageConfig { + versionRange: string; +} + const NGCC_CONFIG_FILENAME = 'ngcc.config.js'; +/** + * Ngcc has a hierarchical configuration system that lets us "fix up" packages that do not + * work with ngcc out of the box. + * + * There are three levels at which configuration can be declared: + * + * * Default level - ngcc comes with built-in configuration for well known cases. + * * Package level - a library author publishes a configuration with their package to fix known + * issues. + * * Project level - the application developer provides a configuration that fixes issues specific + * to the libraries used in their application. + * + * Ngcc will match configuration based on the package name but also on its version. This allows + * configuration to provide different fixes to different version ranges of a package. + * + * * Package level configuration is specific to the package version where the configuration is + * found. + * * Default and project level configuration should provide version ranges to ensure that the + * configuration is only applied to the appropriate versions of a package. + * + * When getting a configuration for a package (via `getConfig()`) the caller should provide the + * version of the package in question, if available. If it is not provided then the first available + * configuration for a package is returned. + */ export class NgccConfiguration { - private defaultConfig: NgccProjectConfig; - private cache = new Map(); + private defaultConfig: NgccProjectConfig; + private projectConfig: NgccProjectConfig; + private cache = new Map(); constructor(private fs: FileSystem, baseDir: AbsoluteFsPath) { - this.defaultConfig = this.processDefaultConfig(baseDir); - const projectConfig = this.loadProjectConfig(baseDir); - for (const packagePath in projectConfig.packages) { - const absPackagePath = resolve(baseDir, 'node_modules', packagePath); - const packageConfig = projectConfig.packages[packagePath]; - packageConfig.entryPoints = - this.processEntryPoints(absPackagePath, packageConfig.entryPoints); - this.cache.set(absPackagePath, packageConfig); - } + this.defaultConfig = this.processProjectConfig(baseDir, DEFAULT_NGCC_CONFIG); + this.projectConfig = this.processProjectConfig(baseDir, this.loadProjectConfig(baseDir)); } - getConfig(packagePath: AbsoluteFsPath): NgccPackageConfig { - if (this.cache.has(packagePath)) { - return this.cache.get(packagePath) !; + /** + * Get a configuration for the given `version` of a package at `packagePath`. + * + * @param packagePath The path to the package whose config we want. + * @param version The version of the package whose config we want, or `null` if the package's + * package.json did not exist or was invalid. + */ + getConfig(packagePath: AbsoluteFsPath, version: string|null): VersionedPackageConfig { + const cacheKey = packagePath + (version !== null ? `@${version}` : ''); + if (this.cache.has(cacheKey)) { + return this.cache.get(cacheKey) !; } - const packageConfig = this.loadPackageConfig(packagePath) || - this.defaultConfig.packages[packagePath] || {entryPoints: {}}; - this.cache.set(packagePath, packageConfig); - return packageConfig; + const projectLevelConfig = + findSatisfactoryVersion(this.projectConfig.packages[packagePath], version); + if (projectLevelConfig !== null) { + this.cache.set(cacheKey, projectLevelConfig); + return projectLevelConfig; + } + + const packageLevelConfig = this.loadPackageConfig(packagePath, version); + if (packageLevelConfig !== null) { + this.cache.set(cacheKey, packageLevelConfig); + return packageLevelConfig; + } + + const defaultLevelConfig = + findSatisfactoryVersion(this.defaultConfig.packages[packagePath], version); + if (defaultLevelConfig !== null) { + this.cache.set(cacheKey, defaultLevelConfig); + return defaultLevelConfig; + } + + return {versionRange: '*', entryPoints: {}}; } - private processDefaultConfig(baseDir: AbsoluteFsPath): NgccProjectConfig { - const defaultConfig: NgccProjectConfig = {packages: {}}; - for (const packagePath in DEFAULT_NGCC_CONFIG.packages) { - const absPackagePath = resolve(baseDir, 'node_modules', packagePath); - const packageConfig = DEFAULT_NGCC_CONFIG.packages[packagePath]; + private processProjectConfig(baseDir: AbsoluteFsPath, projectConfig: NgccProjectConfig): + NgccProjectConfig { + const processedConfig: NgccProjectConfig = {packages: {}}; + for (const packagePathAndVersion in projectConfig.packages) { + const packageConfig = projectConfig.packages[packagePathAndVersion]; if (packageConfig) { - packageConfig.entryPoints = - this.processEntryPoints(absPackagePath, packageConfig.entryPoints); - defaultConfig.packages[absPackagePath] = packageConfig; + const [packagePath, versionRange = '*'] = this.splitPathAndVersion(packagePathAndVersion); + const absPackagePath = resolve(baseDir, 'node_modules', packagePath); + const entryPoints = this.processEntryPoints(absPackagePath, packageConfig); + processedConfig.packages[absPackagePath] = processedConfig.packages[absPackagePath] || []; + processedConfig.packages[absPackagePath].push({versionRange, entryPoints}); } } - return defaultConfig; + return processedConfig; } private loadProjectConfig(baseDir: AbsoluteFsPath): NgccProjectConfig { @@ -136,13 +186,15 @@ export class NgccConfiguration { } } - private loadPackageConfig(packagePath: AbsoluteFsPath): NgccPackageConfig|null { + private loadPackageConfig(packagePath: AbsoluteFsPath, version: string|null): + VersionedPackageConfig|null { const configFilePath = join(packagePath, NGCC_CONFIG_FILENAME); if (this.fs.exists(configFilePath)) { try { - const packageConfig = this.evalSrcFile(configFilePath); - packageConfig.entryPoints = this.processEntryPoints(packagePath, packageConfig.entryPoints); - return packageConfig; + return { + versionRange: version || '*', + entryPoints: this.processEntryPoints(packagePath, this.evalSrcFile(configFilePath)), + }; } catch (e) { throw new Error(`Invalid package configuration file at "${configFilePath}": ` + e.message); } @@ -164,14 +216,41 @@ export class NgccConfiguration { return sandbox.module.exports; } - private processEntryPoints( - packagePath: AbsoluteFsPath, entryPoints: {[entryPointPath: string]: NgccEntryPointConfig;}): + private processEntryPoints(packagePath: AbsoluteFsPath, packageConfig: NgccPackageConfig): {[entryPointPath: string]: NgccEntryPointConfig;} { const processedEntryPoints: {[entryPointPath: string]: NgccEntryPointConfig;} = {}; - for (const entryPointPath in entryPoints) { + for (const entryPointPath in packageConfig.entryPoints) { // Change the keys to be absolute paths - processedEntryPoints[resolve(packagePath, entryPointPath)] = entryPoints[entryPointPath]; + processedEntryPoints[resolve(packagePath, entryPointPath)] = + packageConfig.entryPoints[entryPointPath]; } return processedEntryPoints; } + + private splitPathAndVersion(packagePathAndVersion: string): [string, string|undefined] { + const versionIndex = packagePathAndVersion.lastIndexOf('@'); + // Note that > 0 is because we don't want to match @ at the start of the line + // which is what you would have with a namespaced package, e.g. `@angular/common`. + return versionIndex > 0 ? + [ + packagePathAndVersion.substring(0, versionIndex), + packagePathAndVersion.substring(versionIndex + 1) + ] : + [packagePathAndVersion, undefined]; + } +} + +function findSatisfactoryVersion( + configs: VersionedPackageConfig[] | undefined, version: string | null): VersionedPackageConfig| + null { + if (configs === undefined) { + return null; + } + if (version === null) { + // The package has no version (!) - perhaps the entry-point was from a deep import, which made + // it impossible to find the package.json. + // So just return the first config that matches the package name. + return configs[0]; + } + return configs.find(config => satisfies(version, config.versionRange)) || null; } diff --git a/packages/compiler-cli/ngcc/src/packages/entry_point.ts b/packages/compiler-cli/ngcc/src/packages/entry_point.ts index a8b35ded49..2f9cd800ed 100644 --- a/packages/compiler-cli/ngcc/src/packages/entry_point.ts +++ b/packages/compiler-cli/ngcc/src/packages/entry_point.ts @@ -82,7 +82,9 @@ export function getEntryPointInfo( fs: FileSystem, config: NgccConfiguration, logger: Logger, packagePath: AbsoluteFsPath, entryPointPath: AbsoluteFsPath): EntryPoint|null { const packageJsonPath = resolve(entryPointPath, 'package.json'); - const entryPointConfig = config.getConfig(packagePath).entryPoints[entryPointPath]; + const packageVersion = getPackageVersion(fs, packageJsonPath); + const entryPointConfig = + config.getConfig(packagePath, packageVersion).entryPoints[entryPointPath]; if (entryPointConfig === undefined && !fs.exists(packageJsonPath)) { return null; } @@ -224,3 +226,20 @@ function guessTypingsFromPackageJson( } return null; } + +/** + * Find the version of the package at `packageJsonPath`. + * + * @returns the version string or `null` if the package.json does not exist or is invalid. + */ +function getPackageVersion(fs: FileSystem, packageJsonPath: AbsoluteFsPath): string|null { + try { + if (fs.exists(packageJsonPath)) { + const packageJson = JSON.parse(fs.readFile(packageJsonPath)); + return packageJson['version'] || null; + } + } catch { + // Do nothing + } + return null; +} diff --git a/packages/compiler-cli/ngcc/test/packages/configuration_spec.ts b/packages/compiler-cli/ngcc/test/packages/configuration_spec.ts index 8fb42c1618..875c1aa149 100644 --- a/packages/compiler-cli/ngcc/test/packages/configuration_spec.ts +++ b/packages/compiler-cli/ngcc/test/packages/configuration_spec.ts @@ -32,49 +32,47 @@ runInEachFileSystem(() => { describe('getConfig()', () => { describe('at the package level', () => { - it('should return configuration for a package found in a package level file', () => { - loadTestFiles([{ - name: _Abs('/project-1/node_modules/package-1/ngcc.config.js'), - contents: `module.exports = {entryPoints: { './entry-point-1': {}}}` - }]); - const readFileSpy = spyOn(fs, 'readFile').and.callThrough(); - const configuration = new NgccConfiguration(fs, _Abs('/project-1')); - const config = configuration.getConfig(_Abs('/project-1/node_modules/package-1')); + it('should return configuration for a package found in a package level file, with a matching version', + () => { + loadTestFiles(packageWithConfigFiles('package-1', 'entry-point-1', '1.0.0')); + const readFileSpy = spyOn(fs, 'readFile').and.callThrough(); + const configuration = new NgccConfiguration(fs, _Abs('/project-1')); + const config = + configuration.getConfig(_Abs('/project-1/node_modules/package-1'), '1.0.0'); - expect(config).toEqual( - {entryPoints: {[_Abs('/project-1/node_modules/package-1/entry-point-1')]: {}}}); - expect(readFileSpy) - .toHaveBeenCalledWith(_Abs('/project-1/node_modules/package-1/ngcc.config.js')); - }); + expect(config).toEqual({ + versionRange: '1.0.0', + entryPoints: {[_Abs('/project-1/node_modules/package-1/entry-point-1')]: {}} + }); + expect(readFileSpy) + .toHaveBeenCalledWith(_Abs('/project-1/node_modules/package-1/ngcc.config.js')); + }); it('should used cached configuration for a package if available', () => { - loadTestFiles([{ - name: _Abs('/project-1/node_modules/package-1/ngcc.config.js'), - contents: ` - module.exports = { - entryPoints: { - './entry-point-1': {} - }, - };` - }]); + loadTestFiles(packageWithConfigFiles('package-1', 'entry-point-1', '1.0.0')); const configuration = new NgccConfiguration(fs, _Abs('/project-1')); // Populate the cache - configuration.getConfig(_Abs('/project-1/node_modules/package-1')); + configuration.getConfig(_Abs('/project-1/node_modules/package-1'), '1.0.0'); const readFileSpy = spyOn(fs, 'readFile').and.callThrough(); - const config = configuration.getConfig(_Abs('/project-1/node_modules/package-1')); + const config = + configuration.getConfig(_Abs('/project-1/node_modules/package-1'), '1.0.0'); - expect(config).toEqual( - {entryPoints: {[_Abs('/project-1/node_modules/package-1/entry-point-1')]: {}}}); + expect(config).toEqual({ + versionRange: '1.0.0', + entryPoints: {[_Abs('/project-1/node_modules/package-1/entry-point-1')]: {}} + }); expect(readFileSpy).not.toHaveBeenCalled(); }); it('should return an empty configuration object if there is no matching configuration for the package', () => { + loadTestFiles(packageWithConfigFiles('package-2', 'entry-point-1', '1.0.0')); const configuration = new NgccConfiguration(fs, _Abs('/project-1')); - const config = configuration.getConfig(_Abs('/project-1/node_modules/package-1')); - expect(config).toEqual({entryPoints: {}}); + const config = + configuration.getConfig(_Abs('/project-1/node_modules/package-1'), '1.0.0'); + expect(config).toEqual({versionRange: '*', entryPoints: {}}); }); it('should error if a package level config file is badly formatted', () => { @@ -83,7 +81,7 @@ runInEachFileSystem(() => { contents: `bad js code` }]); const configuration = new NgccConfiguration(fs, _Abs('/project-1')); - expect(() => configuration.getConfig(_Abs('/project-1/node_modules/package-1'))) + expect(() => configuration.getConfig(_Abs('/project-1/node_modules/package-1'), '1.0.0')) .toThrowError( `Invalid package configuration file at "${_Abs('/project-1/node_modules/package-1/ngcc.config.js')}": Unexpected identifier`); }); @@ -94,65 +92,125 @@ runInEachFileSystem(() => { loadTestFiles([{ name: _Abs('/project-1/ngcc.config.js'), contents: ` - module.exports = { - packages: { - 'package-1': { - entryPoints: { - './entry-point-1': {} + module.exports = { + packages: { + 'package-1': { + entryPoints: { + './entry-point-1': {} + }, + }, }, - }, - }, - };` + };` }]); const readFileSpy = spyOn(fs, 'readFile').and.callThrough(); const configuration = new NgccConfiguration(fs, _Abs('/project-1')); expect(readFileSpy).toHaveBeenCalledWith(_Abs('/project-1/ngcc.config.js')); - const config = configuration.getConfig(_Abs('/project-1/node_modules/package-1')); - expect(config).toEqual( - {entryPoints: {[_Abs('/project-1/node_modules/package-1/entry-point-1')]: {}}}); + const config = + configuration.getConfig(_Abs('/project-1/node_modules/package-1'), '1.0.0'); + expect(config).toEqual({ + versionRange: '*', + entryPoints: {[_Abs('/project-1/node_modules/package-1/entry-point-1')]: {}} + }); + }); + + it('should return configuration for the correct version of a package found in a project level file', + () => { + loadTestFiles([{ + name: _Abs('/project-1/ngcc.config.js'), + contents: ` + module.exports = { + packages: { + 'package-1@1.0.0': { + entryPoints: { + './entry-point-1': {} + }, + }, + 'package-1@2.*': { + entryPoints: { + './entry-point-2': {} + }, + }, + 'package-1@^3.2.0': { + entryPoints: { + './entry-point-3': {} + }, + }, + }, + };` + }]); + const configuration = new NgccConfiguration(fs, _Abs('/project-1')); + + expect(configuration.getConfig(_Abs('/project-1/node_modules/package-1'), '1.0.0')) + .toEqual({ + versionRange: '1.0.0', + entryPoints: {[_Abs('/project-1/node_modules/package-1/entry-point-1')]: {}} + }); + expect(configuration.getConfig(_Abs('/project-1/node_modules/package-1'), '2.5.0')) + .toEqual({ + versionRange: '2.*', + entryPoints: {[_Abs('/project-1/node_modules/package-1/entry-point-2')]: {}} + }); + expect(configuration.getConfig(_Abs('/project-1/node_modules/package-1'), '3.2.5')) + .toEqual({ + versionRange: '^3.2.0', + entryPoints: {[_Abs('/project-1/node_modules/package-1/entry-point-3')]: {}} + }); + expect(configuration.getConfig(_Abs('/project-1/node_modules/package-1'), '4.0.0')) + .toEqual({versionRange: '*', entryPoints: {}}); + }); + + it('should not get confused by the @ in namespaced packages', () => { + loadTestFiles([{ + name: _Abs('/project-1/ngcc.config.js'), + contents: ` + module.exports = { + packages: { + '@angular/common': { + entryPoints: { + '.': {} + }, + }, + }, + };` + }]); + const configuration = new NgccConfiguration(fs, _Abs('/project-1')); + + expect(configuration.getConfig(_Abs('/project-1/node_modules/@angular/common'), '1.0.0')) + .toEqual({ + versionRange: '*', + entryPoints: {[_Abs('/project-1/node_modules/@angular/common')]: {}} + }); + }); it('should override package level config with project level config per package', () => { - loadTestFiles([ - { - name: _Abs('/project-1/ngcc.config.js'), - contents: ` - module.exports = { - packages: { - 'package-2': { - entryPoints: { - './project-setting-entry-point': {} + loadTestFiles([{ + name: _Abs('/project-1/ngcc.config.js'), + contents: ` + module.exports = { + packages: { + 'package-2': { + entryPoints: { + './project-setting-entry-point': {} + }, + }, }, - }, - }, - };`, - }, - { - name: _Abs('/project-1/node_modules/package-1/ngcc.config.js'), - contents: ` - module.exports = { - entryPoints: { - './package-setting-entry-point': {} - }, - };`, - }, - { - name: _Abs('/project-1/node_modules/package-2/ngcc.config.js'), - contents: ` - module.exports = { - entryPoints: { - './package-setting-entry-point': {} - }, - };`, - } - ]); + };`, + }]); + loadTestFiles( + packageWithConfigFiles('package-1', 'package-setting-entry-point', '1.0.0')); + loadTestFiles( + packageWithConfigFiles('package-2', 'package-setting-entry-point', '1.0.0')); + const readFileSpy = spyOn(fs, 'readFile').and.callThrough(); const configuration = new NgccConfiguration(fs, _Abs('/project-1')); expect(readFileSpy).toHaveBeenCalledWith(_Abs('/project-1/ngcc.config.js')); - const package1Config = configuration.getConfig(_Abs('/project-1/node_modules/package-1')); + const package1Config = + configuration.getConfig(_Abs('/project-1/node_modules/package-1'), '1.0.0'); expect(package1Config).toEqual({ + versionRange: '1.0.0', entryPoints: {[_Abs('/project-1/node_modules/package-1/package-setting-entry-point')]: {}} }); @@ -162,8 +220,10 @@ runInEachFileSystem(() => { // Note that for `package-2` only the project level entry-point is left. // This is because overriding happens for packages as a whole and there is no attempt to // merge entry-points. - const package2Config = configuration.getConfig(_Abs('/project-1/node_modules/package-2')); + const package2Config = + configuration.getConfig(_Abs('/project-1/node_modules/package-2'), '1.0.0'); expect(package2Config).toEqual({ + versionRange: '*', entryPoints: {[_Abs('/project-1/node_modules/package-2/project-setting-entry-point')]: {}} }); @@ -182,38 +242,36 @@ runInEachFileSystem(() => { afterEach(() => { DEFAULT_NGCC_CONFIG.packages['package-1'] = originalDefaultConfig; }); it('should return configuration for a package found in the default config', () => { - const readFileSpy = spyOn(fs, 'readFile').and.callThrough(); const configuration = new NgccConfiguration(fs, _Abs('/project-1')); expect(readFileSpy).not.toHaveBeenCalled(); - const config = configuration.getConfig(_Abs('/project-1/node_modules/package-1')); + const config = + configuration.getConfig(_Abs('/project-1/node_modules/package-1'), '1.0.0'); expect(config).toEqual({ + versionRange: '*', entryPoints: {[_Abs('/project-1/node_modules/package-1/default-level-entry-point')]: {}} }); }); it('should override default level config with package level config, if provided', () => { - loadTestFiles([{ - name: _Abs('/project-1/node_modules/package-1/ngcc.config.js'), - contents: ` - module.exports = { - entryPoints: {'./package-level-entry-point': {}}, - };`, - }]); + loadTestFiles(packageWithConfigFiles('package-1', 'package-level-entry-point', '1.0.0')); const configuration = new NgccConfiguration(fs, _Abs('/project-1')); - const config = configuration.getConfig(_Abs('/project-1/node_modules/package-1')); + const config = + configuration.getConfig(_Abs('/project-1/node_modules/package-1'), '1.0.0'); // Note that only the package-level-entry-point is left. // This is because overriding happens for packages as a whole and there is no attempt to // merge entry-points. expect(config).toEqual({ + versionRange: '1.0.0', entryPoints: {[_Abs('/project-1/node_modules/package-1/package-level-entry-point')]: {}} }); }); it('should override default level config with project level config, if provided', () => { + loadTestFiles(packageWithConfigFiles('package-1', 'package-level-entry-point', '1.0.0')); loadTestFiles([ { name: _Abs('/project-1/node_modules/package-1/ngcc.config.js'), @@ -238,11 +296,13 @@ runInEachFileSystem(() => { ]); const configuration = new NgccConfiguration(fs, _Abs('/project-1')); - const config = configuration.getConfig(_Abs('/project-1/node_modules/package-1')); + const config = + configuration.getConfig(_Abs('/project-1/node_modules/package-1'), '1.0.0'); // Note that only the project-level-entry-point is left. // This is because overriding happens for packages as a whole and there is no attempt to // merge entry-points. expect(config).toEqual({ + versionRange: '*', entryPoints: {[_Abs('/project-1/node_modules/package-1/project-level-entry-point')]: {}} }); @@ -250,4 +310,17 @@ runInEachFileSystem(() => { }); }); }); + + function packageWithConfigFiles(packageName: string, entryPointName: string, version: string) { + return [ + { + name: _Abs(`/project-1/node_modules/${packageName}/ngcc.config.js`), + contents: `module.exports = {entryPoints: { './${entryPointName}': {}}}` + }, + { + name: _Abs(`/project-1/node_modules/${packageName}/package.json`), + contents: `{ "version": "${version}" }` + } + ]; + } }); diff --git a/packages/compiler-cli/package.json b/packages/compiler-cli/package.json index e99c91773d..a38710db1f 100644 --- a/packages/compiler-cli/package.json +++ b/packages/compiler-cli/package.json @@ -17,6 +17,7 @@ "convert-source-map": "^1.5.1", "dependency-graph": "^0.7.2", "magic-string": "^0.25.0", + "semver": "^6.3.0", "source-map": "^0.6.1", "tslib": "^1.9.0", "yargs": "13.1.0" diff --git a/yarn.lock b/yarn.lock index 14dcc69979..58c05f3e7e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -877,6 +877,11 @@ resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-3.0.14.tgz#0b20a2370e6b1b8322c9c3dfcaa409e6c7c0c0a9" integrity sha512-4GbNCDs98uHCT/OMv40qQC/OpoPbYn9XdXeTiFwHBBFO6eJhYEPUu2zDKirXSbHlvDV8oZ9l8EQ+HrEx/YS9DQ== +"@types/semver@^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-6.0.2.tgz#5e8b09f0e4af53034b1d0fb9977a277847836205" + integrity sha512-G1Ggy7/9Nsa1Jt2yiBR2riEuyK2DFNnqow6R7cromXPMNynackRY1vqFTLz/gwnef1LHokbXThcPhqMRjUbkpQ== + "@types/shelljs@^0.7.8": version "0.7.9" resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.7.9.tgz#3abecb72d9cad9cd4b0e7cb86ed10a97d93ba602" @@ -10057,11 +10062,6 @@ semver@5.1.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.1.0.tgz#85f2cf8550465c4df000cf7d86f6b054106ab9e5" integrity sha1-hfLPhVBGXE3wAM99hvawVBBqueU= -semver@5.4.1: - version "5.4.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" - integrity sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg== - semver@5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" @@ -10072,6 +10072,11 @@ semver@6.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.0.0.tgz#05e359ee571e5ad7ed641a6eec1e547ba52dea65" integrity sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ== +semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + semver@~5.0.1: version "5.0.3" resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a"