Give each CLI module its own paths instead of importing from lib/

Split the paths API in @backstage/cli-common into targetPaths (a lazily
initialized singleton for cwd-based paths) and findOwnPaths (a cached function
for package-relative paths). Most CLI module files only need target paths, which
can now be imported directly without __dirname. The few files that need own paths
use findOwnPaths(__dirname).

Added hierarchical caching to findOwnDir so the filesystem walk only happens
once per package. targetPaths re-resolves automatically if process.cwd() changes.

The deprecated findPaths function is preserved for backward compatibility,
delegating to the new primitives internally.

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Patrik Oldsberg
2026-02-21 13:16:50 +01:00
parent 56bd494b0c
commit 29e91e9159
52 changed files with 332 additions and 303 deletions
+2 -4
View File
@@ -16,10 +16,8 @@
import fs from 'fs-extra';
import { resolve as resolvePath } from 'node:path';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
const DEFAULT_CACHE_BASE_PATH = 'node_modules/.cache/backstage-cli';
@@ -34,7 +32,7 @@ export class SuccessCache {
* location.
*/
static trimPaths(input: string) {
return input.replaceAll(paths.targetRoot, '');
return input.replaceAll(targetPaths.targetRoot, '');
}
constructor(name: string, basePath?: string) {
+2 -4
View File
@@ -20,14 +20,12 @@ import {
} from '@backstage/cli-node';
import { resolve as resolvePath } from 'node:path';
import { Project, SourceFile, SyntaxKind, ts, Type } from 'ts-morph';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
export const createTypeDistProject = async () => {
return new Project({
tsConfigFilePath: paths.resolveTargetRoot('tsconfig.json'),
tsConfigFilePath: targetPaths.resolveTargetRoot('tsconfig.json'),
skipAddingFilesFromTsConfig: true,
});
};
+4 -5
View File
@@ -16,10 +16,9 @@
import fs from 'fs-extra';
import semver from 'semver';
import { findPaths } from '@backstage/cli-common';
import { findOwnPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
const ownPaths = findOwnPaths(__dirname);
import { Lockfile } from './versioning';
/* eslint-disable @backstage/no-relative-monorepo-imports */
@@ -85,12 +84,12 @@ export const packageVersions: Record<string, string> = {
};
export function findVersion() {
const pkgContent = fs.readFileSync(paths.resolveOwn('package.json'), 'utf8');
const pkgContent = fs.readFileSync(ownPaths.resolveOwn('package.json'), 'utf8');
return JSON.parse(pkgContent).version;
}
export const version = findVersion();
export const isDev = fs.pathExistsSync(paths.resolveOwn('src'));
export const isDev = fs.pathExistsSync(ownPaths.resolveOwn('src'));
export function createPackageVersionProvider(
lockfile?: Lockfile,
+2 -2
View File
@@ -21,11 +21,11 @@ const mockDir = createMockDirectory();
jest.mock('@backstage/cli-common', () => ({
...jest.requireActual('@backstage/cli-common'),
findPaths: () => ({
targetPaths: {
resolveTargetRoot(filename: string) {
return mockDir.resolve(filename);
},
}),
},
}));
describe('getHasYarnPlugin', () => {
+2 -4
View File
@@ -17,10 +17,8 @@
import fs from 'fs-extra';
import yaml from 'yaml';
import z from 'zod';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
const yarnRcSchema = z.object({
plugins: z
@@ -38,7 +36,7 @@ const yarnRcSchema = z.object({
* @returns Promise<boolean> - true if the plugin is installed, false otherwise
*/
export async function getHasYarnPlugin(): Promise<boolean> {
const yarnRcPath = paths.resolveTargetRoot('.yarnrc.yml');
const yarnRcPath = targetPaths.resolveTargetRoot('.yarnrc.yml');
const yarnRcContent = await fs.readFile(yarnRcPath, 'utf-8').catch(e => {
if (e.code === 'ENOENT') {
// gracefully continue in case the file doesn't exist
@@ -23,10 +23,8 @@ import {
PackageGraph,
PackageRoles,
} from '@backstage/cli-node';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
import { buildFrontend } from '../../../lib/buildFrontend';
import { buildBackend } from '../../../lib/buildBackend';
import { isValidUrl } from '../../../lib/urls';
@@ -44,19 +42,19 @@ export async function command(opts: OptionValues): Promise<void> {
if (isValidUrl(arg)) {
return arg;
}
return paths.resolveTarget(arg);
return targetPaths.resolveTarget(arg);
});
if (role === 'frontend') {
return buildFrontend({
targetDir: paths.targetDir,
targetDir: targetPaths.targetDir,
configPaths,
writeStats: Boolean(opts.stats),
webpack,
});
}
return buildBackend({
targetDir: paths.targetDir,
targetDir: targetPaths.targetDir,
configPaths,
skipBuildDependencies: Boolean(opts.skipBuildDependencies),
minify: Boolean(opts.minify),
@@ -79,7 +77,7 @@ export async function command(opts: OptionValues): Promise<void> {
if (isModuleFederationRemote) {
console.log('Building package as a module federation remote');
return buildFrontend({
targetDir: paths.targetDir,
targetDir: targetPaths.targetDir,
configPaths: [],
writeStats: Boolean(opts.stats),
isModuleFederationRemote,
@@ -102,7 +100,7 @@ export async function command(opts: OptionValues): Promise<void> {
}
const packageJson = (await fs.readJson(
paths.resolveTarget('package.json'),
targetPaths.resolveTarget('package.json'),
)) as BackstagePackageJson;
return buildPackage({
@@ -18,16 +18,14 @@ import { OptionValues } from 'commander';
import { startPackage } from './startPackage';
import { resolveLinkedWorkspace } from './resolveLinkedWorkspace';
import { findRoleFromCommand } from '../../../lib/role';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
export async function command(opts: OptionValues): Promise<void> {
await startPackage({
role: await findRoleFromCommand(opts),
entrypoint: opts.entrypoint,
targetDir: paths.targetDir,
targetDir: targetPaths.targetDir,
configPaths: opts.config as string[],
checksEnabled: Boolean(opts.check),
linkedWorkspace: await resolveLinkedWorkspace(opts.link),
@@ -16,10 +16,8 @@
import fs from 'fs-extra';
import { resolve as resolvePath } from 'node:path';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
import { runBackend } from '../../../lib/runner';
interface StartBackendOptions {
@@ -46,7 +44,7 @@ export async function startBackend(options: StartBackendOptions) {
export async function startBackendPlugin(options: StartBackendOptions) {
const hasDevIndexEntry = await fs.pathExists(
resolvePath(options.targetDir ?? paths.targetDir, 'dev/index.ts'),
resolvePath(options.targetDir ?? targetPaths.targetDir, 'dev/index.ts'),
);
if (!hasDevIndexEntry) {
console.warn(
@@ -20,10 +20,8 @@ import {
getModuleFederationRemoteOptions,
serveBundle,
} from '../../../../build/lib/bundler';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
import { BackstagePackageJson } from '@backstage/cli-node';
import { hasReactDomClient } from '../../../../build/lib/bundler/hasReactDomClient';
@@ -41,7 +39,7 @@ interface StartAppOptions {
export async function startFrontend(options: StartAppOptions) {
const packageJson = (await readJson(
resolvePath(options.targetDir ?? paths.targetDir, 'package.json'),
resolvePath(options.targetDir ?? targetPaths.targetDir, 'package.json'),
)) as BackstagePackageJson;
if (!hasReactDomClient()) {
@@ -61,7 +59,7 @@ export async function startFrontend(options: StartAppOptions) {
moduleFederationRemote: options.isModuleFederationRemote
? await getModuleFederationRemoteOptions(
packageJson,
resolvePath(paths.targetDir),
resolvePath(targetPaths.targetDir),
)
: undefined,
});
@@ -18,10 +18,8 @@ import chalk from 'chalk';
import { Command, OptionValues } from 'commander';
import { relative as relativePath } from 'node:path';
import { buildPackages, getOutputsForRole } from '../../lib/builder';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
import {
BackstagePackage,
PackageGraph,
@@ -92,7 +90,7 @@ export async function command(opts: OptionValues, cmd: Command): Promise<void> {
targetDir: pkg.dir,
packageJson: pkg.packageJson,
outputs,
logPrefix: `${chalk.cyan(relativePath(paths.targetRoot, pkg.dir))}: `,
logPrefix: `${chalk.cyan(relativePath(targetPaths.targetRoot, pkg.dir))}: `,
workspacePackages: packages,
minify: opts.minify ?? buildOptions.minify,
};
@@ -17,10 +17,8 @@
import { PackageGraph } from '@backstage/cli-node';
import { findTargetPackages } from './start';
import { posix } from 'node:path';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
const mocks = {
app: {
@@ -101,7 +99,7 @@ describe('findTargetPackages', () => {
beforeEach(() => {
jest.clearAllMocks();
jest
.spyOn(paths, 'resolveTargetRoot')
.spyOn(targetPaths, 'resolveTargetRoot')
.mockImplementation((...parts: string[]) => {
return posix.resolve('/root', ...parts);
});
@@ -20,10 +20,8 @@ import {
PackageRole,
} from '@backstage/cli-node';
import { relative as relativePath } from 'node:path';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
import { resolveLinkedWorkspace } from '../package/start/resolveLinkedWorkspace';
import { startPackage } from '../package/start/startPackage';
import { parseArgs } from 'node:util';
@@ -98,7 +96,7 @@ export async function findTargetPackages(
pkg => nameOrPath === pkg.packageJson.name,
);
if (!matchingPackage) {
const absPath = paths.resolveTargetRoot(nameOrPath);
const absPath = targetPaths.resolveTargetRoot(nameOrPath);
matchingPackage = packages.find(
pkg => relativePath(pkg.dir, absPath) === '',
);
@@ -120,7 +118,7 @@ export async function findTargetPackages(
);
if (matchingPackages.length > 1) {
// Final fallback is to check for the package path within the monorepo, packages/app or packages/backend
const expectedPath = paths.resolveTargetRoot(
const expectedPath = targetPaths.resolveTargetRoot(
role === 'frontend' ? 'packages/app' : 'packages/backend',
);
const matchByPath = matchingPackages.find(
@@ -39,10 +39,8 @@ import {
import { forwardFileImports, cssEntryPoints } from './plugins';
import { BuildOptions, Output } from './types';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
import { BackstagePackageJson } from '@backstage/cli-node';
import { readEntryPoints } from '../entryPoints';
@@ -119,7 +117,7 @@ export async function makeRollupConfigs(
options: BuildOptions,
): Promise<RollupOptions[]> {
const configs = new Array<RollupOptions>();
const targetDir = options.targetDir ?? paths.targetDir;
const targetDir = options.targetDir ?? targetPaths.targetDir;
let targetPkg = options.packageJson;
if (!targetPkg) {
@@ -287,9 +285,9 @@ export async function makeRollupConfigs(
const input = Object.fromEntries(
scriptEntryPoints.map(e => [
e.name,
paths.resolveTargetRoot(
targetPaths.resolveTargetRoot(
'dist-types',
relativePath(paths.targetRoot, targetDir),
relativePath(targetPaths.targetRoot, targetDir),
e.path.replace(/\.(?:ts|tsx)$/, '.d.ts'),
),
]),
@@ -18,10 +18,8 @@ import fs from 'fs-extra';
import { rollup, RollupOptions } from 'rollup';
import chalk from 'chalk';
import { relative as relativePath, resolve as resolvePath } from 'node:path';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
import { makeRollupConfigs } from './config';
import { BuildOptions, Output } from './types';
import { PackageRoles, runConcurrentTasks } from '@backstage/cli-node';
@@ -36,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(paths.targetDir, error.id);
const path = relativePath(targetPaths.targetDir, error.id);
const loc = chalk.cyan(`${path}:${line}:${column}`);
if (text === 'Unexpected "<"' && error.id.endsWith('.js')) {
@@ -55,11 +53,11 @@ export function formatErrorMessage(error: any) {
} else {
// Generic rollup errors, log what's available
if (error.loc) {
const file = `${paths.resolveTarget((error.loc.file || error.id)!)}`;
const file = `${targetPaths.resolveTarget((error.loc.file || error.id)!)}`;
const pos = `${error.loc.line}:${error.loc.column}`;
msg += `${file} [${pos}]\n`;
} else if (error.id) {
msg += `${paths.resolveTarget(error.id)}\n`;
msg += `${targetPaths.resolveTarget(error.id)}\n`;
}
msg += `${error}\n`;
@@ -92,7 +90,7 @@ async function rollupBuild(config: RollupOptions) {
export const buildPackage = async (options: BuildOptions) => {
try {
const { resolutions } = await fs.readJson(
paths.resolveTargetRoot('package.json'),
targetPaths.resolveTargetRoot('package.json'),
);
if (resolutions?.esbuild) {
console.warn(
@@ -109,7 +107,7 @@ export const buildPackage = async (options: BuildOptions) => {
const rollupConfigs = await makeRollupConfigs(options);
const targetDir = options.targetDir ?? paths.targetDir;
const targetDir = options.targetDir ?? targetPaths.targetDir;
await fs.remove(resolvePath(targetDir, 'dist'));
const buildTasks = rollupConfigs.map(rollupBuild);
@@ -29,10 +29,8 @@ import { ModuleFederationPlugin } from '@module-federation/enhanced/rspack';
import fs from 'fs-extra';
import { optimization as optimizationConfig } from './optimization';
import pickBy from 'lodash/pickBy';
import { runOutput, findPaths } from '@backstage/cli-common';
import { runOutput, targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const cliPaths = findPaths(__dirname);
import { transforms } from './transforms';
import { version } from '../../../../lib/version';
import yn from 'yn';
@@ -99,7 +97,7 @@ async function readBuildInfo() {
}
const { version: packageVersion } = await fs.readJson(
cliPaths.resolveTarget('package.json'),
targetPaths.resolveTarget('package.json'),
);
return {
@@ -14,15 +14,13 @@
* limitations under the License.
*/
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
export function hasReactDomClient() {
try {
require.resolve('react-dom/client', {
paths: [paths.targetDir],
paths: [targetPaths.targetDir],
});
return true;
} catch {
@@ -17,10 +17,8 @@
import { relative as relativePath } from 'node:path';
import { getPackages } from '@manypkg/get-packages';
import { rspack } from '@rspack/core';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
/**
* This returns of collection of plugins that links a separate workspace into
@@ -55,7 +53,7 @@ export async function createWorkspaceLinkingPlugins(
/^react(?:-router)?(?:-dom)?$/,
resource => {
if (!relativePath(linkedRoot.dir, resource.context).startsWith('..')) {
resource.context = paths.targetDir;
resource.context = targetPaths.targetDir;
}
},
),
@@ -20,10 +20,8 @@ import chokidar from 'chokidar';
import fs from 'fs-extra';
import PQueue from 'p-queue';
import { dirname, join as joinPath, resolve as resolvePath } from 'node:path';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const cliPaths = findPaths(__dirname);
const DETECTED_MODULES_MODULE_NAME = '__backstage-autodetected-plugins__';
@@ -149,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(
cliPaths.targetRoot,
targetPaths.targetRoot,
'node_modules',
`${DETECTED_MODULES_MODULE_NAME}.js`,
);
@@ -16,22 +16,21 @@
import fs from 'fs-extra';
import { resolve as resolvePath } from 'node:path';
import { findPaths } from '@backstage/cli-common';
import { targetPaths, findOwnPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
const ownPaths = findOwnPaths(__dirname);
export type BundlingPathsOptions = {
// bundle entrypoint, e.g. 'src/index'
entry: string;
// Target directory, defaulting to paths.targetDir
// Target directory, defaulting to targetPaths.targetDir
targetDir?: string;
// Relative dist directory, defaulting to 'dist'
dist?: string;
};
export function resolveBundlingPaths(options: BundlingPathsOptions) {
const { entry, targetDir = paths.targetDir } = options;
const { entry, targetDir = targetPaths.targetDir } = options;
const resolveTargetModule = (pathString: string) => {
for (const ext of ['mjs', 'js', 'ts', 'tsx', 'jsx']) {
@@ -52,7 +51,7 @@ export function resolveBundlingPaths(options: BundlingPathsOptions) {
} else {
targetHtml = resolvePath(targetDir, `${entry}.html`);
if (!fs.pathExistsSync(targetHtml)) {
targetHtml = paths.resolveOwn('templates/serve_index.html');
targetHtml = ownPaths.resolveOwn('templates/serve_index.html');
}
}
@@ -70,10 +69,10 @@ export function resolveBundlingPaths(options: BundlingPathsOptions) {
targetSrc: resolvePath(targetDir, 'src'),
targetDev: resolvePath(targetDir, 'dev'),
targetEntry: resolveTargetModule(entry),
targetTsConfig: paths.resolveTargetRoot('tsconfig.json'),
targetTsConfig: targetPaths.resolveTargetRoot('tsconfig.json'),
targetPackageJson: resolvePath(targetDir, 'package.json'),
rootNodeModules: paths.resolveTargetRoot('node_modules'),
root: paths.targetRoot,
rootNodeModules: targetPaths.resolveTargetRoot('node_modules'),
root: targetPaths.targetRoot,
};
}
@@ -22,10 +22,8 @@ import openBrowser from 'react-dev-utils/openBrowser';
import { rspack } from '@rspack/core';
import { RspackDevServer } from '@rspack/dev-server';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const libPaths = findPaths(__dirname);
import { loadCliConfig } from '../../../config/lib/config';
import { createConfig, resolveBaseUrl, resolveEndpoint } from './config';
import { createDetectedModulesEntryPoint } from './packageDetection';
@@ -56,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 ?? libPaths.targetDir, 'package.json'),
resolvePath(options.targetDir ?? targetPaths.targetDir, 'package.json'),
);
let devServer: RspackDevServer | undefined = undefined;
@@ -274,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: [libPaths.targetRoot],
paths: [targetPaths.targetRoot],
});
const reactPkg = require(reactPkgPath);
if (reactPkg.version.startsWith('16.')) {
@@ -25,10 +25,8 @@ import { tmpdir } from 'node:os';
import * as tar from 'tar';
import partition from 'lodash/partition';
import { run, findPaths } from '@backstage/cli-common';
import { run, targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
import {
dependencies as cliDependencies,
devDependencies as cliDevDependencies,
@@ -213,7 +211,7 @@ export async function createDistWorkspace(
targetDir: pkg.dir,
packageJson: pkg.packageJson,
outputs: outputs,
logPrefix: `${chalk.cyan(relativePath(paths.targetRoot, pkg.dir))}: `,
logPrefix: `${chalk.cyan(relativePath(targetPaths.targetRoot, pkg.dir))}: `,
minify: options.minify,
workspacePackages: packages,
});
@@ -248,13 +246,13 @@ export async function createDistWorkspace(
for (const file of files) {
const src = typeof file === 'string' ? file : file.src;
const dest = typeof file === 'string' ? file : file.dest;
await fs.copy(paths.resolveTargetRoot(src), resolvePath(targetDir, dest));
await fs.copy(targetPaths.resolveTargetRoot(src), resolvePath(targetDir, dest));
}
if (options.skeleton) {
const skeletonFiles = targets
.map(target => {
const dir = relativePath(paths.targetRoot, target.dir);
const dir = relativePath(targetPaths.targetRoot, target.dir);
return joinPath(dir, 'package.json');
})
.sort();
@@ -303,7 +301,7 @@ async function moveToDistWorkspace(
fastPackPackages.map(async target => {
console.log(`Moving ${target.name} into dist workspace`);
const outputDir = relativePath(paths.targetRoot, target.dir);
const outputDir = relativePath(targetPaths.targetRoot, target.dir);
const absoluteOutputPath = resolvePath(workspaceDir, outputDir);
await productionPack({
packageDir: target.dir,
@@ -323,7 +321,7 @@ async function moveToDistWorkspace(
cwd: target.dir,
}).waitForExit();
const outputDir = relativePath(paths.targetRoot, target.dir);
const outputDir = relativePath(targetPaths.targetRoot, target.dir);
const absoluteOutputPath = resolvePath(workspaceDir, outputDir);
await fs.ensureDir(absoluteOutputPath);
@@ -22,11 +22,11 @@ const mockDir = createMockDirectory();
jest.mock('@backstage/cli-common', () => ({
...jest.requireActual('@backstage/cli-common'),
findPaths: () => ({
targetPaths: {
resolveTarget(filename: string) {
return mockDir.resolve(filename);
},
}),
},
}));
describe('findRoleFromCommand', () => {
+2 -4
View File
@@ -16,10 +16,8 @@
import fs from 'fs-extra';
import { OptionValues } from 'commander';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
import { PackageRoles, PackageRole } from '@backstage/cli-node';
export async function findRoleFromCommand(
@@ -29,7 +27,7 @@ export async function findRoleFromCommand(
return PackageRoles.getRoleInfo(opts.role)?.role;
}
const pkg = await fs.readJson(paths.resolveTarget('package.json'));
const pkg = await fs.readJson(targetPaths.resolveTarget('package.json'));
const info = PackageRoles.getRoleFromPackage(pkg);
if (!info) {
throw new Error(`Target package must have 'backstage.role' set`);
@@ -21,10 +21,8 @@ import { IpcServer, ServerDataStore } from '../ipc';
import debounce from 'lodash/debounce';
import { fileURLToPath } from 'node:url';
import { isAbsolute as isAbsolutePath } from 'node:path';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
import spawn from 'cross-spawn';
const loaderArgs = [
@@ -138,7 +136,7 @@ export async function runBackend(options: RunBackendOptions) {
...process.env,
BACKSTAGE_CLI_LINKED_WORKSPACE: options.linkedWorkspace,
BACKSTAGE_CLI_CHANNEL: '1',
ESBK_TSCONFIG_PATH: paths.resolveTargetRoot('tsconfig.json'),
ESBK_TSCONFIG_PATH: targetPaths.resolveTargetRoot('tsconfig.json'),
},
serialization: 'advanced',
},
@@ -16,10 +16,8 @@
import { ConfigSources, loadConfigSchema } from '@backstage/config-loader';
import { AppConfig, ConfigReader } from '@backstage/config';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
import { getPackages } from '@manypkg/get-packages';
import { PackageGraph } from '@backstage/cli-node';
import { resolve as resolvePath } from 'node:path';
@@ -37,7 +35,7 @@ type Options = {
};
export async function loadCliConfig(options: Options) {
const targetDir = options.targetDir ?? paths.targetDir;
const targetDir = options.targetDir ?? targetPaths.targetDir;
// Consider all packages in the monorepo when loading in config
const { packages } = await getPackages(targetDir);
@@ -66,7 +64,7 @@ export async function loadCliConfig(options: Options) {
const schema = await loadConfigSchema({
dependencies: localPackageNames,
// Include the package.json in the project root if it exists
packagePaths: [paths.resolveTargetRoot('package.json')],
packagePaths: [targetPaths.resolveTargetRoot('package.json')],
noUndeclaredProperties: options.strict,
});
@@ -76,7 +74,7 @@ export async function loadCliConfig(options: Options) {
? async name => process.env[name] || 'x'
: undefined,
watch: Boolean(options.watch),
rootDir: paths.targetRoot,
rootDir: targetPaths.targetRoot,
argv: options.args.flatMap(t => ['--config', resolvePath(targetDir, t)]),
});
@@ -18,10 +18,8 @@ import fs from 'fs-extra';
import chalk from 'chalk';
import { stringify as stringifyYaml } from 'yaml';
import inquirer, { Question, Answers } from 'inquirer';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
import { GithubCreateAppServer } from './GithubCreateAppServer';
import openBrowser from 'react-dev-utils/openBrowser';
@@ -65,7 +63,7 @@ export default async (org: string) => {
const fileName = `github-app-${slug}-credentials.yaml`;
const content = `# Name: ${name}\n${stringifyYaml(config)}`;
await fs.writeFile(paths.resolveTargetRoot(fileName), content);
await fs.writeFile(targetPaths.resolveTargetRoot(fileName), content);
console.log(`GitHub App configuration written to ${chalk.cyan(fileName)}`);
console.log(
chalk.yellow(
@@ -16,10 +16,9 @@
import { version as cliVersion } from '../../../../package.json';
import os from 'node:os';
import { runOutput, findPaths } from '@backstage/cli-common';
import { runOutput, targetPaths, findOwnPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
const ownPaths = findOwnPaths(__dirname);
import { Lockfile } from '../../../lib/versioning';
import { BackstagePackageJson, PackageGraph } from '@backstage/cli-node';
@@ -59,9 +58,9 @@ function hasBackstageField(packageName: string, targetPath: string): boolean {
export default async (options: InfoOptions) => {
await new Promise(async () => {
const yarnVersion = await runOutput(['yarn', '--version']);
const isLocal = fs.existsSync(paths.resolveOwn('./src'));
const isLocal = fs.existsSync(ownPaths.resolveOwn('./src'));
const backstageFile = paths.resolveTargetRoot('backstage.json');
const backstageFile = targetPaths.resolveTargetRoot('backstage.json');
let backstageVersion = 'N/A';
if (fs.existsSync(backstageFile)) {
try {
@@ -86,9 +85,9 @@ export default async (options: InfoOptions) => {
backstage: backstageVersion,
};
const lockfilePath = paths.resolveTargetRoot('yarn.lock');
const lockfilePath = targetPaths.resolveTargetRoot('yarn.lock');
const lockfile = await Lockfile.load(lockfilePath);
const targetPath = paths.targetRoot;
const targetPath = targetPaths.targetRoot;
// Get workspace package names and their versions
const workspacePackages = new Map<string, string>();
@@ -16,15 +16,13 @@
import fs from 'fs-extra';
import { OptionValues } from 'commander';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
import { ESLint } from 'eslint';
export default async (directories: string[], opts: OptionValues) => {
const eslint = new ESLint({
cwd: paths.targetDir,
cwd: targetPaths.targetDir,
fix: opts.fix,
extensions: ['js', 'jsx', 'ts', 'tsx', 'mjs', 'cjs'],
});
@@ -50,14 +48,14 @@ 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(paths.targetRoot);
process.chdir(targetPaths.targetRoot);
}
const resultText = await formatter.format(results);
if (resultText) {
if (opts.outputFile) {
await fs.writeFile(paths.resolveTarget(opts.outputFile), resultText);
await fs.writeFile(targetPaths.resolveTarget(opts.outputFile), resultText);
} else {
console.log(resultText);
}
@@ -25,10 +25,8 @@ import {
Lockfile,
runWorkerQueueThreads,
} from '@backstage/cli-node';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
import { createScriptOptionsParser } from '../../../../lib/optionsParser';
import { SuccessCache } from '../../../../lib/cache/SuccessCache';
@@ -47,7 +45,7 @@ export async function command(opts: OptionValues, cmd: Command): Promise<void> {
const cacheContext = opts.successCache
? {
entries: await cache.read(),
lockfile: await Lockfile.load(paths.resolveTargetRoot('yarn.lock')),
lockfile: await Lockfile.load(targetPaths.resolveTargetRoot('yarn.lock')),
}
: undefined;
@@ -65,7 +63,7 @@ export async function command(opts: OptionValues, cmd: Command): Promise<void> {
// 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(paths.targetRoot);
process.chdir(targetPaths.targetRoot);
}
// Make sure lint output is colored unless the user explicitly disabled it
@@ -80,7 +78,7 @@ export async function command(opts: OptionValues, cmd: Command): Promise<void> {
const lintOptions = parseLintScript(pkg.packageJson.scripts?.lint);
const base = {
fullDir: pkg.dir,
relativeDir: relativePath(paths.targetRoot, pkg.dir),
relativeDir: relativePath(targetPaths.targetRoot, pkg.dir),
lintOptions,
parentHash: undefined,
};
@@ -116,7 +114,7 @@ export async function command(opts: OptionValues, cmd: Command): Promise<void> {
shouldCache: Boolean(cacheContext),
maxWarnings: opts.maxWarnings ?? -1,
successCache: cacheContext?.entries,
rootDir: paths.targetRoot,
rootDir: targetPaths.targetRoot,
},
workerFactory: async ({
fix,
@@ -266,7 +264,7 @@ export async function command(opts: OptionValues, cmd: Command): Promise<void> {
}
if (opts.outputFile && errorOutput) {
await fs.writeFile(paths.resolveTargetRoot(opts.outputFile), errorOutput);
await fs.writeFile(targetPaths.resolveTargetRoot(opts.outputFile), errorOutput);
}
if (cacheContext) {
@@ -15,13 +15,11 @@
*/
import fs from 'fs-extra';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
export default async function clean() {
await fs.remove(paths.resolveTarget('dist'));
await fs.remove(paths.resolveTarget('dist-types'));
await fs.remove(paths.resolveTarget('coverage'));
await fs.remove(targetPaths.resolveTarget('dist'));
await fs.remove(targetPaths.resolveTarget('dist-types'));
await fs.remove(targetPaths.resolveTarget('coverage'));
}
@@ -18,26 +18,24 @@ import {
productionPack,
revertProductionPack,
} from '../../../../modules/build/lib/packager/productionPack';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
import fs from 'fs-extra';
import { publishPreflightCheck } from '../../lib/publishing';
import { createTypeDistProject } from '../../../../lib/typeDistProject';
export const pre = async () => {
publishPreflightCheck({
dir: paths.targetDir,
packageJson: await fs.readJson(paths.resolveTarget('package.json')),
dir: targetPaths.targetDir,
packageJson: await fs.readJson(targetPaths.resolveTarget('package.json')),
});
await productionPack({
packageDir: paths.targetDir,
packageDir: targetPaths.targetDir,
featureDetectionProject: await createTypeDistProject(),
});
};
export const post = async () => {
await revertProductionPack(paths.targetDir);
await revertProductionPack(targetPaths.targetDir);
};
@@ -18,17 +18,15 @@ import fs from 'fs-extra';
import { resolve as resolvePath } from 'node:path';
import { PackageGraph } from '@backstage/cli-node';
import { run, findPaths } from '@backstage/cli-common';
import { run, targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
export async function command(): Promise<void> {
const packages = await PackageGraph.listTargetPackages();
await fs.remove(paths.resolveTargetRoot('dist'));
await fs.remove(paths.resolveTargetRoot('dist-types'));
await fs.remove(paths.resolveTargetRoot('coverage'));
await fs.remove(targetPaths.resolveTargetRoot('dist'));
await fs.remove(targetPaths.resolveTargetRoot('dist-types'));
await fs.remove(targetPaths.resolveTargetRoot('coverage'));
await Promise.all(
Array.from(Array(10), async () => {
@@ -29,10 +29,8 @@ import {
relative as relativePath,
extname,
} from 'node:path';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
import { publishPreflightCheck } from '../../lib/publishing';
const SCRIPT_EXTS = ['.js', '.jsx', '.ts', '.tsx', '.json'];
@@ -52,7 +50,7 @@ export async function readFixablePackages(): Promise<FixablePackage[]> {
export function printPackageFixHint(packages: FixablePackage[]) {
const changed = packages.filter(pkg => pkg.changed);
if (changed.length > 0) {
const rootPkg = require(paths.resolveTargetRoot('package.json'));
const rootPkg = require(targetPaths.resolveTargetRoot('package.json'));
const fixCmd =
rootPkg.scripts?.fix === 'backstage-cli repo fix'
? 'fix'
@@ -219,7 +217,7 @@ export function fixSideEffects(pkg: FixablePackage) {
}
export function createRepositoryFieldFixer() {
const rootPkg = require(paths.resolveTargetRoot('package.json'));
const rootPkg = require(targetPaths.resolveTargetRoot('package.json'));
const rootRepoField = rootPkg.repository;
if (!rootRepoField) {
return () => {};
@@ -232,7 +230,7 @@ export function createRepositoryFieldFixer() {
return (pkg: FixablePackage) => {
const expectedPath = posix.join(
rootDir,
relativePath(paths.targetRoot, pkg.dir),
relativePath(targetPaths.targetRoot, pkg.dir),
);
const repoField = pkg.packageJson.repository;
@@ -321,7 +319,7 @@ export function fixPluginId(pkg: FixablePackage) {
role === 'backend-plugin-module')
) {
const path = relativePath(
paths.targetRoot,
targetPaths.targetRoot,
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}"`;
@@ -417,7 +415,7 @@ export function fixPluginPackages(
return;
}
const path = relativePath(
paths.targetRoot,
targetPaths.targetRoot,
resolvePath(pkg.dir, 'package.json'),
);
const suggestedRole =
@@ -466,7 +464,7 @@ export function fixPeerModules(pkg: FixablePackage) {
}
const packagePath = relativePath(
paths.targetRoot,
targetPaths.targetRoot,
resolvePath(pkg.dir, 'package.json'),
);
@@ -19,23 +19,21 @@ import { ESLint } from 'eslint';
import { OptionValues } from 'commander';
import { relative as relativePath } from 'node:path';
import { PackageGraph } from '@backstage/cli-node';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
export async function command(opts: OptionValues) {
const packages = await PackageGraph.listTargetPackages();
const eslint = new ESLint({
cwd: paths.targetDir,
cwd: targetPaths.targetDir,
overrideConfig: {
plugins: ['deprecation'],
rules: {
'deprecation/deprecation': 'error',
},
parserOptions: {
project: [paths.resolveTargetRoot('tsconfig.json')],
project: [targetPaths.resolveTargetRoot('tsconfig.json')],
},
},
extensions: ['jsx', 'ts', 'tsx', 'mjs', 'cjs'],
@@ -55,7 +53,7 @@ export async function command(opts: OptionValues) {
continue;
}
const path = relativePath(paths.targetRoot, result.filePath);
const path = relativePath(targetPaths.targetRoot, result.filePath);
deprecations.push({
path,
message: message.message,
@@ -18,13 +18,11 @@ import fs from 'fs-extra';
import { resolve as resolvePath } from 'node:path';
import { getPackages } from '@manypkg/get-packages';
import { PackageRoles } from '@backstage/cli-node';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
export default async () => {
const { packages } = await getPackages(paths.targetDir);
const { packages } = await getPackages(targetPaths.targetDir);
await Promise.all(
packages.map(async ({ dir, packageJson: pkg }) => {
@@ -64,10 +64,14 @@ jest.mock('@backstage/cli-common', () => {
const actual = jest.requireActual('@backstage/cli-common');
return {
...actual,
findPaths: () => ({
resolveTargetRoot(filename: string) {
return mockDir.resolve(filename);
targetPaths: {
resolveTargetRoot: (filename: string) => mockDir.resolve(filename),
get targetDir() {
return mockDir.path;
},
},
findPaths: () => ({
resolveTargetRoot: (filename: string) => mockDir.resolve(filename),
get targetDir() {
return mockDir.path;
},
@@ -13,10 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BACKSTAGE_JSON, bootstrapEnvProxyAgents, findPaths } from '@backstage/cli-common';
import { BACKSTAGE_JSON, bootstrapEnvProxyAgents, targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
bootstrapEnvProxyAgents();
@@ -71,7 +69,7 @@ function extendsDefaultPattern(pattern: string): boolean {
}
export default async (opts: OptionValues) => {
const lockfilePath = paths.resolveTargetRoot('yarn.lock');
const lockfilePath = targetPaths.resolveTargetRoot('yarn.lock');
const lockfile = await Lockfile.load(lockfilePath);
const hasYarnPlugin = await getHasYarnPlugin();
@@ -143,7 +141,7 @@ export default async (opts: OptionValues) => {
}
// First we discover all Backstage dependencies within our own repo
const dependencyMap = await mapDependencies(paths.targetDir, pattern);
const dependencyMap = await mapDependencies(targetPaths.targetDir, pattern);
// Next check with the package registry to see which dependency ranges we need to bump
const versionBumps = new Map<string, PkgVersionInfo[]>();
@@ -420,7 +418,7 @@ export function createVersionFinder(options: {
}
function getBackstageJsonPath() {
return paths.resolveTargetRoot(BACKSTAGE_JSON);
return targetPaths.resolveTargetRoot(BACKSTAGE_JSON);
}
async function getBackstageJson() {
@@ -37,10 +37,14 @@ jest.mock('@backstage/cli-common', () => {
const actual = jest.requireActual('@backstage/cli-common');
return {
...actual,
findPaths: () => ({
resolveTargetRoot(filename: string) {
return mockDir.resolve(filename);
targetPaths: {
resolveTargetRoot: (filename: string) => mockDir.resolve(filename),
get targetDir() {
return mockDir.path;
},
},
findPaths: () => ({
resolveTargetRoot: (filename: string) => mockDir.resolve(filename),
get targetDir() {
return mockDir.path;
},
@@ -16,10 +16,8 @@
import fs from 'fs-extra';
import path from 'node:path';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
const TEAM_ID_RE = /^@[-\w]+\/[-\w]+$/;
const USER_ID_RE = /^@[-\w]+$/;
@@ -85,7 +83,7 @@ export async function addCodeownersEntry(
let filePath = codeownersFilePath;
if (!filePath) {
filePath = await getCodeownersFilePath(paths.targetRoot);
filePath = await getCodeownersFilePath(targetPaths.targetRoot);
if (!filePath) {
return false;
}
@@ -25,10 +25,8 @@ import upperCase from 'lodash/upperCase';
import upperFirst from 'lodash/upperFirst';
import lowerFirst from 'lodash/lowerFirst';
import { Lockfile } from '../../../../lib/versioning';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
import { createPackageVersionProvider } from '../../../../lib/version';
import { getHasYarnPlugin } from '../../../../lib/yarnPlugin';
@@ -52,7 +50,7 @@ export class PortableTemplater {
static async create(options: CreatePortableTemplaterOptions = {}) {
let lockfile: Lockfile | undefined;
try {
lockfile = await Lockfile.load(paths.resolveTargetRoot('yarn.lock'));
lockfile = await Lockfile.load(targetPaths.resolveTargetRoot('yarn.lock'));
} catch {
/* ignored */
}
@@ -16,10 +16,8 @@
import fs from 'fs-extra';
import upperFirst from 'lodash/upperFirst';
import camelCase from 'lodash/camelCase';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
import { Task } from '../tasks';
import { PortableTemplateInput } from '../types';
@@ -55,7 +53,7 @@ export async function installNewPackage(input: PortableTemplateInput) {
}
async function addDependency(input: PortableTemplateInput, path: string) {
const pkgJsonPath = paths.resolveTargetRoot(path);
const pkgJsonPath = targetPaths.resolveTargetRoot(path);
const pkgJson = await fs.readJson(pkgJsonPath).catch(error => {
if (error.code === 'ENOENT') {
@@ -87,7 +85,7 @@ async function tryAddFrontendLegacy(input: PortableTemplateInput) {
);
}
const appDefinitionPath = paths.resolveTargetRoot('packages/app/src/App.tsx');
const appDefinitionPath = targetPaths.resolveTargetRoot('packages/app/src/App.tsx');
if (!(await fs.pathExists(appDefinitionPath))) {
return;
}
@@ -123,7 +121,7 @@ async function tryAddFrontendLegacy(input: PortableTemplateInput) {
}
async function tryAddBackend(input: PortableTemplateInput) {
const backendIndexPath = paths.resolveTargetRoot(
const backendIndexPath = targetPaths.resolveTargetRoot(
'packages/backend/src/index.ts',
);
if (!(await fs.pathExists(backendIndexPath))) {
@@ -17,10 +17,8 @@
import { relative as relativePath } from 'node:path';
import { writeTemplateContents } from './writeTemplateContents';
import { createMockDirectory } from '@backstage/backend-test-utils';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
const baseConfig = {
version: '0.1.0',
@@ -35,7 +33,7 @@ describe('writeTemplateContents', () => {
mockDir.clear();
jest.resetAllMocks();
jest
.spyOn(paths, 'resolveTargetRoot')
.spyOn(targetPaths, 'resolveTargetRoot')
.mockImplementation((...args) => mockDir.resolve(...args));
});
@@ -22,16 +22,14 @@ import { PortableTemplate, PortableTemplateInput } from '../types';
import { ForwardedError, InputError } from '@backstage/errors';
import { isMonoRepo as getIsMonoRepo } from '@backstage/cli-node';
import { PortableTemplater } from './PortableTemplater';
import { isChildPath, findPaths } from '@backstage/cli-common';
import { isChildPath, targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
export async function writeTemplateContents(
template: PortableTemplate,
input: PortableTemplateInput,
): Promise<{ targetDir: string }> {
const targetDir = paths.resolveTargetRoot(input.packagePath);
const targetDir = targetPaths.resolveTargetRoot(input.packagePath);
if (await fs.pathExists(targetDir)) {
throw new InputError(`Package '${input.packagePath}' already exists`);
@@ -16,10 +16,8 @@
import inquirer, { DistinctQuestion } from 'inquirer';
import { getCodeownersFilePath, parseOwnerIds } from '../codeowners';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
import {
PortableTemplateConfig,
PortableTemplateInput,
@@ -41,7 +39,7 @@ export async function collectPortableTemplateInput(
): Promise<PortableTemplateInput> {
const { config, template, prefilledParams } = options;
const codeOwnersFilePath = await getCodeownersFilePath(paths.targetRoot);
const codeOwnersFilePath = await getCodeownersFilePath(targetPaths.targetRoot);
const prompts = getPromptsForRole(template.role);
@@ -20,10 +20,8 @@ import recursiveReaddir from 'recursive-readdir';
import { resolve as resolvePath, relative as relativePath } from 'node:path';
import { dirname } from 'node:path';
import { parse as parseYaml } from 'yaml';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
import {
PortableTemplateFile,
PortableTemplatePointer,
@@ -49,7 +47,7 @@ export async function loadPortableTemplate(
throw new Error('Remote templates are not supported yet');
}
const templateContent = await fs
.readFile(paths.resolveTargetRoot(pointer.target), 'utf-8')
.readFile(targetPaths.resolveTargetRoot(pointer.target), 'utf-8')
.catch(error => {
throw new ForwardedError(
`Failed to load template definition from '${pointer.target}'`,
@@ -16,10 +16,8 @@
import fs from 'fs-extra';
import { resolve as resolvePath, dirname, isAbsolute } from 'node:path';
import { findPaths } from '@backstage/cli-common';
import { targetPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
import { defaultTemplates } from '../defaultTemplates';
import {
PortableTemplateConfig,
@@ -93,7 +91,7 @@ export async function loadPortableTemplateConfig(
): Promise<PortableTemplateConfig> {
const { overrides = {} } = options;
const pkgPath =
options.packagePath ?? paths.resolveTargetRoot('package.json');
options.packagePath ?? targetPaths.resolveTargetRoot('package.json');
const pkgJson = await fs.readJson(pkgPath);
const parsed = pkgJsonWithNewConfigSchema.safeParse(pkgJson);
@@ -16,10 +16,9 @@
import { Command, OptionValues } from 'commander';
import { runCheck, findPaths } from '@backstage/cli-common';
import { runCheck, findOwnPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
const ownPaths = findOwnPaths(__dirname);
function includesAnyOf(hayStack: string[], ...needles: string[]) {
for (const needle of needles) {
@@ -41,7 +40,7 @@ export default async (_opts: OptionValues, cmd: Command) => {
// Only include our config if caller isn't passing their own config
if (!includesAnyOf(args, '-c', '--config')) {
args.push('--config', paths.resolveOwn('config/jest.js'));
args.push('--config', ownPaths.resolveOwn('config/jest.js'));
}
if (!includesAnyOf(args, '--no-passWithNoTests', '--passWithNoTests=false')) {
@@ -24,10 +24,9 @@ import { relative as relativePath } from 'node:path';
import { Command, OptionValues } from 'commander';
import { Lockfile, PackageGraph } from '@backstage/cli-node';
import { runCheck, runOutput, findPaths } from '@backstage/cli-common';
import { runCheck, runOutput, targetPaths, findOwnPaths } from '@backstage/cli-common';
/* eslint-disable-next-line no-restricted-syntax */
const paths = findPaths(__dirname);
const ownPaths = findOwnPaths(__dirname);
import { isChildPath } from '@backstage/cli-common';
import { SuccessCache } from '../../../../lib/cache/SuccessCache';
@@ -66,7 +65,7 @@ interface TestGlobal extends Global {
async function readPackageTreeHashes(graph: PackageGraph) {
const pkgs = Array.from(graph.values()).map(pkg => ({
...pkg,
path: relativePath(paths.targetRoot, pkg.dir),
path: relativePath(targetPaths.targetRoot, pkg.dir),
}));
const output = await runOutput([
'git',
@@ -165,7 +164,7 @@ export async function command(opts: OptionValues, cmd: Command): Promise<void> {
// Only include our config if caller isn't passing their own config
if (!hasFlags('-c', '--config')) {
args.push('--config', paths.resolveOwn('config/jest.js'));
args.push('--config', ownPaths.resolveOwn('config/jest.js'));
}
if (!hasFlags('--passWithNoTests')) {
@@ -344,7 +343,7 @@ export async function command(opts: OptionValues, cmd: Command): Promise<void> {
async filterConfigs(projectConfigs, globalRootConfig) {
const cacheEntries = await cache.read();
const lockfile = await Lockfile.load(
paths.resolveTargetRoot('yarn.lock'),
targetPaths.resolveTargetRoot('yarn.lock'),
);
const getPackageTreeHash = await readPackageTreeHashes(graph);