From edf2b775815b21153c9fe779f28be673e50add19 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 17 Mar 2026 10:54:26 +0100 Subject: [PATCH] cli-module-new: add template for CLI module packages Add a new `cli-module` template to the Backstage CLI that scaffolds CLI module packages. This includes adding the `cli-module` role to the template system, with proper naming conventions and prompts. The generated package includes: - A bin entry point for standalone execution - An index.ts with createCliModule setup - An example command using cleye - Standard package.json with cli-module role Signed-off-by: Patrik Oldsberg Made-with: Cursor --- .changeset/add-cli-module-template-support.md | 5 +++ .changeset/add-cli-module-template.md | 5 +++ .../src/lib/defaultTemplates.ts | 1 + .../collectPortableTemplateInput.ts | 1 + .../preparation/resolvePackageParams.test.ts | 7 ++++ .../lib/preparation/resolvePackageParams.ts | 2 ++ packages/cli-module-new/src/lib/types.ts | 3 +- .../cli/templates/cli-module/.eslintrc.js.hbs | 1 + .../cli/templates/cli-module/README.md.hbs | 5 +++ .../cli/templates/cli-module/bin/{{binName}} | 32 +++++++++++++++++ .../cli/templates/cli-module/package.json.hbs | 35 +++++++++++++++++++ .../cli-module/portable-template.yaml | 5 +++ .../cli-module/src/commands/example.ts | 18 ++++++++++ .../cli/templates/cli-module/src/index.ts.hbs | 20 +++++++++++ 14 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 .changeset/add-cli-module-template-support.md create mode 100644 .changeset/add-cli-module-template.md create mode 100644 packages/cli/templates/cli-module/.eslintrc.js.hbs create mode 100644 packages/cli/templates/cli-module/README.md.hbs create mode 100644 packages/cli/templates/cli-module/bin/{{binName}} create mode 100644 packages/cli/templates/cli-module/package.json.hbs create mode 100644 packages/cli/templates/cli-module/portable-template.yaml create mode 100644 packages/cli/templates/cli-module/src/commands/example.ts create mode 100644 packages/cli/templates/cli-module/src/index.ts.hbs diff --git a/.changeset/add-cli-module-template-support.md b/.changeset/add-cli-module-template-support.md new file mode 100644 index 0000000000..be965d882d --- /dev/null +++ b/.changeset/add-cli-module-template-support.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli-module-new': patch +--- + +Added support for the `cli-module` template role for scaffolding new CLI module packages. diff --git a/.changeset/add-cli-module-template.md b/.changeset/add-cli-module-template.md new file mode 100644 index 0000000000..f6b169906f --- /dev/null +++ b/.changeset/add-cli-module-template.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Added a new `cli-module` template for creating CLI module packages. diff --git a/packages/cli-module-new/src/lib/defaultTemplates.ts b/packages/cli-module-new/src/lib/defaultTemplates.ts index 9d1543c452..dea35a6e99 100644 --- a/packages/cli-module-new/src/lib/defaultTemplates.ts +++ b/packages/cli-module-new/src/lib/defaultTemplates.ts @@ -23,6 +23,7 @@ export const defaultTemplates = [ '@backstage/cli/templates/plugin-common-library', '@backstage/cli/templates/web-library', '@backstage/cli/templates/node-library', + '@backstage/cli/templates/cli-module', '@backstage/cli/templates/catalog-provider-module', '@backstage/cli/templates/scaffolder-backend-module', ]; diff --git a/packages/cli-module-new/src/lib/preparation/collectPortableTemplateInput.ts b/packages/cli-module-new/src/lib/preparation/collectPortableTemplateInput.ts index 7279801d62..63e3791a49 100644 --- a/packages/cli-module-new/src/lib/preparation/collectPortableTemplateInput.ts +++ b/packages/cli-module-new/src/lib/preparation/collectPortableTemplateInput.ts @@ -160,6 +160,7 @@ export function getPromptsForRole( case 'web-library': case 'node-library': case 'common-library': + case 'cli-module': return [namePrompt()]; case 'plugin-web-library': case 'plugin-node-library': diff --git a/packages/cli-module-new/src/lib/preparation/resolvePackageParams.test.ts b/packages/cli-module-new/src/lib/preparation/resolvePackageParams.test.ts index df128d4755..f279b702b1 100644 --- a/packages/cli-module-new/src/lib/preparation/resolvePackageParams.test.ts +++ b/packages/cli-module-new/src/lib/preparation/resolvePackageParams.test.ts @@ -37,6 +37,13 @@ describe.each([ packagePath: 'packages/test', }, ], + [ + { role: 'cli-module', name: 'test' }, + { + packageName: '@internal/cli-module-test', + packagePath: 'packages/cli-module-test', + }, + ], [ { role: 'plugin-web-library', pluginId: 'test' }, { diff --git a/packages/cli-module-new/src/lib/preparation/resolvePackageParams.ts b/packages/cli-module-new/src/lib/preparation/resolvePackageParams.ts index dd85f49fad..8a4f87c5c6 100644 --- a/packages/cli-module-new/src/lib/preparation/resolvePackageParams.ts +++ b/packages/cli-module-new/src/lib/preparation/resolvePackageParams.ts @@ -48,6 +48,8 @@ function getBaseNameForRole( case 'node-library': case 'common-library': return roleParams.name; + case 'cli-module': + return `cli-module-${roleParams.name}`; case 'plugin-web-library': return `${roleParams.pluginId}-react`; case 'plugin-node-library': diff --git a/packages/cli-module-new/src/lib/types.ts b/packages/cli-module-new/src/lib/types.ts index 9f221ea7aa..83a195a43a 100644 --- a/packages/cli-module-new/src/lib/types.ts +++ b/packages/cli-module-new/src/lib/types.ts @@ -50,6 +50,7 @@ export const TEMPLATE_ROLES = [ 'web-library', 'node-library', 'common-library', + 'cli-module', 'plugin-web-library', 'plugin-node-library', 'plugin-common-library', @@ -80,7 +81,7 @@ export type PortableTemplateParams = { export type PortableTemplateInputRoleParams = | { - role: 'web-library' | 'node-library' | 'common-library'; + role: 'web-library' | 'node-library' | 'common-library' | 'cli-module'; name: string; } | { diff --git a/packages/cli/templates/cli-module/.eslintrc.js.hbs b/packages/cli/templates/cli-module/.eslintrc.js.hbs new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/packages/cli/templates/cli-module/.eslintrc.js.hbs @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/packages/cli/templates/cli-module/README.md.hbs b/packages/cli/templates/cli-module/README.md.hbs new file mode 100644 index 0000000000..657b45805b --- /dev/null +++ b/packages/cli/templates/cli-module/README.md.hbs @@ -0,0 +1,5 @@ +# {{packageName}} + +A CLI module that adds commands to the Backstage CLI. + +_This package was created through the Backstage CLI_ diff --git a/packages/cli/templates/cli-module/bin/{{binName}} b/packages/cli/templates/cli-module/bin/{{binName}} new file mode 100644 index 0000000000..59f8267233 --- /dev/null +++ b/packages/cli/templates/cli-module/bin/{{binName}} @@ -0,0 +1,32 @@ +#!/usr/bin/env node +/* + * 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. + */ + +const path = require('node:path'); + +/* eslint-disable-next-line no-restricted-syntax */ +const isLocal = require('node:fs').existsSync( + path.resolve(__dirname, '../src'), +); + +if (isLocal) { + require('@backstage/cli-node/config/nodeTransform.cjs'); +} + +const { runCliModule } = require('@backstage/cli-node'); +const cliModule = require(isLocal ? '../src/index' : '..').default; +const pkg = require('../package.json'); +runCliModule({ module: cliModule, name: pkg.name, version: pkg.version }); diff --git a/packages/cli/templates/cli-module/package.json.hbs b/packages/cli/templates/cli-module/package.json.hbs new file mode 100644 index 0000000000..5e077cca83 --- /dev/null +++ b/packages/cli/templates/cli-module/package.json.hbs @@ -0,0 +1,35 @@ +{ + "name": "{{packageName}}", + "description": "CLI module for Backstage CLI", + "main": "src/index.ts", + "types": "src/index.ts", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "cli-module" + }, + "scripts": { + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/cli-common": "{{versionQuery '@backstage/cli-common'}}", + "@backstage/cli-node": "{{versionQuery '@backstage/cli-node'}}", + "cleye": "^2.3.0" + }, + "devDependencies": { + "@backstage/cli": "{{versionQuery '@backstage/cli'}}" + }, + "files": [ + "dist", + "bin" + ], + "bin": "bin/{{binName}}" +} diff --git a/packages/cli/templates/cli-module/portable-template.yaml b/packages/cli/templates/cli-module/portable-template.yaml new file mode 100644 index 0000000000..72b6c2786b --- /dev/null +++ b/packages/cli/templates/cli-module/portable-template.yaml @@ -0,0 +1,5 @@ +name: cli-module +role: cli-module +description: A CLI module that adds commands to the Backstage CLI +values: + binName: 'backstage-cli-module-{{ name }}' diff --git a/packages/cli/templates/cli-module/src/commands/example.ts b/packages/cli/templates/cli-module/src/commands/example.ts new file mode 100644 index 0000000000..03a8f88fc2 --- /dev/null +++ b/packages/cli/templates/cli-module/src/commands/example.ts @@ -0,0 +1,18 @@ +import { cli } from 'cleye'; +import type { CliCommandContext } from '@backstage/cli-node'; + +export default async ({ args, info }: CliCommandContext) => { + const { flags } = cli( + { + help: info, + booleanFlagNegation: true, + flags: {}, + }, + undefined, + args, + ); + + void flags; + + console.log('Hello from example command!'); +}; diff --git a/packages/cli/templates/cli-module/src/index.ts.hbs b/packages/cli/templates/cli-module/src/index.ts.hbs new file mode 100644 index 0000000000..a947d3d666 --- /dev/null +++ b/packages/cli/templates/cli-module/src/index.ts.hbs @@ -0,0 +1,20 @@ +/***/ +/** + * CLI module for the Backstage CLI. + * + * @packageDocumentation + */ + +import { createCliModule } from '@backstage/cli-node'; +import packageJson from '../package.json'; + +export default createCliModule({ + packageJson, + init: async reg => { + reg.addCommand({ + path: ['example'], + description: 'An example command', + execute: { loader: () => import('./commands/example') }, + }); + }, +});