cli/new: add support for templated template values

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2025-02-08 13:49:57 +01:00
parent b751402fc6
commit 68e87d7f4c
4 changed files with 40 additions and 20 deletions
@@ -24,12 +24,31 @@ import {
resolve as resolvePath,
relative as relativePath,
} from 'path';
import camelCase from 'lodash/camelCase';
import kebabCase from 'lodash/kebabCase';
import lowerCase from 'lodash/lowerCase';
import snakeCase from 'lodash/snakeCase';
import startCase from 'lodash/startCase';
import upperCase from 'lodash/upperCase';
import upperFirst from 'lodash/upperFirst';
import lowerFirst from 'lodash/lowerFirst';
import { paths } from '../../paths';
import { Task } from '../../tasks';
import { Lockfile } from '../../versioning';
import { createPackageVersionProvider } from '../../version';
const helpers = {
camelCase,
kebabCase,
lowerCase,
snakeCase,
startCase,
upperCase,
upperFirst,
lowerFirst,
};
export interface CreateContext {
/** Whether we are creating something in a monorepo or not */
isMonoRepo: boolean;
@@ -44,6 +63,7 @@ export interface CreateContext {
export async function executePluginPackageTemplate(
ctx: CreateContext,
options: {
templateValues: Record<string, string>;
templateDir: string;
targetDir: string;
values: Record<string, unknown>;
@@ -77,6 +97,7 @@ export async function executePluginPackageTemplate(
Task.section('Executing Template');
await templatingTask(
templateDir,
options.templateValues,
tempDir,
values,
createPackageVersionProvider(lockfile),
@@ -104,6 +125,7 @@ export async function executePluginPackageTemplate(
export async function templatingTask(
templateDir: string,
templateValues: Record<string, string>,
destinationDir: string,
context: any,
versionProvider: (name: string, versionHint?: string) => string,
@@ -113,6 +135,12 @@ export async function templatingTask(
throw new Error(`Failed to read template directory: ${error.message}`);
});
const templatedValues = Object.fromEntries(
Object.entries(templateValues).map(([name, tmpl]) => {
return [name, handlebars.compile(tmpl)(context, { helpers })];
}),
);
for (const file of files) {
const destinationFile = file.replace(templateDir, destinationDir);
await fs.ensureDir(dirname(destinationFile));
@@ -126,7 +154,7 @@ export async function templatingTask(
strict: true,
});
const contents = compiled(
{ name: basename(destination), ...context },
{ name: basename(destination), ...context, ...templatedValues },
{
helpers: {
versionQuery(name: string, versionHint: string | unknown) {
@@ -135,6 +163,7 @@ export async function templatingTask(
typeof versionHint === 'string' ? versionHint : undefined,
);
},
...helpers,
},
},
);
@@ -14,8 +14,6 @@
* limitations under the License.
*/
import camelCase from 'lodash/camelCase';
import upperFirst from 'lodash/upperFirst';
import { isMonoRepo } from '@backstage/cli-node';
import { assertError } from '@backstage/errors';
@@ -47,14 +45,6 @@ export async function executePortableTemplate(
const targetDir = paths.resolveTargetRoot(packageInfo.packagePath);
const moduleVar =
params.moduleId ??
`${camelCase(params.id)}Module${camelCase(
params.moduleId,
)[0].toUpperCase()}${camelCase(params.moduleId).slice(1)}`; // used in default-backend-module template
const extensionName = `${upperFirst(camelCase(params.id))}Page`; // used in default-plugin template
const pluginVar = `${camelCase(params.id)}Plugin`; // used in default-backend-plugin and default-plugin template
let modified = false;
try {
await executePluginPackageTemplate(
@@ -68,14 +58,13 @@ export async function executePortableTemplate(
{
targetDir,
templateDir: template.templatePath,
templateValues: template.templateValues,
values: {
name: packageName,
privatePackage: params.private,
pluginVersion: params.baseVersion,
moduleVar,
extensionName,
pluginVar,
...params,
packageName: packageInfo.packageName,
privatePackage: input.globals.private,
packageVersion: input.globals.baseVersion,
license: input.globals.license,
...input.params,
},
},
);
@@ -42,6 +42,7 @@ const templateDefinitionSchema = z
)
.optional(),
additionalActions: z.array(z.string()).optional(),
templateValues: z.record(z.string()).optional(),
})
.strict();
@@ -70,11 +71,11 @@ export async function loadPortableTemplate({
);
}
const { template, ...templateData } = parsed.data;
const { template, templateValues = {}, ...templateData } = parsed.data;
const templatePath = resolvePath(dirname(target), template);
if (!fs.existsSync(templatePath)) {
throw new Error(`Failed to load template contents from '${templatePath}'`);
}
return { id, templatePath, ...templateData };
return { id, templatePath, templateValues, ...templateData };
}
+1
View File
@@ -66,6 +66,7 @@ export type PortableTemplate = {
role: PortableTemplateRole;
prompts?: PortableTemplatePrompt[];
additionalActions?: string[];
templateValues: Record<string, string>;
};
export type PortableTemplateParams = {