diff --git a/packages/cli/src/lib/new/preparation/loadPortableTemplateConfig.test.ts b/packages/cli/src/lib/new/preparation/loadPortableTemplateConfig.test.ts index 5646c03524..2649db8bb8 100644 --- a/packages/cli/src/lib/new/preparation/loadPortableTemplateConfig.test.ts +++ b/packages/cli/src/lib/new/preparation/loadPortableTemplateConfig.test.ts @@ -84,6 +84,54 @@ describe('loadPortableTemplateConfig', () => { }); }); + it('should support pointing to built-in templates', async () => { + mockDir.setContent({ + 'package.json': JSON.stringify({ + backstage: { + new: { + templates: [ + 'backend-plugin', + { id: 'template1', target: 'path/to/template1' }, + 'frontend-plugin', + ], + globals: { + license: 'MIT', + private: true, + namePrefix: '@acme/', + namePluginInfix: 'backstage-plugin-', + }, + }, + }, + }), + }); + + await expect( + loadPortableTemplateConfig({ + packagePath: mockDir.resolve('package.json'), + }), + ).resolves.toEqual({ + isUsingDefaultTemplates: false, + templatePointers: [ + { + id: 'backend-plugin', + description: 'A new backend plugin', + target: expect.stringMatching(/default-backend-plugin.yaml$/), + }, + { id: 'template1', target: 'path/to/template1' }, + { + id: 'frontend-plugin', + description: 'A new frontend plugin', + target: expect.stringMatching(/default-plugin.yaml$/), + }, + ], + license: 'MIT', + private: true, + version: '0.1.0', + packageNamePrefix: '@acme/', + packageNamePluginInfix: 'backstage-plugin-', + }); + }); + it('should use default templates if none are specified', async () => { mockDir.setContent({ 'package.json': JSON.stringify({ @@ -129,7 +177,27 @@ describe('loadPortableTemplateConfig', () => { packagePath: mockDir.resolve('package.json'), }), ).rejects.toThrow( - /^Failed to load templating configuration from '.*'; caused by Validation error/, + /^Failed to load templating configuration from '.*'; caused by Validation error: Expected array/, + ); + }); + + it('should throw an error if built-in template does not exist', async () => { + mockDir.setContent({ + 'package.json': JSON.stringify({ + backstage: { + new: { + templates: ['invalid'], + }, + }, + }), + }); + + await expect( + loadPortableTemplateConfig({ + packagePath: mockDir.resolve('package.json'), + }), + ).rejects.toThrow( + /^Failed to load templating configuration from '.*'; caused by Validation error: Invalid enum value/, ); }); diff --git a/packages/cli/src/lib/new/preparation/loadPortableTemplateConfig.ts b/packages/cli/src/lib/new/preparation/loadPortableTemplateConfig.ts index 144d194062..5ec79acfd7 100644 --- a/packages/cli/src/lib/new/preparation/loadPortableTemplateConfig.ts +++ b/packages/cli/src/lib/new/preparation/loadPortableTemplateConfig.ts @@ -37,13 +37,18 @@ const pkgJsonWithNewConfigSchema = z.object({ .object({ templates: z .array( - z - .object({ - id: z.string(), - description: z.string().optional(), - target: z.string(), - }) - .strict(), + z.union([ + z.enum( + defaultTemplates.map(t => t.id) as [string, ...string[]], + ), + z + .object({ + id: z.string(), + description: z.string().optional(), + target: z.string(), + }) + .strict(), + ]), ) .optional(), globals: z @@ -85,9 +90,21 @@ export async function loadPortableTemplateConfig( const config = parsed.data.backstage?.new; + const templatePointers = + config?.templates?.map(t => { + if (typeof t === 'string') { + const defaultTemplate = defaultTemplates.find(d => d.id === t); + if (!defaultTemplate) { + throw new Error(`Built-in template '${t}' does not exist`); + } + return defaultTemplate; + } + return t; + }) ?? defaultTemplates; + return { isUsingDefaultTemplates: !config?.templates, - templatePointers: config?.templates ?? defaultTemplates, + templatePointers, license: overrides.license ?? config?.globals?.license ?? defaults.license, version: overrides.version ?? config?.globals?.version ?? defaults.version, private: overrides.private ?? config?.globals?.private ?? defaults.private,