Refine cli-plugin-api: use packageJson, inline types, rename context fields
Replace pluginId with packageJson input for createCliPlugin, remove CommandExecuteFn type alias by inlining it, and rename CommandContext.info.description to info.name. Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com> Made-with: Cursor
This commit is contained in:
@@ -21,10 +21,8 @@ export const OpaqueCliPlugin = OpaqueType.create<{
|
||||
public: CliPlugin;
|
||||
versions: {
|
||||
readonly version: 'v1';
|
||||
readonly description: string;
|
||||
init: (registry: {
|
||||
addCommand: (command: BackstageCommand) => void;
|
||||
}) => Promise<void>;
|
||||
readonly packageName: string;
|
||||
readonly commands: Promise<ReadonlyArray<BackstageCommand>>;
|
||||
};
|
||||
}>({
|
||||
type: '@backstage/CliPlugin',
|
||||
|
||||
@@ -11,10 +11,10 @@ export interface BackstageCommand {
|
||||
description: string;
|
||||
// (undocumented)
|
||||
execute:
|
||||
| CommandExecuteFn
|
||||
| ((context: CommandContext) => Promise<void>)
|
||||
| {
|
||||
loader: () => Promise<{
|
||||
default: CommandExecuteFn;
|
||||
default: (context: CommandContext) => Promise<void>;
|
||||
}>;
|
||||
};
|
||||
// (undocumented)
|
||||
@@ -30,8 +30,6 @@ export type CliFeature = CliPlugin;
|
||||
export interface CliPlugin {
|
||||
// (undocumented)
|
||||
readonly $$type: '@backstage/CliPlugin';
|
||||
// (undocumented)
|
||||
readonly pluginId: string;
|
||||
}
|
||||
|
||||
// @public
|
||||
@@ -45,12 +43,11 @@ export interface CommandContext {
|
||||
};
|
||||
}
|
||||
|
||||
// @public
|
||||
export type CommandExecuteFn = (context: CommandContext) => Promise<void>;
|
||||
|
||||
// @public
|
||||
export function createCliPlugin(options: {
|
||||
pluginId: string;
|
||||
packageJson: {
|
||||
name: string;
|
||||
};
|
||||
init: (registry: {
|
||||
addCommand: (command: BackstageCommand) => void;
|
||||
}) => Promise<void>;
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
import { OpaqueCliPlugin } from '@internal/cli';
|
||||
import { describeParentCallSite } from './describeParentCallSite';
|
||||
import { BackstageCommand, CliPlugin } from './types';
|
||||
|
||||
/**
|
||||
@@ -24,14 +23,18 @@ import { BackstageCommand, CliPlugin } from './types';
|
||||
* @public
|
||||
*/
|
||||
export function createCliPlugin(options: {
|
||||
pluginId: string;
|
||||
packageJson: { name: string };
|
||||
init: (registry: {
|
||||
addCommand: (command: BackstageCommand) => void;
|
||||
}) => Promise<void>;
|
||||
}): CliPlugin {
|
||||
const commands: BackstageCommand[] = [];
|
||||
const commandsPromise = options
|
||||
.init({ addCommand: command => commands.push(command) })
|
||||
.then(() => commands);
|
||||
|
||||
return OpaqueCliPlugin.createInstance('v1', {
|
||||
pluginId: options.pluginId,
|
||||
init: options.init,
|
||||
description: `created at '${describeParentCallSite()}'`,
|
||||
packageName: options.packageJson.name,
|
||||
commands: commandsPromise,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 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.
|
||||
*/
|
||||
|
||||
const MESSAGE_MARKER = 'eHgtF5hmbrXyiEvo';
|
||||
|
||||
/**
|
||||
* Internal helper that describes the location of the parent caller.
|
||||
* @internal
|
||||
*/
|
||||
export function describeParentCallSite(
|
||||
ErrorConstructor: { new (message: string): Error } = Error,
|
||||
): string {
|
||||
const { stack } = new ErrorConstructor(MESSAGE_MARKER);
|
||||
if (!stack) {
|
||||
return '<unknown>';
|
||||
}
|
||||
|
||||
const startIndex = stack.includes(MESSAGE_MARKER)
|
||||
? stack.indexOf('\n') + 1
|
||||
: 0;
|
||||
const secondEntryStart =
|
||||
stack.indexOf('\n', stack.indexOf('\n', startIndex) + 1) + 1;
|
||||
const secondEntryEnd = stack.indexOf('\n', secondEntryStart);
|
||||
|
||||
const line = stack.substring(secondEntryStart, secondEntryEnd).trim();
|
||||
if (!line) {
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
// Chrome
|
||||
if (line.includes('(')) {
|
||||
return line.substring(line.indexOf('(') + 1, line.indexOf(')'));
|
||||
}
|
||||
|
||||
// Safari & Firefox
|
||||
if (line.includes('@')) {
|
||||
return line.substring(line.indexOf('@') + 1);
|
||||
}
|
||||
|
||||
return line;
|
||||
}
|
||||
@@ -18,7 +18,6 @@ export { createCliPlugin } from './createCliPlugin';
|
||||
export type {
|
||||
BackstageCommand,
|
||||
CommandContext,
|
||||
CommandExecuteFn,
|
||||
CliPlugin,
|
||||
CliFeature,
|
||||
} from './types';
|
||||
|
||||
@@ -27,19 +27,12 @@ export interface CommandContext {
|
||||
*/
|
||||
usage: string;
|
||||
/**
|
||||
* The description provided for the command
|
||||
* The name of the command, for example: "repo test"
|
||||
*/
|
||||
description: string;
|
||||
name: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that executes a CLI command.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type CommandExecuteFn = (context: CommandContext) => Promise<void>;
|
||||
|
||||
/**
|
||||
* A command definition for a Backstage CLI plugin.
|
||||
*
|
||||
@@ -51,9 +44,11 @@ export interface BackstageCommand {
|
||||
deprecated?: boolean;
|
||||
experimental?: boolean;
|
||||
execute:
|
||||
| CommandExecuteFn
|
||||
| ((context: CommandContext) => Promise<void>)
|
||||
| {
|
||||
loader: () => Promise<{ default: CommandExecuteFn }>;
|
||||
loader: () => Promise<{
|
||||
default: (context: CommandContext) => Promise<void>;
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -70,6 +65,5 @@ export type CliFeature = CliPlugin;
|
||||
* @public
|
||||
*/
|
||||
export interface CliPlugin {
|
||||
readonly pluginId: string;
|
||||
readonly $$type: '@backstage/CliPlugin';
|
||||
}
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
*/
|
||||
|
||||
import { createCliPlugin } from '../../wiring/factory';
|
||||
import packageJson from '../../../package.json';
|
||||
|
||||
export default createCliPlugin({
|
||||
pluginId: 'auth',
|
||||
packageJson,
|
||||
init: async reg => {
|
||||
reg.addCommand({
|
||||
path: ['auth', 'login'],
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
*/
|
||||
|
||||
import { createCliPlugin } from '@backstage/cli-plugin-api';
|
||||
import packageJson from '../../../package.json';
|
||||
|
||||
export const buildPlugin = createCliPlugin({
|
||||
pluginId: 'build',
|
||||
packageJson,
|
||||
init: async reg => {
|
||||
reg.addCommand({
|
||||
path: ['package', 'build'],
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createCliPlugin } from '@backstage/cli-plugin-api';
|
||||
import packageJson from '../../../package.json';
|
||||
|
||||
export const configOption = [
|
||||
'--config <path>',
|
||||
@@ -23,7 +24,7 @@ export const configOption = [
|
||||
] as const;
|
||||
|
||||
export default createCliPlugin({
|
||||
pluginId: 'config',
|
||||
packageJson,
|
||||
init: async reg => {
|
||||
reg.addCommand({
|
||||
path: ['config:docs'],
|
||||
|
||||
@@ -14,9 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createCliPlugin } from '@backstage/cli-plugin-api';
|
||||
import packageJson from '../../../package.json';
|
||||
|
||||
export default createCliPlugin({
|
||||
pluginId: 'new',
|
||||
packageJson,
|
||||
init: async reg => {
|
||||
reg.addCommand({
|
||||
path: ['create-github-app'],
|
||||
|
||||
@@ -14,9 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createCliPlugin } from '@backstage/cli-plugin-api';
|
||||
import packageJson from '../../../package.json';
|
||||
|
||||
export default createCliPlugin({
|
||||
pluginId: 'info',
|
||||
packageJson,
|
||||
init: async reg => {
|
||||
reg.addCommand({
|
||||
path: ['info'],
|
||||
|
||||
@@ -14,9 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createCliPlugin } from '@backstage/cli-plugin-api';
|
||||
import packageJson from '../../../package.json';
|
||||
|
||||
export default createCliPlugin({
|
||||
pluginId: 'lint',
|
||||
packageJson,
|
||||
init: async reg => {
|
||||
reg.addCommand({
|
||||
path: ['package', 'lint'],
|
||||
|
||||
@@ -14,9 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createCliPlugin } from '@backstage/cli-plugin-api';
|
||||
import packageJson from '../../../package.json';
|
||||
|
||||
export default createCliPlugin({
|
||||
pluginId: 'maintenance',
|
||||
packageJson,
|
||||
init: async reg => {
|
||||
reg.addCommand({
|
||||
path: ['repo', 'fix'],
|
||||
|
||||
@@ -125,7 +125,7 @@ const expectLogsToMatch = (
|
||||
expect(receivedLogs.filter(Boolean).sort()).toEqual(expected.sort());
|
||||
};
|
||||
|
||||
const info = { usage: 'backstage-cli versions:bump', description: '' };
|
||||
const info = { usage: 'backstage-cli versions:bump', name: 'versions:bump' };
|
||||
|
||||
describe('bump', () => {
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
@@ -123,7 +123,7 @@ describe('versions:migrate', () => {
|
||||
});
|
||||
|
||||
const { warn, log: logs } = await withLogCollector(async () => {
|
||||
await migrate({ args: [], info: { usage: 'test', description: 'test' } });
|
||||
await migrate({ args: [], info: { usage: 'test', name: 'test' } });
|
||||
});
|
||||
|
||||
expectLogsToMatch(logs, [
|
||||
@@ -229,7 +229,7 @@ describe('versions:migrate', () => {
|
||||
});
|
||||
|
||||
await withLogCollector(async () => {
|
||||
await migrate({ args: [], info: { usage: 'test', description: 'test' } });
|
||||
await migrate({ args: [], info: { usage: 'test', name: 'test' } });
|
||||
});
|
||||
|
||||
expect(runObj.run).toHaveBeenCalledTimes(1);
|
||||
@@ -311,7 +311,7 @@ describe('versions:migrate', () => {
|
||||
});
|
||||
|
||||
await withLogCollector(async () => {
|
||||
await migrate({ args: [], info: { usage: 'test', description: 'test' } });
|
||||
await migrate({ args: [], info: { usage: 'test', name: 'test' } });
|
||||
});
|
||||
|
||||
expect(runObj.run).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -14,9 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createCliPlugin } from '@backstage/cli-plugin-api';
|
||||
import packageJson from '../../../package.json';
|
||||
|
||||
export default createCliPlugin({
|
||||
pluginId: 'migrate',
|
||||
packageJson,
|
||||
init: async reg => {
|
||||
reg.addCommand({
|
||||
path: ['versions:migrate'],
|
||||
|
||||
@@ -41,7 +41,7 @@ describe.each([
|
||||
}
|
||||
const context: CommandContext = {
|
||||
args,
|
||||
info: { usage: 'backstage-cli new', description: 'test' },
|
||||
info: { usage: 'backstage-cli new', name: 'new' },
|
||||
};
|
||||
await newCommand(context);
|
||||
expect(createNewPackage).toHaveBeenCalledWith(
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
*/
|
||||
import { createCliPlugin } from '@backstage/cli-plugin-api';
|
||||
import { NotImplementedError } from '@backstage/errors';
|
||||
import packageJson from '../../../package.json';
|
||||
|
||||
export default createCliPlugin({
|
||||
pluginId: 'new',
|
||||
packageJson,
|
||||
init: async reg => {
|
||||
reg.addCommand({
|
||||
path: ['new'],
|
||||
|
||||
@@ -14,9 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createCliPlugin } from '@backstage/cli-plugin-api';
|
||||
import packageJson from '../../../package.json';
|
||||
|
||||
export default createCliPlugin({
|
||||
pluginId: 'test',
|
||||
packageJson,
|
||||
init: async reg => {
|
||||
reg.addCommand({
|
||||
path: ['repo', 'test'],
|
||||
|
||||
@@ -14,9 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createCliPlugin } from '@backstage/cli-plugin-api';
|
||||
import packageJson from '../../../package.json';
|
||||
|
||||
export default createCliPlugin({
|
||||
pluginId: 'translations',
|
||||
packageJson,
|
||||
init: async reg => {
|
||||
reg.addCommand({
|
||||
path: ['translations', 'export'],
|
||||
|
||||
@@ -29,7 +29,7 @@ describe('CliInitializer', () => {
|
||||
const initializer = new CliInitializer();
|
||||
initializer.add(
|
||||
createCliPlugin({
|
||||
pluginId: 'test',
|
||||
packageJson: { name: '@backstage/test' },
|
||||
init: async reg =>
|
||||
reg.addCommand({
|
||||
path: ['test'],
|
||||
@@ -51,7 +51,7 @@ describe('CliInitializer', () => {
|
||||
const initializer = new CliInitializer();
|
||||
initializer.add(
|
||||
createCliPlugin({
|
||||
pluginId: 'test',
|
||||
packageJson: { name: '@backstage/test' },
|
||||
init: async reg =>
|
||||
reg.addCommand({
|
||||
path: ['test'],
|
||||
@@ -73,7 +73,7 @@ describe('CliInitializer', () => {
|
||||
const initializer = new CliInitializer();
|
||||
initializer.add(
|
||||
createCliPlugin({
|
||||
pluginId: 'test',
|
||||
packageJson: { name: '@backstage/test' },
|
||||
init: async reg =>
|
||||
reg.addCommand({
|
||||
path: ['test'],
|
||||
@@ -98,7 +98,7 @@ describe('CliInitializer', () => {
|
||||
const initializer = new CliInitializer();
|
||||
initializer.add(
|
||||
createCliPlugin({
|
||||
pluginId: 'test',
|
||||
packageJson: { name: '@backstage/test' },
|
||||
init: async reg => {
|
||||
reg.addCommand({
|
||||
path: ['visible'],
|
||||
@@ -125,7 +125,7 @@ describe('CliInitializer', () => {
|
||||
const initializer2 = new CliInitializer();
|
||||
initializer2.add(
|
||||
createCliPlugin({
|
||||
pluginId: 'test',
|
||||
packageJson: { name: '@backstage/test' },
|
||||
init: async reg => {
|
||||
reg.addCommand({
|
||||
path: ['visible'],
|
||||
@@ -153,7 +153,7 @@ describe('CliInitializer', () => {
|
||||
const initializer = new CliInitializer();
|
||||
initializer.add(
|
||||
createCliPlugin({
|
||||
pluginId: 'test',
|
||||
packageJson: { name: '@backstage/test' },
|
||||
init: async reg => {
|
||||
reg.addCommand({
|
||||
path: ['visible'],
|
||||
@@ -188,7 +188,7 @@ describe('CliInitializer', () => {
|
||||
const initializer = new CliInitializer();
|
||||
initializer.add(
|
||||
createCliPlugin({
|
||||
pluginId: 'test',
|
||||
packageJson: { name: '@backstage/test' },
|
||||
init: async reg => {
|
||||
reg.addCommand({
|
||||
path: ['group', 'alpha'],
|
||||
@@ -224,7 +224,7 @@ describe('CliInitializer', () => {
|
||||
const initializer = new CliInitializer();
|
||||
initializer.add(
|
||||
createCliPlugin({
|
||||
pluginId: 'test',
|
||||
packageJson: { name: '@backstage/test' },
|
||||
init: async reg =>
|
||||
reg.addCommand({
|
||||
path: ['test', 'nested', 'command'],
|
||||
|
||||
@@ -56,7 +56,9 @@ export class CliInitializer {
|
||||
async #register(feature: CliFeature) {
|
||||
if (OpaqueCliPlugin.isType(feature)) {
|
||||
const internal = OpaqueCliPlugin.toInternal(feature);
|
||||
await internal.init(this.commandRegistry);
|
||||
for (const command of await internal.commands) {
|
||||
this.commandRegistry.addCommand(command);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Unsupported feature type: ${(feature as any).$$type}`);
|
||||
}
|
||||
@@ -138,7 +140,7 @@ export class CliInitializer {
|
||||
args: [...positionalArgs, ...args.unknown],
|
||||
info: {
|
||||
usage: [programName, ...node.command.path].join(' '),
|
||||
description: node.command.description,
|
||||
name: node.command.path.join(' '),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
export type {
|
||||
CommandContext,
|
||||
CommandExecuteFn,
|
||||
BackstageCommand,
|
||||
CliFeature,
|
||||
CliPlugin,
|
||||
|
||||
Reference in New Issue
Block a user