diff --git a/.changeset/nervous-months-teach.md b/.changeset/nervous-months-teach.md new file mode 100644 index 0000000000..cb477f3879 --- /dev/null +++ b/.changeset/nervous-months-teach.md @@ -0,0 +1,5 @@ +--- +'@backstage/config-loader': patch +--- + +Allow collection of configuration schemas from multiple versions of the same package. diff --git a/packages/config-loader/src/lib/schema/collect.test.ts b/packages/config-loader/src/lib/schema/collect.test.ts index 3600991717..877cbb54e3 100644 --- a/packages/config-loader/src/lib/schema/collect.test.ts +++ b/packages/config-loader/src/lib/schema/collect.test.ts @@ -231,6 +231,74 @@ describe('collectConfigSchemas', () => { ]); }); + it('should load schema from different package versions', async () => { + mockFs({ + node_modules: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + dependencies: { + b: '1', + c: '1', + }, + configSchema: mockSchema, + }), + }, + b: { + 'package.json': JSON.stringify({ + name: 'b', + version: '1', + dependencies: { + c: '2', + }, + configSchema: { ...mockSchema, title: 'b' }, + }), + node_modules: { + c: { + 'package.json': JSON.stringify({ + name: 'c', + version: '2', + configSchema: { ...mockSchema, title: 'c2' }, + }), + }, + }, + }, + c: { + 'package.json': JSON.stringify({ + name: 'c', + version: '1', + configSchema: { ...mockSchema, title: 'c1' }, + }), + }, + }, + }); + + await expect(collectConfigSchemas(['a'], [])).resolves.toEqual([ + { + path: path.join('node_modules', 'a', 'package.json'), + value: mockSchema, + }, + { + path: path.join('node_modules', 'b', 'package.json'), + value: { ...mockSchema, title: 'b' }, + }, + { + path: path.join('node_modules', 'c', 'package.json'), + value: { ...mockSchema, title: 'c1' }, + }, + { + path: path.join( + 'node_modules', + 'b', + 'node_modules', + 'c', + 'package.json', + ), + value: { ...mockSchema, title: 'c2' }, + }, + ]); + }); + it('should not allow unknown schema file types', async () => { mockFs({ node_modules: { diff --git a/packages/config-loader/src/lib/schema/collect.ts b/packages/config-loader/src/lib/schema/collect.ts index 81a28e9d7b..f8b134119b 100644 --- a/packages/config-loader/src/lib/schema/collect.ts +++ b/packages/config-loader/src/lib/schema/collect.ts @@ -43,9 +43,10 @@ export async function collectConfigSchemas( packageNames: string[], packagePaths: string[], ): Promise { - const visitedPackages = new Set(); - const schemas = Array(); - const tsSchemaPaths = Array(); + const schemas = new Array(); + const tsSchemaPaths = new Array(); + const visitedPackageVersions = new Map>(); // pkgName: [versions...] + const currentDir = await fs.realpath(process.cwd()); async function processItem(item: Item) { @@ -59,16 +60,6 @@ export async function collectConfigSchemas( } else if (item.name) { const { name, parentPath } = item; - // Ensures that we only process each package once. We don't bother with - // loading in schemas from duplicates of different versions, as that's not - // supported by Backstage right now anyway. We may want to change that in - // the future though, if it for example becomes possible to load in two - // different versions of e.g. @backstage/core at once. - if (visitedPackages.has(name)) { - return; - } - visitedPackages.add(name); - try { pkgPath = req.resolve( `${name}/package.json`, @@ -86,6 +77,18 @@ export async function collectConfigSchemas( } const pkg = await fs.readJson(pkgPath); + + // Ensures that we only process the same version of each package once. + let versions = visitedPackageVersions.get(pkg.name); + if (versions?.has(pkg.version)) { + return; + } + if (!versions) { + versions = new Set(); + visitedPackageVersions.set(pkg.name, versions); + } + versions.add(pkg.version); + const depNames = [ ...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.devDependencies ?? {}),