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
This commit is contained in:
Pete Bacon Darwin
2019-10-04 11:54:33 +01:00
committed by Miško Hevery
parent 916762440c
commit 90007e97ca
7 changed files with 308 additions and 128 deletions

View File

@ -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}" }`
}
];
}
});