@backstage/cli-node
Advanced tools
| 'use strict'; | ||
| var InternalCliModule = require('../cli-internal/src/InternalCliModule.cjs.js'); | ||
| var InternalCommandNode = require('../cli-internal/src/InternalCommandNode.cjs.js'); | ||
| require('node:fs'); | ||
| require('node:os'); | ||
| require('node:path'); | ||
| require('../cli-internal/src/knownPluginPackages.cjs.js'); | ||
| class CommandGraph { | ||
| #roots = []; | ||
| get roots() { | ||
| return this.#roots; | ||
| } | ||
| add(command, module) { | ||
| const { path } = command; | ||
| let current = this.#roots; | ||
| for (let i = 0; i < path.length - 1; i++) { | ||
| const name2 = path[i]; | ||
| let next = current.find((node) => getNodeName(node) === name2); | ||
| if (!next) { | ||
| next = InternalCommandNode.OpaqueCommandTreeNode.createInstance("v1", { | ||
| name: name2, | ||
| children: [] | ||
| }); | ||
| current.push(next); | ||
| } else if (InternalCommandNode.OpaqueCommandLeafNode.isType(next)) { | ||
| throw new Error( | ||
| formatConflictError( | ||
| path, | ||
| module, | ||
| InternalCommandNode.OpaqueCommandLeafNode.toInternal(next).module | ||
| ) | ||
| ); | ||
| } | ||
| current = InternalCommandNode.OpaqueCommandTreeNode.toInternal(next).children; | ||
| } | ||
| const name = path[path.length - 1]; | ||
| const existing = current.find((node) => getNodeName(node) === name); | ||
| if (existing) { | ||
| throw new Error( | ||
| formatConflictError(path, module, findNodeModule(existing)) | ||
| ); | ||
| } | ||
| current.push( | ||
| InternalCommandNode.OpaqueCommandLeafNode.createInstance("v1", { | ||
| name, | ||
| command, | ||
| module | ||
| }) | ||
| ); | ||
| } | ||
| } | ||
| function getNodeName(node) { | ||
| if (InternalCommandNode.OpaqueCommandTreeNode.isType(node)) { | ||
| return InternalCommandNode.OpaqueCommandTreeNode.toInternal(node).name; | ||
| } | ||
| return InternalCommandNode.OpaqueCommandLeafNode.toInternal(node).name; | ||
| } | ||
| function findNodeModule(node) { | ||
| if (InternalCommandNode.OpaqueCommandLeafNode.isType(node)) { | ||
| return InternalCommandNode.OpaqueCommandLeafNode.toInternal(node).module; | ||
| } | ||
| for (const child of InternalCommandNode.OpaqueCommandTreeNode.toInternal(node).children) { | ||
| const module = findNodeModule(child); | ||
| if (module) { | ||
| return module; | ||
| } | ||
| } | ||
| return void 0; | ||
| } | ||
| function getModuleName(module) { | ||
| if (module && InternalCliModule.OpaqueCliModule.isType(module)) { | ||
| return InternalCliModule.OpaqueCliModule.toInternal(module).packageName; | ||
| } | ||
| return void 0; | ||
| } | ||
| function formatConflictError(path, newModule, existingModule) { | ||
| const command = path.join(" "); | ||
| const newPackage = getModuleName(newModule); | ||
| const existingPackage = getModuleName(existingModule); | ||
| if (newPackage && existingPackage) { | ||
| return `Command "${command}" from "${newPackage}" conflicts with an existing command from "${existingPackage}"`; | ||
| } | ||
| if (newPackage) { | ||
| return `Command "${command}" from "${newPackage}" conflicts with an existing command`; | ||
| } | ||
| if (existingPackage) { | ||
| return `Command "${command}" conflicts with an existing command from "${existingPackage}"`; | ||
| } | ||
| return `Command "${command}" conflicts with an existing command`; | ||
| } | ||
| exports.CommandGraph = CommandGraph; | ||
| //# sourceMappingURL=CommandGraph.cjs.js.map |
| {"version":3,"file":"CommandGraph.cjs.js","sources":["../../src/cli-module/CommandGraph.ts"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n OpaqueCliModule,\n OpaqueCommandLeafNode,\n OpaqueCommandTreeNode,\n} from '@internal/cli';\nimport type { CommandNode } from '@internal/cli';\nimport type { CliCommand, CliModule } from './types';\n\nexport class CommandGraph {\n readonly #roots: CommandNode[] = [];\n\n get roots(): ReadonlyArray<CommandNode> {\n return this.#roots;\n }\n\n add(command: CliCommand, module: CliModule): void {\n const { path } = command;\n let current = this.#roots;\n\n for (let i = 0; i < path.length - 1; i++) {\n const name = path[i];\n let next = current.find(node => getNodeName(node) === name);\n\n if (!next) {\n next = OpaqueCommandTreeNode.createInstance('v1', {\n name,\n children: [],\n });\n current.push(next);\n } else if (OpaqueCommandLeafNode.isType(next)) {\n throw new Error(\n formatConflictError(\n path,\n module,\n OpaqueCommandLeafNode.toInternal(next).module,\n ),\n );\n }\n\n current = OpaqueCommandTreeNode.toInternal(next).children;\n }\n\n const name = path[path.length - 1];\n const existing = current.find(node => getNodeName(node) === name);\n if (existing) {\n throw new Error(\n formatConflictError(path, module, findNodeModule(existing)),\n );\n }\n\n current.push(\n OpaqueCommandLeafNode.createInstance('v1', {\n name,\n command,\n module,\n }),\n );\n }\n}\n\nfunction getNodeName(node: CommandNode): string {\n if (OpaqueCommandTreeNode.isType(node)) {\n return OpaqueCommandTreeNode.toInternal(node).name;\n }\n return OpaqueCommandLeafNode.toInternal(node).name;\n}\n\nfunction findNodeModule(node: CommandNode): CliModule | undefined {\n if (OpaqueCommandLeafNode.isType(node)) {\n return OpaqueCommandLeafNode.toInternal(node).module;\n }\n\n for (const child of OpaqueCommandTreeNode.toInternal(node).children) {\n const module = findNodeModule(child);\n if (module) {\n return module;\n }\n }\n\n return undefined;\n}\n\nfunction getModuleName(module?: CliModule): string | undefined {\n if (module && OpaqueCliModule.isType(module)) {\n return OpaqueCliModule.toInternal(module).packageName;\n }\n return undefined;\n}\n\nfunction formatConflictError(\n path: string[],\n newModule: CliModule,\n existingModule?: CliModule,\n): string {\n const command = path.join(' ');\n const newPackage = getModuleName(newModule);\n const existingPackage = getModuleName(existingModule);\n\n if (newPackage && existingPackage) {\n return `Command \"${command}\" from \"${newPackage}\" conflicts with an existing command from \"${existingPackage}\"`;\n }\n if (newPackage) {\n return `Command \"${command}\" from \"${newPackage}\" conflicts with an existing command`;\n }\n if (existingPackage) {\n return `Command \"${command}\" conflicts with an existing command from \"${existingPackage}\"`;\n }\n return `Command \"${command}\" conflicts with an existing command`;\n}\n"],"names":["name","OpaqueCommandTreeNode","OpaqueCommandLeafNode","OpaqueCliModule"],"mappings":"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"} |
| 'use strict'; | ||
| var InternalCliModule = require('../cli-internal/src/InternalCliModule.cjs.js'); | ||
| var InternalCommandNode = require('../cli-internal/src/InternalCommandNode.cjs.js'); | ||
| require('node:fs'); | ||
| require('node:os'); | ||
| require('node:path'); | ||
| require('../cli-internal/src/knownPluginPackages.cjs.js'); | ||
| var errors = require('@backstage/errors'); | ||
| var chalk = require('chalk'); | ||
| var cleye = require('cleye'); | ||
| var CommandGraph = require('./CommandGraph.cjs.js'); | ||
| function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; } | ||
| var chalk__default = /*#__PURE__*/_interopDefaultCompat(chalk); | ||
| function exit(message, code = 1) { | ||
| process.stderr.write(` | ||
| ${chalk__default.default.red(message)} | ||
| `); | ||
| process.exit(code); | ||
| } | ||
| function exitWithError(error) { | ||
| if (!errors.isError(error)) { | ||
| return exit(errors.stringifyError(error)); | ||
| } | ||
| switch (error.name) { | ||
| case "InputError": | ||
| return exit( | ||
| error.message, | ||
| 74 | ||
| /* input/output error */ | ||
| ); | ||
| case "NotFoundError": | ||
| return exit( | ||
| error.message, | ||
| 127 | ||
| /* command not found */ | ||
| ); | ||
| case "NotImplementedError": | ||
| return exit( | ||
| error.message, | ||
| 64 | ||
| /* command line usage error */ | ||
| ); | ||
| case "AuthenticationError": | ||
| case "NotAllowedError": | ||
| return exit( | ||
| error.message, | ||
| 77 | ||
| /* permission denied */ | ||
| ); | ||
| case "ExitCodeError": | ||
| return exit( | ||
| error.message, | ||
| "code" in error && typeof error.code === "number" ? error.code : 1 | ||
| ); | ||
| default: | ||
| return exit( | ||
| errors.stringifyError(error), | ||
| "code" in error && typeof error.code === "number" ? error.code : 1 | ||
| ); | ||
| } | ||
| } | ||
| function getNodeName(node) { | ||
| if (InternalCommandNode.OpaqueCommandTreeNode.isType(node)) { | ||
| return InternalCommandNode.OpaqueCommandTreeNode.toInternal(node).name; | ||
| } | ||
| return InternalCommandNode.OpaqueCommandLeafNode.toInternal(node).name; | ||
| } | ||
| function getNodeDescription(node) { | ||
| if (InternalCommandNode.OpaqueCommandTreeNode.isType(node)) { | ||
| return InternalCommandNode.OpaqueCommandTreeNode.toInternal(node).name; | ||
| } | ||
| return InternalCommandNode.OpaqueCommandLeafNode.toInternal(node).command.description; | ||
| } | ||
| function createHelpOptions(options) { | ||
| const { nodes, includeHelpCommand, version } = options; | ||
| const visibleNodes = nodes.filter((node) => !InternalCommandNode.isCommandNodeHidden(node)); | ||
| return { | ||
| version, | ||
| render(nodesToRender, renderers) { | ||
| const commandsNode = nodesToRender.find((node) => node.id === "commands"); | ||
| if (commandsNode) { | ||
| const commandRows = visibleNodes.map((node) => [ | ||
| getNodeName(node), | ||
| getNodeDescription(node) | ||
| ]); | ||
| if (includeHelpCommand) { | ||
| commandRows.push(["help", "Display help for command"]); | ||
| } | ||
| commandsNode.data.body.data.tableData = commandRows; | ||
| } | ||
| return renderers.render(nodesToRender); | ||
| } | ||
| }; | ||
| } | ||
| async function executeCommand(commandToExecute, args, programName) { | ||
| const context = { | ||
| args, | ||
| info: { | ||
| usage: [programName, ...commandToExecute.path].join(" "), | ||
| name: commandToExecute.path.join(" ") | ||
| } | ||
| }; | ||
| if (typeof commandToExecute.execute === "function") { | ||
| await commandToExecute.execute(context); | ||
| } else { | ||
| const mod = await commandToExecute.execute.loader(); | ||
| const fn = typeof mod.default === "function" ? mod.default : mod.default.default; | ||
| await fn(context); | ||
| } | ||
| } | ||
| async function runCommandLevel(options) { | ||
| const { nodes, argv, programName, commandPath = [], version } = options; | ||
| const name = [programName, ...commandPath].join(" "); | ||
| const commandArgs = argv.slice(1); | ||
| const commands = nodes.map( | ||
| (node) => cleye.command( | ||
| { | ||
| name: getNodeName(node), | ||
| help: false | ||
| }, | ||
| async () => { | ||
| try { | ||
| if (InternalCommandNode.OpaqueCommandTreeNode.isType(node)) { | ||
| await runCommandLevel({ | ||
| nodes: InternalCommandNode.OpaqueCommandTreeNode.toInternal(node).children, | ||
| argv: commandArgs, | ||
| programName, | ||
| commandPath: [...commandPath, getNodeName(node)], | ||
| version | ||
| }); | ||
| } else { | ||
| await executeCommand( | ||
| InternalCommandNode.OpaqueCommandLeafNode.toInternal(node).command, | ||
| commandArgs, | ||
| programName | ||
| ); | ||
| process.exit(0); | ||
| } | ||
| } catch (error) { | ||
| exitWithError(error); | ||
| } | ||
| } | ||
| ) | ||
| ); | ||
| const includeHelpCommand = !nodes.some((node) => getNodeName(node) === "help"); | ||
| if (includeHelpCommand && nodes.length > 0) { | ||
| commands.push( | ||
| cleye.command( | ||
| { | ||
| name: "help", | ||
| parameters: ["[command...]"], | ||
| help: false | ||
| }, | ||
| async () => { | ||
| await runCommandLevel({ | ||
| nodes, | ||
| argv: [...commandArgs, "--help"], | ||
| programName, | ||
| commandPath, | ||
| version | ||
| }); | ||
| } | ||
| ) | ||
| ); | ||
| } | ||
| await cleye.cli( | ||
| { | ||
| name, | ||
| flags: version ? { | ||
| version: { | ||
| type: Boolean, | ||
| alias: "V", | ||
| description: "Show version" | ||
| } | ||
| } : void 0, | ||
| commands, | ||
| help: createHelpOptions({ | ||
| nodes, | ||
| includeHelpCommand: includeHelpCommand && nodes.length > 0, | ||
| version | ||
| }) | ||
| }, | ||
| (parsed) => { | ||
| if (version && parsed.flags.version) { | ||
| console.log(version); | ||
| process.exit(0); | ||
| return; | ||
| } | ||
| if (argv.length === 0) { | ||
| return; | ||
| } | ||
| console.log(); | ||
| console.log( | ||
| chalk__default.default.red(`Invalid command: ${[...commandPath, ...argv].join(" ")}`) | ||
| ); | ||
| console.log(); | ||
| parsed.showHelp(); | ||
| process.exit(1); | ||
| }, | ||
| [...argv] | ||
| ); | ||
| } | ||
| function handleUnhandledRejection(rejection) { | ||
| exitWithError(new errors.ForwardedError("Unhandled rejection", rejection)); | ||
| } | ||
| function hasVersionFlag(args) { | ||
| const separatorIndex = args.indexOf("--"); | ||
| const options = separatorIndex === -1 ? args : args.slice(0, separatorIndex); | ||
| return options.includes("-V") || options.includes("--version"); | ||
| } | ||
| async function runCli(options) { | ||
| const { modules, name, version } = options; | ||
| const graph = new CommandGraph.CommandGraph(); | ||
| for (const module of modules) { | ||
| if (!InternalCliModule.OpaqueCliModule.isType(module)) { | ||
| throw new Error( | ||
| `Invalid CLI module: expected a module created with createCliModule` | ||
| ); | ||
| } | ||
| for (const commandToAdd of await InternalCliModule.OpaqueCliModule.toInternal(module).commands) { | ||
| graph.add(commandToAdd, module); | ||
| } | ||
| } | ||
| if (!process.listeners("unhandledRejection").includes(handleUnhandledRejection)) { | ||
| process.on("unhandledRejection", handleUnhandledRejection); | ||
| } | ||
| const args = process.argv.slice(2); | ||
| if (version && hasVersionFlag(args)) { | ||
| console.log(version); | ||
| process.exit(0); | ||
| return; | ||
| } | ||
| await runCommandLevel({ | ||
| nodes: graph.roots, | ||
| argv: args, | ||
| programName: name, | ||
| version | ||
| }); | ||
| } | ||
| exports.runCli = runCli; | ||
| //# sourceMappingURL=runCli.cjs.js.map |
| {"version":3,"file":"runCli.cjs.js","sources":["../../src/cli-module/runCli.ts"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n OpaqueCliModule,\n OpaqueCommandLeafNode,\n OpaqueCommandTreeNode,\n isCommandNodeHidden,\n} from '@internal/cli';\nimport type { CommandNode } from '@internal/cli';\nimport { ForwardedError, isError, stringifyError } from '@backstage/errors';\nimport chalk from 'chalk';\nimport { cli, command } from 'cleye';\nimport type { Renderers } from 'cleye';\nimport { CommandGraph } from './CommandGraph';\nimport type { CliCommand, CliModule } from './types';\n\ninterface HelpNode {\n id?: string;\n type: keyof Renderers;\n data: unknown;\n}\n\ninterface CommandsHelpData {\n body: {\n data: {\n tableData: string[][];\n };\n };\n}\n\nfunction exit(message: string, code: number = 1): never {\n process.stderr.write(`\\n${chalk.red(message)}\\n\\n`);\n process.exit(code);\n}\n\nfunction exitWithError(error: unknown): never {\n if (!isError(error)) {\n return exit(stringifyError(error));\n }\n\n switch (error.name) {\n case 'InputError':\n return exit(error.message, 74 /* input/output error */);\n case 'NotFoundError':\n return exit(error.message, 127 /* command not found */);\n case 'NotImplementedError':\n return exit(error.message, 64 /* command line usage error */);\n case 'AuthenticationError':\n case 'NotAllowedError':\n return exit(error.message, 77 /* permission denied */);\n case 'ExitCodeError':\n return exit(\n error.message,\n 'code' in error && typeof error.code === 'number' ? error.code : 1,\n );\n default:\n return exit(\n stringifyError(error),\n 'code' in error && typeof error.code === 'number' ? error.code : 1,\n );\n }\n}\n\nfunction getNodeName(node: CommandNode): string {\n if (OpaqueCommandTreeNode.isType(node)) {\n return OpaqueCommandTreeNode.toInternal(node).name;\n }\n return OpaqueCommandLeafNode.toInternal(node).name;\n}\n\nfunction getNodeDescription(node: CommandNode): string {\n if (OpaqueCommandTreeNode.isType(node)) {\n return OpaqueCommandTreeNode.toInternal(node).name;\n }\n return OpaqueCommandLeafNode.toInternal(node).command.description;\n}\n\nfunction createHelpOptions(options: {\n nodes: ReadonlyArray<CommandNode>;\n includeHelpCommand: boolean;\n version?: string;\n}) {\n const { nodes, includeHelpCommand, version } = options;\n const visibleNodes = nodes.filter(node => !isCommandNodeHidden(node));\n\n return {\n version,\n render(nodesToRender: HelpNode[], renderers: Renderers) {\n const commandsNode = nodesToRender.find(node => node.id === 'commands');\n if (commandsNode) {\n const commandRows = visibleNodes.map(node => [\n getNodeName(node),\n getNodeDescription(node),\n ]);\n if (includeHelpCommand) {\n commandRows.push(['help', 'Display help for command']);\n }\n (commandsNode.data as CommandsHelpData).body.data.tableData =\n commandRows;\n }\n return renderers.render(nodesToRender);\n },\n };\n}\n\nasync function executeCommand(\n commandToExecute: CliCommand,\n args: string[],\n programName: string,\n): Promise<void> {\n const context = {\n args,\n info: {\n usage: [programName, ...commandToExecute.path].join(' '),\n name: commandToExecute.path.join(' '),\n },\n };\n\n if (typeof commandToExecute.execute === 'function') {\n await commandToExecute.execute(context);\n } else {\n const mod = await commandToExecute.execute.loader();\n const fn =\n typeof mod.default === 'function'\n ? mod.default\n : (mod.default as any).default;\n await fn(context);\n }\n}\n\nasync function runCommandLevel(options: {\n nodes: ReadonlyArray<CommandNode>;\n argv: string[];\n programName: string;\n commandPath?: string[];\n version?: string;\n}): Promise<void> {\n const { nodes, argv, programName, commandPath = [], version } = options;\n const name = [programName, ...commandPath].join(' ');\n const commandArgs = argv.slice(1);\n const commands = nodes.map(node =>\n command(\n {\n name: getNodeName(node),\n help: false,\n },\n async () => {\n try {\n if (OpaqueCommandTreeNode.isType(node)) {\n await runCommandLevel({\n nodes: OpaqueCommandTreeNode.toInternal(node).children,\n argv: commandArgs,\n programName,\n commandPath: [...commandPath, getNodeName(node)],\n version,\n });\n } else {\n await executeCommand(\n OpaqueCommandLeafNode.toInternal(node).command,\n commandArgs,\n programName,\n );\n process.exit(0);\n }\n } catch (error: unknown) {\n exitWithError(error);\n }\n },\n ),\n );\n const includeHelpCommand = !nodes.some(node => getNodeName(node) === 'help');\n if (includeHelpCommand && nodes.length > 0) {\n commands.push(\n command(\n {\n name: 'help',\n parameters: ['[command...]'],\n help: false,\n },\n async () => {\n await runCommandLevel({\n nodes,\n argv: [...commandArgs, '--help'],\n programName,\n commandPath,\n version,\n });\n },\n ),\n );\n }\n\n await cli(\n {\n name,\n flags: version\n ? {\n version: {\n type: Boolean,\n alias: 'V',\n description: 'Show version',\n },\n }\n : undefined,\n commands,\n help: createHelpOptions({\n nodes,\n includeHelpCommand: includeHelpCommand && nodes.length > 0,\n version,\n }),\n },\n parsed => {\n if (version && parsed.flags.version) {\n console.log(version);\n process.exit(0);\n return;\n }\n\n if (argv.length === 0) {\n return;\n }\n\n console.log();\n console.log(\n chalk.red(`Invalid command: ${[...commandPath, ...argv].join(' ')}`),\n );\n console.log();\n parsed.showHelp();\n process.exit(1);\n },\n [...argv],\n );\n}\n\nfunction handleUnhandledRejection(rejection: unknown): void {\n exitWithError(new ForwardedError('Unhandled rejection', rejection));\n}\n\nfunction hasVersionFlag(args: string[]): boolean {\n const separatorIndex = args.indexOf('--');\n const options = separatorIndex === -1 ? args : args.slice(0, separatorIndex);\n return options.includes('-V') || options.includes('--version');\n}\n\n/**\n * Runs a collection of CLI modules as an executable program.\n *\n * This is intended for creating custom CLI packages from a fixed set of\n * directly imported modules. Module discovery and override behavior are left\n * to the caller.\n *\n * @example\n * ```ts\n * import { runCli } from '@backstage/cli-node';\n * import buildModule from '@backstage/cli-module-build';\n * import testModule from '@backstage/cli-module-test-jest';\n * import packageJson from '../package.json';\n *\n * runCli({\n * modules: [buildModule, testModule],\n * name: packageJson.name,\n * version: packageJson.version,\n * });\n * ```\n *\n * @public\n */\nexport async function runCli(options: {\n /** The CLI modules whose commands are included in the program. */\n modules: ReadonlyArray<CliModule>;\n /** The program name shown in help output and usage strings. */\n name: string;\n /** The version string shown when `--version` is passed. */\n version?: string;\n}): Promise<void> {\n const { modules, name, version } = options;\n const graph = new CommandGraph();\n\n for (const module of modules) {\n if (!OpaqueCliModule.isType(module)) {\n throw new Error(\n `Invalid CLI module: expected a module created with createCliModule`,\n );\n }\n\n for (const commandToAdd of await OpaqueCliModule.toInternal(module)\n .commands) {\n graph.add(commandToAdd, module);\n }\n }\n\n if (\n !process.listeners('unhandledRejection').includes(handleUnhandledRejection)\n ) {\n process.on('unhandledRejection', handleUnhandledRejection);\n }\n\n const args = process.argv.slice(2);\n if (version && hasVersionFlag(args)) {\n console.log(version);\n process.exit(0);\n return;\n }\n\n await runCommandLevel({\n nodes: graph.roots,\n argv: args,\n programName: name,\n version,\n });\n}\n"],"names":["chalk","isError","stringifyError","OpaqueCommandTreeNode","OpaqueCommandLeafNode","isCommandNodeHidden","command","cli","ForwardedError","CommandGraph","OpaqueCliModule"],"mappings":"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"} |
+13
-4
| # @backstage/cli-node | ||
| ## 0.0.0-nightly-20260519032312 | ||
| ## 0.0.0-nightly-20260616032439 | ||
| ### Patch Changes | ||
| - 357d639: Fixed a bug in `PackageGraph.listChangedPackages` where removed dependencies were not detected during lockfile analysis. The dependency graph from the previous lockfile was not being merged, causing transitive dependency removals to be missed. | ||
| - b521571: Added `runCli` for creating executable CLI packages from a fixed collection of directly imported CLI modules, with validation for conflicting command paths. The single-module `runCliModule` helper is now deprecated. | ||
| - Updated dependencies | ||
| - @backstage/errors@0.0.0-nightly-20260519032312 | ||
| - @backstage/cli-common@0.0.0-nightly-20260519032312 | ||
| - @backstage/cli-common@0.2.2 | ||
| - @backstage/errors@1.3.1 | ||
| - @backstage/types@1.2.2 | ||
| ## 0.3.2 | ||
| ### Patch Changes | ||
| - 357d639: Fixed a bug in `PackageGraph.listChangedPackages` where removed dependencies were not detected during lockfile analysis. The dependency graph from the previous lockfile was not being merged, causing transitive dependency removals to be missed. | ||
| - Updated dependencies | ||
| - @backstage/errors@1.3.1 | ||
| - @backstage/cli-common@0.2.2 | ||
| ## 0.3.2-next.1 | ||
@@ -14,0 +23,0 @@ |
| 'use strict'; | ||
| var InternalCliModule = require('../cli-internal/src/InternalCliModule.cjs.js'); | ||
| var InternalCommandNode = require('../cli-internal/src/InternalCommandNode.cjs.js'); | ||
| require('node:fs'); | ||
| require('node:os'); | ||
| require('node:path'); | ||
| require('../cli-internal/src/knownPluginPackages.cjs.js'); | ||
| var commander = require('commander'); | ||
| var chalk = require('chalk'); | ||
| var errors = require('@backstage/errors'); | ||
| var runCli = require('./runCli.cjs.js'); | ||
| function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; } | ||
| var chalk__default = /*#__PURE__*/_interopDefaultCompat(chalk); | ||
| function buildCommandGraph(commands) { | ||
| const graph = []; | ||
| for (const command of commands) { | ||
| const { path } = command; | ||
| let current = graph; | ||
| for (let i = 0; i < path.length - 1; i++) { | ||
| const name = path[i]; | ||
| let next = current.find( | ||
| (n) => InternalCommandNode.OpaqueCommandTreeNode.isType(n) && InternalCommandNode.OpaqueCommandTreeNode.toInternal(n).name === name | ||
| ); | ||
| if (!next) { | ||
| next = InternalCommandNode.OpaqueCommandTreeNode.createInstance("v1", { | ||
| name, | ||
| children: [] | ||
| }); | ||
| current.push(next); | ||
| } | ||
| current = InternalCommandNode.OpaqueCommandTreeNode.toInternal(next).children; | ||
| } | ||
| current.push( | ||
| InternalCommandNode.OpaqueCommandLeafNode.createInstance("v1", { | ||
| name: path[path.length - 1], | ||
| command | ||
| }) | ||
| ); | ||
| } | ||
| return graph; | ||
| } | ||
| function exitWithError(error) { | ||
| process.stderr.write(` | ||
| ${chalk__default.default.red(errors.stringifyError(error))} | ||
| `); | ||
| process.exit( | ||
| errors.isError(error) && "code" in error && typeof error.code === "number" ? error.code : 1 | ||
| ); | ||
| } | ||
| function registerCommands(graph, program, programName) { | ||
| const queue = graph.map((node) => ({ node, argParser: program })); | ||
| while (queue.length) { | ||
| const { node, argParser } = queue.shift(); | ||
| if (InternalCommandNode.OpaqueCommandTreeNode.isType(node)) { | ||
| const internal = InternalCommandNode.OpaqueCommandTreeNode.toInternal(node); | ||
| const treeParser = argParser.command(`${internal.name} [command]`, { | ||
| hidden: InternalCommandNode.isCommandNodeHidden(node) | ||
| }).description(internal.name); | ||
| queue.push( | ||
| ...internal.children.map((child) => ({ | ||
| node: child, | ||
| argParser: treeParser | ||
| })) | ||
| ); | ||
| } else { | ||
| const internal = InternalCommandNode.OpaqueCommandLeafNode.toInternal(node); | ||
| argParser.command(internal.name, { | ||
| hidden: !!internal.command.deprecated || !!internal.command.experimental | ||
| }).description(internal.command.description).helpOption(false).allowUnknownOption(true).allowExcessArguments(true).action(async () => { | ||
| try { | ||
| const args = program.parseOptions(process.argv); | ||
| const nonProcessArgs = args.operands.slice(2); | ||
| const positionalArgs = []; | ||
| let index = 0; | ||
| for (let argIndex = 0; argIndex < nonProcessArgs.length; argIndex++) { | ||
| if (argIndex === index && internal.command.path[argIndex] === nonProcessArgs[argIndex]) { | ||
| index += 1; | ||
| continue; | ||
| } | ||
| positionalArgs.push(nonProcessArgs[argIndex]); | ||
| } | ||
| const context = { | ||
| args: [...positionalArgs, ...args.unknown], | ||
| info: { | ||
| usage: [programName, ...internal.command.path].join(" "), | ||
| name: internal.command.path.join(" ") | ||
| } | ||
| }; | ||
| if (typeof internal.command.execute === "function") { | ||
| await internal.command.execute(context); | ||
| } else { | ||
| const mod = await internal.command.execute.loader(); | ||
| const fn = typeof mod.default === "function" ? mod.default : mod.default.default; | ||
| await fn(context); | ||
| } | ||
| process.exit(0); | ||
| } catch (error) { | ||
| exitWithError(error); | ||
| } | ||
| }); | ||
| } | ||
| } | ||
| } | ||
| async function runCliModule(options) { | ||
| const { module: cliModule, name, version } = options; | ||
| if (!InternalCliModule.OpaqueCliModule.isType(cliModule)) { | ||
| throw new Error( | ||
| `Invalid CLI module: expected a module created with createCliModule` | ||
| ); | ||
| } | ||
| const internal = InternalCliModule.OpaqueCliModule.toInternal(cliModule); | ||
| const commands = await internal.commands; | ||
| const graph = buildCommandGraph(commands); | ||
| const program = new commander.Command(); | ||
| program.name(name).allowUnknownOption(true).allowExcessArguments(true); | ||
| if (version) { | ||
| program.version(version); | ||
| } | ||
| registerCommands(graph, program, name); | ||
| program.on("command:*", () => { | ||
| console.log(); | ||
| console.log(chalk__default.default.red(`Invalid command: ${program.args.join(" ")}`)); | ||
| console.log(); | ||
| program.outputHelp(); | ||
| process.exit(1); | ||
| return runCli.runCli({ | ||
| modules: [cliModule], | ||
| name, | ||
| version | ||
| }); | ||
| process.on("unhandledRejection", (rejection) => { | ||
| exitWithError(rejection); | ||
| }); | ||
| await program.parseAsync(process.argv); | ||
| } | ||
@@ -136,0 +13,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"runCliModule.cjs.js","sources":["../../src/cli-module/runCliModule.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n OpaqueCliModule,\n OpaqueCommandTreeNode,\n OpaqueCommandLeafNode,\n isCommandNodeHidden,\n} from '@internal/cli';\nimport type { CommandNode } from '@internal/cli';\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport { isError, stringifyError } from '@backstage/errors';\nimport type { CliModule, CliCommand } from './types';\n\nfunction buildCommandGraph(commands: ReadonlyArray<CliCommand>): CommandNode[] {\n const graph: CommandNode[] = [];\n\n for (const command of commands) {\n const { path } = command;\n let current = graph;\n\n for (let i = 0; i < path.length - 1; i++) {\n const name = path[i];\n let next = current.find(\n n =>\n OpaqueCommandTreeNode.isType(n) &&\n OpaqueCommandTreeNode.toInternal(n).name === name,\n );\n if (!next) {\n next = OpaqueCommandTreeNode.createInstance('v1', {\n name,\n children: [],\n });\n current.push(next);\n }\n current = OpaqueCommandTreeNode.toInternal(next).children;\n }\n\n current.push(\n OpaqueCommandLeafNode.createInstance('v1', {\n name: path[path.length - 1],\n command,\n }),\n );\n }\n\n return graph;\n}\n\nfunction exitWithError(error: unknown): never {\n process.stderr.write(`\\n${chalk.red(stringifyError(error))}\\n\\n`);\n process.exit(\n isError(error) && 'code' in error && typeof error.code === 'number'\n ? error.code\n : 1,\n );\n}\n\nfunction registerCommands(\n graph: CommandNode[],\n program: Command,\n programName: string,\n): void {\n const queue = graph.map(node => ({ node, argParser: program }));\n\n while (queue.length) {\n const { node, argParser } = queue.shift()!;\n\n if (OpaqueCommandTreeNode.isType(node)) {\n const internal = OpaqueCommandTreeNode.toInternal(node);\n const treeParser = argParser\n .command(`${internal.name} [command]`, {\n hidden: isCommandNodeHidden(node),\n })\n .description(internal.name);\n\n queue.push(\n ...internal.children.map(child => ({\n node: child,\n argParser: treeParser,\n })),\n );\n } else {\n const internal = OpaqueCommandLeafNode.toInternal(node);\n argParser\n .command(internal.name, {\n hidden:\n !!internal.command.deprecated || !!internal.command.experimental,\n })\n .description(internal.command.description)\n .helpOption(false)\n .allowUnknownOption(true)\n .allowExcessArguments(true)\n .action(async () => {\n try {\n const args = program.parseOptions(process.argv);\n\n const nonProcessArgs = args.operands.slice(2);\n const positionalArgs = [];\n let index = 0;\n for (\n let argIndex = 0;\n argIndex < nonProcessArgs.length;\n argIndex++\n ) {\n if (\n argIndex === index &&\n internal.command.path[argIndex] === nonProcessArgs[argIndex]\n ) {\n index += 1;\n continue;\n }\n positionalArgs.push(nonProcessArgs[argIndex]);\n }\n const context = {\n args: [...positionalArgs, ...args.unknown],\n info: {\n usage: [programName, ...internal.command.path].join(' '),\n name: internal.command.path.join(' '),\n },\n };\n\n if (typeof internal.command.execute === 'function') {\n await internal.command.execute(context);\n } else {\n const mod = await internal.command.execute.loader();\n const fn =\n typeof mod.default === 'function'\n ? mod.default\n : (mod.default as any).default;\n await fn(context);\n }\n process.exit(0);\n } catch (error: unknown) {\n exitWithError(error);\n }\n });\n }\n }\n}\n\n/**\n * Runs a CLI module as a standalone program.\n *\n * This helper extracts the commands from a {@link CliModule} and exposes\n * them as a fully functional CLI with help output and argument parsing.\n * It is intended to be called from a module package's `bin` entry point\n * so that the module can be executed directly without being wired into\n * a larger CLI host.\n *\n * @example\n * ```ts\n * #!/usr/bin/env node\n * import { runCliModule } from '@backstage/cli-node';\n * import cliModule from './index';\n *\n * runCliModule({\n * module: cliModule,\n * name: 'backstage-auth',\n * version: require('../package.json').version,\n * });\n * ```\n *\n * @public\n */\nexport async function runCliModule(options: {\n /** The CLI module to run. */\n module: CliModule;\n /** The program name shown in help output and usage strings. */\n name: string;\n /** The version string shown when `--version` is passed. */\n version?: string;\n}): Promise<void> {\n const { module: cliModule, name, version } = options;\n\n if (!OpaqueCliModule.isType(cliModule)) {\n throw new Error(\n `Invalid CLI module: expected a module created with createCliModule`,\n );\n }\n\n const internal = OpaqueCliModule.toInternal(cliModule);\n const commands = await internal.commands;\n const graph = buildCommandGraph(commands);\n\n const program = new Command();\n program.name(name).allowUnknownOption(true).allowExcessArguments(true);\n\n if (version) {\n program.version(version);\n }\n\n registerCommands(graph, program, name);\n\n program.on('command:*', () => {\n console.log();\n console.log(chalk.red(`Invalid command: ${program.args.join(' ')}`));\n console.log();\n program.outputHelp();\n process.exit(1);\n });\n\n process.on('unhandledRejection', rejection => {\n exitWithError(rejection);\n });\n\n await program.parseAsync(process.argv);\n}\n"],"names":["OpaqueCommandTreeNode","OpaqueCommandLeafNode","chalk","stringifyError","isError","isCommandNodeHidden","OpaqueCliModule","Command"],"mappings":"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"} | ||
| {"version":3,"file":"runCliModule.cjs.js","sources":["../../src/cli-module/runCliModule.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { CliModule } from './types';\nimport { runCli } from './runCli';\n\n/**\n * Runs a CLI module as a standalone program.\n *\n * @deprecated Use {@link runCli} with a single entry in the `modules` array instead.\n * @public\n */\nexport async function runCliModule(options: {\n /** The CLI module to run. */\n module: CliModule;\n /** The program name shown in help output and usage strings. */\n name: string;\n /** The version string shown when `--version` is passed. */\n version?: string;\n}): Promise<void> {\n const { module: cliModule, name, version } = options;\n\n return runCli({\n modules: [cliModule],\n name,\n version,\n });\n}\n"],"names":["runCli"],"mappings":";;;;AAyBA,eAAsB,aAAa,OAAA,EAOjB;AAChB,EAAA,MAAM,EAAE,MAAA,EAAQ,SAAA,EAAW,IAAA,EAAM,SAAQ,GAAI,OAAA;AAE7C,EAAA,OAAOA,aAAA,CAAO;AAAA,IACZ,OAAA,EAAS,CAAC,SAAS,CAAA;AAAA,IACnB,IAAA;AAAA,IACA;AAAA,GACD,CAAA;AACH;;;;"} |
@@ -6,2 +6,3 @@ 'use strict'; | ||
| var createCliModule = require('./cli-module/createCliModule.cjs.js'); | ||
| var runCli = require('./cli-module/runCli.cjs.js'); | ||
| var runCliModule = require('./cli-module/runCliModule.cjs.js'); | ||
@@ -22,2 +23,3 @@ var runConcurrentTasks = require('./concurrency/runConcurrentTasks.cjs.js'); | ||
| exports.createCliModule = createCliModule.createCliModule; | ||
| exports.runCli = runCli.runCli; | ||
| exports.runCliModule = runCliModule.runCliModule; | ||
@@ -24,0 +26,0 @@ exports.runConcurrentTasks = runConcurrentTasks.runConcurrentTasks; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;"} | ||
| {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"} |
+28
-14
@@ -218,20 +218,19 @@ import { Package } from '@manypkg/get-packages'; | ||
| /** | ||
| * Runs a CLI module as a standalone program. | ||
| * Runs a collection of CLI modules as an executable program. | ||
| * | ||
| * This helper extracts the commands from a {@link CliModule} and exposes | ||
| * them as a fully functional CLI with help output and argument parsing. | ||
| * It is intended to be called from a module package's `bin` entry point | ||
| * so that the module can be executed directly without being wired into | ||
| * a larger CLI host. | ||
| * This is intended for creating custom CLI packages from a fixed set of | ||
| * directly imported modules. Module discovery and override behavior are left | ||
| * to the caller. | ||
| * | ||
| * @example | ||
| * ```ts | ||
| * #!/usr/bin/env node | ||
| * import { runCliModule } from '@backstage/cli-node'; | ||
| * import cliModule from './index'; | ||
| * import { runCli } from '@backstage/cli-node'; | ||
| * import buildModule from '@backstage/cli-module-build'; | ||
| * import testModule from '@backstage/cli-module-test-jest'; | ||
| * import packageJson from '../package.json'; | ||
| * | ||
| * runCliModule({ | ||
| * module: cliModule, | ||
| * name: 'backstage-auth', | ||
| * version: require('../package.json').version, | ||
| * runCli({ | ||
| * modules: [buildModule, testModule], | ||
| * name: packageJson.name, | ||
| * version: packageJson.version, | ||
| * }); | ||
@@ -242,2 +241,17 @@ * ``` | ||
| */ | ||
| declare function runCli(options: { | ||
| /** The CLI modules whose commands are included in the program. */ | ||
| modules: ReadonlyArray<CliModule>; | ||
| /** The program name shown in help output and usage strings. */ | ||
| name: string; | ||
| /** The version string shown when `--version` is passed. */ | ||
| version?: string; | ||
| }): Promise<void>; | ||
| /** | ||
| * Runs a CLI module as a standalone program. | ||
| * | ||
| * @deprecated Use {@link runCli} with a single entry in the `modules` array instead. | ||
| * @public | ||
| */ | ||
| declare function runCliModule(options: { | ||
@@ -634,3 +648,3 @@ /** The CLI module to run. */ | ||
| export { CliAuth, GitUtils, Lockfile, PackageGraph, PackageRoles, SuccessCache, createCliModule, hasBackstageYarnPlugin, isMonoRepo, packageFeatureType, runCliModule, runConcurrentTasks, runWorkerQueueThreads }; | ||
| export { CliAuth, GitUtils, Lockfile, PackageGraph, PackageRoles, SuccessCache, createCliModule, hasBackstageYarnPlugin, isMonoRepo, packageFeatureType, runCli, runCliModule, runConcurrentTasks, runWorkerQueueThreads }; | ||
| export type { BackstagePackage, BackstagePackageFeatureType, BackstagePackageJson, CliAuthCreateOptions, CliCommand, CliCommandContext, CliModule, ConcurrentTasksOptions, LockfileDiff, LockfileDiffEntry, LockfileQueryEntry, PackageGraphNode, PackageOutputType, PackagePlatform, PackageRole, PackageRoleInfo, WorkerQueueThreadsOptions }; |
+10
-10
| { | ||
| "name": "@backstage/cli-node", | ||
| "version": "0.0.0-nightly-20260519032312", | ||
| "version": "0.0.0-nightly-20260616032439", | ||
| "description": "Node.js library for Backstage CLIs", | ||
@@ -35,4 +35,4 @@ "backstage": { | ||
| "dependencies": { | ||
| "@backstage/cli-common": "0.0.0-nightly-20260519032312", | ||
| "@backstage/errors": "0.0.0-nightly-20260519032312", | ||
| "@backstage/cli-common": "0.2.2", | ||
| "@backstage/errors": "1.3.1", | ||
| "@backstage/types": "1.2.2", | ||
@@ -43,3 +43,3 @@ "@manypkg/get-packages": "^1.1.3", | ||
| "chalk": "^4.0.0", | ||
| "commander": "^12.0.0", | ||
| "cleye": "^2.6.0", | ||
| "fs-extra": "^11.2.0", | ||
@@ -53,11 +53,8 @@ "pirates": "^4.0.6", | ||
| "devDependencies": { | ||
| "@backstage/backend-test-utils": "0.0.0-nightly-20260519032312", | ||
| "@backstage/cli": "0.0.0-nightly-20260519032312", | ||
| "@backstage/test-utils": "0.0.0-nightly-20260519032312", | ||
| "@backstage/backend-test-utils": "0.0.0-nightly-20260616032439", | ||
| "@backstage/cli": "0.0.0-nightly-20260616032439", | ||
| "@backstage/test-utils": "0.0.0-nightly-20260616032439", | ||
| "@types/proper-lockfile": "^4", | ||
| "@types/yarnpkg__lockfile": "^1.1.4" | ||
| }, | ||
| "optionalDependencies": { | ||
| "keytar": "^7.9.0" | ||
| }, | ||
| "peerDependencies": { | ||
@@ -71,2 +68,5 @@ "@swc/core": "^1.15.6" | ||
| }, | ||
| "optionalDependencies": { | ||
| "keytar": "^7.9.0" | ||
| }, | ||
| "typesVersions": { | ||
@@ -73,0 +73,0 @@ "*": { |
+19
-0
@@ -7,2 +7,21 @@ # @backstage/cli-node | ||
| ## Custom CLIs | ||
| Use `runCli` to create an executable CLI from a fixed set of directly imported modules: | ||
| ```ts | ||
| import { runCli } from '@backstage/cli-node'; | ||
| import buildModule from '@backstage/cli-module-build'; | ||
| import testModule from '@backstage/cli-module-test-jest'; | ||
| import packageJson from '../package.json'; | ||
| runCli({ | ||
| modules: [buildModule, testModule], | ||
| name: packageJson.name, | ||
| version: packageJson.version, | ||
| }); | ||
| ``` | ||
| The caller owns module selection. The runner does not discover modules or apply module overrides. | ||
| ## Documentation | ||
@@ -9,0 +28,0 @@ |
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
260495
8.09%54
8%2845
8.71%30
172.73%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
Updated
Updated