cli/new: refactor prompts
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -33,16 +33,16 @@ export default async (opts: ArgOptions) => {
|
||||
...globals
|
||||
} = opts;
|
||||
|
||||
const argOptions = parseOptions(rawArgOptions);
|
||||
const prefilledParams = parseParams(rawArgOptions);
|
||||
|
||||
await createNewPackage({
|
||||
argOptions,
|
||||
prefilledParams,
|
||||
preselectedTemplateId,
|
||||
globals,
|
||||
});
|
||||
};
|
||||
|
||||
function parseOptions(optionStrings: string[]): Record<string, string> {
|
||||
function parseParams(optionStrings: string[]): Record<string, string> {
|
||||
const options: Record<string, string> = {};
|
||||
|
||||
for (const str of optionStrings) {
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2024 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.
|
||||
*/
|
||||
|
||||
import inquirer from 'inquirer';
|
||||
import { NewConfig } from '../config/types';
|
||||
import { collectTemplateParams } from './collectTemplateParams';
|
||||
|
||||
describe('collectTemplateParams', () => {
|
||||
const baseOptions = {
|
||||
config: {
|
||||
isUsingDefaultTemplates: false,
|
||||
templatePointers: [],
|
||||
globals: {},
|
||||
} satisfies NewConfig,
|
||||
template: {
|
||||
id: 'test',
|
||||
templatePath: '/test',
|
||||
targetPath: '/example',
|
||||
},
|
||||
globals: {},
|
||||
prefilledParams: {},
|
||||
};
|
||||
|
||||
it('should return default values if not provided', async () => {
|
||||
await expect(collectTemplateParams(baseOptions)).resolves.toEqual({
|
||||
id: '',
|
||||
private: true,
|
||||
baseVersion: '0.1.0',
|
||||
owner: '',
|
||||
license: 'Apache-2.0',
|
||||
targetPath: '/example',
|
||||
scope: '',
|
||||
moduleId: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('should include all non-standard global and prompt values', async () => {
|
||||
await expect(
|
||||
collectTemplateParams({ ...baseOptions, globals: { foo: 'bar' } }),
|
||||
).resolves.toEqual({
|
||||
id: '',
|
||||
private: true,
|
||||
baseVersion: '0.1.0',
|
||||
owner: '',
|
||||
license: 'Apache-2.0',
|
||||
targetPath: '/example',
|
||||
scope: '',
|
||||
moduleId: '',
|
||||
foo: 'bar',
|
||||
});
|
||||
});
|
||||
|
||||
it('should use prefilled parameters', async () => {
|
||||
await expect(
|
||||
collectTemplateParams({
|
||||
...baseOptions,
|
||||
template: {
|
||||
...baseOptions.template,
|
||||
prompts: ['id'],
|
||||
},
|
||||
prefilledParams: { id: 'test' },
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
id: 'test',
|
||||
private: true,
|
||||
baseVersion: '0.1.0',
|
||||
owner: '',
|
||||
license: 'Apache-2.0',
|
||||
targetPath: '/example',
|
||||
scope: '',
|
||||
moduleId: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('should prompt for parameters', async () => {
|
||||
jest.spyOn(inquirer, 'prompt').mockResolvedValueOnce({ id: 'test' });
|
||||
|
||||
await expect(
|
||||
collectTemplateParams({
|
||||
...baseOptions,
|
||||
template: {
|
||||
...baseOptions.template,
|
||||
prompts: ['id'],
|
||||
},
|
||||
prefilledParams: {},
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
id: 'test',
|
||||
private: true,
|
||||
baseVersion: '0.1.0',
|
||||
owner: '',
|
||||
license: 'Apache-2.0',
|
||||
targetPath: '/example',
|
||||
scope: '',
|
||||
moduleId: '',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { getCodeownersFilePath } from '../../codeowners';
|
||||
import { paths } from '../../paths';
|
||||
import { NewConfig } from '../config/types';
|
||||
import { promptOptions } from './prompts';
|
||||
import { Template } from '../types';
|
||||
import { Options } from '../utils';
|
||||
|
||||
type CollectTemplateParamsOptions = {
|
||||
config: NewConfig;
|
||||
template: Template;
|
||||
globals: Record<string, string | number | boolean>;
|
||||
prefilledParams: Record<string, string | number | boolean>;
|
||||
};
|
||||
|
||||
const defaultParams = {
|
||||
id: '',
|
||||
owner: '',
|
||||
license: 'Apache-2.0',
|
||||
scope: '',
|
||||
moduleId: '',
|
||||
baseVersion: '0.1.0',
|
||||
private: true,
|
||||
};
|
||||
|
||||
export async function collectTemplateParams(
|
||||
options: CollectTemplateParamsOptions,
|
||||
): Promise<Options> {
|
||||
const { config, template, globals, prefilledParams } = options;
|
||||
|
||||
const codeOwnersFilePath = await getCodeownersFilePath(paths.targetRoot);
|
||||
|
||||
const prompts = template.prompts ?? [];
|
||||
|
||||
const prefilledAnswers = Object.fromEntries(
|
||||
prompts.flatMap(prompt => {
|
||||
const id = typeof prompt === 'string' ? prompt : prompt.id;
|
||||
const answer = prefilledParams[id];
|
||||
return answer ? [[id, answer]] : [];
|
||||
}),
|
||||
);
|
||||
|
||||
const promptAnswers = await promptOptions({
|
||||
prompts:
|
||||
prompts.filter(
|
||||
prompt =>
|
||||
!Object.hasOwn(
|
||||
prefilledAnswers,
|
||||
typeof prompt === 'string' ? prompt : prompt.id,
|
||||
),
|
||||
) ?? [],
|
||||
globals: config.globals,
|
||||
codeOwnersFilePath,
|
||||
});
|
||||
|
||||
return {
|
||||
...defaultParams,
|
||||
...globals,
|
||||
...prefilledAnswers,
|
||||
...promptAnswers,
|
||||
targetPath: template.targetPath,
|
||||
};
|
||||
}
|
||||
+2
-2
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
|
||||
import inquirer from 'inquirer';
|
||||
import { Prompt, ConfigurablePrompt } from './types';
|
||||
import { parseOwnerIds } from '../codeowners';
|
||||
import { Prompt, ConfigurablePrompt } from '../types';
|
||||
import { parseOwnerIds } from '../../codeowners';
|
||||
|
||||
export function pluginIdPrompt(): Prompt<{ id: string }> {
|
||||
return {
|
||||
@@ -21,15 +21,15 @@ import { assertError } from '@backstage/errors';
|
||||
|
||||
import { paths } from '../paths';
|
||||
import { Task } from '../tasks';
|
||||
import { addCodeownersEntry, getCodeownersFilePath } from '../codeowners';
|
||||
import { addCodeownersEntry } from '../codeowners';
|
||||
|
||||
import { promptOptions } from './prompts';
|
||||
import { populateOptions, createDirName, resolvePackageName } from './utils';
|
||||
import { createDirName, resolvePackageName } from './utils';
|
||||
import { runAdditionalActions } from './additionalActions';
|
||||
import { executePluginPackageTemplate } from './executeTemplate';
|
||||
import { TemporaryDirectoryManager } from './TemporaryDirectoryManager';
|
||||
import { loadNewConfig } from './config/loadNewConfig';
|
||||
import { NewTemplateLoader } from './loader/NewTemplateLoader';
|
||||
import { collectTemplateParams } from './collection/collectTemplateParams';
|
||||
|
||||
export type CreateNewPackageOptions = {
|
||||
preselectedTemplateId?: string;
|
||||
@@ -40,7 +40,7 @@ export type CreateNewPackageOptions = {
|
||||
license?: string;
|
||||
baseVersion?: string;
|
||||
};
|
||||
argOptions: { [name in string]?: string };
|
||||
prefilledParams: Record<string, string>;
|
||||
};
|
||||
|
||||
export async function createNewPackage(options: CreateNewPackageOptions) {
|
||||
@@ -52,29 +52,12 @@ export async function createNewPackage(options: CreateNewPackageOptions) {
|
||||
);
|
||||
const template = await NewTemplateLoader.loadTemplate(selectedTemplate);
|
||||
|
||||
const codeOwnersFilePath = await getCodeownersFilePath(paths.targetRoot);
|
||||
|
||||
const prefilledAnswers = Object.fromEntries(
|
||||
(template.prompts ?? []).flatMap(prompt => {
|
||||
const id = typeof prompt === 'string' ? prompt : prompt.id;
|
||||
const answer = options.argOptions[id];
|
||||
return answer ? [[id, answer]] : [];
|
||||
}),
|
||||
);
|
||||
const promptAnswers = await promptOptions({
|
||||
prompts:
|
||||
template.prompts?.filter(
|
||||
prompt =>
|
||||
!Object.hasOwn(
|
||||
prefilledAnswers,
|
||||
typeof prompt === 'string' ? prompt : prompt.id,
|
||||
),
|
||||
) ?? [],
|
||||
globals: newConfig.globals,
|
||||
codeOwnersFilePath,
|
||||
const params = await collectTemplateParams({
|
||||
config: newConfig,
|
||||
template,
|
||||
globals: options.globals,
|
||||
prefilledParams: options.prefilledParams,
|
||||
});
|
||||
const answers = { ...prefilledAnswers, ...promptAnswers };
|
||||
const params = populateOptions({ ...answers, ...options.globals }, template);
|
||||
|
||||
const tmpDirManager = TemporaryDirectoryManager.create();
|
||||
|
||||
|
||||
@@ -13,12 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {
|
||||
resolvePackageName,
|
||||
populateOptions,
|
||||
createDirName,
|
||||
Options,
|
||||
} from './utils';
|
||||
import { resolvePackageName, createDirName, Options } from './utils';
|
||||
import { Template } from './types';
|
||||
|
||||
describe('resolvePackageName', () => {
|
||||
@@ -83,58 +78,6 @@ describe('resolvePackageName', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('populateOptions', () => {
|
||||
it('should return default values if not provided', () => {
|
||||
expect(populateOptions({}, { targetPath: '/example' } as Template)).toEqual(
|
||||
{
|
||||
id: '',
|
||||
private: true,
|
||||
baseVersion: '0.1.0',
|
||||
owner: '',
|
||||
license: 'Apache-2.0',
|
||||
targetPath: '/example',
|
||||
scope: '',
|
||||
moduleId: '',
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('should include all non-standard global and prompt values', () => {
|
||||
expect(
|
||||
populateOptions({ foo: 'bar' }, {
|
||||
targetPath: '/example',
|
||||
} as Template),
|
||||
).toEqual({
|
||||
id: '',
|
||||
private: true,
|
||||
baseVersion: '0.1.0',
|
||||
owner: '',
|
||||
license: 'Apache-2.0',
|
||||
targetPath: '/example',
|
||||
scope: '',
|
||||
moduleId: '',
|
||||
foo: 'bar',
|
||||
});
|
||||
});
|
||||
|
||||
it('should priority global targetPath over the targetPath specified in template', () => {
|
||||
expect(
|
||||
populateOptions({ targetPath: '/global' }, {
|
||||
targetPath: '/example',
|
||||
} as Template),
|
||||
).toEqual({
|
||||
id: '',
|
||||
private: true,
|
||||
baseVersion: '0.1.0',
|
||||
owner: '',
|
||||
license: 'Apache-2.0',
|
||||
targetPath: '/global',
|
||||
scope: '',
|
||||
moduleId: '',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('createDirName', () => {
|
||||
it('should return name in the backend-module format if backendModulePrefix is set to true', () => {
|
||||
expect(
|
||||
|
||||
@@ -49,23 +49,6 @@ export const resolvePackageName = (options: {
|
||||
return plugin ? `backstage-plugin-${baseName}` : baseName;
|
||||
};
|
||||
|
||||
export function populateOptions(
|
||||
options: { [name in string]?: string | boolean },
|
||||
template: Template,
|
||||
): Options {
|
||||
return {
|
||||
id: '',
|
||||
owner: '',
|
||||
license: 'Apache-2.0',
|
||||
scope: '',
|
||||
moduleId: '',
|
||||
baseVersion: '0.1.0',
|
||||
private: true,
|
||||
targetPath: template.targetPath,
|
||||
...options,
|
||||
};
|
||||
}
|
||||
|
||||
export function createDirName(template: Template, options: Options) {
|
||||
if (!options.id) {
|
||||
throw new Error(`id prompt is mandatory for all cli templates`);
|
||||
|
||||
Reference in New Issue
Block a user