diff --git a/.changeset/cli-common-cached-paths.md b/.changeset/cli-common-cached-paths.md index a82a5434b0..6d9ec14297 100644 --- a/.changeset/cli-common-cached-paths.md +++ b/.changeset/cli-common-cached-paths.md @@ -3,3 +3,26 @@ --- Added `targetPaths` and `findOwnPaths` as replacements for `findPaths`, with a cleaner separation between target project paths and package-relative paths. + +To migrate existing `findPaths` usage: + +```ts +// Before +import { findPaths } from '@backstage/cli-common'; +const paths = findPaths(__dirname); + +// After — for target project paths (cwd-based): +import { targetPaths } from '@backstage/cli-common'; +// paths.targetDir → targetPaths.dir +// paths.targetRoot → targetPaths.rootDir +// paths.resolveTarget('src') → targetPaths.resolve('src') +// paths.resolveTargetRoot('yarn.lock') → targetPaths.resolveRoot('yarn.lock') + +// After — for package-relative paths: +import { findOwnPaths } from '@backstage/cli-common'; +const own = findOwnPaths(__dirname); +// paths.ownDir → own.dir +// paths.ownRoot → own.rootDir +// paths.resolveOwn('config/jest.js') → own.resolve('config/jest.js') +// paths.resolveOwnRoot('tsconfig.json') → own.resolveRoot('tsconfig.json') +``` diff --git a/.changeset/migrate-to-target-paths.md b/.changeset/migrate-to-target-paths.md index b3f983bffc..a3c0c0c57e 100644 --- a/.changeset/migrate-to-target-paths.md +++ b/.changeset/migrate-to-target-paths.md @@ -5,7 +5,7 @@ '@backstage/config-loader': patch '@backstage/create-app': patch '@backstage/repo-tools': patch -'@backstage/techdocs-cli': patch +'@techdocs/cli': patch --- Migrated from deprecated `findPaths` to `targetPaths` and `findOwnPaths` from `@backstage/cli-common`. diff --git a/packages/backend-dynamic-feature-service/src/manager/plugin-manager.test.ts b/packages/backend-dynamic-feature-service/src/manager/plugin-manager.test.ts index 011aae0457..b778e4df6e 100644 --- a/packages/backend-dynamic-feature-service/src/manager/plugin-manager.test.ts +++ b/packages/backend-dynamic-feature-service/src/manager/plugin-manager.test.ts @@ -40,37 +40,24 @@ import { Logs, MockedLogger, LogContent } from '../__testUtils__/testUtils'; import { PluginScanner } from '../scanner/plugin-scanner'; import { targetPaths } from '@backstage/cli-common'; import { createMockDirectory } from '@backstage/backend-test-utils'; - -jest.mock('@backstage/cli-common', () => { - const path = require('path'); - const actual = jest.requireActual( - '@backstage/cli-common', - ); - const mockRoot = path.resolve(__dirname, '..', '..', '..', '..'); - return { - ...actual, - targetPaths: { - resolve: (...paths: string[]) => path.join(mockRoot, ...paths), - resolveRoot: (...paths: string[]) => path.join(mockRoot, ...paths), - }, - findPaths: (searchDir: string) => ({ - targetRoot: mockRoot, - targetDir: mockRoot, - ownDir: path.dirname(searchDir), - ownRoot: mockRoot, - resolveOwn: (...p: string[]) => path.join(path.dirname(searchDir), ...p), - resolveOwnRoot: (...p: string[]) => path.join(mockRoot, ...p), - resolveTarget: (...p: string[]) => path.join(mockRoot, ...p), - resolveTargetRoot: (...p: string[]) => path.join(mockRoot, ...p), - }), - }; -}); import { rootLifecycleServiceFactory } from '@backstage/backend-defaults/rootLifecycle'; import { BackstagePackageJson, PackageRole } from '@backstage/cli-node'; describe('backend-dynamic-feature-service', () => { const mockDir = createMockDirectory(); + beforeAll(() => { + jest + .spyOn(targetPaths, 'resolveRoot') + .mockImplementation((...paths: string[]) => + require('path').resolve(__dirname, '../../../..', ...paths), + ); + }); + + afterAll(() => { + jest.restoreAllMocks(); + }); + describe('loadPlugins', () => { afterEach(() => { jest.resetModules(); @@ -1069,7 +1056,7 @@ describe('backend-dynamic-feature-service', () => { otherMockDir.resolve('a-dynamic-plugin'), ); expect(mockedModuleLoader.bootstrap).toHaveBeenCalledWith( - targetPaths.resolveRoot(), + targetPaths.rootDir, [realPath], new Map([ [ diff --git a/packages/backend-dynamic-feature-service/src/manager/plugin-manager.ts b/packages/backend-dynamic-feature-service/src/manager/plugin-manager.ts index 5fe4f7241c..b79a796eb9 100644 --- a/packages/backend-dynamic-feature-service/src/manager/plugin-manager.ts +++ b/packages/backend-dynamic-feature-service/src/manager/plugin-manager.ts @@ -56,7 +56,7 @@ export class DynamicPluginManager implements DynamicPluginProvider { options: DynamicPluginManagerOptions, ): Promise { /* eslint-disable-next-line no-restricted-syntax */ - const backstageRoot = targetPaths.resolveRoot(); + const backstageRoot = targetPaths.rootDir; const scanner = PluginScanner.create({ config: options.config, logger: options.logger, diff --git a/packages/backend-dynamic-feature-service/src/schemas/schemas.ts b/packages/backend-dynamic-feature-service/src/schemas/schemas.ts index 3a1bc666b0..400f57829a 100644 --- a/packages/backend-dynamic-feature-service/src/schemas/schemas.ts +++ b/packages/backend-dynamic-feature-service/src/schemas/schemas.ts @@ -100,7 +100,7 @@ const dynamicPluginsSchemasServiceFactoryWithOptions = ( config, logger, // eslint-disable-next-line no-restricted-syntax - backstageRoot: targetPaths.resolveRoot(), + backstageRoot: targetPaths.rootDir, preferAlpha: true, }); diff --git a/packages/cli-common/report.api.md b/packages/cli-common/report.api.md index 96475c6bd1..18f2ccb63c 100644 --- a/packages/cli-common/report.api.md +++ b/packages/cli-common/report.api.md @@ -21,12 +21,23 @@ export class ExitCodeError extends CustomErrorBase { } // @public +export function findOwnPaths(searchDir: string): OwnPaths; + +// @public @deprecated export function findPaths(searchDir: string): Paths; // @public export function isChildPath(base: string, path: string): boolean; // @public +export type OwnPaths = { + dir: string; + rootDir: string; + resolve: ResolveFunc; + resolveRoot: ResolveFunc; +}; + +// @public @deprecated export type Paths = { ownDir: string; ownRoot: string; @@ -68,4 +79,15 @@ export function runOutput( args: string[], options?: RunOptions, ): Promise; + +// @public +export type TargetPaths = { + dir: string; + rootDir: string; + resolve: ResolveFunc; + resolveRoot: ResolveFunc; +}; + +// @public +export const targetPaths: TargetPaths; ``` diff --git a/packages/cli-common/src/paths.ts b/packages/cli-common/src/paths.ts index 15cdc7b89c..f4fded7cd4 100644 --- a/packages/cli-common/src/paths.ts +++ b/packages/cli-common/src/paths.ts @@ -33,6 +33,12 @@ export type ResolveFunc = (...paths: string[]) => string; * @public */ export type TargetPaths = { + /** The target package directory. */ + dir: string; + + /** The target monorepo root directory. */ + rootDir: string; + /** Resolve a path relative to the target package directory. */ resolve: ResolveFunc; @@ -46,6 +52,12 @@ export type TargetPaths = { * @public */ export type OwnPaths = { + /** The package root directory. */ + dir: string; + + /** The monorepo root directory containing the package. */ + rootDir: string; + /** Resolve a path relative to the package root. */ resolve: ResolveFunc; @@ -98,48 +110,12 @@ export function findRootPath( ); } -// Hierarchical cache for ownDir lookups. When we resolve a searchDir to its -// package root, we also cache every intermediate directory along the way. This -// means sibling directories only need to walk up until they hit a cached ancestor. -const ownDirCache = new Map(); - // Finds the root of a given package export function findOwnDir(searchDir: string) { - const visited: string[] = []; - let dir = searchDir; - - for (let i = 0; i < 1000; i++) { - const cached = ownDirCache.get(dir); - if (cached !== undefined) { - for (const d of visited) { - ownDirCache.set(d, cached); - } - return cached; - } - - visited.push(dir); - - const packagePath = resolvePath(dir, 'package.json'); - if (fs.existsSync(packagePath)) { - for (const d of visited) { - ownDirCache.set(d, dir); - } - return dir; - } - - const newDir = dirname(dir); - if (newDir === dir) { - break; - } - dir = newDir; - } - - throw new Error( - `No package.json found while searching for package root of ${searchDir}`, - ); + return OwnPathsImpl.findDir(searchDir); } -// Finds the root of the monorepo that the package exists in. Only accessible when running inside Backstage repo. +// Finds the root of the monorepo that the package exists in. export function findOwnRootDir(ownDir: string) { const isLocal = fs.existsSync(resolvePath(ownDir, 'src')); if (!isLocal) { @@ -151,46 +127,131 @@ export function findOwnRootDir(ownDir: string) { return resolvePath(ownDir, '../..'); } -// Cached target path state. Re-resolves when process.cwd() changes. -let cachedTargetCwd: string | undefined; -let cachedTargetDir: string | undefined; -let cachedTargetRoot: string | undefined; +// Hierarchical directory cache shared across all OwnPathsImpl instances. +// When we resolve a searchDir to its package root, we also cache every +// intermediate directory, so sibling directories share work. +const dirCache = new Map(); -function getTargetDir(): string { - const cwd = process.cwd(); - if (cachedTargetDir !== undefined && cachedTargetCwd === cwd) { - return cachedTargetDir; +class OwnPathsImpl implements OwnPaths { + static #instanceCache = new Map(); + + static find(searchDir: string): OwnPathsImpl { + const dir = OwnPathsImpl.findDir(searchDir); + let instance = OwnPathsImpl.#instanceCache.get(dir); + if (!instance) { + instance = new OwnPathsImpl(dir); + OwnPathsImpl.#instanceCache.set(dir, instance); + } + return instance; } - cachedTargetCwd = cwd; - cachedTargetRoot = undefined; - // Drive letter can end up being lowercased here on Windows, bring back to uppercase for consistency - cachedTargetDir = fs - .realpathSync(cwd) - .replace(/^[a-z]:/, str => str.toLocaleUpperCase('en-US')); - return cachedTargetDir; + + static findDir(searchDir: string): string { + const visited: string[] = []; + let dir = searchDir; + + for (let i = 0; i < 1000; i++) { + const cached = dirCache.get(dir); + if (cached !== undefined) { + for (const d of visited) { + dirCache.set(d, cached); + } + return cached; + } + + visited.push(dir); + + if (fs.existsSync(resolvePath(dir, 'package.json'))) { + for (const d of visited) { + dirCache.set(d, dir); + } + return dir; + } + + const newDir = dirname(dir); + if (newDir === dir) { + break; + } + dir = newDir; + } + + throw new Error( + `No package.json found while searching for package root of ${searchDir}`, + ); + } + + #dir: string; + #rootDir: string | undefined; + + private constructor(dir: string) { + this.#dir = dir; + } + + get dir(): string { + return this.#dir; + } + + get rootDir(): string { + this.#rootDir ??= findOwnRootDir(this.#dir); + return this.#rootDir; + } + + resolve = (...paths: string[]): string => { + return resolvePath(this.#dir, ...paths); + }; + + resolveRoot = (...paths: string[]): string => { + return resolvePath(this.rootDir, ...paths); + }; } -function getTargetRoot(): string { - // Ensure targetDir is fresh, which also invalidates targetRoot on cwd change - const targetDir = getTargetDir(); - if (cachedTargetRoot !== undefined) { - return cachedTargetRoot; +class TargetPathsImpl implements TargetPaths { + #cwd: string | undefined; + #dir: string | undefined; + #rootDir: string | undefined; + + get dir(): string { + const cwd = process.cwd(); + if (this.#dir !== undefined && this.#cwd === cwd) { + return this.#dir; + } + this.#cwd = cwd; + this.#rootDir = undefined; + // Drive letter can end up being lowercased here on Windows, bring back to uppercase for consistency + this.#dir = fs + .realpathSync(cwd) + .replace(/^[a-z]:/, str => str.toLocaleUpperCase('en-US')); + return this.#dir; } - // We're not always running in a monorepo, so we lazy init this to only crash - // commands that require a monorepo when we're not in one. - cachedTargetRoot = - findRootPath(targetDir, path => { - try { - const content = fs.readFileSync(path, 'utf8'); - const data = JSON.parse(content); - return Boolean(data.workspaces); - } catch (error) { - throw new Error( - `Failed to parse package.json file while searching for root, ${error}`, - ); - } - }) ?? targetDir; - return cachedTargetRoot; + + get rootDir(): string { + // Access dir first to ensure cwd is fresh, which also invalidates rootDir on cwd change + const dir = this.dir; + if (this.#rootDir !== undefined) { + return this.#rootDir; + } + // Lazy init to only crash commands that require a monorepo when we're not in one + this.#rootDir = + findRootPath(dir, path => { + try { + const content = fs.readFileSync(path, 'utf8'); + const data = JSON.parse(content); + return Boolean(data.workspaces); + } catch (error) { + throw new Error( + `Failed to parse package.json file while searching for root, ${error}`, + ); + } + }) ?? dir; + return this.#rootDir; + } + + resolve = (...paths: string[]): string => { + return resolvePath(this.dir, ...paths); + }; + + resolveRoot = (...paths: string[]): string => { + return resolvePath(this.rootDir, ...paths); + }; } /** @@ -198,18 +259,8 @@ function getTargetRoot(): string { * for cwd-based path resolution without needing `__dirname`. * * @public - * @example - * - * import { targetPaths } from '\@backstage/cli-common'; - * - * const lockfile = targetPaths.resolveRoot('yarn.lock'); */ -export const targetPaths: TargetPaths = { - resolve: (...paths) => resolvePath(getTargetDir(), ...paths), - resolveRoot: (...paths) => resolvePath(getTargetRoot(), ...paths), -}; - -const ownPathsCache = new Map(); +export const targetPaths: TargetPaths = new TargetPathsImpl(); /** * Find paths relative to the package that the calling code lives in. @@ -219,35 +270,9 @@ const ownPathsCache = new Map(); * subdirectories within the same package share work. * * @public - * @example - * - * import { findOwnPaths } from '\@backstage/cli-common'; - * - * const own = findOwnPaths(__dirname); - * const config = own.resolve('config/jest.js'); */ export function findOwnPaths(searchDir: string): OwnPaths { - const ownDir = findOwnDir(searchDir); - const cached = ownPathsCache.get(ownDir); - if (cached) { - return cached; - } - - let ownRoot = ''; - const getOwnRoot = () => { - if (!ownRoot) { - ownRoot = findOwnRootDir(ownDir); - } - return ownRoot; - }; - - const paths: OwnPaths = { - resolve: (...p) => resolvePath(ownDir, ...p), - resolveRoot: (...p) => resolvePath(getOwnRoot(), ...p), - }; - - ownPathsCache.set(ownDir, paths); - return paths; + return OwnPathsImpl.find(searchDir); } /** @@ -265,16 +290,16 @@ export function findPaths(searchDir: string): Paths { const own = findOwnPaths(searchDir); return { get ownDir() { - return own.resolve(); + return own.dir; }, get ownRoot() { - return own.resolveRoot(); + return own.rootDir; }, get targetDir() { - return targetPaths.resolve(); + return targetPaths.dir; }, get targetRoot() { - return targetPaths.resolveRoot(); + return targetPaths.rootDir; }, resolveOwn: own.resolve, resolveOwnRoot: own.resolveRoot, diff --git a/packages/cli-node/src/paths.ts b/packages/cli-node/src/paths.ts index 57c98ea0a8..47b17c1825 100644 --- a/packages/cli-node/src/paths.ts +++ b/packages/cli-node/src/paths.ts @@ -21,16 +21,16 @@ const ownPaths = findOwnPaths(__dirname); /* eslint-disable-next-line no-restricted-syntax */ export const paths = { get ownDir() { - return ownPaths.resolve(); + return ownPaths.dir; }, get ownRoot() { - return ownPaths.resolveRoot(); + return ownPaths.rootDir; }, get targetDir() { - return targetPaths.resolve(); + return targetPaths.dir; }, get targetRoot() { - return targetPaths.resolveRoot(); + return targetPaths.rootDir; }, resolveOwn: ownPaths.resolve, resolveOwnRoot: ownPaths.resolveRoot, diff --git a/packages/cli/src/lib/cache/SuccessCache.ts b/packages/cli/src/lib/cache/SuccessCache.ts index 495baf660c..2dc01593b4 100644 --- a/packages/cli/src/lib/cache/SuccessCache.ts +++ b/packages/cli/src/lib/cache/SuccessCache.ts @@ -32,7 +32,7 @@ export class SuccessCache { * location. */ static trimPaths(input: string) { - return input.replaceAll(targetPaths.resolveRoot(), ''); + return input.replaceAll(targetPaths.rootDir, ''); } constructor(name: string, basePath?: string) { diff --git a/packages/cli/src/modules/build/commands/package/build/command.ts b/packages/cli/src/modules/build/commands/package/build/command.ts index 408d7dab52..944be832f0 100644 --- a/packages/cli/src/modules/build/commands/package/build/command.ts +++ b/packages/cli/src/modules/build/commands/package/build/command.ts @@ -47,14 +47,14 @@ export async function command(opts: OptionValues): Promise { if (role === 'frontend') { return buildFrontend({ - targetDir: targetPaths.resolve(), + targetDir: targetPaths.dir, configPaths, writeStats: Boolean(opts.stats), webpack, }); } return buildBackend({ - targetDir: targetPaths.resolve(), + targetDir: targetPaths.dir, configPaths, skipBuildDependencies: Boolean(opts.skipBuildDependencies), minify: Boolean(opts.minify), @@ -77,7 +77,7 @@ export async function command(opts: OptionValues): Promise { if (isModuleFederationRemote) { console.log('Building package as a module federation remote'); return buildFrontend({ - targetDir: targetPaths.resolve(), + targetDir: targetPaths.dir, configPaths: [], writeStats: Boolean(opts.stats), isModuleFederationRemote, diff --git a/packages/cli/src/modules/build/commands/package/start/command.ts b/packages/cli/src/modules/build/commands/package/start/command.ts index 9bc0bfc841..c1abd7ed14 100644 --- a/packages/cli/src/modules/build/commands/package/start/command.ts +++ b/packages/cli/src/modules/build/commands/package/start/command.ts @@ -25,7 +25,7 @@ export async function command(opts: OptionValues): Promise { await startPackage({ role: await findRoleFromCommand(opts), entrypoint: opts.entrypoint, - targetDir: targetPaths.resolve(), + targetDir: targetPaths.dir, configPaths: opts.config as string[], checksEnabled: Boolean(opts.check), linkedWorkspace: await resolveLinkedWorkspace(opts.link), diff --git a/packages/cli/src/modules/build/commands/package/start/startBackend.ts b/packages/cli/src/modules/build/commands/package/start/startBackend.ts index 3ea1e9bbe9..a36a93b8ff 100644 --- a/packages/cli/src/modules/build/commands/package/start/startBackend.ts +++ b/packages/cli/src/modules/build/commands/package/start/startBackend.ts @@ -44,7 +44,7 @@ export async function startBackend(options: StartBackendOptions) { export async function startBackendPlugin(options: StartBackendOptions) { const hasDevIndexEntry = await fs.pathExists( - resolvePath(options.targetDir ?? targetPaths.resolve(), 'dev/index.ts'), + resolvePath(options.targetDir ?? targetPaths.dir, 'dev/index.ts'), ); if (!hasDevIndexEntry) { console.warn( diff --git a/packages/cli/src/modules/build/commands/package/start/startFrontend.ts b/packages/cli/src/modules/build/commands/package/start/startFrontend.ts index 413ce6b752..c81fd2fd66 100644 --- a/packages/cli/src/modules/build/commands/package/start/startFrontend.ts +++ b/packages/cli/src/modules/build/commands/package/start/startFrontend.ts @@ -39,7 +39,7 @@ interface StartAppOptions { export async function startFrontend(options: StartAppOptions) { const packageJson = (await readJson( - resolvePath(options.targetDir ?? targetPaths.resolve(), 'package.json'), + resolvePath(options.targetDir ?? targetPaths.dir, 'package.json'), )) as BackstagePackageJson; if (!hasReactDomClient()) { @@ -59,7 +59,7 @@ export async function startFrontend(options: StartAppOptions) { moduleFederationRemote: options.isModuleFederationRemote ? await getModuleFederationRemoteOptions( packageJson, - resolvePath(targetPaths.resolve()), + resolvePath(targetPaths.dir), ) : undefined, }); diff --git a/packages/cli/src/modules/build/commands/repo/build.ts b/packages/cli/src/modules/build/commands/repo/build.ts index 8b1391ecf9..dd65e926b2 100644 --- a/packages/cli/src/modules/build/commands/repo/build.ts +++ b/packages/cli/src/modules/build/commands/repo/build.ts @@ -90,7 +90,7 @@ export async function command(opts: OptionValues, cmd: Command): Promise { targetDir: pkg.dir, packageJson: pkg.packageJson, outputs, - logPrefix: `${chalk.cyan(relativePath(targetPaths.resolveRoot(), pkg.dir))}: `, + logPrefix: `${chalk.cyan(relativePath(targetPaths.rootDir, pkg.dir))}: `, workspacePackages: packages, minify: opts.minify ?? buildOptions.minify, }; diff --git a/packages/cli/src/modules/build/lib/builder/config.ts b/packages/cli/src/modules/build/lib/builder/config.ts index 08b8221a77..4123d2ccf3 100644 --- a/packages/cli/src/modules/build/lib/builder/config.ts +++ b/packages/cli/src/modules/build/lib/builder/config.ts @@ -117,7 +117,7 @@ export async function makeRollupConfigs( options: BuildOptions, ): Promise { const configs = new Array(); - const targetDir = options.targetDir ?? targetPaths.resolve(); + const targetDir = options.targetDir ?? targetPaths.dir; let targetPkg = options.packageJson; if (!targetPkg) { @@ -287,7 +287,7 @@ export async function makeRollupConfigs( e.name, targetPaths.resolveRoot( 'dist-types', - relativePath(targetPaths.resolveRoot(), targetDir), + relativePath(targetPaths.rootDir, targetDir), e.path.replace(/\.(?:ts|tsx)$/, '.d.ts'), ), ]), diff --git a/packages/cli/src/modules/build/lib/builder/packager.ts b/packages/cli/src/modules/build/lib/builder/packager.ts index 527fbbba0e..cee6d0a11c 100644 --- a/packages/cli/src/modules/build/lib/builder/packager.ts +++ b/packages/cli/src/modules/build/lib/builder/packager.ts @@ -34,7 +34,7 @@ export function formatErrorMessage(error: any) { msg += `\n\n`; for (const { text, location } of error.errors) { const { line, column } = location; - const path = relativePath(targetPaths.resolve(), error.id); + const path = relativePath(targetPaths.dir, error.id); const loc = chalk.cyan(`${path}:${line}:${column}`); if (text === 'Unexpected "<"' && error.id.endsWith('.js')) { @@ -107,7 +107,7 @@ export const buildPackage = async (options: BuildOptions) => { const rollupConfigs = await makeRollupConfigs(options); - const targetDir = options.targetDir ?? targetPaths.resolve(); + const targetDir = options.targetDir ?? targetPaths.dir; await fs.remove(resolvePath(targetDir, 'dist')); const buildTasks = rollupConfigs.map(rollupBuild); diff --git a/packages/cli/src/modules/build/lib/bundler/hasReactDomClient.ts b/packages/cli/src/modules/build/lib/bundler/hasReactDomClient.ts index 6acd96f8c1..0a60840047 100644 --- a/packages/cli/src/modules/build/lib/bundler/hasReactDomClient.ts +++ b/packages/cli/src/modules/build/lib/bundler/hasReactDomClient.ts @@ -20,7 +20,7 @@ import { targetPaths } from '@backstage/cli-common'; export function hasReactDomClient() { try { require.resolve('react-dom/client', { - paths: [targetPaths.resolve()], + paths: [targetPaths.dir], }); return true; } catch { diff --git a/packages/cli/src/modules/build/lib/bundler/linkWorkspaces.ts b/packages/cli/src/modules/build/lib/bundler/linkWorkspaces.ts index 935e14e85a..10a2e7d209 100644 --- a/packages/cli/src/modules/build/lib/bundler/linkWorkspaces.ts +++ b/packages/cli/src/modules/build/lib/bundler/linkWorkspaces.ts @@ -53,7 +53,7 @@ export async function createWorkspaceLinkingPlugins( /^react(?:-router)?(?:-dom)?$/, resource => { if (!relativePath(linkedRoot.dir, resource.context).startsWith('..')) { - resource.context = targetPaths.resolve(); + resource.context = targetPaths.dir; } }, ), diff --git a/packages/cli/src/modules/build/lib/bundler/packageDetection.ts b/packages/cli/src/modules/build/lib/bundler/packageDetection.ts index 3f361819f3..cb2c3f588f 100644 --- a/packages/cli/src/modules/build/lib/bundler/packageDetection.ts +++ b/packages/cli/src/modules/build/lib/bundler/packageDetection.ts @@ -147,7 +147,7 @@ export async function createDetectedModulesEntryPoint(options: { // Previous versions of the CLI would write the detected modules file to the // root `node_modules`, this makes sure that doesn't exist to minimize risk of conflicts const legacyDetectedModulesPath = joinPath( - targetPaths.resolveRoot(), + targetPaths.rootDir, 'node_modules', `${DETECTED_MODULES_MODULE_NAME}.js`, ); diff --git a/packages/cli/src/modules/build/lib/bundler/paths.ts b/packages/cli/src/modules/build/lib/bundler/paths.ts index d0d56e6396..4e79f77884 100644 --- a/packages/cli/src/modules/build/lib/bundler/paths.ts +++ b/packages/cli/src/modules/build/lib/bundler/paths.ts @@ -21,14 +21,14 @@ import { targetPaths, findOwnPaths } from '@backstage/cli-common'; export type BundlingPathsOptions = { // bundle entrypoint, e.g. 'src/index' entry: string; - // Target directory, defaulting to targetPaths.resolve() + // Target directory, defaulting to targetPaths.dir targetDir?: string; // Relative dist directory, defaulting to 'dist' dist?: string; }; export function resolveBundlingPaths(options: BundlingPathsOptions) { - const { entry, targetDir = targetPaths.resolve() } = options; + const { entry, targetDir = targetPaths.dir } = options; const resolveTargetModule = (pathString: string) => { for (const ext of ['mjs', 'js', 'ts', 'tsx', 'jsx']) { @@ -70,7 +70,7 @@ export function resolveBundlingPaths(options: BundlingPathsOptions) { targetTsConfig: targetPaths.resolveRoot('tsconfig.json'), targetPackageJson: resolvePath(targetDir, 'package.json'), rootNodeModules: targetPaths.resolveRoot('node_modules'), - root: targetPaths.resolveRoot(), + root: targetPaths.rootDir, }; } diff --git a/packages/cli/src/modules/build/lib/bundler/server.ts b/packages/cli/src/modules/build/lib/bundler/server.ts index 27e0cbd034..57d6bed052 100644 --- a/packages/cli/src/modules/build/lib/bundler/server.ts +++ b/packages/cli/src/modules/build/lib/bundler/server.ts @@ -54,7 +54,7 @@ DEPRECATION WARNING: React Router Beta is deprecated and support for it will be checkReactVersion(); const { name } = await fs.readJson( - resolvePath(options.targetDir ?? targetPaths.resolve(), 'package.json'), + resolvePath(options.targetDir ?? targetPaths.dir, 'package.json'), ); let devServer: RspackDevServer | undefined = undefined; @@ -272,7 +272,7 @@ function checkReactVersion() { try { // Make sure we're looking at the root of the target repo const reactPkgPath = require.resolve('react/package.json', { - paths: [targetPaths.resolveRoot()], + paths: [targetPaths.rootDir], }); const reactPkg = require(reactPkgPath); if (reactPkg.version.startsWith('16.')) { diff --git a/packages/cli/src/modules/build/lib/packager/createDistWorkspace.ts b/packages/cli/src/modules/build/lib/packager/createDistWorkspace.ts index 6d55fdac0f..e1adcd6c20 100644 --- a/packages/cli/src/modules/build/lib/packager/createDistWorkspace.ts +++ b/packages/cli/src/modules/build/lib/packager/createDistWorkspace.ts @@ -211,7 +211,7 @@ export async function createDistWorkspace( targetDir: pkg.dir, packageJson: pkg.packageJson, outputs: outputs, - logPrefix: `${chalk.cyan(relativePath(targetPaths.resolveRoot(), pkg.dir))}: `, + logPrefix: `${chalk.cyan(relativePath(targetPaths.rootDir, pkg.dir))}: `, minify: options.minify, workspacePackages: packages, }); @@ -252,7 +252,7 @@ export async function createDistWorkspace( if (options.skeleton) { const skeletonFiles = targets .map(target => { - const dir = relativePath(targetPaths.resolveRoot(), target.dir); + const dir = relativePath(targetPaths.rootDir, target.dir); return joinPath(dir, 'package.json'); }) .sort(); @@ -301,7 +301,7 @@ async function moveToDistWorkspace( fastPackPackages.map(async target => { console.log(`Moving ${target.name} into dist workspace`); - const outputDir = relativePath(targetPaths.resolveRoot(), target.dir); + const outputDir = relativePath(targetPaths.rootDir, target.dir); const absoluteOutputPath = resolvePath(workspaceDir, outputDir); await productionPack({ packageDir: target.dir, @@ -321,7 +321,7 @@ async function moveToDistWorkspace( cwd: target.dir, }).waitForExit(); - const outputDir = relativePath(targetPaths.resolveRoot(), target.dir); + const outputDir = relativePath(targetPaths.rootDir, target.dir); const absoluteOutputPath = resolvePath(workspaceDir, outputDir); await fs.ensureDir(absoluteOutputPath); diff --git a/packages/cli/src/modules/build/lib/role.test.ts b/packages/cli/src/modules/build/lib/role.test.ts index 1e26f78f33..76b202ae98 100644 --- a/packages/cli/src/modules/build/lib/role.test.ts +++ b/packages/cli/src/modules/build/lib/role.test.ts @@ -23,6 +23,12 @@ const mockDir = createMockDirectory(); jest.mock('@backstage/cli-common', () => ({ ...jest.requireActual('@backstage/cli-common'), targetPaths: { + get dir() { + return mockDir.path; + }, + get rootDir() { + return mockDir.path; + }, resolve: (...args: string[]) => mockDir.resolve(...args), resolveRoot: (...args: string[]) => mockDir.resolve(...args), }, diff --git a/packages/cli/src/modules/config/lib/config.ts b/packages/cli/src/modules/config/lib/config.ts index b819d1da7b..1d920eb31e 100644 --- a/packages/cli/src/modules/config/lib/config.ts +++ b/packages/cli/src/modules/config/lib/config.ts @@ -35,7 +35,7 @@ type Options = { }; export async function loadCliConfig(options: Options) { - const targetDir = options.targetDir ?? targetPaths.resolve(); + const targetDir = options.targetDir ?? targetPaths.dir; // Consider all packages in the monorepo when loading in config const { packages } = await getPackages(targetDir); @@ -74,7 +74,7 @@ export async function loadCliConfig(options: Options) { ? async name => process.env[name] || 'x' : undefined, watch: Boolean(options.watch), - rootDir: targetPaths.resolveRoot(), + rootDir: targetPaths.rootDir, argv: options.args.flatMap(t => ['--config', resolvePath(targetDir, t)]), }); diff --git a/packages/cli/src/modules/info/commands/info.ts b/packages/cli/src/modules/info/commands/info.ts index b0ce28fb7d..6fcf78cc84 100644 --- a/packages/cli/src/modules/info/commands/info.ts +++ b/packages/cli/src/modules/info/commands/info.ts @@ -84,7 +84,7 @@ export default async (options: InfoOptions) => { const lockfilePath = targetPaths.resolveRoot('yarn.lock'); const lockfile = await Lockfile.load(lockfilePath); - const targetPath = targetPaths.resolveRoot(); + const targetPath = targetPaths.rootDir; // Get workspace package names and their versions const workspacePackages = new Map(); diff --git a/packages/cli/src/modules/lint/commands/package/lint.ts b/packages/cli/src/modules/lint/commands/package/lint.ts index f75937f6d1..0a453ac71a 100644 --- a/packages/cli/src/modules/lint/commands/package/lint.ts +++ b/packages/cli/src/modules/lint/commands/package/lint.ts @@ -22,7 +22,7 @@ import { ESLint } from 'eslint'; export default async (directories: string[], opts: OptionValues) => { const eslint = new ESLint({ - cwd: targetPaths.resolve(), + cwd: targetPaths.dir, fix: opts.fix, extensions: ['js', 'jsx', 'ts', 'tsx', 'mjs', 'cjs'], }); @@ -48,7 +48,7 @@ export default async (directories: string[], opts: OptionValues) => { // This formatter uses the cwd to format file paths, so let's have that happen from the root instead if (opts.format === 'eslint-formatter-friendly') { - process.chdir(targetPaths.resolveRoot()); + process.chdir(targetPaths.rootDir); } const resultText = await formatter.format(results); diff --git a/packages/cli/src/modules/lint/commands/repo/lint.ts b/packages/cli/src/modules/lint/commands/repo/lint.ts index 3bdb8aae97..dafcff45c6 100644 --- a/packages/cli/src/modules/lint/commands/repo/lint.ts +++ b/packages/cli/src/modules/lint/commands/repo/lint.ts @@ -63,7 +63,7 @@ export async function command(opts: OptionValues, cmd: Command): Promise { // This formatter uses the cwd to format file paths, so let's have that happen from the root instead if (opts.format === 'eslint-formatter-friendly') { - process.chdir(targetPaths.resolveRoot()); + process.chdir(targetPaths.rootDir); } // Make sure lint output is colored unless the user explicitly disabled it @@ -78,7 +78,7 @@ export async function command(opts: OptionValues, cmd: Command): Promise { const lintOptions = parseLintScript(pkg.packageJson.scripts?.lint); const base = { fullDir: pkg.dir, - relativeDir: relativePath(targetPaths.resolveRoot(), pkg.dir), + relativeDir: relativePath(targetPaths.rootDir, pkg.dir), lintOptions, parentHash: undefined, }; @@ -114,7 +114,7 @@ export async function command(opts: OptionValues, cmd: Command): Promise { shouldCache: Boolean(cacheContext), maxWarnings: opts.maxWarnings ?? -1, successCache: cacheContext?.entries, - rootDir: targetPaths.resolveRoot(), + rootDir: targetPaths.rootDir, }, workerFactory: async ({ fix, diff --git a/packages/cli/src/modules/maintenance/commands/package/pack.ts b/packages/cli/src/modules/maintenance/commands/package/pack.ts index fd4c9dc162..e5e176d17e 100644 --- a/packages/cli/src/modules/maintenance/commands/package/pack.ts +++ b/packages/cli/src/modules/maintenance/commands/package/pack.ts @@ -26,16 +26,16 @@ import { createTypeDistProject } from '../../../../lib/typeDistProject'; export const pre = async () => { publishPreflightCheck({ - dir: targetPaths.resolve(), + dir: targetPaths.dir, packageJson: await fs.readJson(targetPaths.resolve('package.json')), }); await productionPack({ - packageDir: targetPaths.resolve(), + packageDir: targetPaths.dir, featureDetectionProject: await createTypeDistProject(), }); }; export const post = async () => { - await revertProductionPack(targetPaths.resolve()); + await revertProductionPack(targetPaths.dir); }; diff --git a/packages/cli/src/modules/maintenance/commands/repo/fix.ts b/packages/cli/src/modules/maintenance/commands/repo/fix.ts index 973436da97..073cb69ea1 100644 --- a/packages/cli/src/modules/maintenance/commands/repo/fix.ts +++ b/packages/cli/src/modules/maintenance/commands/repo/fix.ts @@ -230,7 +230,7 @@ export function createRepositoryFieldFixer() { return (pkg: FixablePackage) => { const expectedPath = posix.join( rootDir, - relativePath(targetPaths.resolveRoot(), pkg.dir), + relativePath(targetPaths.rootDir, pkg.dir), ); const repoField = pkg.packageJson.repository; @@ -319,7 +319,7 @@ export function fixPluginId(pkg: FixablePackage) { role === 'backend-plugin-module') ) { const path = relativePath( - targetPaths.resolveRoot(), + targetPaths.rootDir, resolvePath(pkg.dir, 'package.json'), ); const msg = `Failed to guess plugin ID for "${pkg.packageJson.name}", please set the 'backstage.pluginId' field manually in "${path}"`; @@ -415,7 +415,7 @@ export function fixPluginPackages( return; } const path = relativePath( - targetPaths.resolveRoot(), + targetPaths.rootDir, resolvePath(pkg.dir, 'package.json'), ); const suggestedRole = @@ -464,7 +464,7 @@ export function fixPeerModules(pkg: FixablePackage) { } const packagePath = relativePath( - targetPaths.resolveRoot(), + targetPaths.rootDir, resolvePath(pkg.dir, 'package.json'), ); diff --git a/packages/cli/src/modules/maintenance/commands/repo/list-deprecations.ts b/packages/cli/src/modules/maintenance/commands/repo/list-deprecations.ts index 5f80330247..f473e55ce6 100644 --- a/packages/cli/src/modules/maintenance/commands/repo/list-deprecations.ts +++ b/packages/cli/src/modules/maintenance/commands/repo/list-deprecations.ts @@ -26,7 +26,7 @@ export async function command(opts: OptionValues) { const packages = await PackageGraph.listTargetPackages(); const eslint = new ESLint({ - cwd: targetPaths.resolve(), + cwd: targetPaths.dir, overrideConfig: { plugins: ['deprecation'], rules: { @@ -53,7 +53,7 @@ export async function command(opts: OptionValues) { continue; } - const path = relativePath(targetPaths.resolveRoot(), result.filePath); + const path = relativePath(targetPaths.rootDir, result.filePath); deprecations.push({ path, message: message.message, diff --git a/packages/cli/src/modules/migrate/commands/packageRole.ts b/packages/cli/src/modules/migrate/commands/packageRole.ts index 878d69a312..32e20f10eb 100644 --- a/packages/cli/src/modules/migrate/commands/packageRole.ts +++ b/packages/cli/src/modules/migrate/commands/packageRole.ts @@ -22,7 +22,7 @@ import { targetPaths } from '@backstage/cli-common'; export default async () => { - const { packages } = await getPackages(targetPaths.resolve()); + const { packages } = await getPackages(targetPaths.dir); await Promise.all( packages.map(async ({ dir, packageJson: pkg }) => { diff --git a/packages/cli/src/modules/migrate/commands/versions/bump.test.ts b/packages/cli/src/modules/migrate/commands/versions/bump.test.ts index 8f7ac2577b..eafe141e57 100644 --- a/packages/cli/src/modules/migrate/commands/versions/bump.test.ts +++ b/packages/cli/src/modules/migrate/commands/versions/bump.test.ts @@ -65,6 +65,12 @@ jest.mock('@backstage/cli-common', () => { return { ...actual, targetPaths: { + get dir() { + return mockDir.path; + }, + get rootDir() { + return mockDir.path; + }, resolve: (...args: string[]) => mockDir.resolve(...args), resolveRoot: (...args: string[]) => mockDir.resolve(...args), }, diff --git a/packages/cli/src/modules/migrate/commands/versions/bump.ts b/packages/cli/src/modules/migrate/commands/versions/bump.ts index c508babb05..ff8852de71 100644 --- a/packages/cli/src/modules/migrate/commands/versions/bump.ts +++ b/packages/cli/src/modules/migrate/commands/versions/bump.ts @@ -141,7 +141,7 @@ export default async (opts: OptionValues) => { } // First we discover all Backstage dependencies within our own repo - const dependencyMap = await mapDependencies(targetPaths.resolve(), pattern); + const dependencyMap = await mapDependencies(targetPaths.dir, pattern); // Next check with the package registry to see which dependency ranges we need to bump const versionBumps = new Map(); diff --git a/packages/cli/src/modules/migrate/commands/versions/migrate.test.ts b/packages/cli/src/modules/migrate/commands/versions/migrate.test.ts index 75361b0a07..74ba9ae975 100644 --- a/packages/cli/src/modules/migrate/commands/versions/migrate.test.ts +++ b/packages/cli/src/modules/migrate/commands/versions/migrate.test.ts @@ -38,6 +38,12 @@ jest.mock('@backstage/cli-common', () => { return { ...actual, targetPaths: { + get dir() { + return mockDir.path; + }, + get rootDir() { + return mockDir.path; + }, resolve: (...args: string[]) => mockDir.resolve(...args), resolveRoot: (...args: string[]) => mockDir.resolve(...args), }, diff --git a/packages/cli/src/modules/new/lib/codeowners/codeowners.ts b/packages/cli/src/modules/new/lib/codeowners/codeowners.ts index 7842acaaa9..d8e3d0c02f 100644 --- a/packages/cli/src/modules/new/lib/codeowners/codeowners.ts +++ b/packages/cli/src/modules/new/lib/codeowners/codeowners.ts @@ -83,7 +83,7 @@ export async function addCodeownersEntry( let filePath = codeownersFilePath; if (!filePath) { - filePath = await getCodeownersFilePath(targetPaths.resolveRoot()); + filePath = await getCodeownersFilePath(targetPaths.rootDir); if (!filePath) { return false; } diff --git a/packages/cli/src/modules/new/lib/preparation/collectPortableTemplateInput.ts b/packages/cli/src/modules/new/lib/preparation/collectPortableTemplateInput.ts index 5bf9de64ca..7279801d62 100644 --- a/packages/cli/src/modules/new/lib/preparation/collectPortableTemplateInput.ts +++ b/packages/cli/src/modules/new/lib/preparation/collectPortableTemplateInput.ts @@ -39,7 +39,7 @@ export async function collectPortableTemplateInput( ): Promise { const { config, template, prefilledParams } = options; - const codeOwnersFilePath = await getCodeownersFilePath(targetPaths.resolveRoot()); + const codeOwnersFilePath = await getCodeownersFilePath(targetPaths.rootDir); const prompts = getPromptsForRole(template.role); diff --git a/packages/cli/src/modules/test/commands/repo/test.ts b/packages/cli/src/modules/test/commands/repo/test.ts index 6e8e22c140..46af4e6e4d 100644 --- a/packages/cli/src/modules/test/commands/repo/test.ts +++ b/packages/cli/src/modules/test/commands/repo/test.ts @@ -68,7 +68,7 @@ interface TestGlobal extends Global { async function readPackageTreeHashes(graph: PackageGraph) { const pkgs = Array.from(graph.values()).map(pkg => ({ ...pkg, - path: relativePath(targetPaths.resolveRoot(), pkg.dir), + path: relativePath(targetPaths.rootDir, pkg.dir), })); const output = await runOutput([ 'git', diff --git a/packages/config-loader/src/sources/ConfigSources.ts b/packages/config-loader/src/sources/ConfigSources.ts index 45659b4cac..153da69dbd 100644 --- a/packages/config-loader/src/sources/ConfigSources.ts +++ b/packages/config-loader/src/sources/ConfigSources.ts @@ -157,7 +157,7 @@ export class ConfigSources { static defaultForTargets( options: ConfigSourcesDefaultForTargetsOptions, ): ConfigSource { - const rootDir = options.rootDir ?? targetPaths.resolveRoot(); + const rootDir = options.rootDir ?? targetPaths.rootDir; const argSources = options.targets.map(arg => { if (arg.type === 'url') { diff --git a/packages/create-app/src/createApp.test.ts b/packages/create-app/src/createApp.test.ts index 4fb5438d70..56dded39ee 100644 --- a/packages/create-app/src/createApp.test.ts +++ b/packages/create-app/src/createApp.test.ts @@ -41,6 +41,12 @@ jest.mock('@backstage/cli-common', () => { findPaths: jest.fn(), findOwnPaths: () => mockOwnPaths, targetPaths: { + get dir() { + return MOCK_TARGET_DIR; + }, + get rootDir() { + return '/mock/target-root'; + }, resolve: (...paths: string[]) => pathModule.resolve(MOCK_TARGET_DIR, ...paths), resolveRoot: (...paths: string[]) => diff --git a/packages/create-app/src/createApp.ts b/packages/create-app/src/createApp.ts index 3f63cd0a60..9b09f59904 100644 --- a/packages/create-app/src/createApp.ts +++ b/packages/create-app/src/createApp.ts @@ -76,8 +76,8 @@ export default async (opts: OptionValues): Promise => { // Use `--path` argument as application directory when specified, otherwise // create a directory using `answers.name` const appDir = opts.path - ? resolvePath(targetPaths.resolve(), opts.path) - : resolvePath(targetPaths.resolve(), answers.name); + ? resolvePath(targetPaths.dir, opts.path) + : resolvePath(targetPaths.dir, answers.name); Task.log(); Task.log('Creating the app...'); @@ -100,7 +100,7 @@ export default async (opts: OptionValues): Promise => { // Template to temporary location, and then move files Task.section('Checking if the directory is available'); - await checkAppExistsTask(targetPaths.resolve(), answers.name); + await checkAppExistsTask(targetPaths.dir, answers.name); Task.section('Creating a temporary app directory'); const tempDir = await fs.mkdtemp(resolvePath(os.tmpdir(), answers.name)); diff --git a/packages/repo-tools/src/commands/api-reports/api-reports/createTemporaryTsConfig.ts b/packages/repo-tools/src/commands/api-reports/api-reports/createTemporaryTsConfig.ts index 028466dbab..5cf62f8bac 100644 --- a/packages/repo-tools/src/commands/api-reports/api-reports/createTemporaryTsConfig.ts +++ b/packages/repo-tools/src/commands/api-reports/api-reports/createTemporaryTsConfig.ts @@ -16,10 +16,10 @@ import fs from 'fs-extra'; import { join } from 'node:path'; -import { paths as cliPaths } from '../../../lib/paths'; +import { targetPaths } from '@backstage/cli-common'; export async function createTemporaryTsConfig(includedPackageDirs: string[]) { - const path = cliPaths.resolveTargetRoot('tsconfig.tmp.json'); + const path = targetPaths.resolveRoot('tsconfig.tmp.json'); process.once('exit', () => { fs.removeSync(path); diff --git a/packages/repo-tools/src/commands/api-reports/api-reports/generateTypeDeclarations.ts b/packages/repo-tools/src/commands/api-reports/api-reports/generateTypeDeclarations.ts index a51c9ed17b..1165c24a77 100644 --- a/packages/repo-tools/src/commands/api-reports/api-reports/generateTypeDeclarations.ts +++ b/packages/repo-tools/src/commands/api-reports/api-reports/generateTypeDeclarations.ts @@ -16,7 +16,7 @@ import fs from 'fs-extra'; import { run, ExitCodeError } from '@backstage/cli-common'; -import { paths as cliPaths } from '../../../lib/paths'; +import { targetPaths } from '@backstage/cli-common'; /** * Generates the TypeScript declaration files for the specified project, using the provided `tsconfig.json` file. @@ -29,7 +29,7 @@ import { paths as cliPaths } from '../../../lib/paths'; * @returns {Promise} A promise that resolves when the declaration files have been generated. */ export async function generateTypeDeclarations(tsconfigFilePath: string) { - await fs.remove(cliPaths.resolveTargetRoot('dist-types')); + await fs.remove(targetPaths.resolveRoot('dist-types')); try { await run( [ @@ -43,7 +43,7 @@ export async function generateTypeDeclarations(tsconfigFilePath: string) { 'false', ], { - cwd: cliPaths.targetRoot, + cwd: targetPaths.rootDir, }, ).waitForExit(); } catch (error) { diff --git a/packages/repo-tools/src/commands/api-reports/api-reports/patchApiReportGeneration.ts b/packages/repo-tools/src/commands/api-reports/api-reports/patchApiReportGeneration.ts index 40778594ec..3ac3eef99b 100644 --- a/packages/repo-tools/src/commands/api-reports/api-reports/patchApiReportGeneration.ts +++ b/packages/repo-tools/src/commands/api-reports/api-reports/patchApiReportGeneration.ts @@ -18,7 +18,7 @@ import { ExtractorMessage } from '@microsoft/api-extractor'; import { AstDeclaration } from '@microsoft/api-extractor/lib/analyzer/AstDeclaration'; import { Program } from 'typescript'; import { tryRunPrettier } from '../common'; -import { paths as cliPaths } from '../../../lib/paths'; +import { targetPaths } from '@backstage/cli-common'; let applied = false; @@ -162,7 +162,7 @@ export function patchApiReportGeneration() { parser: 'markdown', // We need a real-looking filepath for proper config resolution, not just a directory // Ideally, the real filepath would be better, but it would require too much patching, for very little gain. - filepath: `${cliPaths.targetRoot}/report.api.md`, + filepath: `${targetPaths.rootDir}/report.api.md`, }); }; } diff --git a/packages/repo-tools/src/commands/api-reports/api-reports/runApiExtraction.ts b/packages/repo-tools/src/commands/api-reports/api-reports/runApiExtraction.ts index da8278f9ad..89b87b9a0b 100644 --- a/packages/repo-tools/src/commands/api-reports/api-reports/runApiExtraction.ts +++ b/packages/repo-tools/src/commands/api-reports/api-reports/runApiExtraction.ts @@ -31,11 +31,11 @@ import { resolve as resolvePath, } from 'node:path'; import { getPackageExportDetails } from '../../../lib/getPackageExportDetails'; -import { paths as cliPaths } from '../../../lib/paths'; +import { targetPaths } from '@backstage/cli-common'; import { logApiReportInstructions } from '../common'; import { patchApiReportGeneration } from './patchApiReportGeneration'; -const tmpDir = cliPaths.resolveTargetRoot( +const tmpDir = targetPaths.resolveRoot( './node_modules/.cache/api-extractor', ); @@ -100,7 +100,7 @@ async function findPackageEntryPoints(packageDirs: string[]): Promise< return Promise.all( packageDirs.map(async packageDir => { const pkg = await fs.readJson( - cliPaths.resolveTargetRoot(packageDir, 'package.json'), + targetPaths.resolveRoot(packageDir, 'package.json'), ); return getPackageExportDetails(pkg).map(details => { @@ -143,7 +143,7 @@ export async function runApiExtraction({ // inspected. const allDistTypesEntryPointPaths = allEntryPoints.map( ({ packageDir, distTypesPath }) => { - return cliPaths.resolveTargetRoot( + return targetPaths.resolveRoot( './dist-types', packageDir, distTypesPath, @@ -172,8 +172,8 @@ export async function runApiExtraction({ ? allowWarnings.some(aw => aw === packageDir || minimatch(packageDir, aw)) : allowWarnings; - const projectFolder = cliPaths.resolveTargetRoot(packageDir); - const packageFolder = cliPaths.resolveTargetRoot( + const projectFolder = targetPaths.resolveRoot(packageDir); + const packageFolder = targetPaths.resolveRoot( './dist-types', packageDir, ); diff --git a/packages/repo-tools/src/commands/api-reports/buildApiReports.test.ts b/packages/repo-tools/src/commands/api-reports/buildApiReports.test.ts index ab76aec1c1..000ef11cac 100644 --- a/packages/repo-tools/src/commands/api-reports/buildApiReports.test.ts +++ b/packages/repo-tools/src/commands/api-reports/buildApiReports.test.ts @@ -16,7 +16,7 @@ import { createMockDirectory } from '@backstage/backend-test-utils'; import { normalize } from 'node:path'; -import * as pathsLib from '../../lib/paths'; +import { targetPaths } from '@backstage/cli-common'; import { categorizePackageDirs } from './categorizePackageDirs'; @@ -51,14 +51,12 @@ jest.mock('./categorizePackageDirs', () => ({ }), })); -const projectPaths = pathsLib.paths; - const mockDir = createMockDirectory(); -jest.spyOn(projectPaths, 'targetRoot', 'get').mockReturnValue(mockDir.path); +jest.spyOn(targetPaths, 'rootDir', 'get').mockReturnValue(mockDir.path); jest - .spyOn(projectPaths, 'resolveTargetRoot') - .mockImplementation((...path) => mockDir.resolve(...path)); + .spyOn(targetPaths, 'resolveRoot') + .mockImplementation((...path: string[]) => mockDir.resolve(...path)); jest.spyOn(PackageGraph, 'listTargetPackages').mockResolvedValue([ { dir: normalize(mockDir.resolve('packages/package-a')), @@ -85,7 +83,7 @@ jest.spyOn(PackageGraph, 'listTargetPackages').mockResolvedValue([ describe('buildApiReports', () => { beforeEach(() => { mockDir.setContent({ - [projectPaths.targetRoot]: { + [targetPaths.rootDir]: { 'package.json': JSON.stringify({ workspaces: { packages: ['packages/*', 'plugins/*'] }, }), diff --git a/packages/repo-tools/src/commands/api-reports/buildApiReports.ts b/packages/repo-tools/src/commands/api-reports/buildApiReports.ts index 74b110236f..87b3f62a6c 100644 --- a/packages/repo-tools/src/commands/api-reports/buildApiReports.ts +++ b/packages/repo-tools/src/commands/api-reports/buildApiReports.ts @@ -16,7 +16,8 @@ import { OptionValues } from 'commander'; import { categorizePackageDirs } from './categorizePackageDirs'; -import { paths as cliPaths, resolvePackagePaths } from '../../lib/paths'; +import { targetPaths } from '@backstage/cli-common'; +import { resolvePackagePaths } from '../../lib/paths'; import { runSqlExtraction } from './sql-reports'; import { runCliExtraction } from './cli-reports'; import { @@ -37,7 +38,7 @@ type Options = { } & OptionValues; export async function buildApiReports(paths: string[] = [], opts: Options) { - const tmpDir = cliPaths.resolveTargetRoot( + const tmpDir = targetPaths.resolveRoot( './node_modules/.cache/api-extractor', ); @@ -73,7 +74,7 @@ export async function buildApiReports(paths: string[] = [], opts: Options) { temporaryTsConfigPath = await createTemporaryTsConfig(selectedPackageDirs); } const tsconfigFilePath = - temporaryTsConfigPath ?? cliPaths.resolveTargetRoot('tsconfig.json'); + temporaryTsConfigPath ?? targetPaths.resolveRoot('tsconfig.json'); if (runTsc) { console.log('# Compiling TypeScript'); @@ -116,7 +117,7 @@ export async function buildApiReports(paths: string[] = [], opts: Options) { console.log('# Generating package documentation'); await buildDocs({ inputDir: tmpDir, - outputDir: cliPaths.resolveTargetRoot('docs/reference'), + outputDir: targetPaths.resolveRoot('docs/reference'), }); } } diff --git a/packages/repo-tools/src/commands/api-reports/categorizePackageDirs.ts b/packages/repo-tools/src/commands/api-reports/categorizePackageDirs.ts index 86694c3d68..7dfaa64524 100644 --- a/packages/repo-tools/src/commands/api-reports/categorizePackageDirs.ts +++ b/packages/repo-tools/src/commands/api-reports/categorizePackageDirs.ts @@ -15,7 +15,7 @@ */ import fs from 'fs-extra'; -import { paths as cliPaths } from '../../lib/paths'; +import { targetPaths } from '@backstage/cli-common'; export async function categorizePackageDirs(packageDirs: string[]) { const dirs = packageDirs.slice(); @@ -34,7 +34,7 @@ export async function categorizePackageDirs(packageDirs: string[]) { } const pkgJson = await fs - .readJson(cliPaths.resolveTargetRoot(dir, 'package.json')) + .readJson(targetPaths.resolveRoot(dir, 'package.json')) .catch(error => { if (error.code === 'ENOENT') { return undefined; @@ -46,7 +46,7 @@ export async function categorizePackageDirs(packageDirs: string[]) { return; // Ignore packages without roles } if ( - await fs.pathExists(cliPaths.resolveTargetRoot(dir, 'migrations')) + await fs.pathExists(targetPaths.resolveRoot(dir, 'migrations')) ) { sqlPackageDirs.push(dir); } diff --git a/packages/repo-tools/src/commands/api-reports/cli-reports/runCliExtraction.ts b/packages/repo-tools/src/commands/api-reports/cli-reports/runCliExtraction.ts index 8aa0eea02c..146c83bcfb 100644 --- a/packages/repo-tools/src/commands/api-reports/cli-reports/runCliExtraction.ts +++ b/packages/repo-tools/src/commands/api-reports/cli-reports/runCliExtraction.ts @@ -22,7 +22,7 @@ import { import fs from 'fs-extra'; import { createBinRunner } from '../../util'; import { CliHelpPage, CliModel } from './types'; -import { paths as cliPaths } from '../../../lib/paths'; +import { targetPaths } from '@backstage/cli-common'; import { generateCliReport } from './generateCliReport'; import { logApiReportInstructions } from '../common'; @@ -115,7 +115,7 @@ export async function runCliExtraction({ }: CliExtractionOptions) { for (const packageDir of packageDirs) { console.log(`## Processing ${packageDir}`); - const fullDir = cliPaths.resolveTargetRoot(packageDir); + const fullDir = targetPaths.resolveRoot(packageDir); const pkgJson = await fs.readJson(resolvePath(fullDir, 'package.json')); if (!pkgJson.bin) { @@ -162,7 +162,7 @@ export async function runCliExtraction({ console.log(''); console.log( `The conflicting file is ${relativePath( - cliPaths.targetRoot, + targetPaths.rootDir, reportPath, )}, expecting the following content:`, ); diff --git a/packages/repo-tools/src/commands/api-reports/common/tryRunPrettier.ts b/packages/repo-tools/src/commands/api-reports/common/tryRunPrettier.ts index 174b68f499..2c95e71647 100644 --- a/packages/repo-tools/src/commands/api-reports/common/tryRunPrettier.ts +++ b/packages/repo-tools/src/commands/api-reports/common/tryRunPrettier.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { paths as cliPaths } from '../../../lib/paths'; +import { targetPaths } from '@backstage/cli-common'; import type { Config } from 'prettier'; /** @@ -33,7 +33,7 @@ export async function tryRunPrettierAsync( // Filepath for proper config resolution const filepath = extraConfig.filepath ?? - `${cliPaths.targetRoot}/should-not-be-ignored.any`; + `${targetPaths.rootDir}/should-not-be-ignored.any`; const config = (await prettier.resolveConfig(filepath, { editorconfig: true })) ?? {}; const formattedContent = prettier.format(content, { @@ -68,7 +68,7 @@ export function createPrettierSyncFormatter( // We need a filepath for proper config resolution, not just a directory const filepath = extraConfig.filepath ?? - `${cliPaths.targetRoot}/should-not-be-ignored.any`; + `${targetPaths.rootDir}/should-not-be-ignored.any`; const resolveConfig = // @ts-expect-error: v2 requires .sync, @prettier/sync v3 does not prettierSync.resolveConfig?.sync ?? prettierSync.resolveConfig; diff --git a/packages/repo-tools/src/commands/api-reports/sql-reports/runSqlExtraction.ts b/packages/repo-tools/src/commands/api-reports/sql-reports/runSqlExtraction.ts index 6268d0d30d..375ce07476 100644 --- a/packages/repo-tools/src/commands/api-reports/sql-reports/runSqlExtraction.ts +++ b/packages/repo-tools/src/commands/api-reports/sql-reports/runSqlExtraction.ts @@ -16,7 +16,7 @@ import fs, { readJson } from 'fs-extra'; import { relative as relativePath } from 'node:path'; -import { paths as cliPaths } from '../../../lib/paths'; +import { targetPaths } from '@backstage/cli-common'; import { diff as justDiff } from 'just-diff'; import { SchemaInfo } from './types'; import { getPgSchemaInfo } from './getPgSchemaInfo'; @@ -43,14 +43,14 @@ export async function runSqlExtraction(options: SqlExtractionOptions) { let dbIndex = 1; for (const packageDir of options.packageDirs) { - const migrationDir = cliPaths.resolveTargetRoot(packageDir, 'migrations'); + const migrationDir = targetPaths.resolveRoot(packageDir, 'migrations'); if (!(await fs.pathExists(migrationDir))) { console.log(`No SQL migrations found in ${packageDir}`); continue; } const { name: pkgName } = await readJson( - cliPaths.resolveTargetRoot(packageDir, 'package.json'), + targetPaths.resolveRoot(packageDir, 'package.json'), ); const migrationFiles = await fs.readdir(migrationDir, { @@ -95,7 +95,7 @@ async function runSingleSqlExtraction( knex: Knex, options: SqlExtractionOptions, ) { - const migrationDir = cliPaths.resolveTargetRoot( + const migrationDir = targetPaths.resolveRoot( targetDir, 'migrations', migrationTarget, @@ -152,7 +152,7 @@ async function runSingleSqlExtraction( break; } } - const reportPath = cliPaths.resolveTargetRoot( + const reportPath = targetPaths.resolveRoot( targetDir, `report${migrationTarget === '.' ? '' : `-${migrationTarget}`}.sql.md`, ); @@ -182,7 +182,7 @@ async function runSingleSqlExtraction( console.log(''); console.log( `The conflicting file is ${relativePath( - cliPaths.targetRoot, + targetPaths.rootDir, reportPath, )}, expecting the following content:`, ); diff --git a/packages/repo-tools/src/commands/knip-reports/knip-extractor.ts b/packages/repo-tools/src/commands/knip-reports/knip-extractor.ts index ab5af976bf..d7eadb3523 100644 --- a/packages/repo-tools/src/commands/knip-reports/knip-extractor.ts +++ b/packages/repo-tools/src/commands/knip-reports/knip-extractor.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { paths as cliPaths } from '../../lib/paths'; +import { targetPaths } from '@backstage/cli-common'; import pLimit from 'p-limit'; import os from 'node:os'; import { relative as relativePath, resolve as resolvePath } from 'node:path'; @@ -100,9 +100,9 @@ async function handlePackage({ }: KnipPackageOptions) { console.log(`## Processing ${packageDir}`); - const fullDir = cliPaths.resolveTargetRoot(packageDir); + const fullDir = targetPaths.resolveRoot(packageDir); const reportPath = resolvePath(fullDir, 'knip-report.md'); - const run = createBinRunner(cliPaths.targetRoot, ''); + const run = createBinRunner(targetPaths.rootDir, ''); let report = await run( `${knipDir}/knip.js`, @@ -149,7 +149,7 @@ async function handlePackage({ console.log(''); console.log( `The conflicting file is ${relativePath( - cliPaths.targetRoot, + targetPaths.rootDir, reportPath, )}, expecting the following content:`, ); @@ -168,8 +168,8 @@ export async function runKnipReports({ packageDirs, isLocalBuild, }: KnipExtractionOptions) { - const knipDir = cliPaths.resolveTargetRoot('./node_modules/knip/bin/'); - const knipConfigPath = cliPaths.resolveTargetRoot('./knip.json'); + const knipDir = targetPaths.resolveRoot('./node_modules/knip/bin/'); + const knipConfigPath = targetPaths.resolveRoot('./knip.json'); const limiter = pLimit(os.cpus().length); await generateKnipConfig({ knipConfigPath }); diff --git a/packages/repo-tools/src/commands/lint-legacy-backend-exports/lint-legacy-backend-exports.ts b/packages/repo-tools/src/commands/lint-legacy-backend-exports/lint-legacy-backend-exports.ts index 3f0424c547..9e2c664e60 100644 --- a/packages/repo-tools/src/commands/lint-legacy-backend-exports/lint-legacy-backend-exports.ts +++ b/packages/repo-tools/src/commands/lint-legacy-backend-exports/lint-legacy-backend-exports.ts @@ -16,11 +16,11 @@ import { Project } from 'ts-morph'; import { BackstagePackageJson, PackageGraph } from '@backstage/cli-node'; import fs from 'fs-extra'; -import { paths as cliPaths } from '../../lib/paths'; +import { targetPaths } from '@backstage/cli-common'; import path from 'node:path'; const project = new Project({ - tsConfigFilePath: cliPaths.resolveTargetRoot('tsconfig.json'), + tsConfigFilePath: targetPaths.resolveRoot('tsconfig.json'), }); function readPackageJson(pkg: string) { diff --git a/packages/repo-tools/src/commands/package-docs/command.ts b/packages/repo-tools/src/commands/package-docs/command.ts index 39568e1b00..13a0603e41 100644 --- a/packages/repo-tools/src/commands/package-docs/command.ts +++ b/packages/repo-tools/src/commands/package-docs/command.ts @@ -15,7 +15,8 @@ */ import { exec } from 'node:child_process'; import { promisify } from 'node:util'; -import { paths as cliPaths, resolvePackagePaths } from '../../lib/paths'; +import { targetPaths } from '@backstage/cli-common'; +import { resolvePackagePaths } from '../../lib/paths'; import { createTemporaryTsConfig } from './utils'; import { readFile, rm, writeFile } from 'node:fs/promises'; import pLimit from 'p-limit'; @@ -74,7 +75,7 @@ async function generateDocJson(pkg: string) { const temporaryTsConfigPath: string = await createTemporaryTsConfig(pkg); const packageJson = JSON.parse( - await readFile(cliPaths.resolveTargetRoot(pkg, 'package.json'), 'utf-8'), + await readFile(targetPaths.resolveRoot(pkg, 'package.json'), 'utf-8'), ); const exports = getExports(packageJson); @@ -85,17 +86,17 @@ async function generateDocJson(pkg: string) { return false; } - await mkdirp(cliPaths.resolveTargetRoot(`dist-types`, pkg)); + await mkdirp(targetPaths.resolveRoot(`dist-types`, pkg)); const { stdout, stderr } = await execAsync( [ - cliPaths.resolveTargetRoot('node_modules/.bin/typedoc'), + targetPaths.resolveRoot('node_modules/.bin/typedoc'), '--json', - cliPaths.resolveTargetRoot(`dist-types`, pkg, 'docs.json'), + targetPaths.resolveRoot(`dist-types`, pkg, 'docs.json'), '--tsconfig', temporaryTsConfigPath, '--basePath', - cliPaths.targetRoot, + targetPaths.rootDir, '--skipErrorChecking', ...(getExports(packageJson).flatMap(e => [ '--entryPoints', @@ -117,7 +118,7 @@ export default async function packageDocs(paths: string[] = [], opts: any) { console.warn('!!! This is an experimental command !!!'); const existingDocsJsonPaths = glob.sync( - cliPaths.resolveTargetRoot('dist-types/**/docs.json'), + targetPaths.resolveRoot('dist-types/**/docs.json'), ); if (existingDocsJsonPaths.length > 0) { console.warn( @@ -129,7 +130,7 @@ export default async function packageDocs(paths: string[] = [], opts: any) { } } console.warn('!!! Deleting existing docs output !!!'); - await rm(cliPaths.resolveTargetRoot('type-docs'), { + await rm(targetPaths.resolveRoot('type-docs'), { recursive: true, force: true, }); @@ -140,8 +141,8 @@ export default async function packageDocs(paths: string[] = [], opts: any) { }); const cache = await PackageDocsCache.loadAsync( - cliPaths.resolveTargetRoot(), - await Lockfile.load(cliPaths.resolveTargetRoot('yarn.lock')), + targetPaths.rootDir, + await Lockfile.load(targetPaths.resolveRoot('yarn.lock')), ); console.log(`### Generating docs.`); @@ -150,7 +151,7 @@ export default async function packageDocs(paths: string[] = [], opts: any) { limit(async () => { const pkgJson = JSON.parse( await readFile( - cliPaths.resolveTargetRoot(pkg, 'package.json'), + targetPaths.resolveRoot(pkg, 'package.json'), 'utf-8', ), ); @@ -173,7 +174,7 @@ export default async function packageDocs(paths: string[] = [], opts: any) { if (success) { await cache.write( pkg, - cliPaths.resolveTargetRoot(`dist-types`, pkg), + targetPaths.resolveRoot(`dist-types`, pkg), ); } } catch (e) { @@ -187,7 +188,7 @@ export default async function packageDocs(paths: string[] = [], opts: any) { const generatedPackageDirs = []; for (const pkg of selectedPackageDirs) { try { - const docsJsonPath = cliPaths.resolveTargetRoot( + const docsJsonPath = targetPaths.resolveRoot( `dist-types/${pkg}/docs.json`, ); const docsJson = JSON.parse(await readFile(docsJsonPath, 'utf-8')); @@ -211,7 +212,7 @@ export default async function packageDocs(paths: string[] = [], opts: any) { const { stdout, stderr } = await execAsync( [ - cliPaths.resolveTargetRoot('node_modules/.bin/typedoc'), + targetPaths.resolveRoot('node_modules/.bin/typedoc'), '--entryPointStrategy', 'merge', ...generatedPackageDirs.flatMap(pkg => [ @@ -220,13 +221,13 @@ export default async function packageDocs(paths: string[] = [], opts: any) { ]), ...HIGHLIGHT_LANGUAGES.flatMap(e => ['--highlightLanguages', e]), '--out', - cliPaths.resolveTargetRoot('type-docs'), - ...(existsSync(cliPaths.resolveTargetRoot('typedoc.json')) - ? ['--options', cliPaths.resolveTargetRoot('typedoc.json')] + targetPaths.resolveRoot('type-docs'), + ...(existsSync(targetPaths.resolveRoot('typedoc.json')) + ? ['--options', targetPaths.resolveRoot('typedoc.json')] : []), ].join(' '), { - cwd: cliPaths.targetRoot, + cwd: targetPaths.rootDir, }, ); diff --git a/packages/repo-tools/src/commands/package-docs/utils.ts b/packages/repo-tools/src/commands/package-docs/utils.ts index 2cb95443b6..41720f8055 100644 --- a/packages/repo-tools/src/commands/package-docs/utils.ts +++ b/packages/repo-tools/src/commands/package-docs/utils.ts @@ -14,11 +14,11 @@ * limitations under the License. */ +import { findOwnPaths } from '@backstage/cli-common'; import fs from 'fs-extra'; -import { paths as cliPaths } from '../../lib/paths'; export async function createTemporaryTsConfig(dir: string) { - const path = cliPaths.resolveOwnRoot(dir, 'tsconfig.typedoc.tmp.json'); + const path = findOwnPaths(__dirname).resolveRoot(dir, 'tsconfig.typedoc.tmp.json'); process.once('exit', () => { fs.removeSync(path); diff --git a/packages/repo-tools/src/commands/package/schema/openapi/diff.ts b/packages/repo-tools/src/commands/package/schema/openapi/diff.ts index 2f0af30881..851b2b3a48 100644 --- a/packages/repo-tools/src/commands/package/schema/openapi/diff.ts +++ b/packages/repo-tools/src/commands/package/schema/openapi/diff.ts @@ -16,7 +16,7 @@ import chalk from 'chalk'; import { exec } from '../../../../lib/exec'; import { getPathToCurrentOpenApiSpec } from '../../../../lib/openapi/helpers'; -import { paths as cliPaths } from '../../../../lib/paths'; +import { targetPaths } from '@backstage/cli-common'; import { OptionValues } from 'commander'; import { env } from 'node:process'; import { readFile, rm } from 'node:fs/promises'; @@ -53,7 +53,7 @@ async function check(opts: OptionValues) { baseRef, ], { - cwd: cliPaths.targetRoot, + cwd: targetPaths.rootDir, env: { CI: opts.json ? '1' : undefined, ...env }, }, ); @@ -65,7 +65,7 @@ async function check(opts: OptionValues) { if (opts.json) { const file = ( - await readFile(resolve(cliPaths.targetRoot, 'ci-run-details.json')) + await readFile(resolve(targetPaths.rootDir, 'ci-run-details.json')) ).toString(); const results = JSON.parse(file); console.log(file); @@ -73,7 +73,7 @@ async function check(opts: OptionValues) { throw new Error('Some checks failed'); } - await rm(resolve(cliPaths.targetRoot, 'ci-run-details.json')); + await rm(resolve(targetPaths.rootDir, 'ci-run-details.json')); } else { console.log(reduceOpticOutput(output)); if (!opts.ignore && failed) { diff --git a/packages/repo-tools/src/commands/package/schema/openapi/fuzz.ts b/packages/repo-tools/src/commands/package/schema/openapi/fuzz.ts index 29b038ac5a..12e5d7d527 100644 --- a/packages/repo-tools/src/commands/package/schema/openapi/fuzz.ts +++ b/packages/repo-tools/src/commands/package/schema/openapi/fuzz.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import fs from 'fs-extra'; -import { paths as cliPaths } from '../../../../lib/paths'; +import { targetPaths } from '@backstage/cli-common'; import chalk from 'chalk'; import { spawn } from '../../../../lib/exec'; import { getPathToCurrentOpenApiSpec } from '../../../../lib/openapi/helpers'; @@ -40,7 +40,7 @@ async function fuzz(opts: OptionValues) { await fs.readFile(resolvedOpenapiPath, 'utf8'), ) as { info: { title: string } }; const configSource = ConfigSources.default({ - rootDir: cliPaths.targetRoot, + rootDir: targetPaths.rootDir, }); const config = await ConfigSources.toConfig(configSource); const pluginId = openapiSpec.info.title; @@ -48,7 +48,7 @@ async function fuzz(opts: OptionValues) { if (opts.debug) { args.push( '--cassette-path', - cliPaths.resolveTargetRoot(join('.cassettes', `${pluginId}.yml`)), + targetPaths.resolveRoot(join('.cassettes', `${pluginId}.yml`)), ); } diff --git a/packages/repo-tools/src/commands/package/schema/openapi/generate/client.ts b/packages/repo-tools/src/commands/package/schema/openapi/generate/client.ts index 24f8496a0d..17033d881a 100644 --- a/packages/repo-tools/src/commands/package/schema/openapi/generate/client.ts +++ b/packages/repo-tools/src/commands/package/schema/openapi/generate/client.ts @@ -24,11 +24,11 @@ import { OUTPUT_PATH, } from '../../../../../lib/openapi/constants'; import { deduplicateImports } from '../../../../../lib/openapi/dedupe-imports'; +import { targetPaths } from '@backstage/cli-common'; import { getPathToCurrentOpenApiSpec, toGeneratorAdditionalProperties, } from '../../../../../lib/openapi/helpers'; -import { paths as cliPaths } from '../../../../../lib/paths'; async function generate( outputDirectory: string, @@ -36,7 +36,7 @@ async function generate( abortSignal?: AbortController, ) { const resolvedOpenapiPath = await getPathToCurrentOpenApiSpec(); - const resolvedOutputDirectory = cliPaths.resolveTargetRoot( + const resolvedOutputDirectory = targetPaths.resolveRoot( outputDirectory, OUTPUT_PATH, ); @@ -95,7 +95,7 @@ async function generate( signal: abortSignal?.signal, }); - const prettier = cliPaths.resolveTargetRoot('node_modules/.bin/prettier'); + const prettier = targetPaths.resolveRoot('node_modules/.bin/prettier'); if (prettier) { await exec(`${prettier} --write ${parentDirectory}`, [], { signal: abortSignal?.signal, diff --git a/packages/repo-tools/src/commands/package/schema/openapi/generate/server.ts b/packages/repo-tools/src/commands/package/schema/openapi/generate/server.ts index be70ee68c4..88c6df781a 100644 --- a/packages/repo-tools/src/commands/package/schema/openapi/generate/server.ts +++ b/packages/repo-tools/src/commands/package/schema/openapi/generate/server.ts @@ -27,23 +27,23 @@ import { TS_SCHEMA_PATH, } from '../../../../../lib/openapi/constants'; import { deduplicateImports } from '../../../../../lib/openapi/dedupe-imports'; +import { targetPaths } from '@backstage/cli-common'; import { getPathToCurrentOpenApiSpec, getRelativePathToFile, toGeneratorAdditionalProperties, } from '../../../../../lib/openapi/helpers'; -import { paths as cliPaths } from '../../../../../lib/paths'; async function generateSpecFile() { const openapiPath = await getPathToCurrentOpenApiSpec(); const yaml = YAML.load(await fs.readFile(openapiPath, 'utf8')); - const tsPath = cliPaths.resolveTarget(TS_SCHEMA_PATH); + const tsPath = targetPaths.resolve(TS_SCHEMA_PATH); const schemaDir = dirname(tsPath); await fs.mkdirp(schemaDir); - const oldTsPath = cliPaths.resolveTarget(OLD_SCHEMA_PATH); + const oldTsPath = targetPaths.resolve(OLD_SCHEMA_PATH); if (fs.existsSync(oldTsPath)) { console.warn(`Removing old schema file at ${oldTsPath}`); fs.removeSync(oldTsPath); @@ -77,9 +77,9 @@ export const createOpenApiRouter = async ( ); await exec(`yarn backstage-cli package lint`, ['--fix', tsPath, indexFile]); - if (await cliPaths.resolveTargetRoot('node_modules/.bin/prettier')) { + if (await targetPaths.resolveRoot('node_modules/.bin/prettier')) { await exec(`yarn prettier`, ['--write', tsPath, indexFile], { - cwd: cliPaths.targetRoot, + cwd: targetPaths.rootDir, }); } } @@ -150,7 +150,7 @@ async function generate( }, ); - const prettier = cliPaths.resolveTargetRoot('node_modules/.bin/prettier'); + const prettier = targetPaths.resolveRoot('node_modules/.bin/prettier'); if (prettier) { await exec(`${prettier} --write ${resolvedOutputDirectory}`, [], { signal: abortSignal?.signal, diff --git a/packages/repo-tools/src/commands/package/schema/openapi/init.ts b/packages/repo-tools/src/commands/package/schema/openapi/init.ts index 00e82f65df..558a9a7387 100644 --- a/packages/repo-tools/src/commands/package/schema/openapi/init.ts +++ b/packages/repo-tools/src/commands/package/schema/openapi/init.ts @@ -14,8 +14,8 @@ * limitations under the License. */ import fs from 'fs-extra'; +import { targetPaths } from '@backstage/cli-common'; import { YAML_SCHEMA_PATH } from '../../../../lib/openapi/constants'; -import { paths as cliPaths } from '../../../../lib/paths'; import chalk from 'chalk'; import { exec } from '../../../../lib/exec'; import { @@ -49,7 +49,7 @@ capture: ${YAML_SCHEMA_PATH}: # 🔧 Runnable example with simple get requests. # Run with "PORT=3000 optic capture ${YAML_SCHEMA_PATH} --update interactive" in '${ - cliPaths.targetDir + targetPaths.dir }' # You can change the server and the 'requests' section to experiment server: @@ -64,7 +64,7 @@ capture: ).join(' ')} `, ); - if (await cliPaths.resolveTargetRoot('node_modules/.bin/prettier')) { + if (await targetPaths.resolveRoot('node_modules/.bin/prettier')) { await exec(`yarn prettier`, ['--write', opticConfigFilePath]); } } diff --git a/packages/repo-tools/src/commands/repo/schema/openapi/diff.ts b/packages/repo-tools/src/commands/repo/schema/openapi/diff.ts index 02f3db0c43..2648f78f06 100644 --- a/packages/repo-tools/src/commands/repo/schema/openapi/diff.ts +++ b/packages/repo-tools/src/commands/repo/schema/openapi/diff.ts @@ -16,16 +16,16 @@ import { PackageGraph } from '@backstage/cli-node'; import { OptionValues } from 'commander'; import { exec } from '../../../../lib/exec'; +import { targetPaths } from '@backstage/cli-common'; import { CiRunDetails, generateCompareSummaryMarkdown, } from '../../../../lib/openapi/optic/helpers'; -import { paths as cliPaths } from '../../../../lib/paths'; import { YAML_SCHEMA_PATH } from '../../../../lib/openapi/constants'; function cleanUpApiName(e: { apiName: string }) { e.apiName = e.apiName - .replace(cliPaths.targetDir, '') + .replace(targetPaths.dir, '') .replace(YAML_SCHEMA_PATH, ''); } @@ -46,7 +46,7 @@ export async function command(opts: OptionValues) { const changedOpenApiSpecs = changedFiles .split('\n') .filter(e => e.endsWith(YAML_SCHEMA_PATH)) - .map(e => cliPaths.resolveTarget(e)); + .map(e => targetPaths.resolve(e)); // filter packages by changedFiles packages = packages.filter(pkg => diff --git a/packages/repo-tools/src/commands/repo/schema/openapi/test.ts b/packages/repo-tools/src/commands/repo/schema/openapi/test.ts index 233c50a1fa..123a1182ea 100644 --- a/packages/repo-tools/src/commands/repo/schema/openapi/test.ts +++ b/packages/repo-tools/src/commands/repo/schema/openapi/test.ts @@ -17,9 +17,9 @@ import fs from 'fs-extra'; import { join } from 'node:path'; import chalk from 'chalk'; +import { findOwnPaths, targetPaths } from '@backstage/cli-common'; import { runner } from '../../../../lib/runner'; import { YAML_SCHEMA_PATH } from '../../../../lib/openapi/constants'; -import { paths as cliPaths } from '../../../../lib/paths'; import { exec } from '../../../../lib/exec'; import { getPathToOpenApiSpec } from '../../../../lib/openapi/helpers'; @@ -42,7 +42,9 @@ async function test( let opticLocation = ''; try { opticLocation = ( - await exec(`yarn bin optic`, [], { cwd: cliPaths.ownRoot }) + await exec(`yarn bin optic`, [], { + cwd: findOwnPaths(__dirname).rootDir, + }) ).stdout as string; } catch (err) { throw new Error( @@ -79,7 +81,7 @@ async function test( throw err; } if ( - (await cliPaths.resolveTargetRoot('node_modules/.bin/prettier')) && + (await targetPaths.resolveRoot('node_modules/.bin/prettier')) && options?.update ) { await exec(`yarn prettier`, ['--write', openapiPath]); diff --git a/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts b/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts index 2aa0392fae..9834009d09 100644 --- a/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts +++ b/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts @@ -19,8 +19,8 @@ import { isEqual } from 'lodash'; import { join } from 'node:path'; import chalk from 'chalk'; import { relative as relativePath, resolve as resolvePath } from 'node:path'; +import { targetPaths } from '@backstage/cli-common'; import { runner } from '../../../../lib/runner'; -import { paths as cliPaths } from '../../../../lib/paths'; import { OLD_SCHEMA_PATH, TS_SCHEMA_PATH, @@ -60,7 +60,7 @@ async function verify(directoryPath: string) { throw new Error(`\`${TS_SCHEMA_PATH}\` needs to have a 'spec' export.`); } if (!isEqual(schema.spec, yaml)) { - const path = relativePath(cliPaths.targetRoot, directoryPath); + const path = relativePath(targetPaths.rootDir, directoryPath); throw new Error( `\`${YAML_SCHEMA_PATH}\` and \`${TS_SCHEMA_PATH}\` do not match. Please run \`yarn backstage-repo-tools package schema openapi generate\` from '${path}' to regenerate \`${TS_SCHEMA_PATH}\`.`, ); diff --git a/packages/repo-tools/src/lib/openapi/helpers.ts b/packages/repo-tools/src/lib/openapi/helpers.ts index 5d1bcf58c7..d6b6927660 100644 --- a/packages/repo-tools/src/lib/openapi/helpers.ts +++ b/packages/repo-tools/src/lib/openapi/helpers.ts @@ -18,8 +18,8 @@ import Parser from '@apidevtools/swagger-parser'; import fs, { pathExists } from 'fs-extra'; import YAML from 'js-yaml'; import { cloneDeep } from 'lodash'; +import { targetPaths } from '@backstage/cli-common'; import { resolve } from 'node:path'; -import { paths } from '../paths'; import { YAML_SCHEMA_PATH } from './constants'; export const getPathToFile = async (directory: string, filename: string) => { @@ -27,7 +27,7 @@ export const getPathToFile = async (directory: string, filename: string) => { }; export const getRelativePathToFile = async (filename: string) => { - return await getPathToFile(paths.targetDir, filename); + return await getPathToFile(targetPaths.dir, filename); }; export const assertExists = async (path: string) => { diff --git a/packages/repo-tools/src/lib/paths.ts b/packages/repo-tools/src/lib/paths.ts index 2e50b687a4..3f1c5ce3f4 100644 --- a/packages/repo-tools/src/lib/paths.ts +++ b/packages/repo-tools/src/lib/paths.ts @@ -14,27 +14,11 @@ * limitations under the License. */ -import { targetPaths, findOwnPaths } from '@backstage/cli-common'; +import { targetPaths } from '@backstage/cli-common'; import { PackageGraph } from '@backstage/cli-node'; import { Minimatch } from 'minimatch'; import { isAbsolute, relative as relativePath } from 'node:path'; -/* eslint-disable-next-line no-restricted-syntax */ -export const paths = { - get targetDir() { - return targetPaths.resolve(); - }, - get targetRoot() { - return targetPaths.resolveRoot(); - }, - get ownRoot() { - return findOwnPaths(__dirname).resolveRoot(); - }, - resolveTarget: targetPaths.resolve, - resolveTargetRoot: targetPaths.resolveRoot, - resolveOwnRoot: (...p: string[]) => findOwnPaths(__dirname).resolveRoot(...p), -}; - /** @internal */ export interface ResolvePackagesOptions { paths?: string[]; @@ -54,7 +38,7 @@ export async function resolvePackagePaths( for (const path of providedPaths) { const matches = packages.some( ({ dir }) => - new Minimatch(path).match(relativePath(targetPaths.resolveRoot(), dir)) || + new Minimatch(path).match(relativePath(targetPaths.rootDir, dir)) || isChildPath(dir, path), ); if (!matches) { @@ -70,7 +54,7 @@ export async function resolvePackagePaths( packages = packages.filter(({ dir }) => providedPaths.some( path => - new Minimatch(path).match(relativePath(targetPaths.resolveRoot(), dir)) || + new Minimatch(path).match(relativePath(targetPaths.rootDir, dir)) || isChildPath(dir, path), ), ); @@ -79,7 +63,7 @@ export async function resolvePackagePaths( if (include) { packages = packages.filter(pkg => include.some(pattern => - new Minimatch(pattern).match(relativePath(targetPaths.resolveRoot(), pkg.dir)), + new Minimatch(pattern).match(relativePath(targetPaths.rootDir, pkg.dir)), ), ); } @@ -89,13 +73,13 @@ export async function resolvePackagePaths( exclude.some( pattern => !new Minimatch(pattern).match( - relativePath(targetPaths.resolveRoot(), pkg.dir), + relativePath(targetPaths.rootDir, pkg.dir), ), ), ); } - return packages.map(pkg => relativePath(targetPaths.resolveRoot(), pkg.dir)); + return packages.map(pkg => relativePath(targetPaths.rootDir, pkg.dir)); } /** @internal */ diff --git a/packages/repo-tools/src/lib/runner.ts b/packages/repo-tools/src/lib/runner.ts index 7061a195ad..193a267cf0 100644 --- a/packages/repo-tools/src/lib/runner.ts +++ b/packages/repo-tools/src/lib/runner.ts @@ -14,10 +14,10 @@ * limitations under the License. */ +import { targetPaths } from '@backstage/cli-common'; import { resolvePackagePaths } from './paths'; import pLimit from 'p-limit'; import { relative as relativePath } from 'node:path'; -import { paths as cliPaths } from './paths'; import portFinder from 'portfinder'; export async function runner( @@ -58,7 +58,7 @@ export async function runner( } return { - relativeDir: relativePath(cliPaths.targetRoot, pkg), + relativeDir: relativePath(targetPaths.rootDir, pkg), resultText, }; }), diff --git a/packages/yarn-plugin/src/index.test.ts b/packages/yarn-plugin/src/index.test.ts index 2b907d7a96..4acd6a446f 100644 --- a/packages/yarn-plugin/src/index.test.ts +++ b/packages/yarn-plugin/src/index.test.ts @@ -86,7 +86,7 @@ describe('Backstage yarn plugin', () => { let initialLockFileContent: string | undefined; beforeAll(async () => { - const targetRoot = targetPaths.resolveRoot(); + const targetRoot = targetPaths.rootDir; await executeCommand('yarn', ['build'], { cwd: joinPath(targetRoot, 'packages/yarn-plugin'), }); diff --git a/packages/yarn-plugin/src/util/getWorkspaceRoot.test.ts b/packages/yarn-plugin/src/util/getWorkspaceRoot.test.ts index 750e1d5c5e..0f44429707 100644 --- a/packages/yarn-plugin/src/util/getWorkspaceRoot.test.ts +++ b/packages/yarn-plugin/src/util/getWorkspaceRoot.test.ts @@ -50,6 +50,12 @@ describe('getWorkspaceRoot', () => { jest.doMock('@backstage/cli-common', () => ({ ...jest.requireActual('@backstage/cli-common'), targetPaths: { + get dir() { + return mockResolveRoot(); + }, + get rootDir() { + return mockResolveRoot(); + }, resolveRoot: mockResolveRoot, }, })); diff --git a/packages/yarn-plugin/src/util/getWorkspaceRoot.ts b/packages/yarn-plugin/src/util/getWorkspaceRoot.ts index 9b12abb59d..57e03daabe 100644 --- a/packages/yarn-plugin/src/util/getWorkspaceRoot.ts +++ b/packages/yarn-plugin/src/util/getWorkspaceRoot.ts @@ -18,5 +18,5 @@ import { npath } from '@yarnpkg/fslib'; import { targetPaths } from '@backstage/cli-common'; export const getWorkspaceRoot = () => { - return npath.toPortablePath(targetPaths.resolveRoot()); + return npath.toPortablePath(targetPaths.rootDir); };