Move yarn new templates from @backstage/cli to @backstage/cli-module-new

The built-in templates used by `yarn new` are moved from `packages/cli/templates/`
to `packages/cli-module-new/templates/`, colocating them with the code that
consumes them.

A backwards compatibility rewrite in the template resolution ensures that
existing `@backstage/cli/templates/*` references in root `package.json`
configurations continue to work by transparently resolving them to
`@backstage/cli-module-new/templates/*`.

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor
This commit is contained in:
Patrik Oldsberg
2026-03-17 12:39:05 +01:00
parent e9eb585978
commit ea90ab0590
129 changed files with 146 additions and 49 deletions
@@ -15,15 +15,15 @@
*/
export const defaultTemplates = [
'@backstage/cli/templates/frontend-plugin',
'@backstage/cli/templates/backend-plugin',
'@backstage/cli/templates/backend-plugin-module',
'@backstage/cli/templates/plugin-web-library',
'@backstage/cli/templates/plugin-node-library',
'@backstage/cli/templates/plugin-common-library',
'@backstage/cli/templates/web-library',
'@backstage/cli/templates/node-library',
'@backstage/cli/templates/cli-module',
'@backstage/cli/templates/catalog-provider-module',
'@backstage/cli/templates/scaffolder-backend-module',
'@backstage/cli-module-new/templates/frontend-plugin',
'@backstage/cli-module-new/templates/backend-plugin',
'@backstage/cli-module-new/templates/backend-plugin-module',
'@backstage/cli-module-new/templates/plugin-web-library',
'@backstage/cli-module-new/templates/plugin-node-library',
'@backstage/cli-module-new/templates/plugin-common-library',
'@backstage/cli-module-new/templates/web-library',
'@backstage/cli-module-new/templates/node-library',
'@backstage/cli-module-new/templates/cli-module',
'@backstage/cli-module-new/templates/catalog-provider-module',
'@backstage/cli-module-new/templates/scaffolder-backend-module',
];
@@ -200,6 +200,72 @@ describe('loadPortableTemplateConfig', () => {
});
});
it('should rewrite legacy @backstage/cli/templates paths', async () => {
mockDir.setContent({
'package.json': JSON.stringify({
backstage: {
cli: {
new: {
templates: [
'@backstage/cli/templates/backend-plugin',
'@backstage/cli/templates/frontend-plugin',
],
},
},
},
}),
node_modules: {
'@backstage': {
'cli-module-new': {
templates: {
'backend-plugin': {
[TEMPLATE_FILE_NAME]:
'name: backend-plugin\nrole: backend-plugin\n',
},
'frontend-plugin': {
[TEMPLATE_FILE_NAME]:
'name: frontend-plugin\nrole: frontend-plugin\n',
},
},
},
},
},
});
await expect(
loadPortableTemplateConfig({
packagePath: mockDir.resolve('package.json'),
}),
).resolves.toEqual({
isUsingDefaultTemplates: false,
templatePointers: [
{
name: 'backend-plugin',
target: realpathSync(
mockDir.resolve(
'node_modules/@backstage/cli-module-new/templates/backend-plugin',
TEMPLATE_FILE_NAME,
),
),
},
{
name: 'frontend-plugin',
target: realpathSync(
mockDir.resolve(
'node_modules/@backstage/cli-module-new/templates/frontend-plugin',
TEMPLATE_FILE_NAME,
),
),
},
],
license: 'Apache-2.0',
private: true,
version: '0.1.0',
packageNamePrefix: '@internal/',
packageNamePluginInfix: 'backstage-plugin-',
});
});
it('should reject templates with conflicting names', async () => {
mockDir.setContent({
'package.json': JSON.stringify({
@@ -157,6 +157,9 @@ export async function loadPortableTemplateConfig(
};
}
const CLI_TEMPLATE_PREFIX = '@backstage/cli/templates/';
const CLI_MODULE_NEW_TEMPLATE_PREFIX = '@backstage/cli-module-new/templates/';
function resolveLocalTemplatePath(pointer: string, basePath: string): string {
if (isAbsolute(pointer)) {
throw new Error(`Template target may not be an absolute path`);
@@ -166,7 +169,14 @@ function resolveLocalTemplatePath(pointer: string, basePath: string): string {
return resolvePath(basePath, pointer, TEMPLATE_FILE_NAME);
}
return require.resolve(`${pointer}/${TEMPLATE_FILE_NAME}`, {
// Rewrite legacy @backstage/cli/templates/* paths to @backstage/cli-module-new/templates/*
const resolvedPointer = pointer.startsWith(CLI_TEMPLATE_PREFIX)
? `${CLI_MODULE_NEW_TEMPLATE_PREFIX}${pointer.slice(
CLI_TEMPLATE_PREFIX.length,
)}`
: pointer;
return require.resolve(`${resolvedPointer}/${TEMPLATE_FILE_NAME}`, {
paths: [basePath],
});
}