cli/new: refactor package info resolution
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -30,15 +30,30 @@ export default async (opts: ArgOptions) => {
|
||||
const {
|
||||
option: rawArgOptions,
|
||||
select: preselectedTemplateId,
|
||||
...globals
|
||||
scope,
|
||||
...otherGlobals
|
||||
} = opts;
|
||||
|
||||
const prefilledParams = parseParams(rawArgOptions);
|
||||
|
||||
let pluginInfix: string | undefined = undefined;
|
||||
let packagePrefix: string | undefined = undefined;
|
||||
if (scope) {
|
||||
const backstagePrefix = scope.startsWith('backstage') ? '' : 'backstage-';
|
||||
packagePrefix = scope.includes('/')
|
||||
? `@${scope}${backstagePrefix}`
|
||||
: `@${scope}/${backstagePrefix}`;
|
||||
pluginInfix = scope.includes('backstage') ? 'plugin-' : 'backstage-plugin-';
|
||||
}
|
||||
|
||||
await createNewPackage({
|
||||
prefilledParams,
|
||||
preselectedTemplateId,
|
||||
globals,
|
||||
globals: {
|
||||
...otherGlobals,
|
||||
packagePrefix,
|
||||
pluginInfix,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import { PortableTemplateGlobals, PortableTemplateParams } from './types';
|
||||
|
||||
export type CreateNewPackageOptions = {
|
||||
preselectedTemplateId?: string;
|
||||
globals: PortableTemplateGlobals;
|
||||
globals: Partial<PortableTemplateGlobals>;
|
||||
prefilledParams: PortableTemplateParams;
|
||||
};
|
||||
|
||||
|
||||
@@ -23,12 +23,12 @@ import { paths } from '../../paths';
|
||||
import { Task } from '../../tasks';
|
||||
import { addCodeownersEntry } from '../../codeowners';
|
||||
|
||||
import { createDirName, resolvePackageName } from './utils';
|
||||
import { runAdditionalActions } from './additionalActions';
|
||||
import { executePluginPackageTemplate } from './executePluginPackageTemplate';
|
||||
import { TemporaryDirectoryManager } from './TemporaryDirectoryManager';
|
||||
import { PortableTemplateConfig, PortableTemplateInput } from '../types';
|
||||
import { PortableTemplate } from '../types';
|
||||
import { resolvePackageInfo } from './resolvePackageInfo';
|
||||
|
||||
type ExecuteNewTemplateOptions = {
|
||||
config: PortableTemplateConfig;
|
||||
@@ -43,14 +43,9 @@ export async function executePortableTemplate(
|
||||
|
||||
const tmpDirManager = TemporaryDirectoryManager.create();
|
||||
|
||||
const dirName = createDirName(template, params);
|
||||
const targetDir = paths.resolveTargetRoot(params.targetPath, dirName);
|
||||
const packageInfo = resolvePackageInfo(input);
|
||||
|
||||
const packageName = resolvePackageName({
|
||||
baseName: dirName,
|
||||
scope: params.scope,
|
||||
plugin: template.plugin ?? true,
|
||||
});
|
||||
const targetDir = paths.resolveTargetRoot(packageInfo.packagePath);
|
||||
|
||||
const moduleVar =
|
||||
params.moduleId ??
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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 { resolvePackageInfo } from './resolvePackageInfo';
|
||||
|
||||
const baseGlobals = {
|
||||
baseVersion: '0.0.0',
|
||||
license: 'Apache-2.0',
|
||||
private: true,
|
||||
packagePrefix: '@internal/',
|
||||
pluginInfix: 'plugin-',
|
||||
};
|
||||
|
||||
describe.each([
|
||||
[
|
||||
{ role: 'web-library', name: 'test' },
|
||||
{
|
||||
packageName: '@internal/test',
|
||||
packagePath: 'packages/test',
|
||||
},
|
||||
],
|
||||
[
|
||||
{ role: 'node-library', name: 'test' },
|
||||
{
|
||||
packageName: '@internal/test',
|
||||
packagePath: 'packages/test',
|
||||
},
|
||||
],
|
||||
[
|
||||
{ role: 'common-library', name: 'test' },
|
||||
{
|
||||
packageName: '@internal/test',
|
||||
packagePath: 'packages/test',
|
||||
},
|
||||
],
|
||||
[
|
||||
{ role: 'plugin-web-library', pluginId: 'test' },
|
||||
{
|
||||
packageName: '@internal/plugin-test-react',
|
||||
packagePath: 'plugins/test-react',
|
||||
},
|
||||
],
|
||||
[
|
||||
{ role: 'plugin-node-library', pluginId: 'test' },
|
||||
{
|
||||
packageName: '@internal/plugin-test-node',
|
||||
packagePath: 'plugins/test-node',
|
||||
},
|
||||
],
|
||||
[
|
||||
{ role: 'plugin-common-library', pluginId: 'test' },
|
||||
{
|
||||
packageName: '@internal/plugin-test-common',
|
||||
packagePath: 'plugins/test-common',
|
||||
},
|
||||
],
|
||||
[
|
||||
{ role: 'frontend-plugin', pluginId: 'test' },
|
||||
{
|
||||
packageName: '@internal/plugin-test',
|
||||
packagePath: 'plugins/test',
|
||||
},
|
||||
],
|
||||
[
|
||||
{ role: 'backend-plugin', pluginId: 'test' },
|
||||
{
|
||||
packageName: '@internal/plugin-test-backend',
|
||||
packagePath: 'plugins/test-backend',
|
||||
},
|
||||
],
|
||||
[
|
||||
{ role: 'frontend-plugin-module', pluginId: 'test1', moduleId: 'test2' },
|
||||
{
|
||||
packageName: '@internal/plugin-test1-module-test2',
|
||||
packagePath: 'plugins/test1-module-test2',
|
||||
},
|
||||
],
|
||||
[
|
||||
{ role: 'backend-plugin-module', pluginId: 'test1', moduleId: 'test2' },
|
||||
{
|
||||
packageName: '@internal/plugin-test1-backend-module-test2',
|
||||
packagePath: 'plugins/test1-backend-module-test2',
|
||||
},
|
||||
],
|
||||
] as const)('resolvePackageInfo', (roleParams, packageInfo) => {
|
||||
it(`should generate correct info with default config for ${roleParams.role}`, () => {
|
||||
expect(
|
||||
resolvePackageInfo({
|
||||
builtInParams: {},
|
||||
roleParams,
|
||||
params: {},
|
||||
globals: baseGlobals,
|
||||
}),
|
||||
).toEqual(packageInfo);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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 { join as joinPath } from 'path';
|
||||
import {
|
||||
PortableTemplateInput,
|
||||
PortableTemplateInputRoleParams,
|
||||
} from '../types';
|
||||
|
||||
export type PortableTemplatePackageInfo = {
|
||||
packageName: string;
|
||||
packagePath: string;
|
||||
};
|
||||
|
||||
export function resolvePackageInfo(
|
||||
input: PortableTemplateInput,
|
||||
): PortableTemplatePackageInfo {
|
||||
const baseName = getBaseNameForRole(input.roleParams);
|
||||
const isPlugin = input.roleParams.role.includes('plugin');
|
||||
const pluginInfix = isPlugin ? input.globals.pluginInfix : '';
|
||||
return {
|
||||
packageName: `${input.globals.packagePrefix}${pluginInfix}${baseName}`,
|
||||
packagePath: joinPath(isPlugin ? 'plugins' : 'packages', baseName),
|
||||
};
|
||||
}
|
||||
|
||||
function getBaseNameForRole(
|
||||
roleParams: PortableTemplateInputRoleParams,
|
||||
): string {
|
||||
switch (roleParams.role) {
|
||||
case 'web-library':
|
||||
case 'node-library':
|
||||
case 'common-library':
|
||||
return roleParams.name;
|
||||
case 'plugin-web-library':
|
||||
return `${roleParams.pluginId}-react`;
|
||||
case 'plugin-node-library':
|
||||
return `${roleParams.pluginId}-node`;
|
||||
case 'plugin-common-library':
|
||||
return `${roleParams.pluginId}-common`;
|
||||
case 'frontend-plugin':
|
||||
return `${roleParams.pluginId}`;
|
||||
case 'frontend-plugin-module':
|
||||
return `${roleParams.pluginId}-module-${roleParams.moduleId}`;
|
||||
case 'backend-plugin':
|
||||
return `${roleParams.pluginId}-backend`;
|
||||
case 'backend-plugin-module':
|
||||
return `${roleParams.pluginId}-backend-module-${roleParams.moduleId}`;
|
||||
default:
|
||||
throw new Error(`Unknown role ${(roleParams as { role: string }).role}`);
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* 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 { resolvePackageName, createDirName, Options } from './utils';
|
||||
import { PortableTemplate } from '../types';
|
||||
|
||||
describe('resolvePackageName', () => {
|
||||
it('should generate correct name without scope', () => {
|
||||
expect(resolvePackageName({ baseName: 'test', plugin: true })).toEqual(
|
||||
'backstage-plugin-test',
|
||||
);
|
||||
expect(resolvePackageName({ baseName: 'test', plugin: false })).toEqual(
|
||||
'test',
|
||||
);
|
||||
});
|
||||
|
||||
it('should generate correct name for backstage scope', () => {
|
||||
expect(
|
||||
resolvePackageName({
|
||||
baseName: 'test',
|
||||
scope: 'backstage',
|
||||
plugin: true,
|
||||
}),
|
||||
).toEqual('@backstage/plugin-test');
|
||||
expect(
|
||||
resolvePackageName({
|
||||
baseName: 'test',
|
||||
scope: 'backstage',
|
||||
plugin: false,
|
||||
}),
|
||||
).toEqual('@backstage/test');
|
||||
});
|
||||
|
||||
it('should generate correct name for custom scope', () => {
|
||||
expect(
|
||||
resolvePackageName({
|
||||
baseName: 'test',
|
||||
scope: 'custom',
|
||||
plugin: true,
|
||||
}),
|
||||
).toEqual('@custom/backstage-plugin-test');
|
||||
expect(
|
||||
resolvePackageName({
|
||||
baseName: 'test',
|
||||
scope: 'custom',
|
||||
plugin: false,
|
||||
}),
|
||||
).toEqual('@custom/test');
|
||||
});
|
||||
|
||||
it('should generate correct name for custom scope and custom prefix', () => {
|
||||
expect(
|
||||
resolvePackageName({
|
||||
baseName: 'test',
|
||||
scope: 'custom/myapp.',
|
||||
plugin: true,
|
||||
}),
|
||||
).toEqual('@custom/myapp.backstage-plugin-test');
|
||||
expect(
|
||||
resolvePackageName({
|
||||
baseName: 'test',
|
||||
scope: 'custom/myapp.',
|
||||
plugin: false,
|
||||
}),
|
||||
).toEqual('@custom/myapp.test');
|
||||
});
|
||||
});
|
||||
|
||||
describe('createDirName', () => {
|
||||
it('should return name in the backend-module format if backendModulePrefix is set to true', () => {
|
||||
expect(
|
||||
createDirName(
|
||||
{ backendModulePrefix: true } as PortableTemplate,
|
||||
{
|
||||
id: 'foo',
|
||||
moduleId: 'bar',
|
||||
} as Options,
|
||||
),
|
||||
).toEqual('foo-backend-module-bar');
|
||||
});
|
||||
|
||||
it('should throw an error if backendModulePrefix is configured as true but is missing moduleId', () => {
|
||||
expect(() =>
|
||||
createDirName(
|
||||
{ backendModulePrefix: true } as PortableTemplate,
|
||||
{
|
||||
id: 'foo',
|
||||
moduleId: '',
|
||||
} as Options,
|
||||
),
|
||||
).toThrow('backendModulePrefix requires moduleId prompt');
|
||||
});
|
||||
|
||||
it('should append the suffix value if one is provided', () => {
|
||||
expect(
|
||||
createDirName(
|
||||
{ suffix: 'foo' } as PortableTemplate,
|
||||
{ id: 'bar' } as Options,
|
||||
),
|
||||
).toEqual('bar-foo');
|
||||
});
|
||||
|
||||
it('should return id if neither backendModulePrefix nor suffix is specified', () => {
|
||||
expect(
|
||||
createDirName({} as PortableTemplate, { id: 'foo' } as Options),
|
||||
).toEqual('foo');
|
||||
});
|
||||
});
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* 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 { PortableTemplate } from '../types';
|
||||
|
||||
export const resolvePackageName = (options: {
|
||||
baseName: string;
|
||||
scope?: string;
|
||||
plugin: boolean;
|
||||
}) => {
|
||||
const { baseName, scope, plugin } = options;
|
||||
if (scope) {
|
||||
if (plugin) {
|
||||
const pluginName = scope.startsWith('backstage')
|
||||
? 'plugin'
|
||||
: 'backstage-plugin';
|
||||
return scope.includes('/')
|
||||
? `@${scope}${pluginName}-${baseName}`
|
||||
: `@${scope}/${pluginName}-${baseName}`;
|
||||
}
|
||||
return scope.includes('/')
|
||||
? `@${scope}${baseName}`
|
||||
: `@${scope}/${baseName}`;
|
||||
}
|
||||
|
||||
return plugin ? `backstage-plugin-${baseName}` : baseName;
|
||||
};
|
||||
|
||||
export function createDirName(template: PortableTemplate, options: Options) {
|
||||
if (!options.id) {
|
||||
throw new Error(`id prompt is mandatory for all cli templates`);
|
||||
}
|
||||
if (template.backendModulePrefix) {
|
||||
if (!options.moduleId) {
|
||||
throw new Error(`backendModulePrefix requires moduleId prompt`);
|
||||
}
|
||||
return `${options.id}-backend-module-${options.moduleId}`;
|
||||
} else if (template.suffix) {
|
||||
return `${options.id}-${template.suffix}`;
|
||||
}
|
||||
return options.id;
|
||||
}
|
||||
@@ -26,7 +26,8 @@ const defaultGlobals = {
|
||||
license: 'Apache-2.0',
|
||||
baseVersion: '0.1.0',
|
||||
private: true,
|
||||
scope: undefined,
|
||||
packagePrefix: '@internal/',
|
||||
pluginInfix: 'plugin-',
|
||||
};
|
||||
|
||||
const pkgJsonWithNewConfigSchema = z.object({
|
||||
@@ -49,7 +50,8 @@ const pkgJsonWithNewConfigSchema = z.object({
|
||||
license: z.string().optional(),
|
||||
baseVersion: z.string().optional(),
|
||||
private: z.boolean().optional(),
|
||||
scope: z.string().optional(),
|
||||
packagePrefix: z.string().optional(),
|
||||
pluginInfix: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
@@ -61,7 +63,7 @@ const pkgJsonWithNewConfigSchema = z.object({
|
||||
|
||||
type LoadConfigOptions = {
|
||||
packagePath?: string;
|
||||
globalOverrides?: PortableTemplateGlobals;
|
||||
globalOverrides?: Partial<PortableTemplateGlobals>;
|
||||
};
|
||||
|
||||
export async function loadPortableTemplateConfig(
|
||||
|
||||
@@ -73,10 +73,11 @@ export type PortableTemplateParams = {
|
||||
};
|
||||
|
||||
export type PortableTemplateGlobals = {
|
||||
license?: string;
|
||||
baseVersion?: string;
|
||||
private?: boolean;
|
||||
scope?: string;
|
||||
license: string;
|
||||
baseVersion: string;
|
||||
private: boolean;
|
||||
packagePrefix: string;
|
||||
pluginInfix: string;
|
||||
};
|
||||
|
||||
export type PortableTemplateInputRoleParams =
|
||||
@@ -107,5 +108,5 @@ export type PortableTemplateInput = {
|
||||
roleParams: PortableTemplateInputRoleParams;
|
||||
builtInParams: PortableTemplateInputBuiltInParams;
|
||||
params: PortableTemplateParams;
|
||||
globals: PortableTemplateParams;
|
||||
globals: PortableTemplateGlobals;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user