Merge cli-plugin-api into cli-node

Move createCliPlugin and related types from the standalone
@backstage/cli-plugin-api package into @backstage/cli-node and
remove the now-empty package.

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor
This commit is contained in:
Patrik Oldsberg
2026-03-13 07:39:15 +01:00
parent a90939e138
commit 7d055ef0c4
30 changed files with 65 additions and 135 deletions
+40
View File
@@ -6,6 +6,21 @@
import { JsonValue } from '@backstage/types';
import { Package } from '@manypkg/get-packages';
// @public
export interface BackstageCommand {
deprecated?: boolean;
description: string;
execute:
| ((context: CommandContext) => Promise<void>)
| {
loader: () => Promise<{
default: (context: CommandContext) => Promise<void>;
}>;
};
experimental?: boolean;
path: string[];
}
// @public
export type BackstagePackage = {
dir: string;
@@ -86,6 +101,21 @@ export interface BackstagePackageJson {
version: string;
}
// @public
export interface CliPlugin {
// (undocumented)
readonly $$type: '@backstage/CliPlugin';
}
// @public
export interface CommandContext {
args: string[];
info: {
usage: string;
name: string;
};
}
// @public
export type ConcurrentTasksOptions<TItem> = {
concurrencyFactor?: number;
@@ -93,6 +123,16 @@ export type ConcurrentTasksOptions<TItem> = {
worker: (item: TItem) => Promise<void>;
};
// @public
export function createCliPlugin(options: {
packageJson: {
name: string;
};
init: (registry: {
addCommand: (command: BackstageCommand) => void;
}) => Promise<void>;
}): CliPlugin;
// @public
export class GitUtils {
static listChangedFiles(ref: string): Promise<string[]>;
@@ -0,0 +1,67 @@
/*
* 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 { OpaqueCliPlugin } from '@internal/cli';
import { BackstageCommand, CliPlugin } from './types';
/**
* Creates a new CLI plugin that provides commands to the Backstage CLI.
*
* The `init` callback is invoked immediately at creation time and is used
* to register commands via the provided registry. The commands are then
* made available to the CLI host once the returned promise resolves.
*
* @example
* ```
* import { createCliPlugin } from '@backstage/cli-node';
* import packageJson from '../package.json';
*
* export default createCliPlugin({
* packageJson,
* init: async reg => {
* reg.addCommand({
* path: ['repo', 'test'],
* description: 'Run tests across the repository',
* execute: { loader: () => import('./commands/test') },
* });
* },
* });
* ```
*
* @public
*/
export function createCliPlugin(options: {
/** The `package.json` contents of the plugin package, used to identify the plugin. */
packageJson: { name: string };
/**
* An initialization callback that registers commands with the CLI.
* Called immediately when the plugin is created.
*/
init: (registry: {
/** Registers a new command with the CLI. */
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', {
packageName: options.packageJson.name,
commands: commandsPromise,
});
}
+18
View File
@@ -0,0 +1,18 @@
/*
* 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 { createCliPlugin } from './createCliPlugin';
export type { BackstageCommand, CommandContext, CliPlugin } from './types';
+118
View File
@@ -0,0 +1,118 @@
/*
* 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.
*/
/**
* The context provided to a CLI command at the time of execution.
*
* Contains the parsed arguments and metadata about the command being run.
*
* @public
*/
export interface CommandContext {
/**
* The remaining arguments passed to the command after the command path
* has been resolved. This includes both positional arguments and flags.
*
* For example, running `backstage-cli repo test --verbose src/` would
* result in `args` being `['--verbose', 'src/']`.
*/
args: string[];
/**
* Metadata about the command being executed.
*/
info: {
/**
* The full usage string of the command including the program name,
* for example `"backstage-cli repo test"`.
*/
usage: string;
/**
* The name of the command as defined by its path,
* for example `"repo test"`.
*/
name: string;
};
}
/**
* A command definition for a Backstage CLI plugin.
*
* Each command is identified by a `path` that determines its position in
* the command tree. For example, a path of `['repo', 'test']` registers
* the command as `backstage-cli repo test`.
*
* Commands can either provide an `execute` function directly, or use a
* `loader` for deferred loading of the implementation. The loader pattern
* is recommended for commands with heavy dependencies, as it avoids
* loading the implementation until the command is actually invoked.
*
* @public
*/
export interface BackstageCommand {
/**
* The path segments that define the command's position in the CLI tree.
* For example, `['repo', 'test']` maps to `backstage-cli repo test`.
*/
path: string[];
/**
* A short description of the command, displayed in help output.
*/
description: string;
/**
* If `true`, the command is deprecated and will be hidden from help output
* but can still be invoked.
*/
deprecated?: boolean;
/**
* If `true`, the command is experimental and will be hidden from help
* output but can still be invoked.
*/
experimental?: boolean;
/**
* The command implementation, either as a direct function or as a loader
* that returns the implementation as a default export. The loader form
* is useful for deferring heavy imports until the command is invoked.
*
* @example
* Direct execution:
* ```
* execute: async ({ args }) => { ... }
* ```
*
* @example
* Deferred loading:
* ```
* execute: { loader: () => import('./my-command') }
* ```
*/
execute:
| ((context: CommandContext) => Promise<void>)
| {
loader: () => Promise<{
default: (context: CommandContext) => Promise<void>;
}>;
};
}
/**
* An opaque representation of a Backstage CLI plugin, created
* using {@link createCliPlugin}.
*
* @public
*/
export interface CliPlugin {
readonly $$type: '@backstage/CliPlugin';
}
+1
View File
@@ -21,6 +21,7 @@
*/
export * from './cache';
export * from './cli-plugin';
export * from './concurrency';
export * from './git';
export * from './monorepo';