cli/new: peek into local template definitions
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -15,54 +15,13 @@
|
||||
*/
|
||||
|
||||
export const defaultTemplates = [
|
||||
{
|
||||
id: 'backend-module',
|
||||
target: require.resolve(
|
||||
'@backstage/cli/templates/default-backend-module.yaml',
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'backend-plugin',
|
||||
target: require.resolve(
|
||||
'@backstage/cli/templates/default-backend-plugin.yaml',
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'plugin-common',
|
||||
target: require.resolve(
|
||||
'@backstage/cli/templates/default-common-plugin-package.yaml',
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'plugin-node',
|
||||
target: require.resolve(
|
||||
'@backstage/cli/templates/default-node-plugin-package.yaml',
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'frontend-plugin',
|
||||
target: require.resolve('@backstage/cli/templates/default-plugin.yaml'),
|
||||
},
|
||||
{
|
||||
id: 'plugin-react',
|
||||
target: require.resolve(
|
||||
'@backstage/cli/templates/default-react-plugin-package.yaml',
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'node-library',
|
||||
target: require.resolve(
|
||||
'@backstage/cli/templates/node-library-package.yaml',
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'web-library',
|
||||
target: require.resolve(
|
||||
'@backstage/cli/templates/web-library-package.yaml',
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'scaffolder-module',
|
||||
target: require.resolve('@backstage/cli/templates/scaffolder-module.yaml'),
|
||||
},
|
||||
'@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/scaffolder-backend-module',
|
||||
];
|
||||
|
||||
@@ -15,9 +15,11 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import { resolve as resolvePath, dirname, extname } from 'node:path';
|
||||
import { paths } from '../../paths';
|
||||
import { defaultTemplates } from '../defaultTemplates';
|
||||
import { PortableTemplateConfig } from '../types';
|
||||
import { PortableTemplateConfig, PortableTemplatePointer } from '../types';
|
||||
import { parse as parseYaml } from 'yaml';
|
||||
import { z } from 'zod';
|
||||
import { fromZodError } from 'zod-validation-error';
|
||||
import { ForwardedError } from '@backstage/errors';
|
||||
@@ -31,27 +33,9 @@ const defaults = {
|
||||
packageNamePluginInfix: 'plugin-',
|
||||
};
|
||||
|
||||
const builtInTemplateIds = defaultTemplates.map(t => `default-${t.id}`) as [
|
||||
string,
|
||||
...string[],
|
||||
];
|
||||
|
||||
const newConfigSchema = z
|
||||
.object({
|
||||
templates: z
|
||||
.array(
|
||||
z.union([
|
||||
z.enum(builtInTemplateIds),
|
||||
z
|
||||
.object({
|
||||
id: z.string(),
|
||||
description: z.string().optional(),
|
||||
target: z.string(),
|
||||
})
|
||||
.strict(),
|
||||
]),
|
||||
)
|
||||
.optional(),
|
||||
templates: z.array(z.string()).optional(),
|
||||
globals: z
|
||||
.object({
|
||||
license: z.string().optional(),
|
||||
@@ -100,19 +84,21 @@ export async function loadPortableTemplateConfig(
|
||||
|
||||
const config = parsed.data.backstage?.cli?.new;
|
||||
|
||||
const templatePointers =
|
||||
config?.templates?.map(t => {
|
||||
if (typeof t === 'string') {
|
||||
const defaultTemplate = defaultTemplates.find(
|
||||
d => t === `default-${d.id}`,
|
||||
const basePath = dirname(pkgPath);
|
||||
const templatePointers = await Promise.all(
|
||||
(config?.templates ?? defaultTemplates).map(pointer => {
|
||||
try {
|
||||
const templatePath = resolveLocalTemplatePath(pointer, basePath);
|
||||
|
||||
return peekLocalTemplateDefinition(templatePath);
|
||||
} catch (error) {
|
||||
throw new ForwardedError(
|
||||
`Failed to load template definition '${pointer}'`,
|
||||
error,
|
||||
);
|
||||
if (!defaultTemplate) {
|
||||
throw new Error(`Built-in template '${t}' does not exist`);
|
||||
}
|
||||
return defaultTemplate;
|
||||
}
|
||||
return t;
|
||||
}) ?? defaultTemplates;
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
isUsingDefaultTemplates: !config?.templates,
|
||||
@@ -134,3 +120,42 @@ export async function loadPortableTemplateConfig(
|
||||
defaults.packageNamePluginInfix,
|
||||
};
|
||||
}
|
||||
|
||||
function resolveLocalTemplatePath(pointer: string, basePath: string): string {
|
||||
const isDirectoryPointer = extname(pointer) === '';
|
||||
|
||||
if (pointer.startsWith('.')) {
|
||||
if (isDirectoryPointer) {
|
||||
return resolvePath(basePath, pointer, 'template.yaml');
|
||||
}
|
||||
return resolvePath(basePath, pointer);
|
||||
}
|
||||
|
||||
if (isDirectoryPointer) {
|
||||
return require.resolve(`${pointer}/template.yaml`, { paths: [basePath] });
|
||||
}
|
||||
return require.resolve(pointer, { paths: [basePath] });
|
||||
}
|
||||
|
||||
const partialTemplateDefinitionSchema = z.object({
|
||||
name: z.string(),
|
||||
description: z.string().optional(),
|
||||
});
|
||||
|
||||
async function peekLocalTemplateDefinition(
|
||||
target: string,
|
||||
): Promise<PortableTemplatePointer> {
|
||||
const content = await fs.readFile(target, 'utf8');
|
||||
|
||||
const rawTemplate = parseYaml(content);
|
||||
const parsed = partialTemplateDefinitionSchema.safeParse(rawTemplate);
|
||||
if (!parsed.success) {
|
||||
throw fromZodError(parsed.error);
|
||||
}
|
||||
|
||||
return {
|
||||
name: parsed.data.name,
|
||||
description: parsed.data.description,
|
||||
target,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export type PortableTemplateConfig = {
|
||||
};
|
||||
|
||||
export type PortableTemplatePointer = {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
target: string;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user