Replace findPaths with targetPaths and findOwnPaths
Split the path resolution API in @backstage/cli-common into targetPaths (cwd-based singleton) and findOwnPaths (package-relative). Migrate all consumers across the repo away from the deprecated findPaths. Rename TargetPaths/OwnPaths properties to resolve/resolveRoot, removing the redundant type prefix from property names. Make findOwnPaths calls lazy in modules - called inside functions rather than at module scope. Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
---
|
||||
'@backstage/cli-common': patch
|
||||
'@backstage/cli-common': minor
|
||||
---
|
||||
|
||||
Added hierarchical caching to `findOwnDir`, avoiding redundant filesystem walks when `findPaths` is called from multiple locations within the same package.
|
||||
Added `targetPaths` and `findOwnPaths` as replacements for `findPaths`, with a cleaner separation between target project paths and package-relative paths.
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
'@backstage/cli-node': patch
|
||||
'@backstage/backend-dynamic-feature-service': patch
|
||||
'@backstage/codemods': patch
|
||||
'@backstage/config-loader': patch
|
||||
'@backstage/create-app': patch
|
||||
'@backstage/repo-tools': patch
|
||||
'@backstage/techdocs-cli': patch
|
||||
---
|
||||
|
||||
Migrated from deprecated `findPaths` to `targetPaths` and `findOwnPaths` from `@backstage/cli-common`.
|
||||
@@ -38,8 +38,33 @@ import { createSpecializedBackend } from '@backstage/backend-app-api';
|
||||
import { ConfigSources } from '@backstage/config-loader';
|
||||
import { Logs, MockedLogger, LogContent } from '../__testUtils__/testUtils';
|
||||
import { PluginScanner } from '../scanner/plugin-scanner';
|
||||
import { findPaths } from '@backstage/cli-common';
|
||||
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<typeof import('@backstage/cli-common')>(
|
||||
'@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';
|
||||
|
||||
@@ -956,7 +981,7 @@ describe('backend-dynamic-feature-service', () => {
|
||||
|
||||
mockDir.setContent({
|
||||
'package.json': fs.readFileSync(
|
||||
findPaths(__dirname).resolveTargetRoot('package.json'),
|
||||
targetPaths.resolveRoot('package.json'),
|
||||
),
|
||||
'dynamic-plugins-root': {},
|
||||
'dynamic-plugins-root/a-dynamic-plugin': ctx =>
|
||||
@@ -1044,7 +1069,7 @@ describe('backend-dynamic-feature-service', () => {
|
||||
otherMockDir.resolve('a-dynamic-plugin'),
|
||||
);
|
||||
expect(mockedModuleLoader.bootstrap).toHaveBeenCalledWith(
|
||||
findPaths(__dirname).targetRoot,
|
||||
targetPaths.resolveRoot(),
|
||||
[realPath],
|
||||
new Map<string, ScannedPluginManifest>([
|
||||
[
|
||||
|
||||
@@ -35,7 +35,7 @@ import {
|
||||
createServiceRef,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { PackageRole, PackageRoles } from '@backstage/cli-node';
|
||||
import { findPaths } from '@backstage/cli-common';
|
||||
import { targetPaths } from '@backstage/cli-common';
|
||||
import * as fs from 'node:fs';
|
||||
|
||||
/**
|
||||
@@ -56,7 +56,7 @@ export class DynamicPluginManager implements DynamicPluginProvider {
|
||||
options: DynamicPluginManagerOptions,
|
||||
): Promise<DynamicPluginManager> {
|
||||
/* eslint-disable-next-line no-restricted-syntax */
|
||||
const backstageRoot = findPaths(__dirname).targetRoot;
|
||||
const backstageRoot = targetPaths.resolveRoot();
|
||||
const scanner = PluginScanner.create({
|
||||
config: options.config,
|
||||
logger: options.logger,
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
createServiceFactory,
|
||||
createServiceRef,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { findPaths } from '@backstage/cli-common';
|
||||
import { targetPaths } from '@backstage/cli-common';
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import * as path from 'node:path';
|
||||
@@ -100,7 +100,7 @@ const dynamicPluginsSchemasServiceFactoryWithOptions = (
|
||||
config,
|
||||
logger,
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
backstageRoot: findPaths(__dirname).targetRoot,
|
||||
backstageRoot: targetPaths.resolveRoot(),
|
||||
preferAlpha: true,
|
||||
});
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ const searchLoader = createBackendFeatureLoader({
|
||||
*loader({ config }) {
|
||||
yield import('@backstage/plugin-search-backend');
|
||||
yield import('@backstage/plugin-search-backend-module-catalog');
|
||||
yield import('@backstage-community/plugin-search-backend-module-explore');
|
||||
yield import('@backstage/plugin-search-backend-module-explore');
|
||||
yield import('@backstage/plugin-search-backend-module-techdocs');
|
||||
if (config.has('search.elasticsearch')) {
|
||||
yield import('@backstage/plugin-search-backend-module-elasticsearch');
|
||||
|
||||
@@ -26,45 +26,31 @@ import { dirname, resolve as resolvePath } from 'node:path';
|
||||
export type ResolveFunc = (...paths: string[]) => string;
|
||||
|
||||
/**
|
||||
* Paths relative to the target project that the CLI is operating on, based on
|
||||
* `process.cwd()`. This can be imported directly — no `__dirname` needed.
|
||||
*
|
||||
* Lazily initialized on first access and re-resolved if `process.cwd()` changes.
|
||||
* Resolved paths relative to the target project, based on `process.cwd()`.
|
||||
* Lazily initialized on first property access. Re-resolves automatically
|
||||
* when `process.cwd()` changes.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type TargetPaths = {
|
||||
// The location of the app that the cli is being executed in
|
||||
targetDir: string;
|
||||
/** Resolve a path relative to the target package directory. */
|
||||
resolve: ResolveFunc;
|
||||
|
||||
// The monorepo root package of the app that the cli is being executed in.
|
||||
targetRoot: string;
|
||||
|
||||
// Resolve a path relative to the app
|
||||
resolveTarget: ResolveFunc;
|
||||
|
||||
// Resolve a path relative to the app repo root
|
||||
resolveTargetRoot: ResolveFunc;
|
||||
/** Resolve a path relative to the target repo root. */
|
||||
resolveRoot: ResolveFunc;
|
||||
};
|
||||
|
||||
/**
|
||||
* Paths relative to the package that the calling code lives in. Requires
|
||||
* `__dirname` to locate the package root.
|
||||
* Resolved paths relative to a specific package in the repository.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type OwnPaths = {
|
||||
// Root dir of the package itself, containing package.json
|
||||
ownDir: string;
|
||||
/** Resolve a path relative to the package root. */
|
||||
resolve: ResolveFunc;
|
||||
|
||||
// Monorepo root dir of the package. Only accessible when running inside Backstage repo.
|
||||
ownRoot: string;
|
||||
|
||||
// Resolve a path relative to own package
|
||||
resolveOwn: ResolveFunc;
|
||||
|
||||
// Resolve a path relative to own monorepo root. Only accessible when running inside Backstage repo.
|
||||
resolveOwnRoot: ResolveFunc;
|
||||
/** Resolve a path relative to the monorepo root containing the package. */
|
||||
resolveRoot: ResolveFunc;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -211,41 +197,34 @@ function getTargetRoot(): string {
|
||||
* Lazily resolved paths relative to the target project. Import this directly
|
||||
* for cwd-based path resolution without needing `__dirname`.
|
||||
*
|
||||
* Re-resolves automatically if `process.cwd()` changes.
|
||||
*
|
||||
* @public
|
||||
* @example
|
||||
*
|
||||
* import { targetPaths } from '@backstage/cli-common';
|
||||
* import { targetPaths } from '\@backstage/cli-common';
|
||||
*
|
||||
* const root = targetPaths.targetRoot;
|
||||
* const lockfile = targetPaths.resolveTargetRoot('yarn.lock');
|
||||
* const lockfile = targetPaths.resolveRoot('yarn.lock');
|
||||
*/
|
||||
export const targetPaths: TargetPaths = {
|
||||
get targetDir() {
|
||||
return getTargetDir();
|
||||
},
|
||||
get targetRoot() {
|
||||
return getTargetRoot();
|
||||
},
|
||||
resolveTarget: (...paths) => resolvePath(getTargetDir(), ...paths),
|
||||
resolveTargetRoot: (...paths) => resolvePath(getTargetRoot(), ...paths),
|
||||
resolve: (...paths) => resolvePath(getTargetDir(), ...paths),
|
||||
resolveRoot: (...paths) => resolvePath(getTargetRoot(), ...paths),
|
||||
};
|
||||
|
||||
const ownPathsCache = new Map<string, OwnPaths>();
|
||||
|
||||
/**
|
||||
* Find paths relative to the package that the calling code lives in. Cheap to
|
||||
* call repeatedly — results are cached per package root, and the package root
|
||||
* lookup uses a hierarchical cache so sibling directories share work.
|
||||
* Find paths relative to the package that the calling code lives in.
|
||||
*
|
||||
* Results are cached per package root, and the package root lookup uses a
|
||||
* hierarchical directory cache so that multiple calls from different
|
||||
* subdirectories within the same package share work.
|
||||
*
|
||||
* @public
|
||||
* @example
|
||||
*
|
||||
* import { findOwnPaths } from '@backstage/cli-common';
|
||||
* import { findOwnPaths } from '\@backstage/cli-common';
|
||||
*
|
||||
* const ownPaths = findOwnPaths(__dirname);
|
||||
* const config = ownPaths.resolveOwn('config/jest.js');
|
||||
* const own = findOwnPaths(__dirname);
|
||||
* const config = own.resolve('config/jest.js');
|
||||
*/
|
||||
export function findOwnPaths(searchDir: string): OwnPaths {
|
||||
const ownDir = findOwnDir(searchDir);
|
||||
@@ -263,12 +242,8 @@ export function findOwnPaths(searchDir: string): OwnPaths {
|
||||
};
|
||||
|
||||
const paths: OwnPaths = {
|
||||
ownDir,
|
||||
get ownRoot() {
|
||||
return getOwnRoot();
|
||||
},
|
||||
resolveOwn: (...p) => resolvePath(ownDir, ...p),
|
||||
resolveOwnRoot: (...p) => resolvePath(getOwnRoot(), ...p),
|
||||
resolve: (...p) => resolvePath(ownDir, ...p),
|
||||
resolveRoot: (...p) => resolvePath(getOwnRoot(), ...p),
|
||||
};
|
||||
|
||||
ownPathsCache.set(ownDir, paths);
|
||||
@@ -290,21 +265,21 @@ export function findPaths(searchDir: string): Paths {
|
||||
const own = findOwnPaths(searchDir);
|
||||
return {
|
||||
get ownDir() {
|
||||
return own.ownDir;
|
||||
return own.resolve();
|
||||
},
|
||||
get ownRoot() {
|
||||
return own.ownRoot;
|
||||
return own.resolveRoot();
|
||||
},
|
||||
get targetDir() {
|
||||
return targetPaths.targetDir;
|
||||
return targetPaths.resolve();
|
||||
},
|
||||
get targetRoot() {
|
||||
return targetPaths.targetRoot;
|
||||
return targetPaths.resolveRoot();
|
||||
},
|
||||
resolveOwn: own.resolveOwn,
|
||||
resolveOwnRoot: own.resolveOwnRoot,
|
||||
resolveTarget: targetPaths.resolveTarget,
|
||||
resolveTargetRoot: targetPaths.resolveTargetRoot,
|
||||
resolveOwn: own.resolve,
|
||||
resolveOwnRoot: own.resolveRoot,
|
||||
resolveTarget: targetPaths.resolve,
|
||||
resolveTargetRoot: targetPaths.resolveRoot,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,26 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { findPaths } from '@backstage/cli-common';
|
||||
import { targetPaths, findOwnPaths } from '@backstage/cli-common';
|
||||
|
||||
const ownPaths = findOwnPaths(__dirname);
|
||||
|
||||
/* eslint-disable-next-line no-restricted-syntax */
|
||||
export const paths = findPaths(__dirname);
|
||||
export const paths = {
|
||||
get ownDir() {
|
||||
return ownPaths.resolve();
|
||||
},
|
||||
get ownRoot() {
|
||||
return ownPaths.resolveRoot();
|
||||
},
|
||||
get targetDir() {
|
||||
return targetPaths.resolve();
|
||||
},
|
||||
get targetRoot() {
|
||||
return targetPaths.resolveRoot();
|
||||
},
|
||||
resolveOwn: ownPaths.resolve,
|
||||
resolveOwnRoot: ownPaths.resolveRoot,
|
||||
resolveTarget: targetPaths.resolve,
|
||||
resolveTargetRoot: targetPaths.resolveRoot,
|
||||
};
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ export class SuccessCache {
|
||||
* location.
|
||||
*/
|
||||
static trimPaths(input: string) {
|
||||
return input.replaceAll(targetPaths.targetRoot, '');
|
||||
return input.replaceAll(targetPaths.resolveRoot(), '');
|
||||
}
|
||||
|
||||
constructor(name: string, basePath?: string) {
|
||||
|
||||
@@ -25,7 +25,7 @@ import { targetPaths } from '@backstage/cli-common';
|
||||
|
||||
export const createTypeDistProject = async () => {
|
||||
return new Project({
|
||||
tsConfigFilePath: targetPaths.resolveTargetRoot('tsconfig.json'),
|
||||
tsConfigFilePath: targetPaths.resolveRoot('tsconfig.json'),
|
||||
skipAddingFilesFromTsConfig: true,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
import fs from 'fs-extra';
|
||||
import semver from 'semver';
|
||||
import { findOwnPaths } from '@backstage/cli-common';
|
||||
import { Lockfile } from './versioning';
|
||||
|
||||
const ownPaths = findOwnPaths(__dirname);
|
||||
import { Lockfile } from './versioning';
|
||||
|
||||
/* eslint-disable @backstage/no-relative-monorepo-imports */
|
||||
/*
|
||||
@@ -84,12 +84,12 @@ export const packageVersions: Record<string, string> = {
|
||||
};
|
||||
|
||||
export function findVersion() {
|
||||
const pkgContent = fs.readFileSync(ownPaths.resolveOwn('package.json'), 'utf8');
|
||||
const pkgContent = fs.readFileSync(ownPaths.resolve('package.json'), 'utf8');
|
||||
return JSON.parse(pkgContent).version;
|
||||
}
|
||||
|
||||
export const version = findVersion();
|
||||
export const isDev = fs.pathExistsSync(ownPaths.resolveOwn('src'));
|
||||
export const isDev = fs.pathExistsSync(ownPaths.resolve('src'));
|
||||
|
||||
export function createPackageVersionProvider(
|
||||
lockfile?: Lockfile,
|
||||
|
||||
@@ -15,22 +15,21 @@
|
||||
*/
|
||||
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
import { targetPaths } from '@backstage/cli-common';
|
||||
import { getHasYarnPlugin } from './yarnPlugin';
|
||||
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
jest.mock('@backstage/cli-common', () => ({
|
||||
...jest.requireActual('@backstage/cli-common'),
|
||||
targetPaths: {
|
||||
resolveTargetRoot(filename: string) {
|
||||
return mockDir.resolve(filename);
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
describe('getHasYarnPlugin', () => {
|
||||
beforeEach(() => {
|
||||
mockDir.clear();
|
||||
jest
|
||||
.spyOn(targetPaths, 'resolveRoot')
|
||||
.mockImplementation((...args: string[]) => mockDir.resolve(...args));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should return false when .yarnrc.yml does not exist', async () => {
|
||||
|
||||
@@ -36,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 = targetPaths.resolveTargetRoot('.yarnrc.yml');
|
||||
const yarnRcPath = targetPaths.resolveRoot('.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
|
||||
|
||||
@@ -42,19 +42,19 @@ export async function command(opts: OptionValues): Promise<void> {
|
||||
if (isValidUrl(arg)) {
|
||||
return arg;
|
||||
}
|
||||
return targetPaths.resolveTarget(arg);
|
||||
return targetPaths.resolve(arg);
|
||||
});
|
||||
|
||||
if (role === 'frontend') {
|
||||
return buildFrontend({
|
||||
targetDir: targetPaths.targetDir,
|
||||
targetDir: targetPaths.resolve(),
|
||||
configPaths,
|
||||
writeStats: Boolean(opts.stats),
|
||||
webpack,
|
||||
});
|
||||
}
|
||||
return buildBackend({
|
||||
targetDir: targetPaths.targetDir,
|
||||
targetDir: targetPaths.resolve(),
|
||||
configPaths,
|
||||
skipBuildDependencies: Boolean(opts.skipBuildDependencies),
|
||||
minify: Boolean(opts.minify),
|
||||
@@ -77,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: targetPaths.targetDir,
|
||||
targetDir: targetPaths.resolve(),
|
||||
configPaths: [],
|
||||
writeStats: Boolean(opts.stats),
|
||||
isModuleFederationRemote,
|
||||
@@ -100,7 +100,7 @@ export async function command(opts: OptionValues): Promise<void> {
|
||||
}
|
||||
|
||||
const packageJson = (await fs.readJson(
|
||||
targetPaths.resolveTarget('package.json'),
|
||||
targetPaths.resolve('package.json'),
|
||||
)) as BackstagePackageJson;
|
||||
|
||||
return buildPackage({
|
||||
|
||||
@@ -25,7 +25,7 @@ export async function command(opts: OptionValues): Promise<void> {
|
||||
await startPackage({
|
||||
role: await findRoleFromCommand(opts),
|
||||
entrypoint: opts.entrypoint,
|
||||
targetDir: targetPaths.targetDir,
|
||||
targetDir: targetPaths.resolve(),
|
||||
configPaths: opts.config as string[],
|
||||
checksEnabled: Boolean(opts.check),
|
||||
linkedWorkspace: await resolveLinkedWorkspace(opts.link),
|
||||
|
||||
@@ -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.targetDir, 'dev/index.ts'),
|
||||
resolvePath(options.targetDir ?? targetPaths.resolve(), 'dev/index.ts'),
|
||||
);
|
||||
if (!hasDevIndexEntry) {
|
||||
console.warn(
|
||||
|
||||
@@ -39,7 +39,7 @@ interface StartAppOptions {
|
||||
|
||||
export async function startFrontend(options: StartAppOptions) {
|
||||
const packageJson = (await readJson(
|
||||
resolvePath(options.targetDir ?? targetPaths.targetDir, 'package.json'),
|
||||
resolvePath(options.targetDir ?? targetPaths.resolve(), 'package.json'),
|
||||
)) as BackstagePackageJson;
|
||||
|
||||
if (!hasReactDomClient()) {
|
||||
@@ -59,7 +59,7 @@ export async function startFrontend(options: StartAppOptions) {
|
||||
moduleFederationRemote: options.isModuleFederationRemote
|
||||
? await getModuleFederationRemoteOptions(
|
||||
packageJson,
|
||||
resolvePath(targetPaths.targetDir),
|
||||
resolvePath(targetPaths.resolve()),
|
||||
)
|
||||
: undefined,
|
||||
});
|
||||
|
||||
@@ -90,7 +90,7 @@ export async function command(opts: OptionValues, cmd: Command): Promise<void> {
|
||||
targetDir: pkg.dir,
|
||||
packageJson: pkg.packageJson,
|
||||
outputs,
|
||||
logPrefix: `${chalk.cyan(relativePath(targetPaths.targetRoot, pkg.dir))}: `,
|
||||
logPrefix: `${chalk.cyan(relativePath(targetPaths.resolveRoot(), pkg.dir))}: `,
|
||||
workspacePackages: packages,
|
||||
minify: opts.minify ?? buildOptions.minify,
|
||||
};
|
||||
|
||||
@@ -99,7 +99,7 @@ describe('findTargetPackages', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest
|
||||
.spyOn(targetPaths, 'resolveTargetRoot')
|
||||
.spyOn(targetPaths, 'resolveRoot')
|
||||
.mockImplementation((...parts: string[]) => {
|
||||
return posix.resolve('/root', ...parts);
|
||||
});
|
||||
|
||||
@@ -96,7 +96,7 @@ export async function findTargetPackages(
|
||||
pkg => nameOrPath === pkg.packageJson.name,
|
||||
);
|
||||
if (!matchingPackage) {
|
||||
const absPath = targetPaths.resolveTargetRoot(nameOrPath);
|
||||
const absPath = targetPaths.resolveRoot(nameOrPath);
|
||||
matchingPackage = packages.find(
|
||||
pkg => relativePath(pkg.dir, absPath) === '',
|
||||
);
|
||||
@@ -118,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 = targetPaths.resolveTargetRoot(
|
||||
const expectedPath = targetPaths.resolveRoot(
|
||||
role === 'frontend' ? 'packages/app' : 'packages/backend',
|
||||
);
|
||||
const matchByPath = matchingPackages.find(
|
||||
|
||||
@@ -117,7 +117,7 @@ export async function makeRollupConfigs(
|
||||
options: BuildOptions,
|
||||
): Promise<RollupOptions[]> {
|
||||
const configs = new Array<RollupOptions>();
|
||||
const targetDir = options.targetDir ?? targetPaths.targetDir;
|
||||
const targetDir = options.targetDir ?? targetPaths.resolve();
|
||||
|
||||
let targetPkg = options.packageJson;
|
||||
if (!targetPkg) {
|
||||
@@ -285,9 +285,9 @@ export async function makeRollupConfigs(
|
||||
const input = Object.fromEntries(
|
||||
scriptEntryPoints.map(e => [
|
||||
e.name,
|
||||
targetPaths.resolveTargetRoot(
|
||||
targetPaths.resolveRoot(
|
||||
'dist-types',
|
||||
relativePath(targetPaths.targetRoot, targetDir),
|
||||
relativePath(targetPaths.resolveRoot(), targetDir),
|
||||
e.path.replace(/\.(?:ts|tsx)$/, '.d.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.targetDir, error.id);
|
||||
const path = relativePath(targetPaths.resolve(), error.id);
|
||||
const loc = chalk.cyan(`${path}:${line}:${column}`);
|
||||
|
||||
if (text === 'Unexpected "<"' && error.id.endsWith('.js')) {
|
||||
@@ -53,11 +53,11 @@ export function formatErrorMessage(error: any) {
|
||||
} else {
|
||||
// Generic rollup errors, log what's available
|
||||
if (error.loc) {
|
||||
const file = `${targetPaths.resolveTarget((error.loc.file || error.id)!)}`;
|
||||
const file = `${targetPaths.resolve((error.loc.file || error.id)!)}`;
|
||||
const pos = `${error.loc.line}:${error.loc.column}`;
|
||||
msg += `${file} [${pos}]\n`;
|
||||
} else if (error.id) {
|
||||
msg += `${targetPaths.resolveTarget(error.id)}\n`;
|
||||
msg += `${targetPaths.resolve(error.id)}\n`;
|
||||
}
|
||||
|
||||
msg += `${error}\n`;
|
||||
@@ -90,7 +90,7 @@ async function rollupBuild(config: RollupOptions) {
|
||||
export const buildPackage = async (options: BuildOptions) => {
|
||||
try {
|
||||
const { resolutions } = await fs.readJson(
|
||||
targetPaths.resolveTargetRoot('package.json'),
|
||||
targetPaths.resolveRoot('package.json'),
|
||||
);
|
||||
if (resolutions?.esbuild) {
|
||||
console.warn(
|
||||
@@ -107,7 +107,7 @@ export const buildPackage = async (options: BuildOptions) => {
|
||||
|
||||
const rollupConfigs = await makeRollupConfigs(options);
|
||||
|
||||
const targetDir = options.targetDir ?? targetPaths.targetDir;
|
||||
const targetDir = options.targetDir ?? targetPaths.resolve();
|
||||
await fs.remove(resolvePath(targetDir, 'dist'));
|
||||
|
||||
const buildTasks = rollupConfigs.map(rollupBuild);
|
||||
|
||||
@@ -97,7 +97,7 @@ async function readBuildInfo() {
|
||||
}
|
||||
|
||||
const { version: packageVersion } = await fs.readJson(
|
||||
targetPaths.resolveTarget('package.json'),
|
||||
targetPaths.resolve('package.json'),
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -20,7 +20,7 @@ import { targetPaths } from '@backstage/cli-common';
|
||||
export function hasReactDomClient() {
|
||||
try {
|
||||
require.resolve('react-dom/client', {
|
||||
paths: [targetPaths.targetDir],
|
||||
paths: [targetPaths.resolve()],
|
||||
});
|
||||
return true;
|
||||
} catch {
|
||||
|
||||
@@ -53,7 +53,7 @@ export async function createWorkspaceLinkingPlugins(
|
||||
/^react(?:-router)?(?:-dom)?$/,
|
||||
resource => {
|
||||
if (!relativePath(linkedRoot.dir, resource.context).startsWith('..')) {
|
||||
resource.context = targetPaths.targetDir;
|
||||
resource.context = targetPaths.resolve();
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
@@ -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.targetRoot,
|
||||
targetPaths.resolveRoot(),
|
||||
'node_modules',
|
||||
`${DETECTED_MODULES_MODULE_NAME}.js`,
|
||||
);
|
||||
|
||||
@@ -18,19 +18,17 @@ import fs from 'fs-extra';
|
||||
import { resolve as resolvePath } from 'node:path';
|
||||
import { targetPaths, findOwnPaths } from '@backstage/cli-common';
|
||||
|
||||
const ownPaths = findOwnPaths(__dirname);
|
||||
|
||||
export type BundlingPathsOptions = {
|
||||
// bundle entrypoint, e.g. 'src/index'
|
||||
entry: string;
|
||||
// Target directory, defaulting to targetPaths.targetDir
|
||||
// Target directory, defaulting to targetPaths.resolve()
|
||||
targetDir?: string;
|
||||
// Relative dist directory, defaulting to 'dist'
|
||||
dist?: string;
|
||||
};
|
||||
|
||||
export function resolveBundlingPaths(options: BundlingPathsOptions) {
|
||||
const { entry, targetDir = targetPaths.targetDir } = options;
|
||||
const { entry, targetDir = targetPaths.resolve() } = options;
|
||||
|
||||
const resolveTargetModule = (pathString: string) => {
|
||||
for (const ext of ['mjs', 'js', 'ts', 'tsx', 'jsx']) {
|
||||
@@ -51,7 +49,7 @@ export function resolveBundlingPaths(options: BundlingPathsOptions) {
|
||||
} else {
|
||||
targetHtml = resolvePath(targetDir, `${entry}.html`);
|
||||
if (!fs.pathExistsSync(targetHtml)) {
|
||||
targetHtml = ownPaths.resolveOwn('templates/serve_index.html');
|
||||
targetHtml = findOwnPaths(__dirname).resolve('templates/serve_index.html');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,10 +67,10 @@ export function resolveBundlingPaths(options: BundlingPathsOptions) {
|
||||
targetSrc: resolvePath(targetDir, 'src'),
|
||||
targetDev: resolvePath(targetDir, 'dev'),
|
||||
targetEntry: resolveTargetModule(entry),
|
||||
targetTsConfig: targetPaths.resolveTargetRoot('tsconfig.json'),
|
||||
targetTsConfig: targetPaths.resolveRoot('tsconfig.json'),
|
||||
targetPackageJson: resolvePath(targetDir, 'package.json'),
|
||||
rootNodeModules: targetPaths.resolveTargetRoot('node_modules'),
|
||||
root: targetPaths.targetRoot,
|
||||
rootNodeModules: targetPaths.resolveRoot('node_modules'),
|
||||
root: targetPaths.resolveRoot(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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.targetDir, 'package.json'),
|
||||
resolvePath(options.targetDir ?? targetPaths.resolve(), '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.targetRoot],
|
||||
paths: [targetPaths.resolveRoot()],
|
||||
});
|
||||
const reactPkg = require(reactPkgPath);
|
||||
if (reactPkg.version.startsWith('16.')) {
|
||||
|
||||
@@ -211,7 +211,7 @@ export async function createDistWorkspace(
|
||||
targetDir: pkg.dir,
|
||||
packageJson: pkg.packageJson,
|
||||
outputs: outputs,
|
||||
logPrefix: `${chalk.cyan(relativePath(targetPaths.targetRoot, pkg.dir))}: `,
|
||||
logPrefix: `${chalk.cyan(relativePath(targetPaths.resolveRoot(), pkg.dir))}: `,
|
||||
minify: options.minify,
|
||||
workspacePackages: packages,
|
||||
});
|
||||
@@ -246,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(targetPaths.resolveTargetRoot(src), resolvePath(targetDir, dest));
|
||||
await fs.copy(targetPaths.resolveRoot(src), resolvePath(targetDir, dest));
|
||||
}
|
||||
|
||||
if (options.skeleton) {
|
||||
const skeletonFiles = targets
|
||||
.map(target => {
|
||||
const dir = relativePath(targetPaths.targetRoot, target.dir);
|
||||
const dir = relativePath(targetPaths.resolveRoot(), 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.targetRoot, target.dir);
|
||||
const outputDir = relativePath(targetPaths.resolveRoot(), 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.targetRoot, target.dir);
|
||||
const outputDir = relativePath(targetPaths.resolveRoot(), target.dir);
|
||||
const absoluteOutputPath = resolvePath(workspaceDir, outputDir);
|
||||
await fs.ensureDir(absoluteOutputPath);
|
||||
|
||||
|
||||
@@ -23,9 +23,8 @@ const mockDir = createMockDirectory();
|
||||
jest.mock('@backstage/cli-common', () => ({
|
||||
...jest.requireActual('@backstage/cli-common'),
|
||||
targetPaths: {
|
||||
resolveTarget(filename: string) {
|
||||
return mockDir.resolve(filename);
|
||||
},
|
||||
resolve: (...args: string[]) => mockDir.resolve(...args),
|
||||
resolveRoot: (...args: string[]) => mockDir.resolve(...args),
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ export async function findRoleFromCommand(
|
||||
return PackageRoles.getRoleInfo(opts.role)?.role;
|
||||
}
|
||||
|
||||
const pkg = await fs.readJson(targetPaths.resolveTarget('package.json'));
|
||||
const pkg = await fs.readJson(targetPaths.resolve('package.json'));
|
||||
const info = PackageRoles.getRoleFromPackage(pkg);
|
||||
if (!info) {
|
||||
throw new Error(`Target package must have 'backstage.role' set`);
|
||||
|
||||
@@ -136,7 +136,7 @@ export async function runBackend(options: RunBackendOptions) {
|
||||
...process.env,
|
||||
BACKSTAGE_CLI_LINKED_WORKSPACE: options.linkedWorkspace,
|
||||
BACKSTAGE_CLI_CHANNEL: '1',
|
||||
ESBK_TSCONFIG_PATH: targetPaths.resolveTargetRoot('tsconfig.json'),
|
||||
ESBK_TSCONFIG_PATH: targetPaths.resolveRoot('tsconfig.json'),
|
||||
},
|
||||
serialization: 'advanced',
|
||||
},
|
||||
|
||||
@@ -35,7 +35,7 @@ type Options = {
|
||||
};
|
||||
|
||||
export async function loadCliConfig(options: Options) {
|
||||
const targetDir = options.targetDir ?? targetPaths.targetDir;
|
||||
const targetDir = options.targetDir ?? targetPaths.resolve();
|
||||
|
||||
// Consider all packages in the monorepo when loading in config
|
||||
const { packages } = await getPackages(targetDir);
|
||||
@@ -64,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: [targetPaths.resolveTargetRoot('package.json')],
|
||||
packagePaths: [targetPaths.resolveRoot('package.json')],
|
||||
noUndeclaredProperties: options.strict,
|
||||
});
|
||||
|
||||
@@ -74,7 +74,7 @@ export async function loadCliConfig(options: Options) {
|
||||
? async name => process.env[name] || 'x'
|
||||
: undefined,
|
||||
watch: Boolean(options.watch),
|
||||
rootDir: targetPaths.targetRoot,
|
||||
rootDir: targetPaths.resolveRoot(),
|
||||
argv: options.args.flatMap(t => ['--config', resolvePath(targetDir, t)]),
|
||||
});
|
||||
|
||||
|
||||
@@ -63,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(targetPaths.resolveTargetRoot(fileName), content);
|
||||
await fs.writeFile(targetPaths.resolveRoot(fileName), content);
|
||||
console.log(`GitHub App configuration written to ${chalk.cyan(fileName)}`);
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
|
||||
@@ -17,9 +17,6 @@
|
||||
import { version as cliVersion } from '../../../../package.json';
|
||||
import os from 'node:os';
|
||||
import { runOutput, targetPaths, findOwnPaths } from '@backstage/cli-common';
|
||||
|
||||
const ownPaths = findOwnPaths(__dirname);
|
||||
|
||||
import { Lockfile } from '../../../lib/versioning';
|
||||
import { BackstagePackageJson, PackageGraph } from '@backstage/cli-node';
|
||||
import { minimatch } from 'minimatch';
|
||||
@@ -58,9 +55,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(ownPaths.resolveOwn('./src'));
|
||||
const isLocal = fs.existsSync(findOwnPaths(__dirname).resolve('./src'));
|
||||
|
||||
const backstageFile = targetPaths.resolveTargetRoot('backstage.json');
|
||||
const backstageFile = targetPaths.resolveRoot('backstage.json');
|
||||
let backstageVersion = 'N/A';
|
||||
if (fs.existsSync(backstageFile)) {
|
||||
try {
|
||||
@@ -85,9 +82,9 @@ export default async (options: InfoOptions) => {
|
||||
backstage: backstageVersion,
|
||||
};
|
||||
|
||||
const lockfilePath = targetPaths.resolveTargetRoot('yarn.lock');
|
||||
const lockfilePath = targetPaths.resolveRoot('yarn.lock');
|
||||
const lockfile = await Lockfile.load(lockfilePath);
|
||||
const targetPath = targetPaths.targetRoot;
|
||||
const targetPath = targetPaths.resolveRoot();
|
||||
|
||||
// Get workspace package names and their versions
|
||||
const workspacePackages = new Map<string, string>();
|
||||
|
||||
@@ -22,7 +22,7 @@ import { ESLint } from 'eslint';
|
||||
|
||||
export default async (directories: string[], opts: OptionValues) => {
|
||||
const eslint = new ESLint({
|
||||
cwd: targetPaths.targetDir,
|
||||
cwd: targetPaths.resolve(),
|
||||
fix: opts.fix,
|
||||
extensions: ['js', 'jsx', 'ts', 'tsx', 'mjs', 'cjs'],
|
||||
});
|
||||
@@ -48,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(targetPaths.targetRoot);
|
||||
process.chdir(targetPaths.resolveRoot());
|
||||
}
|
||||
|
||||
const resultText = await formatter.format(results);
|
||||
|
||||
if (resultText) {
|
||||
if (opts.outputFile) {
|
||||
await fs.writeFile(targetPaths.resolveTarget(opts.outputFile), resultText);
|
||||
await fs.writeFile(targetPaths.resolve(opts.outputFile), resultText);
|
||||
} else {
|
||||
console.log(resultText);
|
||||
}
|
||||
|
||||
@@ -45,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(targetPaths.resolveTargetRoot('yarn.lock')),
|
||||
lockfile: await Lockfile.load(targetPaths.resolveRoot('yarn.lock')),
|
||||
}
|
||||
: undefined;
|
||||
|
||||
@@ -63,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(targetPaths.targetRoot);
|
||||
process.chdir(targetPaths.resolveRoot());
|
||||
}
|
||||
|
||||
// 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<void> {
|
||||
const lintOptions = parseLintScript(pkg.packageJson.scripts?.lint);
|
||||
const base = {
|
||||
fullDir: pkg.dir,
|
||||
relativeDir: relativePath(targetPaths.targetRoot, pkg.dir),
|
||||
relativeDir: relativePath(targetPaths.resolveRoot(), pkg.dir),
|
||||
lintOptions,
|
||||
parentHash: undefined,
|
||||
};
|
||||
@@ -114,7 +114,7 @@ export async function command(opts: OptionValues, cmd: Command): Promise<void> {
|
||||
shouldCache: Boolean(cacheContext),
|
||||
maxWarnings: opts.maxWarnings ?? -1,
|
||||
successCache: cacheContext?.entries,
|
||||
rootDir: targetPaths.targetRoot,
|
||||
rootDir: targetPaths.resolveRoot(),
|
||||
},
|
||||
workerFactory: async ({
|
||||
fix,
|
||||
@@ -264,7 +264,7 @@ export async function command(opts: OptionValues, cmd: Command): Promise<void> {
|
||||
}
|
||||
|
||||
if (opts.outputFile && errorOutput) {
|
||||
await fs.writeFile(targetPaths.resolveTargetRoot(opts.outputFile), errorOutput);
|
||||
await fs.writeFile(targetPaths.resolveRoot(opts.outputFile), errorOutput);
|
||||
}
|
||||
|
||||
if (cacheContext) {
|
||||
|
||||
@@ -19,7 +19,7 @@ import { targetPaths } from '@backstage/cli-common';
|
||||
|
||||
|
||||
export default async function clean() {
|
||||
await fs.remove(targetPaths.resolveTarget('dist'));
|
||||
await fs.remove(targetPaths.resolveTarget('dist-types'));
|
||||
await fs.remove(targetPaths.resolveTarget('coverage'));
|
||||
await fs.remove(targetPaths.resolve('dist'));
|
||||
await fs.remove(targetPaths.resolve('dist-types'));
|
||||
await fs.remove(targetPaths.resolve('coverage'));
|
||||
}
|
||||
|
||||
@@ -26,16 +26,16 @@ import { createTypeDistProject } from '../../../../lib/typeDistProject';
|
||||
|
||||
export const pre = async () => {
|
||||
publishPreflightCheck({
|
||||
dir: targetPaths.targetDir,
|
||||
packageJson: await fs.readJson(targetPaths.resolveTarget('package.json')),
|
||||
dir: targetPaths.resolve(),
|
||||
packageJson: await fs.readJson(targetPaths.resolve('package.json')),
|
||||
});
|
||||
|
||||
await productionPack({
|
||||
packageDir: targetPaths.targetDir,
|
||||
packageDir: targetPaths.resolve(),
|
||||
featureDetectionProject: await createTypeDistProject(),
|
||||
});
|
||||
};
|
||||
|
||||
export const post = async () => {
|
||||
await revertProductionPack(targetPaths.targetDir);
|
||||
await revertProductionPack(targetPaths.resolve());
|
||||
};
|
||||
|
||||
@@ -24,9 +24,9 @@ import { run, targetPaths } from '@backstage/cli-common';
|
||||
export async function command(): Promise<void> {
|
||||
const packages = await PackageGraph.listTargetPackages();
|
||||
|
||||
await fs.remove(targetPaths.resolveTargetRoot('dist'));
|
||||
await fs.remove(targetPaths.resolveTargetRoot('dist-types'));
|
||||
await fs.remove(targetPaths.resolveTargetRoot('coverage'));
|
||||
await fs.remove(targetPaths.resolveRoot('dist'));
|
||||
await fs.remove(targetPaths.resolveRoot('dist-types'));
|
||||
await fs.remove(targetPaths.resolveRoot('coverage'));
|
||||
|
||||
await Promise.all(
|
||||
Array.from(Array(10), async () => {
|
||||
|
||||
@@ -50,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(targetPaths.resolveTargetRoot('package.json'));
|
||||
const rootPkg = require(targetPaths.resolveRoot('package.json'));
|
||||
const fixCmd =
|
||||
rootPkg.scripts?.fix === 'backstage-cli repo fix'
|
||||
? 'fix'
|
||||
@@ -217,7 +217,7 @@ export function fixSideEffects(pkg: FixablePackage) {
|
||||
}
|
||||
|
||||
export function createRepositoryFieldFixer() {
|
||||
const rootPkg = require(targetPaths.resolveTargetRoot('package.json'));
|
||||
const rootPkg = require(targetPaths.resolveRoot('package.json'));
|
||||
const rootRepoField = rootPkg.repository;
|
||||
if (!rootRepoField) {
|
||||
return () => {};
|
||||
@@ -230,7 +230,7 @@ export function createRepositoryFieldFixer() {
|
||||
return (pkg: FixablePackage) => {
|
||||
const expectedPath = posix.join(
|
||||
rootDir,
|
||||
relativePath(targetPaths.targetRoot, pkg.dir),
|
||||
relativePath(targetPaths.resolveRoot(), pkg.dir),
|
||||
);
|
||||
const repoField = pkg.packageJson.repository;
|
||||
|
||||
@@ -319,7 +319,7 @@ export function fixPluginId(pkg: FixablePackage) {
|
||||
role === 'backend-plugin-module')
|
||||
) {
|
||||
const path = relativePath(
|
||||
targetPaths.targetRoot,
|
||||
targetPaths.resolveRoot(),
|
||||
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.targetRoot,
|
||||
targetPaths.resolveRoot(),
|
||||
resolvePath(pkg.dir, 'package.json'),
|
||||
);
|
||||
const suggestedRole =
|
||||
@@ -464,7 +464,7 @@ export function fixPeerModules(pkg: FixablePackage) {
|
||||
}
|
||||
|
||||
const packagePath = relativePath(
|
||||
targetPaths.targetRoot,
|
||||
targetPaths.resolveRoot(),
|
||||
resolvePath(pkg.dir, 'package.json'),
|
||||
);
|
||||
|
||||
|
||||
@@ -26,14 +26,14 @@ export async function command(opts: OptionValues) {
|
||||
const packages = await PackageGraph.listTargetPackages();
|
||||
|
||||
const eslint = new ESLint({
|
||||
cwd: targetPaths.targetDir,
|
||||
cwd: targetPaths.resolve(),
|
||||
overrideConfig: {
|
||||
plugins: ['deprecation'],
|
||||
rules: {
|
||||
'deprecation/deprecation': 'error',
|
||||
},
|
||||
parserOptions: {
|
||||
project: [targetPaths.resolveTargetRoot('tsconfig.json')],
|
||||
project: [targetPaths.resolveRoot('tsconfig.json')],
|
||||
},
|
||||
},
|
||||
extensions: ['jsx', 'ts', 'tsx', 'mjs', 'cjs'],
|
||||
@@ -53,7 +53,7 @@ export async function command(opts: OptionValues) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const path = relativePath(targetPaths.targetRoot, result.filePath);
|
||||
const path = relativePath(targetPaths.resolveRoot(), result.filePath);
|
||||
deprecations.push({
|
||||
path,
|
||||
message: message.message,
|
||||
|
||||
@@ -22,7 +22,7 @@ import { targetPaths } from '@backstage/cli-common';
|
||||
|
||||
|
||||
export default async () => {
|
||||
const { packages } = await getPackages(targetPaths.targetDir);
|
||||
const { packages } = await getPackages(targetPaths.resolve());
|
||||
|
||||
await Promise.all(
|
||||
packages.map(async ({ dir, packageJson: pkg }) => {
|
||||
|
||||
@@ -65,13 +65,11 @@ jest.mock('@backstage/cli-common', () => {
|
||||
return {
|
||||
...actual,
|
||||
targetPaths: {
|
||||
resolveTargetRoot: (filename: string) => mockDir.resolve(filename),
|
||||
get targetDir() {
|
||||
return mockDir.path;
|
||||
},
|
||||
resolve: (...args: string[]) => mockDir.resolve(...args),
|
||||
resolveRoot: (...args: string[]) => mockDir.resolve(...args),
|
||||
},
|
||||
findPaths: () => ({
|
||||
resolveTargetRoot: (filename: string) => mockDir.resolve(filename),
|
||||
resolveTargetRoot: (...args: string[]) => mockDir.resolve(...args),
|
||||
get targetDir() {
|
||||
return mockDir.path;
|
||||
},
|
||||
|
||||
@@ -69,7 +69,7 @@ function extendsDefaultPattern(pattern: string): boolean {
|
||||
}
|
||||
|
||||
export default async (opts: OptionValues) => {
|
||||
const lockfilePath = targetPaths.resolveTargetRoot('yarn.lock');
|
||||
const lockfilePath = targetPaths.resolveRoot('yarn.lock');
|
||||
const lockfile = await Lockfile.load(lockfilePath);
|
||||
const hasYarnPlugin = await getHasYarnPlugin();
|
||||
|
||||
@@ -141,7 +141,7 @@ export default async (opts: OptionValues) => {
|
||||
}
|
||||
|
||||
// First we discover all Backstage dependencies within our own repo
|
||||
const dependencyMap = await mapDependencies(targetPaths.targetDir, pattern);
|
||||
const dependencyMap = await mapDependencies(targetPaths.resolve(), pattern);
|
||||
|
||||
// Next check with the package registry to see which dependency ranges we need to bump
|
||||
const versionBumps = new Map<string, PkgVersionInfo[]>();
|
||||
@@ -418,7 +418,7 @@ export function createVersionFinder(options: {
|
||||
}
|
||||
|
||||
function getBackstageJsonPath() {
|
||||
return targetPaths.resolveTargetRoot(BACKSTAGE_JSON);
|
||||
return targetPaths.resolveRoot(BACKSTAGE_JSON);
|
||||
}
|
||||
|
||||
async function getBackstageJson() {
|
||||
|
||||
@@ -38,13 +38,11 @@ jest.mock('@backstage/cli-common', () => {
|
||||
return {
|
||||
...actual,
|
||||
targetPaths: {
|
||||
resolveTargetRoot: (filename: string) => mockDir.resolve(filename),
|
||||
get targetDir() {
|
||||
return mockDir.path;
|
||||
},
|
||||
resolve: (...args: string[]) => mockDir.resolve(...args),
|
||||
resolveRoot: (...args: string[]) => mockDir.resolve(...args),
|
||||
},
|
||||
findPaths: () => ({
|
||||
resolveTargetRoot: (filename: string) => mockDir.resolve(filename),
|
||||
resolveTargetRoot: (...args: string[]) => mockDir.resolve(...args),
|
||||
get targetDir() {
|
||||
return mockDir.path;
|
||||
},
|
||||
|
||||
@@ -83,7 +83,7 @@ export async function addCodeownersEntry(
|
||||
|
||||
let filePath = codeownersFilePath;
|
||||
if (!filePath) {
|
||||
filePath = await getCodeownersFilePath(targetPaths.targetRoot);
|
||||
filePath = await getCodeownersFilePath(targetPaths.resolveRoot());
|
||||
if (!filePath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ export class PortableTemplater {
|
||||
static async create(options: CreatePortableTemplaterOptions = {}) {
|
||||
let lockfile: Lockfile | undefined;
|
||||
try {
|
||||
lockfile = await Lockfile.load(targetPaths.resolveTargetRoot('yarn.lock'));
|
||||
lockfile = await Lockfile.load(targetPaths.resolveRoot('yarn.lock'));
|
||||
} catch {
|
||||
/* ignored */
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ export async function installNewPackage(input: PortableTemplateInput) {
|
||||
}
|
||||
|
||||
async function addDependency(input: PortableTemplateInput, path: string) {
|
||||
const pkgJsonPath = targetPaths.resolveTargetRoot(path);
|
||||
const pkgJsonPath = targetPaths.resolveRoot(path);
|
||||
|
||||
const pkgJson = await fs.readJson(pkgJsonPath).catch(error => {
|
||||
if (error.code === 'ENOENT') {
|
||||
@@ -85,7 +85,7 @@ async function tryAddFrontendLegacy(input: PortableTemplateInput) {
|
||||
);
|
||||
}
|
||||
|
||||
const appDefinitionPath = targetPaths.resolveTargetRoot('packages/app/src/App.tsx');
|
||||
const appDefinitionPath = targetPaths.resolveRoot('packages/app/src/App.tsx');
|
||||
if (!(await fs.pathExists(appDefinitionPath))) {
|
||||
return;
|
||||
}
|
||||
@@ -121,7 +121,7 @@ async function tryAddFrontendLegacy(input: PortableTemplateInput) {
|
||||
}
|
||||
|
||||
async function tryAddBackend(input: PortableTemplateInput) {
|
||||
const backendIndexPath = targetPaths.resolveTargetRoot(
|
||||
const backendIndexPath = targetPaths.resolveRoot(
|
||||
'packages/backend/src/index.ts',
|
||||
);
|
||||
if (!(await fs.pathExists(backendIndexPath))) {
|
||||
|
||||
@@ -33,8 +33,8 @@ describe('writeTemplateContents', () => {
|
||||
mockDir.clear();
|
||||
jest.resetAllMocks();
|
||||
jest
|
||||
.spyOn(targetPaths, 'resolveTargetRoot')
|
||||
.mockImplementation((...args) => mockDir.resolve(...args));
|
||||
.spyOn(targetPaths, 'resolveRoot')
|
||||
.mockImplementation((...args: string[]) => mockDir.resolve(...args));
|
||||
});
|
||||
|
||||
it('should write an empty template', async () => {
|
||||
|
||||
@@ -29,7 +29,7 @@ export async function writeTemplateContents(
|
||||
template: PortableTemplate,
|
||||
input: PortableTemplateInput,
|
||||
): Promise<{ targetDir: string }> {
|
||||
const targetDir = targetPaths.resolveTargetRoot(input.packagePath);
|
||||
const targetDir = targetPaths.resolveRoot(input.packagePath);
|
||||
|
||||
if (await fs.pathExists(targetDir)) {
|
||||
throw new InputError(`Package '${input.packagePath}' already exists`);
|
||||
|
||||
@@ -39,7 +39,7 @@ export async function collectPortableTemplateInput(
|
||||
): Promise<PortableTemplateInput> {
|
||||
const { config, template, prefilledParams } = options;
|
||||
|
||||
const codeOwnersFilePath = await getCodeownersFilePath(targetPaths.targetRoot);
|
||||
const codeOwnersFilePath = await getCodeownersFilePath(targetPaths.resolveRoot());
|
||||
|
||||
const prompts = getPromptsForRole(template.role);
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ export async function loadPortableTemplate(
|
||||
throw new Error('Remote templates are not supported yet');
|
||||
}
|
||||
const templateContent = await fs
|
||||
.readFile(targetPaths.resolveTargetRoot(pointer.target), 'utf-8')
|
||||
.readFile(targetPaths.resolveRoot(pointer.target), 'utf-8')
|
||||
.catch(error => {
|
||||
throw new ForwardedError(
|
||||
`Failed to load template definition from '${pointer.target}'`,
|
||||
|
||||
@@ -91,7 +91,7 @@ export async function loadPortableTemplateConfig(
|
||||
): Promise<PortableTemplateConfig> {
|
||||
const { overrides = {} } = options;
|
||||
const pkgPath =
|
||||
options.packagePath ?? targetPaths.resolveTargetRoot('package.json');
|
||||
options.packagePath ?? targetPaths.resolveRoot('package.json');
|
||||
const pkgJson = await fs.readJson(pkgPath);
|
||||
|
||||
const parsed = pkgJsonWithNewConfigSchema.safeParse(pkgJson);
|
||||
|
||||
@@ -18,8 +18,6 @@ import { Command, OptionValues } from 'commander';
|
||||
|
||||
import { runCheck, findOwnPaths } from '@backstage/cli-common';
|
||||
|
||||
const ownPaths = findOwnPaths(__dirname);
|
||||
|
||||
function includesAnyOf(hayStack: string[], ...needles: string[]) {
|
||||
for (const needle of needles) {
|
||||
if (hayStack.includes(needle)) {
|
||||
@@ -40,7 +38,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', ownPaths.resolveOwn('config/jest.js'));
|
||||
args.push('--config', findOwnPaths(__dirname).resolve('config/jest.js'));
|
||||
}
|
||||
|
||||
if (!includesAnyOf(args, '--no-passWithNoTests', '--passWithNoTests=false')) {
|
||||
|
||||
@@ -24,10 +24,13 @@ import { relative as relativePath } from 'node:path';
|
||||
import { Command, OptionValues } from 'commander';
|
||||
import { Lockfile, PackageGraph } from '@backstage/cli-node';
|
||||
|
||||
import { runCheck, runOutput, targetPaths, findOwnPaths } from '@backstage/cli-common';
|
||||
|
||||
const ownPaths = findOwnPaths(__dirname);
|
||||
import { isChildPath } from '@backstage/cli-common';
|
||||
import {
|
||||
runCheck,
|
||||
runOutput,
|
||||
targetPaths,
|
||||
findOwnPaths,
|
||||
isChildPath,
|
||||
} from '@backstage/cli-common';
|
||||
import { SuccessCache } from '../../../../lib/cache/SuccessCache';
|
||||
|
||||
type JestProject = {
|
||||
@@ -65,7 +68,7 @@ interface TestGlobal extends Global {
|
||||
async function readPackageTreeHashes(graph: PackageGraph) {
|
||||
const pkgs = Array.from(graph.values()).map(pkg => ({
|
||||
...pkg,
|
||||
path: relativePath(targetPaths.targetRoot, pkg.dir),
|
||||
path: relativePath(targetPaths.resolveRoot(), pkg.dir),
|
||||
}));
|
||||
const output = await runOutput([
|
||||
'git',
|
||||
@@ -164,7 +167,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', ownPaths.resolveOwn('config/jest.js'));
|
||||
args.push('--config', findOwnPaths(__dirname).resolve('config/jest.js'));
|
||||
}
|
||||
|
||||
if (!hasFlags('--passWithNoTests')) {
|
||||
@@ -343,7 +346,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(
|
||||
targetPaths.resolveTargetRoot('yarn.lock'),
|
||||
targetPaths.resolveRoot('yarn.lock'),
|
||||
);
|
||||
const getPackageTreeHash = await readPackageTreeHashes(graph);
|
||||
|
||||
|
||||
@@ -16,17 +16,16 @@
|
||||
|
||||
import { relative as relativePath } from 'node:path';
|
||||
import { OptionValues } from 'commander';
|
||||
import { findPaths, run } from '@backstage/cli-common';
|
||||
import { findOwnPaths, run } from '@backstage/cli-common';
|
||||
import { platform } from 'node:os';
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
const paths = findPaths(__dirname);
|
||||
|
||||
export function createCodemodAction(name: string) {
|
||||
return async (dirs: string[], opts: OptionValues) => {
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
const paths = findOwnPaths(__dirname);
|
||||
const transformPath = relativePath(
|
||||
process.cwd(),
|
||||
paths.resolveOwn('transforms', `${name}.js`),
|
||||
paths.resolve('transforms', `${name}.js`),
|
||||
);
|
||||
|
||||
const args = [
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
} from './RemoteConfigSource';
|
||||
import { ConfigSource, SubstitutionFunc } from './types';
|
||||
import { ObservableConfigProxy } from './ObservableConfigProxy';
|
||||
import { findPaths } from '@backstage/cli-common';
|
||||
import { targetPaths } from '@backstage/cli-common';
|
||||
|
||||
/**
|
||||
* A target to read configuration from.
|
||||
@@ -157,7 +157,7 @@ export class ConfigSources {
|
||||
static defaultForTargets(
|
||||
options: ConfigSourcesDefaultForTargetsOptions,
|
||||
): ConfigSource {
|
||||
const rootDir = options.rootDir ?? findPaths(process.cwd()).targetRoot;
|
||||
const rootDir = options.rootDir ?? targetPaths.resolveRoot();
|
||||
|
||||
const argSources = options.targets.map(arg => {
|
||||
if (arg.type === 'url') {
|
||||
|
||||
@@ -19,12 +19,36 @@ import path from 'node:path';
|
||||
import { Command } from 'commander';
|
||||
import * as tasks from './lib/tasks';
|
||||
import createApp from './createApp';
|
||||
import { findPaths } from '@backstage/cli-common';
|
||||
import { findOwnPaths, targetPaths } from '@backstage/cli-common';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
jest.mock('./lib/tasks');
|
||||
|
||||
jest.mock('@backstage/cli-common', () => {
|
||||
const pathModule = require('node:path');
|
||||
const actual = jest.requireActual('@backstage/cli-common');
|
||||
const MOCK_CREATE_APP_ROOT = '/mock/create-app-root';
|
||||
const MOCK_TARGET_DIR = '/mock/target-dir';
|
||||
const mockOwnPaths = {
|
||||
resolve: (...paths: string[]) =>
|
||||
pathModule.join(MOCK_CREATE_APP_ROOT, ...paths),
|
||||
resolveRoot: (...paths: string[]) =>
|
||||
pathModule.join('/mock/monorepo-root', ...paths),
|
||||
};
|
||||
return {
|
||||
...actual,
|
||||
findPaths: jest.fn(),
|
||||
findOwnPaths: () => mockOwnPaths,
|
||||
targetPaths: {
|
||||
resolve: (...paths: string[]) =>
|
||||
pathModule.resolve(MOCK_TARGET_DIR, ...paths),
|
||||
resolveRoot: (...paths: string[]) =>
|
||||
pathModule.resolve('/mock/target-root', ...paths),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
// By mocking this the filesystem mocks won't mess with reading all of the package.jsons
|
||||
jest.mock('./lib/versions', () => ({
|
||||
packageVersions: { root: '1.0.0' },
|
||||
@@ -64,12 +88,7 @@ describe('command entrypoint', () => {
|
||||
expect(tryInitGitRepositoryMock).toHaveBeenCalled();
|
||||
expect(templatingMock).toHaveBeenCalled();
|
||||
expect(templatingMock.mock.lastCall?.[0]).toEqual(
|
||||
findPaths(__dirname).resolveTarget(
|
||||
'packages',
|
||||
'create-app',
|
||||
'templates',
|
||||
'default-app',
|
||||
),
|
||||
findOwnPaths(__dirname).resolve('templates/default-app'),
|
||||
);
|
||||
expect(templatingMock.mock.lastCall?.[1]).toContain(
|
||||
path.join(tmpdir(), 'MyApp'),
|
||||
@@ -85,12 +104,7 @@ describe('command entrypoint', () => {
|
||||
expect(tryInitGitRepositoryMock).toHaveBeenCalled();
|
||||
expect(templatingMock).toHaveBeenCalled();
|
||||
expect(templatingMock.mock.lastCall?.[0]).toEqual(
|
||||
findPaths(__dirname).resolveTarget(
|
||||
'packages',
|
||||
'create-app',
|
||||
'templates',
|
||||
'default-app',
|
||||
),
|
||||
findOwnPaths(__dirname).resolve('templates/default-app'),
|
||||
);
|
||||
expect(templatingMock.mock.lastCall?.[1]).toEqual('myDirectory');
|
||||
expect(buildAppMock).toHaveBeenCalled();
|
||||
@@ -103,12 +117,7 @@ describe('command entrypoint', () => {
|
||||
expect(tryInitGitRepositoryMock).toHaveBeenCalled();
|
||||
expect(templatingMock).toHaveBeenCalled();
|
||||
expect(templatingMock.mock.lastCall?.[0]).toEqual(
|
||||
findPaths(__dirname).resolveTarget(
|
||||
'packages',
|
||||
'create-app',
|
||||
'templates',
|
||||
'next-app',
|
||||
),
|
||||
findOwnPaths(__dirname).resolve('templates/next-app'),
|
||||
);
|
||||
expect(templatingMock.mock.lastCall?.[1]).toContain(
|
||||
path.join(tmpdir(), 'MyApp'),
|
||||
@@ -127,7 +136,7 @@ describe('command entrypoint', () => {
|
||||
expect(tryInitGitRepositoryMock).toHaveBeenCalled();
|
||||
expect(templatingMock).toHaveBeenCalled();
|
||||
expect(templatingMock.mock.lastCall?.[0]).toEqual(
|
||||
findPaths(__dirname).resolveTarget('templateDirectory'),
|
||||
targetPaths.resolve('templateDirectory'),
|
||||
);
|
||||
expect(templatingMock.mock.lastCall?.[1]).toEqual('myDirectory');
|
||||
expect(buildAppMock).toHaveBeenCalled();
|
||||
|
||||
@@ -18,7 +18,7 @@ import chalk from 'chalk';
|
||||
import { OptionValues } from 'commander';
|
||||
import inquirer, { Answers } from 'inquirer';
|
||||
import { resolve as resolvePath } from 'node:path';
|
||||
import { findPaths } from '@backstage/cli-common';
|
||||
import { targetPaths, findOwnPaths } from '@backstage/cli-common';
|
||||
import os from 'node:os';
|
||||
import fs from 'fs-extra';
|
||||
import {
|
||||
@@ -36,8 +36,6 @@ import {
|
||||
const DEFAULT_BRANCH = 'master';
|
||||
|
||||
export default async (opts: OptionValues): Promise<void> => {
|
||||
/* eslint-disable-next-line no-restricted-syntax */
|
||||
const paths = findPaths(__dirname);
|
||||
const answers: Answers = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
@@ -67,19 +65,19 @@ export default async (opts: OptionValues): Promise<void> => {
|
||||
|
||||
// Pick the built-in template based on the --next flag
|
||||
const builtInTemplate = opts.next
|
||||
? paths.resolveOwn('templates/next-app')
|
||||
: paths.resolveOwn('templates/default-app');
|
||||
? findOwnPaths(__dirname).resolve('templates/next-app')
|
||||
: findOwnPaths(__dirname).resolve('templates/default-app');
|
||||
|
||||
// Use `--template-path` argument as template when specified. Otherwise, use the default template.
|
||||
const templateDir = opts.templatePath
|
||||
? paths.resolveTarget(opts.templatePath)
|
||||
? targetPaths.resolve(opts.templatePath)
|
||||
: builtInTemplate;
|
||||
|
||||
// Use `--path` argument as application directory when specified, otherwise
|
||||
// create a directory using `answers.name`
|
||||
const appDir = opts.path
|
||||
? resolvePath(paths.targetDir, opts.path)
|
||||
: resolvePath(paths.targetDir, answers.name);
|
||||
? resolvePath(targetPaths.resolve(), opts.path)
|
||||
: resolvePath(targetPaths.resolve(), answers.name);
|
||||
|
||||
Task.log();
|
||||
Task.log('Creating the app...');
|
||||
@@ -102,7 +100,7 @@ export default async (opts: OptionValues): Promise<void> => {
|
||||
// Template to temporary location, and then move files
|
||||
|
||||
Task.section('Checking if the directory is available');
|
||||
await checkAppExistsTask(paths.targetDir, answers.name);
|
||||
await checkAppExistsTask(targetPaths.resolve(), answers.name);
|
||||
|
||||
Task.section('Creating a temporary app directory');
|
||||
const tempDir = await fs.mkdtemp(resolvePath(os.tmpdir(), answers.name));
|
||||
|
||||
@@ -27,12 +27,9 @@ import { waitFor, print } from '../lib/helpers';
|
||||
import mysql from 'mysql2/promise';
|
||||
import pgtools from 'pgtools';
|
||||
|
||||
import { findPaths, runOutput, run } from '@backstage/cli-common';
|
||||
import { findOwnPaths, runOutput, run } from '@backstage/cli-common';
|
||||
import { OptionValues } from 'commander';
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
const paths = findPaths(__dirname);
|
||||
|
||||
const templatePackagePaths = [
|
||||
'packages/cli/templates/frontend-plugin/package.json.hbs',
|
||||
'packages/create-app/templates/default-app/package.json.hbs',
|
||||
@@ -138,7 +135,7 @@ async function buildDistWorkspace(workspaceName: string, rootDir: string) {
|
||||
}
|
||||
|
||||
for (const pkgJsonPath of templatePackagePaths) {
|
||||
const jsonPath = paths.resolveOwnRoot(pkgJsonPath);
|
||||
const jsonPath = findOwnPaths(__dirname).resolveRoot(pkgJsonPath);
|
||||
const pkgTemplate = await fs.readFile(jsonPath, 'utf8');
|
||||
const pkg = JSON.parse(
|
||||
handlebars.compile(pkgTemplate)(
|
||||
@@ -196,7 +193,7 @@ async function buildDistWorkspace(workspaceName: string, rootDir: string) {
|
||||
print('Pinning yarn version in workspace');
|
||||
await pinYarnVersion(workspaceDir);
|
||||
|
||||
const yarnPatchesPath = paths.resolveOwnRoot('.yarn/patches');
|
||||
const yarnPatchesPath = findOwnPaths(__dirname).resolveRoot('.yarn/patches');
|
||||
if (await fs.pathExists(yarnPatchesPath)) {
|
||||
print('Copying yarn patches');
|
||||
await fs.copy(yarnPatchesPath, resolvePath(workspaceDir, '.yarn/patches'));
|
||||
@@ -214,7 +211,10 @@ async function buildDistWorkspace(workspaceName: string, rootDir: string) {
|
||||
* Pin the yarn version in a directory to the one we're using in the Backstage repo
|
||||
*/
|
||||
async function pinYarnVersion(dir: string) {
|
||||
const yarnRc = await fs.readFile(paths.resolveOwnRoot('.yarnrc.yml'), 'utf8');
|
||||
const yarnRc = await fs.readFile(
|
||||
findOwnPaths(__dirname).resolveRoot('.yarnrc.yml'),
|
||||
'utf8',
|
||||
);
|
||||
const yarnRcLines = yarnRc.split(/\r?\n/);
|
||||
const yarnPathLine = yarnRcLines.find(line => line.startsWith('yarnPath:'));
|
||||
if (!yarnPathLine) {
|
||||
@@ -225,8 +225,9 @@ async function pinYarnVersion(dir: string) {
|
||||
throw new Error(`Invalid 'yarnPath' in ${yarnRc}`);
|
||||
}
|
||||
const [, localYarnPath] = match;
|
||||
const yarnPath = paths.resolveOwnRoot(localYarnPath);
|
||||
const yarnPluginPath = paths.resolveOwnRoot(
|
||||
const ownPaths = findOwnPaths(__dirname);
|
||||
const yarnPath = ownPaths.resolveRoot(localYarnPath);
|
||||
const yarnPluginPath = ownPaths.resolveRoot(
|
||||
localYarnPath,
|
||||
'../../plugins/@yarnpkg/plugin-workspace-tools.cjs',
|
||||
);
|
||||
@@ -328,7 +329,7 @@ async function createApp(
|
||||
*/
|
||||
async function overrideYarnLockSeed(appDir: string) {
|
||||
const content = await fs.readFile(
|
||||
paths.resolveOwnRoot('packages/create-app/seed-yarn.lock'),
|
||||
findOwnPaths(__dirname).resolveRoot('packages/create-app/seed-yarn.lock'),
|
||||
'utf8',
|
||||
);
|
||||
const trimmedContent = content
|
||||
|
||||
@@ -14,13 +14,26 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { findPaths } from '@backstage/cli-common';
|
||||
import { targetPaths, findOwnPaths } 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 = findPaths(__dirname);
|
||||
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 {
|
||||
@@ -41,7 +54,7 @@ export async function resolvePackagePaths(
|
||||
for (const path of providedPaths) {
|
||||
const matches = packages.some(
|
||||
({ dir }) =>
|
||||
new Minimatch(path).match(relativePath(paths.targetRoot, dir)) ||
|
||||
new Minimatch(path).match(relativePath(targetPaths.resolveRoot(), dir)) ||
|
||||
isChildPath(dir, path),
|
||||
);
|
||||
if (!matches) {
|
||||
@@ -57,7 +70,7 @@ export async function resolvePackagePaths(
|
||||
packages = packages.filter(({ dir }) =>
|
||||
providedPaths.some(
|
||||
path =>
|
||||
new Minimatch(path).match(relativePath(paths.targetRoot, dir)) ||
|
||||
new Minimatch(path).match(relativePath(targetPaths.resolveRoot(), dir)) ||
|
||||
isChildPath(dir, path),
|
||||
),
|
||||
);
|
||||
@@ -66,7 +79,7 @@ export async function resolvePackagePaths(
|
||||
if (include) {
|
||||
packages = packages.filter(pkg =>
|
||||
include.some(pattern =>
|
||||
new Minimatch(pattern).match(relativePath(paths.targetRoot, pkg.dir)),
|
||||
new Minimatch(pattern).match(relativePath(targetPaths.resolveRoot(), pkg.dir)),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -76,13 +89,13 @@ export async function resolvePackagePaths(
|
||||
exclude.some(
|
||||
pattern =>
|
||||
!new Minimatch(pattern).match(
|
||||
relativePath(paths.targetRoot, pkg.dir),
|
||||
relativePath(targetPaths.resolveRoot(), pkg.dir),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return packages.map(pkg => relativePath(paths.targetRoot, pkg.dir));
|
||||
return packages.map(pkg => relativePath(targetPaths.resolveRoot(), pkg.dir));
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import { OptionValues } from 'commander';
|
||||
import path from 'node:path';
|
||||
import openBrowser from 'react-dev-utils/openBrowser';
|
||||
import { findPaths, RunOnOutput } from '@backstage/cli-common';
|
||||
import { findOwnPaths, RunOnOutput } from '@backstage/cli-common';
|
||||
import HTTPServer from '../../lib/httpServer';
|
||||
import { runMkdocsServer } from '../../lib/mkdocsServer';
|
||||
import { createLogger } from '../../lib/utility';
|
||||
@@ -39,8 +39,7 @@ function findPreviewBundlePath(): string {
|
||||
// This can be tested by running `yarn pack` and extracting the resulting tarball into a directory.
|
||||
// Within the extracted directory, run `npm install --only=prod`.
|
||||
// Once that's done you can test the CLI in any directory using `node <tmp-dir>/package <command>`.
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
return findPaths(__dirname).resolveOwn('dist/embedded-app');
|
||||
return findOwnPaths(__dirname).resolve('dist/embedded-app');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import { spawn, SpawnOptionsWithoutStdio } from 'node:child_process';
|
||||
import fs from 'fs-extra';
|
||||
import yaml from 'yaml';
|
||||
import { buildDepTreeFromFiles } from 'snyk-nodejs-lockfile-parser';
|
||||
import { findPaths } from '@backstage/cli-common';
|
||||
import { targetPaths } from '@backstage/cli-common';
|
||||
import { createMockDirectory } from '@backstage/backend-test-utils';
|
||||
|
||||
jest.setTimeout(30_000);
|
||||
@@ -86,7 +86,7 @@ describe('Backstage yarn plugin', () => {
|
||||
let initialLockFileContent: string | undefined;
|
||||
|
||||
beforeAll(async () => {
|
||||
const { targetRoot } = findPaths(process.cwd());
|
||||
const targetRoot = targetPaths.resolveRoot();
|
||||
await executeCommand('yarn', ['build'], {
|
||||
cwd: joinPath(targetRoot, 'packages/yarn-plugin'),
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { findPaths, Paths } from '@backstage/cli-common';
|
||||
import { targetPaths } from '@backstage/cli-common';
|
||||
|
||||
const setPlatform = (platform: string) => {
|
||||
Object.defineProperty(process, `platform`, {
|
||||
@@ -37,7 +37,7 @@ describe('getWorkspaceRoot', () => {
|
||||
`('platform: $platform', ({ platform, native, portable }) => {
|
||||
let realPlatform: string;
|
||||
let getWorkspaceRoot: () => string;
|
||||
let mockFindPaths: jest.MockedFunction<typeof findPaths>;
|
||||
let mockResolveRoot: jest.MockedFunction<typeof targetPaths.resolveRoot>;
|
||||
|
||||
beforeEach(() => {
|
||||
realPlatform = process.platform;
|
||||
@@ -45,11 +45,13 @@ describe('getWorkspaceRoot', () => {
|
||||
|
||||
jest.resetModules();
|
||||
|
||||
mockFindPaths = jest.fn();
|
||||
mockResolveRoot = jest.fn();
|
||||
|
||||
jest.doMock('@backstage/cli-common', () => ({
|
||||
...jest.requireActual('@backstage/cli-common'),
|
||||
findPaths: mockFindPaths,
|
||||
targetPaths: {
|
||||
resolveRoot: mockResolveRoot,
|
||||
},
|
||||
}));
|
||||
|
||||
getWorkspaceRoot = require('./getWorkspaceRoot').getWorkspaceRoot;
|
||||
@@ -60,9 +62,7 @@ describe('getWorkspaceRoot', () => {
|
||||
});
|
||||
|
||||
it('returns an appropriately-formatted workspace root path', () => {
|
||||
mockFindPaths.mockReturnValue({
|
||||
targetRoot: native,
|
||||
} as Paths);
|
||||
mockResolveRoot.mockReturnValue(native);
|
||||
|
||||
expect(getWorkspaceRoot()).toEqual(portable);
|
||||
});
|
||||
|
||||
@@ -14,11 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { npath, ppath } from '@yarnpkg/fslib';
|
||||
import { findPaths } from '@backstage/cli-common';
|
||||
import { npath } from '@yarnpkg/fslib';
|
||||
import { targetPaths } from '@backstage/cli-common';
|
||||
|
||||
export const getWorkspaceRoot = () => {
|
||||
return npath.toPortablePath(
|
||||
findPaths(npath.fromPortablePath(ppath.cwd())).targetRoot,
|
||||
);
|
||||
return npath.toPortablePath(targetPaths.resolveRoot());
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user