cli/new: refactor types

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2025-02-07 14:28:28 +01:00
parent 077aa5d2e1
commit ac04e0482e
11 changed files with 60 additions and 94 deletions
@@ -15,7 +15,7 @@
*/
import inquirer from 'inquirer';
import { NewConfig } from '../config/types';
import { NewConfig } from '../types';
import { collectTemplateParams } from './collectTemplateParams';
describe('collectTemplateParams', () => {
@@ -16,14 +16,14 @@
import { getCodeownersFilePath } from '../../codeowners';
import { paths } from '../../paths';
import { NewConfig } from '../config/types';
import { NewConfig } from '../types';
import { promptOptions } from './prompts';
import { Template } from '../types';
import { NewTemplate } from '../types';
import { Options } from '../execution/utils';
type CollectTemplateParamsOptions = {
config: NewConfig;
template: Template;
template: NewTemplate;
globals: Record<string, string | number | boolean>;
prefilledParams: Record<string, string | number | boolean>;
};
@@ -14,10 +14,14 @@
* limitations under the License.
*/
import inquirer from 'inquirer';
import { Prompt, ConfigurablePrompt } from '../types';
import inquirer, { Answers, DistinctQuestion } from 'inquirer';
import { NewTemplatePrompt } from '../types';
import { parseOwnerIds } from '../../codeowners';
export type Prompt<TOptions extends Answers> = DistinctQuestion<TOptions> & {
name: string;
};
export function pluginIdPrompt(): Prompt<{ id: string }> {
return {
type: 'input',
@@ -94,12 +98,12 @@ export async function promptOptions({
globals,
codeOwnersFilePath,
}: {
prompts: ConfigurablePrompt[];
prompts: NewTemplatePrompt[];
globals: { [name in string]?: string | boolean | number };
codeOwnersFilePath: string | undefined;
}): Promise<Record<string, string>> {
const answers = await inquirer.prompt(
prompts.map((prompt: ConfigurablePrompt) => {
prompts.map((prompt: NewTemplatePrompt) => {
if (typeof prompt === 'string') {
switch (prompt) {
case 'id':
@@ -17,7 +17,7 @@
import fs from 'fs-extra';
import { paths } from '../../paths';
import { defaultTemplates } from '../defaultTemplates';
import { NewConfig } from './types';
import { NewConfig } from '../types';
import { z } from 'zod';
import { fromZodError } from 'zod-validation-error';
import { ForwardedError } from '@backstage/errors';
-39
View File
@@ -1,39 +0,0 @@
/*
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export type NewConfig = {
/**
* The pointers to templates that can be used.
*/
templatePointers: NewTemplatePointer[];
/**
* Whether the default set of templates are being used or not.
*/
isUsingDefaultTemplates: boolean;
/**
* Templating globals that should apply to all templates.
*/
globals: {
[KName in string]?: number | string | boolean;
};
};
export type NewTemplatePointer = {
id: string;
target: string;
};
@@ -29,7 +29,17 @@ import { paths } from '../../paths';
import { Task } from '../../tasks';
import { Lockfile } from '../../versioning';
import { createPackageVersionProvider } from '../../version';
import { CreateContext } from '../types';
export interface CreateContext {
/** Whether we are creating something in a monorepo or not */
isMonoRepo: boolean;
/** Creates a temporary directory. This will always be deleted after creation is done. */
createTemporaryDirectory(name: string): Promise<string>;
/** Signal that the creation process got to a point where permanent modifications were made */
markAsModified(): void;
}
export async function executePluginPackageTemplate(
ctx: CreateContext,
@@ -27,13 +27,13 @@ import { createDirName, resolvePackageName } from './utils';
import { runAdditionalActions } from './additionalActions';
import { executePluginPackageTemplate } from './executePluginPackageTemplate';
import { TemporaryDirectoryManager } from './TemporaryDirectoryManager';
import { NewConfig } from '../config/types';
import { Template } from '../types';
import { NewConfig } from '../types';
import { NewTemplate } from '../types';
import { Options } from './utils';
type ExecuteNewTemplateOptions = {
config: NewConfig;
template: Template;
template: NewTemplate;
params: Options;
};
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { resolvePackageName, createDirName, Options } from './utils';
import { Template } from '../types';
import { NewTemplate } from '../types';
describe('resolvePackageName', () => {
it('should generate correct name without scope', () => {
@@ -82,7 +82,7 @@ describe('createDirName', () => {
it('should return name in the backend-module format if backendModulePrefix is set to true', () => {
expect(
createDirName(
{ backendModulePrefix: true } as Template,
{ backendModulePrefix: true } as NewTemplate,
{
id: 'foo',
moduleId: 'bar',
@@ -94,7 +94,7 @@ describe('createDirName', () => {
it('should throw an error if backendModulePrefix is configured as true but is missing moduleId', () => {
expect(() =>
createDirName(
{ backendModulePrefix: true } as Template,
{ backendModulePrefix: true } as NewTemplate,
{
id: 'foo',
moduleId: '',
@@ -105,12 +105,12 @@ describe('createDirName', () => {
it('should append the suffix value if one is provided', () => {
expect(
createDirName({ suffix: 'foo' } as Template, { id: 'bar' } as Options),
createDirName({ suffix: 'foo' } as NewTemplate, { id: 'bar' } as Options),
).toEqual('bar-foo');
});
it('should return id if neither backendModulePrefix nor suffix is specified', () => {
expect(createDirName({} as Template, { id: 'foo' } as Options)).toEqual(
expect(createDirName({} as NewTemplate, { id: 'foo' } as Options)).toEqual(
'foo',
);
});
+2 -2
View File
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Template } from '../types';
import { NewTemplate } from '../types';
export interface Options extends Record<string, string | boolean> {
id: string;
@@ -49,7 +49,7 @@ export const resolvePackageName = (options: {
return plugin ? `backstage-plugin-${baseName}` : baseName;
};
export function createDirName(template: Template, options: Options) {
export function createDirName(template: NewTemplate, options: Options) {
if (!options.id) {
throw new Error(`id prompt is mandatory for all cli templates`);
}
@@ -21,8 +21,8 @@ import { resolve as resolvePath } from 'path';
import { dirname } from 'node:path';
import { parse as parseYaml } from 'yaml';
import { paths } from '../../paths';
import { NewConfig, NewTemplatePointer } from '../config/types';
import { Template } from '../types';
import { NewConfig, NewTemplatePointer } from '../types';
import { NewTemplate } from '../types';
import { ForwardedError } from '@backstage/errors';
import { fromZodError } from 'zod-validation-error';
@@ -87,7 +87,7 @@ export class NewTemplateLoader {
static async loadTemplate({
id,
target,
}: NewTemplatePointer): Promise<Template> {
}: NewTemplatePointer): Promise<NewTemplate> {
if (target.match(/https?:\/\//)) {
throw new Error('Remote templates are not supported yet');
}
+23 -32
View File
@@ -14,35 +14,31 @@
* limitations under the License.
*/
import { Answers, DistinctQuestion } from 'inquirer';
export type NewConfig = {
/**
* The pointers to templates that can be used.
*/
templatePointers: NewTemplatePointer[];
export type CliConfig =
| {
/** Setting this to false will omit default backstage-cli new templates */
defaults?: boolean;
/** Where you can explicitly declare templates */
templates?: TemplateLocation[];
/** For configuring global values that applies to all new plugins/packages */
globals?: Record<string, string>;
}
| undefined;
/**
* Whether the default set of templates are being used or not.
*/
isUsingDefaultTemplates: boolean;
export interface CreateContext {
/** Whether we are creating something in a monorepo or not */
isMonoRepo: boolean;
/** Creates a temporary directory. This will always be deleted after creation is done. */
createTemporaryDirectory(name: string): Promise<string>;
/** Signal that the creation process got to a point where permanent modifications were made */
markAsModified(): void;
}
export type Prompt<TOptions extends Answers> = DistinctQuestion<TOptions> & {
name: string;
/**
* Templating globals that should apply to all templates.
*/
globals: {
[KName in string]?: number | string | boolean;
};
};
export type ConfigurablePrompt =
export type NewTemplatePointer = {
id: string;
target: string;
};
export type NewTemplatePrompt =
| {
id: string;
prompt: string;
@@ -51,7 +47,7 @@ export type ConfigurablePrompt =
}
| string;
export interface Template {
export interface NewTemplate {
id: string;
description?: string;
templatePath: string;
@@ -59,11 +55,6 @@ export interface Template {
plugin?: boolean;
backendModulePrefix?: boolean;
suffix?: string;
prompts?: ConfigurablePrompt[];
prompts?: NewTemplatePrompt[];
additionalActions?: string[];
}
export interface TemplateLocation {
id: string;
target: string;
}