feat: harmonize the package naming with cli

Custom scopes should be named as `backstage-plugin-` while backstage
scoped ones shoul be named just `plugin-`. Also allow custom prefix
after the scope for example `custom/myapp.` to be used as scope.

Signed-off-by: Heikki Hellgren <heikki.hellgren@op.fi>
This commit is contained in:
Heikki Hellgren
2024-01-24 12:27:11 +02:00
parent 4021ccce5e
commit 5c05f8ac0f
13 changed files with 186 additions and 39 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Harmonize the package naming and allow custom prefix
@@ -19,7 +19,7 @@ import chalk from 'chalk';
import camelCase from 'lodash/camelCase';
import { paths } from '../../paths';
import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners';
import { createFactory, CreateContext } from '../types';
import { CreateContext, createFactory } from '../types';
import { addPackageDependency, Task } from '../../tasks';
import {
moduleIdIdPrompt,
@@ -27,6 +27,7 @@ import {
pluginIdPrompt,
} from './common/prompts';
import { executePluginPackageTemplate } from './common/tasks';
import { resolvePackageName } from './common/util';
type Options = {
id: string;
@@ -45,9 +46,11 @@ export const backendModule = createFactory<Options>({
async create(options: Options, ctx: CreateContext) {
const { id: pluginId, moduleId } = options;
const dirName = `${pluginId}-backend-module-${moduleId}`;
const name = ctx.scope
? `@${ctx.scope}/plugin-${dirName}`
: `backstage-plugin-${dirName}`;
const name = resolvePackageName({
baseName: dirName,
scope: ctx.scope,
plugin: true,
});
Task.log();
Task.log(`Creating backend module ${chalk.cyan(name)}`);
@@ -19,10 +19,11 @@ import chalk from 'chalk';
import camelCase from 'lodash/camelCase';
import { paths } from '../../paths';
import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners';
import { createFactory, CreateContext } from '../types';
import { CreateContext, createFactory } from '../types';
import { addPackageDependency, Task } from '../../tasks';
import { ownerPrompt, pluginIdPrompt } from './common/prompts';
import { executePluginPackageTemplate } from './common/tasks';
import { resolvePackageName } from './common/util';
type Options = {
id: string;
@@ -40,9 +41,11 @@ export const backendPlugin = createFactory<Options>({
async create(options: Options, ctx: CreateContext) {
const { id } = options;
const pluginId = `${id}-backend`;
const name = ctx.scope
? `@${ctx.scope}/plugin-${pluginId}`
: `backstage-plugin-${pluginId}`;
const name = resolvePackageName({
baseName: pluginId,
scope: ctx.scope,
plugin: true,
});
Task.log();
Task.log(`Creating backend plugin ${chalk.cyan(name)}`);
@@ -0,0 +1,78 @@
/*
* 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 } from './util';
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');
});
});
@@ -0,0 +1,38 @@
/*
* 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.
*/
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;
};
@@ -180,7 +180,7 @@ const router = (
fs.readJson(mockDir.resolve('packages/app/package.json')),
).resolves.toEqual({
dependencies: {
'@internal/plugin-test': '^1.0.0',
'@internal/backstage-plugin-test': '^1.0.0',
},
});
@@ -188,7 +188,7 @@ const router = (
fs.readFile(mockDir.resolve('packages/app/src/App.tsx'), 'utf8'),
).resolves.toBe(`
import { createApp } from '@backstage/app-defaults';
import { TestPage } from '@internal/plugin-test';
import { TestPage } from '@internal/backstage-plugin-test';
const router = (
<FlatRoutes>
@@ -20,10 +20,11 @@ import camelCase from 'lodash/camelCase';
import upperFirst from 'lodash/upperFirst';
import { paths } from '../../paths';
import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners';
import { createFactory, CreateContext } from '../types';
import { CreateContext, createFactory } from '../types';
import { addPackageDependency, Task } from '../../tasks';
import { ownerPrompt, pluginIdPrompt } from './common/prompts';
import { executePluginPackageTemplate } from './common/tasks';
import { resolvePackageName } from './common/util';
type Options = {
id: string;
@@ -41,9 +42,11 @@ export const frontendPlugin = createFactory<Options>({
async create(options: Options, ctx: CreateContext) {
const { id } = options;
const name = ctx.scope
? `@${ctx.scope}/plugin-${id}`
: `backstage-plugin-${id}`;
const name = resolvePackageName({
baseName: id,
scope: ctx.scope,
plugin: true,
});
const extensionName = `${upperFirst(camelCase(id))}Page`;
Task.log();
@@ -17,10 +17,11 @@
import chalk from 'chalk';
import { paths } from '../../paths';
import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners';
import { createFactory, CreateContext } from '../types';
import { CreateContext, createFactory } from '../types';
import { Task } from '../../tasks';
import { ownerPrompt, pluginIdPrompt } from './common/prompts';
import { executePluginPackageTemplate } from './common/tasks';
import { resolvePackageName } from './common/util';
type Options = {
id: string;
@@ -37,7 +38,11 @@ export const nodeLibraryPackage = createFactory<Options>({
optionsPrompts: [pluginIdPrompt(), ownerPrompt()],
async create(options: Options, ctx: CreateContext) {
const { id } = options;
const name = ctx.scope ? `@${ctx.scope}/${id}` : `${id}`;
const name = resolvePackageName({
baseName: id,
scope: ctx.scope,
plugin: false,
});
Task.log();
Task.log(`Creating node-library package ${chalk.cyan(name)}`);
@@ -17,10 +17,11 @@
import chalk from 'chalk';
import { paths } from '../../paths';
import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners';
import { createFactory, CreateContext } from '../types';
import { CreateContext, createFactory } from '../types';
import { Task } from '../../tasks';
import { ownerPrompt, pluginIdPrompt } from './common/prompts';
import { executePluginPackageTemplate } from './common/tasks';
import { resolvePackageName } from './common/util';
type Options = {
id: string;
@@ -38,9 +39,11 @@ export const pluginCommon = createFactory<Options>({
async create(options: Options, ctx: CreateContext) {
const { id } = options;
const suffix = `${id}-common`;
const name = ctx.scope
? `@${ctx.scope}/plugin-${suffix}`
: `backstage-plugin-${suffix}`;
const name = resolvePackageName({
baseName: suffix,
scope: ctx.scope,
plugin: true,
});
Task.log();
Task.log(`Creating backend plugin ${chalk.cyan(name)}`);
@@ -17,10 +17,11 @@
import chalk from 'chalk';
import { paths } from '../../paths';
import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners';
import { createFactory, CreateContext } from '../types';
import { CreateContext, createFactory } from '../types';
import { Task } from '../../tasks';
import { ownerPrompt, pluginIdPrompt } from './common/prompts';
import { executePluginPackageTemplate } from './common/tasks';
import { resolvePackageName } from './common/util';
type Options = {
id: string;
@@ -38,9 +39,11 @@ export const pluginNode = createFactory<Options>({
async create(options: Options, ctx: CreateContext) {
const { id } = options;
const suffix = `${id}-node`;
const name = ctx.scope
? `@${ctx.scope}/plugin-${suffix}`
: `backstage-plugin-${suffix}`;
const name = resolvePackageName({
baseName: suffix,
scope: ctx.scope,
plugin: true,
});
Task.log();
Task.log(`Creating Node.js plugin library ${chalk.cyan(name)}`);
@@ -17,10 +17,11 @@
import chalk from 'chalk';
import { paths } from '../../paths';
import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners';
import { createFactory, CreateContext } from '../types';
import { CreateContext, createFactory } from '../types';
import { Task } from '../../tasks';
import { ownerPrompt, pluginIdPrompt } from './common/prompts';
import { executePluginPackageTemplate } from './common/tasks';
import { resolvePackageName } from './common/util';
type Options = {
id: string;
@@ -38,9 +39,11 @@ export const pluginWeb = createFactory<Options>({
async create(options: Options, ctx: CreateContext) {
const { id } = options;
const suffix = `${id}-react`;
const name = ctx.scope
? `@${ctx.scope}/plugin-${suffix}`
: `backstage-plugin-${suffix}`;
const name = resolvePackageName({
baseName: suffix,
scope: ctx.scope,
plugin: true,
});
Task.log();
Task.log(`Creating web plugin library ${chalk.cyan(name)}`);
@@ -17,10 +17,11 @@
import chalk from 'chalk';
import { paths } from '../../paths';
import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners';
import { createFactory, CreateContext } from '../types';
import { CreateContext, createFactory } from '../types';
import { Task } from '../../tasks';
import { ownerPrompt } from './common/prompts';
import { executePluginPackageTemplate } from './common/tasks';
import { resolvePackageName } from './common/util';
type Options = {
id: string;
@@ -55,14 +56,11 @@ export const scaffolderModule = createFactory<Options>({
const { id } = options;
const slug = `scaffolder-backend-module-${id}`;
let name = `backstage-plugin-${slug}`;
if (ctx.scope) {
if (ctx.scope === 'backstage') {
name = `@backstage/plugin-${slug}`;
} else {
name = `@${ctx.scope}/backstage-plugin-${slug}`;
}
}
const name = resolvePackageName({
baseName: slug,
scope: ctx.scope,
plugin: true,
});
Task.log();
Task.log(`Creating module ${chalk.cyan(name)}`);
@@ -17,10 +17,11 @@
import chalk from 'chalk';
import { paths } from '../../paths';
import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners';
import { createFactory, CreateContext } from '../types';
import { CreateContext, createFactory } from '../types';
import { Task } from '../../tasks';
import { ownerPrompt, pluginIdPrompt } from './common/prompts';
import { executePluginPackageTemplate } from './common/tasks';
import { resolvePackageName } from './common/util';
type Options = {
id: string;
@@ -37,7 +38,11 @@ export const webLibraryPackage = createFactory<Options>({
optionsPrompts: [pluginIdPrompt(), ownerPrompt()],
async create(options: Options, ctx: CreateContext) {
const { id } = options;
const name = ctx.scope ? `@${ctx.scope}/${id}` : `${id}`;
const name = resolvePackageName({
baseName: id,
scope: ctx.scope,
plugin: false,
});
Task.log();
Task.log(`Creating web-library package ${chalk.cyan(name)}`);