From 2fcba39deaf8050fb256fdc9fc0e033df4e10bba Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 25 Feb 2026 11:47:27 +0100 Subject: [PATCH] Internalize CLI lib utilities into consuming modules Move typeDistProject.ts into the build module, duplicate optionsParser.ts into build and lint modules, inline configOption in the build module, and simplify the deprecated migrate package-exports command to remove cross-module dependencies. Signed-off-by: Patrik Oldsberg --- .changeset/cli-internalize-lib-modules.md | 5 ++ .../modules/build/commands/package/pack.ts | 2 +- .../src/modules/build/commands/repo/build.ts | 2 +- packages/cli/src/modules/build/index.ts | 8 ++- .../__testUtils__/createFeatureEnvironment.ts | 0 .../build/lib/bundler/moduleFederation.ts | 2 +- .../{ => modules/build}/lib/optionsParser.ts | 0 .../build/lib/packager/createDistWorkspace.ts | 2 +- .../build/lib/packager/productionPack.ts | 2 +- .../build}/lib/typeDistProject.test.ts | 0 .../build}/lib/typeDistProject.ts | 0 .../src/modules/lint/commands/repo/lint.ts | 2 +- .../cli/src/modules/lint/lib/optionsParser.ts | 70 +++++++++++++++++++ .../migrate/commands/packageExports.ts | 15 +--- 14 files changed, 89 insertions(+), 21 deletions(-) create mode 100644 .changeset/cli-internalize-lib-modules.md rename packages/cli/src/{ => modules/build}/lib/__testUtils__/createFeatureEnvironment.ts (100%) rename packages/cli/src/{ => modules/build}/lib/optionsParser.ts (100%) rename packages/cli/src/{ => modules/build}/lib/typeDistProject.test.ts (100%) rename packages/cli/src/{ => modules/build}/lib/typeDistProject.ts (100%) create mode 100644 packages/cli/src/modules/lint/lib/optionsParser.ts diff --git a/.changeset/cli-internalize-lib-modules.md b/.changeset/cli-internalize-lib-modules.md new file mode 100644 index 0000000000..f9ef219a3a --- /dev/null +++ b/.changeset/cli-internalize-lib-modules.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Internal refactor to move shared utilities into their consuming modules, reducing cross-module dependencies. diff --git a/packages/cli/src/modules/build/commands/package/pack.ts b/packages/cli/src/modules/build/commands/package/pack.ts index 737b4b6931..67ef7bca8b 100644 --- a/packages/cli/src/modules/build/commands/package/pack.ts +++ b/packages/cli/src/modules/build/commands/package/pack.ts @@ -22,7 +22,7 @@ import { targetPaths } from '@backstage/cli-common'; import fs from 'fs-extra'; import { publishPreflightCheck } from '../../lib/publishing'; -import { createTypeDistProject } from '../../../../lib/typeDistProject'; +import { createTypeDistProject } from '../../../build/lib/typeDistProject'; export const pre = async () => { publishPreflightCheck({ diff --git a/packages/cli/src/modules/build/commands/repo/build.ts b/packages/cli/src/modules/build/commands/repo/build.ts index dd65e926b2..59d0a839e4 100644 --- a/packages/cli/src/modules/build/commands/repo/build.ts +++ b/packages/cli/src/modules/build/commands/repo/build.ts @@ -28,7 +28,7 @@ import { } from '@backstage/cli-node'; import { buildFrontend } from '../../lib/buildFrontend'; import { buildBackend } from '../../lib/buildBackend'; -import { createScriptOptionsParser } from '../../../../lib/optionsParser'; +import { createScriptOptionsParser } from '../../lib/optionsParser'; export async function command(opts: OptionValues, cmd: Command): Promise { let packages = await PackageGraph.listTargetPackages(); diff --git a/packages/cli/src/modules/build/index.ts b/packages/cli/src/modules/build/index.ts index 277993d252..30a9b3a19e 100644 --- a/packages/cli/src/modules/build/index.ts +++ b/packages/cli/src/modules/build/index.ts @@ -17,7 +17,13 @@ import { Command, Option } from 'commander'; import { createCliPlugin } from '../../wiring/factory'; import { lazy } from '../../wiring/lazy'; -import { configOption } from '../config'; + +const configOption = [ + '--config ', + 'Config files to load instead of app-config.yaml', + (opt: string, opts: string[]) => (opts ? [...opts, opt] : [opt]), + Array(), +] as const; export function registerPackageCommands(command: Command) { command diff --git a/packages/cli/src/lib/__testUtils__/createFeatureEnvironment.ts b/packages/cli/src/modules/build/lib/__testUtils__/createFeatureEnvironment.ts similarity index 100% rename from packages/cli/src/lib/__testUtils__/createFeatureEnvironment.ts rename to packages/cli/src/modules/build/lib/__testUtils__/createFeatureEnvironment.ts diff --git a/packages/cli/src/modules/build/lib/bundler/moduleFederation.ts b/packages/cli/src/modules/build/lib/bundler/moduleFederation.ts index 307cb5cebc..69e1415b5d 100644 --- a/packages/cli/src/modules/build/lib/bundler/moduleFederation.ts +++ b/packages/cli/src/modules/build/lib/bundler/moduleFederation.ts @@ -20,7 +20,7 @@ import { readEntryPoints } from '../entryPoints'; import { createTypeDistProject, getEntryPointDefaultFeatureType, -} from '../../../../lib/typeDistProject'; +} from '../typeDistProject'; import { BACKSTAGE_RUNTIME_SHARED_DEPENDENCIES_GLOBAL, defaultRemoteSharedDependencies, diff --git a/packages/cli/src/lib/optionsParser.ts b/packages/cli/src/modules/build/lib/optionsParser.ts similarity index 100% rename from packages/cli/src/lib/optionsParser.ts rename to packages/cli/src/modules/build/lib/optionsParser.ts diff --git a/packages/cli/src/modules/build/lib/packager/createDistWorkspace.ts b/packages/cli/src/modules/build/lib/packager/createDistWorkspace.ts index 50fb0f4e31..9f63525186 100644 --- a/packages/cli/src/modules/build/lib/packager/createDistWorkspace.ts +++ b/packages/cli/src/modules/build/lib/packager/createDistWorkspace.ts @@ -44,7 +44,7 @@ import { PackageGraphNode, runConcurrentTasks, } from '@backstage/cli-node'; -import { createTypeDistProject } from '../../../../lib/typeDistProject'; +import { createTypeDistProject } from '../typeDistProject'; // These packages aren't safe to pack in parallel since the CLI depends on them const UNSAFE_PACKAGES = [ diff --git a/packages/cli/src/modules/build/lib/packager/productionPack.ts b/packages/cli/src/modules/build/lib/packager/productionPack.ts index ce9f15388c..55eb6edc48 100644 --- a/packages/cli/src/modules/build/lib/packager/productionPack.ts +++ b/packages/cli/src/modules/build/lib/packager/productionPack.ts @@ -19,7 +19,7 @@ import npmPackList from 'npm-packlist'; import { resolve as resolvePath, posix as posixPath } from 'node:path'; import { BackstagePackageJson } from '@backstage/cli-node'; import { readEntryPoints } from '../entryPoints'; -import { getEntryPointDefaultFeatureType } from '../../../../lib/typeDistProject'; +import { getEntryPointDefaultFeatureType } from '../typeDistProject'; import { Project } from 'ts-morph'; const PKG_PATH = 'package.json'; diff --git a/packages/cli/src/lib/typeDistProject.test.ts b/packages/cli/src/modules/build/lib/typeDistProject.test.ts similarity index 100% rename from packages/cli/src/lib/typeDistProject.test.ts rename to packages/cli/src/modules/build/lib/typeDistProject.test.ts diff --git a/packages/cli/src/lib/typeDistProject.ts b/packages/cli/src/modules/build/lib/typeDistProject.ts similarity index 100% rename from packages/cli/src/lib/typeDistProject.ts rename to packages/cli/src/modules/build/lib/typeDistProject.ts diff --git a/packages/cli/src/modules/lint/commands/repo/lint.ts b/packages/cli/src/modules/lint/commands/repo/lint.ts index dafcff45c6..d0b4f6f37e 100644 --- a/packages/cli/src/modules/lint/commands/repo/lint.ts +++ b/packages/cli/src/modules/lint/commands/repo/lint.ts @@ -27,7 +27,7 @@ import { } from '@backstage/cli-node'; import { targetPaths } from '@backstage/cli-common'; -import { createScriptOptionsParser } from '../../../../lib/optionsParser'; +import { createScriptOptionsParser } from '../../lib/optionsParser'; import { SuccessCache } from '../../../../lib/cache/SuccessCache'; function depCount(pkg: BackstagePackageJson) { diff --git a/packages/cli/src/modules/lint/lib/optionsParser.ts b/packages/cli/src/modules/lint/lib/optionsParser.ts new file mode 100644 index 0000000000..45c7eac0aa --- /dev/null +++ b/packages/cli/src/modules/lint/lib/optionsParser.ts @@ -0,0 +1,70 @@ +/* + * 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 { Command } from 'commander'; + +export function createScriptOptionsParser( + anyCmd: Command, + commandPath: string[], +) { + // Regardless of what command instance is passed in we want to find + // the root command and resolve the path from there + let rootCmd = anyCmd; + while (rootCmd.parent) { + rootCmd = rootCmd.parent; + } + + // Now find the command that was requested + let targetCmd = rootCmd as Command | undefined; + for (const name of commandPath) { + targetCmd = targetCmd?.commands.find(c => c.name() === name) as + | Command + | undefined; + } + + if (!targetCmd) { + throw new Error( + `Could not find package command '${commandPath.join(' ')}'`, + ); + } + const cmd = targetCmd; + + const expectedScript = `backstage-cli ${commandPath.join(' ')}`; + + return (scriptStr?: string) => { + if (!scriptStr || !scriptStr.startsWith(expectedScript)) { + return undefined; + } + + const argsStr = scriptStr.slice(expectedScript.length).trim(); + + // Can't clone or copy or even use commands as prototype, so we mutate + // the necessary members instead, and then reset them once we're done + const currentOpts = (cmd as any)._optionValues; + const currentStore = (cmd as any)._storeOptionsAsProperties; + + const result: Record = {}; + (cmd as any)._storeOptionsAsProperties = false; + (cmd as any)._optionValues = result; + + // Triggers the writing of options to the result object + cmd.parseOptions(argsStr.split(' ')); + + (cmd as any)._storeOptionsAsProperties = currentOpts; + (cmd as any)._optionValues = currentStore; + + return result; + }; +} diff --git a/packages/cli/src/modules/migrate/commands/packageExports.ts b/packages/cli/src/modules/migrate/commands/packageExports.ts index 4eb741ce4d..0d02782509 100644 --- a/packages/cli/src/modules/migrate/commands/packageExports.ts +++ b/packages/cli/src/modules/migrate/commands/packageExports.ts @@ -14,21 +14,8 @@ * limitations under the License. */ -import { - fixPackageExports, - readFixablePackages, - writeFixedPackages, -} from '../../maintenance/commands/repo/fix'; - export async function command() { console.log( - 'The `migrate package-exports` command is deprecated, use `repo fix` instead.', + 'The `migrate package-exports` command has been removed, use `repo fix` instead.', ); - const packages = await readFixablePackages(); - - for (const pkg of packages) { - fixPackageExports(pkg); - } - - await writeFixedPackages(packages); }