From 326240eb91fe5eee2b12755d4aa70684130008cf Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Wed, 1 Apr 2020 16:16:02 +0300 Subject: [PATCH] fix(ngcc): allow ngcc configuration to match pre-release versions of packages (#36370) Ngcc supports providing a project-level configuration to affect how certain dependencies are processed and also has a built-in fallback configuration for some unmaintained packages. Each entry in these configurations could be scoped to specific versions of a package by providing a version range. If no version range is provided for a package, it defaults to `*` (with the intention of matching any version). Previously, the installed version of a package was tested against the version range using the [semver][1] package's `satisfies()` function with the default options. By default, `satisfies()` does not match pre-releases (see [here][2] for more details on reasoning). While this makes sense when determining what version of a dependency to install (trying to avoid unexpected breaking changes), it is not desired in the case of ngcc. This commit fixes it by explicitly specifying that pre-release versions should be matched normally. [1]: https://www.npmjs.com/package/semver [2]: https://github.com/npm/node-semver#prerelease-tags PR Close #36370 --- .../ngcc/src/packages/configuration.ts | 4 +- .../ngcc/test/packages/configuration_spec.ts | 211 +++++++++++++++++- 2 files changed, 212 insertions(+), 3 deletions(-) diff --git a/packages/compiler-cli/ngcc/src/packages/configuration.ts b/packages/compiler-cli/ngcc/src/packages/configuration.ts index 133d1ad2a8..ea1660e917 100644 --- a/packages/compiler-cli/ngcc/src/packages/configuration.ts +++ b/packages/compiler-cli/ngcc/src/packages/configuration.ts @@ -304,5 +304,7 @@ function findSatisfactoryVersion( // So just return the first config that matches the package name. return configs[0]; } - return configs.find(config => satisfies(version, config.versionRange)) || null; + return configs.find( + config => satisfies(version, config.versionRange, {includePrerelease: true})) || + null; } diff --git a/packages/compiler-cli/ngcc/test/packages/configuration_spec.ts b/packages/compiler-cli/ngcc/test/packages/configuration_spec.ts index c74a2d2f87..f91abd6c7d 100644 --- a/packages/compiler-cli/ngcc/test/packages/configuration_spec.ts +++ b/packages/compiler-cli/ngcc/test/packages/configuration_spec.ts @@ -214,6 +214,114 @@ runInEachFileSystem(() => { .toEqual({versionRange: '*', entryPoints: {}}); }); + it('should correctly handle pre-release versions and version ranges', () => { + loadTestFiles([ + { + name: _Abs('/project-1/ngcc.config.js'), + contents: ` + module.exports = { + packages: { + 'package-1': { + entryPoints: { + './entry-point-1': {}, + }, + }, + 'package-2@1.0.0-beta.2': { + entryPoints: { + './entry-point-2': {}, + }, + }, + 'package-3@>=1.0.0-beta.2': { + entryPoints: { + './entry-point-3': {}, + }, + }, + }, + }; + `, + }, + ]); + + const configuration = new NgccConfiguration(fs, _Abs('/project-1')); + const getConfig = (packageName: string, version: string | null) => + configuration.getConfig(_Abs(`/project-1/node_modules/${packageName}`), version); + + // Default version range: * + expect(getConfig('package-1', '1.0.0-beta.2')) + .toEqual( + { + versionRange: '*', + entryPoints: {[_Abs('/project-1/node_modules/package-1/entry-point-1')]: {}}, + }, + 'Config for package-1@1.0.0-beta.2'); + + // Version range: 1.0.0-beta.2 + expect(getConfig('package-2', '1.0.0-beta.2')) + .toEqual( + { + versionRange: '1.0.0-beta.2', + entryPoints: {[_Abs('/project-1/node_modules/package-2/entry-point-2')]: {}}, + }, + 'Config for package-2@1.0.0-beta.2'); + + expect(getConfig('package-2', '1.0.0')) + .toEqual( + { + versionRange: '*', + entryPoints: {}, + }, + 'Config for package-2@1.0.0'); + + expect(getConfig('package-2', null)) + .toEqual( + { + versionRange: '1.0.0-beta.2', + entryPoints: {[_Abs('/project-1/node_modules/package-2/entry-point-2')]: {}}, + }, + 'Config for package-2@null'); + + // Version range: >=1.0.0-beta.2 + expect(getConfig('package-3', '1.0.0-beta.2')) + .toEqual( + { + versionRange: '>=1.0.0-beta.2', + entryPoints: {[_Abs('/project-1/node_modules/package-3/entry-point-3')]: {}}, + }, + 'Config for package-3@1.0.0-beta.2'); + + expect(getConfig('package-3', '1.0.0')) + .toEqual( + { + versionRange: '>=1.0.0-beta.2', + entryPoints: {[_Abs('/project-1/node_modules/package-3/entry-point-3')]: {}}, + }, + 'Config for package-3@1.0.0'); + + expect(getConfig('package-3', '2.0.0')) + .toEqual( + { + versionRange: '>=1.0.0-beta.2', + entryPoints: {[_Abs('/project-1/node_modules/package-3/entry-point-3')]: {}}, + }, + 'Config for package-3@2.0.0'); + + expect(getConfig('package-3', '1.0.0-beta.1')) + .toEqual( + { + versionRange: '*', + entryPoints: {}, + }, + 'Config for package-3@1.0.0-beta.1'); + + expect(getConfig('package-3', '0.9.99')) + .toEqual( + { + versionRange: '*', + entryPoints: {}, + }, + 'Config for package-3@0.9.99'); + }); + it('should not get confused by the @ in namespaced packages', () => { loadTestFiles([{ name: _Abs('/project-1/ngcc.config.js'), @@ -287,13 +395,13 @@ runInEachFileSystem(() => { }); describe('at the default level', () => { - const originalDefaultConfig = DEFAULT_NGCC_CONFIG.packages['package-1']; + const originalDefaultConfig = JSON.stringify(DEFAULT_NGCC_CONFIG.packages); beforeEach(() => { DEFAULT_NGCC_CONFIG.packages['package-1'] = { entryPoints: {'./default-level-entry-point': {}}, }; }); - afterEach(() => { DEFAULT_NGCC_CONFIG.packages['package-1'] = originalDefaultConfig; }); + afterEach(() => DEFAULT_NGCC_CONFIG.packages = JSON.parse(originalDefaultConfig)); it('should return configuration for a package found in the default config', () => { const readFileSpy = spyOn(fs, 'readFile').and.callThrough(); @@ -361,6 +469,105 @@ runInEachFileSystem(() => { {[_Abs('/project-1/node_modules/package-1/project-level-entry-point')]: {}} }); }); + + it('should correctly handle pre-release versions and version ranges', () => { + Object.assign(DEFAULT_NGCC_CONFIG.packages, { + 'package-1': { + entryPoints: { + './entry-point-1': {}, + }, + }, + 'package-2@1.0.0-beta.2': { + entryPoints: { + './entry-point-2': {}, + }, + }, + 'package-3@>=1.0.0-beta.2': { + entryPoints: { + './entry-point-3': {}, + }, + }, + }); + + const configuration = new NgccConfiguration(fs, _Abs('/project-1')); + const getConfig = (packageName: string, version: string | null) => + configuration.getConfig(_Abs(`/project-1/node_modules/${packageName}`), version); + + // Default version range: * + expect(getConfig('package-1', '1.0.0-beta.2')) + .toEqual( + { + versionRange: '*', + entryPoints: {[_Abs('/project-1/node_modules/package-1/entry-point-1')]: {}}, + }, + 'Config for package-1@1.0.0-beta.2'); + + // Version range: 1.0.0-beta.2 + expect(getConfig('package-2', '1.0.0-beta.2')) + .toEqual( + { + versionRange: '1.0.0-beta.2', + entryPoints: {[_Abs('/project-1/node_modules/package-2/entry-point-2')]: {}}, + }, + 'Config for package-2@1.0.0-beta.2'); + + expect(getConfig('package-2', '1.0.0')) + .toEqual( + { + versionRange: '*', + entryPoints: {}, + }, + 'Config for package-2@1.0.0'); + + expect(getConfig('package-2', null)) + .toEqual( + { + versionRange: '1.0.0-beta.2', + entryPoints: {[_Abs('/project-1/node_modules/package-2/entry-point-2')]: {}}, + }, + 'Config for package-2@null'); + + // Version range: >=1.0.0-beta.2 + expect(getConfig('package-3', '1.0.0-beta.2')) + .toEqual( + { + versionRange: '>=1.0.0-beta.2', + entryPoints: {[_Abs('/project-1/node_modules/package-3/entry-point-3')]: {}}, + }, + 'Config for package-3@1.0.0-beta.2'); + + expect(getConfig('package-3', '1.0.0')) + .toEqual( + { + versionRange: '>=1.0.0-beta.2', + entryPoints: {[_Abs('/project-1/node_modules/package-3/entry-point-3')]: {}}, + }, + 'Config for package-3@1.0.0'); + + expect(getConfig('package-3', '2.0.0')) + .toEqual( + { + versionRange: '>=1.0.0-beta.2', + entryPoints: {[_Abs('/project-1/node_modules/package-3/entry-point-3')]: {}}, + }, + 'Config for package-3@2.0.0'); + + expect(getConfig('package-3', '1.0.0-beta.1')) + .toEqual( + { + versionRange: '*', + entryPoints: {}, + }, + 'Config for package-3@1.0.0-beta.1'); + + expect(getConfig('package-3', '0.9.99')) + .toEqual( + { + versionRange: '*', + entryPoints: {}, + }, + 'Config for package-3@0.9.99'); + }); }); }); });