@backstage/cli-common 0.2.0-next.0 → 0.2.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @backstage/cli-common
2
2
 
3
+ ## 0.2.0-next.1
4
+
5
+ ### Patch Changes
6
+
7
+ - e44b6a9: The `findOwnRootDir` utility now searches for the monorepo root by traversing up the directory tree looking for a `package.json` with `workspaces`, instead of assuming a fixed `../..` relative path. If no workspaces root is found during this traversal, `findOwnRootDir` now throws to enforce stricter validation of the repository layout.
8
+ - Updated dependencies
9
+ - @backstage/errors@1.2.7
10
+
3
11
  ## 0.2.0-next.0
4
12
 
5
13
  ### Minor Changes
package/dist/paths.cjs.js CHANGED
@@ -32,7 +32,21 @@ function findOwnRootDir(ownDir) {
32
32
  "Tried to access monorepo package root dir outside of Backstage repository"
33
33
  );
34
34
  }
35
- return node_path.resolve(ownDir, "../..");
35
+ const rootDir = findRootPath(ownDir, (pkgJsonPath) => {
36
+ try {
37
+ const content = fs__default.default.readFileSync(pkgJsonPath, "utf8");
38
+ const data = JSON.parse(content);
39
+ return Boolean(data.workspaces);
40
+ } catch (error) {
41
+ throw new Error(
42
+ `Failed to read package.json at '${pkgJsonPath}', ${error}`
43
+ );
44
+ }
45
+ });
46
+ if (!rootDir) {
47
+ throw new Error(`No monorepo root found when searching from '${ownDir}'`);
48
+ }
49
+ return rootDir;
36
50
  }
37
51
  const dirCache = /* @__PURE__ */ new Map();
38
52
  class OwnPathsImpl {
@@ -1 +1 @@
1
- {"version":3,"file":"paths.cjs.js","sources":["../src/paths.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport fs from 'node:fs';\nimport { dirname, resolve as resolvePath } from 'node:path';\n\n/**\n * A function that takes a set of path fragments and resolves them into a\n * single complete path, relative to some root.\n *\n * @public\n */\nexport type ResolveFunc = (...paths: string[]) => string;\n\n/**\n * Resolved paths relative to the target project, based on `process.cwd()`.\n * Lazily initialized on first property access. Re-resolves automatically\n * when `process.cwd()` changes.\n *\n * @public\n */\nexport type TargetPaths = {\n /** The target package directory. */\n dir: string;\n\n /** The target monorepo root directory. */\n rootDir: string;\n\n /** Resolve a path relative to the target package directory. */\n resolve: ResolveFunc;\n\n /** Resolve a path relative to the target repo root. */\n resolveRoot: ResolveFunc;\n};\n\n/**\n * Resolved paths relative to a specific package in the repository.\n *\n * @public\n */\nexport type OwnPaths = {\n /** The package root directory. */\n dir: string;\n\n /** The monorepo root directory containing the package. */\n rootDir: string;\n\n /** Resolve a path relative to the package root. */\n resolve: ResolveFunc;\n\n /** Resolve a path relative to the monorepo root containing the package. */\n resolveRoot: ResolveFunc;\n};\n\n/**\n * Common paths and resolve functions used by the cli.\n * Currently assumes it is being executed within a monorepo.\n *\n * @public\n * @deprecated Use {@link targetPaths} and {@link findOwnPaths} instead.\n */\nexport type Paths = {\n ownDir: string;\n ownRoot: string;\n targetDir: string;\n targetRoot: string;\n resolveOwn: ResolveFunc;\n resolveOwnRoot: ResolveFunc;\n resolveTarget: ResolveFunc;\n resolveTargetRoot: ResolveFunc;\n};\n\n// Looks for a package.json with a workspace config to identify the root of the monorepo\nexport function findRootPath(\n searchDir: string,\n filterFunc: (pkgJsonPath: string) => boolean,\n): string | undefined {\n let path = searchDir;\n\n // Some confidence check to avoid infinite loop\n for (let i = 0; i < 1000; i++) {\n const packagePath = resolvePath(path, 'package.json');\n const exists = fs.existsSync(packagePath);\n if (exists && filterFunc(packagePath)) {\n return path;\n }\n\n const newPath = dirname(path);\n if (newPath === path) {\n return undefined;\n }\n path = newPath;\n }\n\n throw new Error(\n `Iteration limit reached when searching for root package.json at ${searchDir}`,\n );\n}\n\n// Finds the root of the monorepo that the package exists in.\nexport function findOwnRootDir(ownDir: string) {\n const isLocal = fs.existsSync(resolvePath(ownDir, 'src'));\n if (!isLocal) {\n throw new Error(\n 'Tried to access monorepo package root dir outside of Backstage repository',\n );\n }\n\n return resolvePath(ownDir, '../..');\n}\n\n// Hierarchical directory cache shared across all OwnPathsImpl instances.\n// When we resolve a searchDir to its package root, we also cache every\n// intermediate directory, so sibling directories share work.\nconst dirCache = new Map<string, string>();\n\nclass OwnPathsImpl implements OwnPaths {\n static #instanceCache = new Map<string, OwnPathsImpl>();\n\n static find(searchDir: string): OwnPathsImpl {\n const dir = OwnPathsImpl.findDir(searchDir);\n let instance = OwnPathsImpl.#instanceCache.get(dir);\n if (!instance) {\n instance = new OwnPathsImpl(dir);\n OwnPathsImpl.#instanceCache.set(dir, instance);\n }\n return instance;\n }\n\n static findDir(searchDir: string): string {\n const visited: string[] = [];\n let dir = searchDir;\n\n for (let i = 0; i < 1000; i++) {\n const cached = dirCache.get(dir);\n if (cached !== undefined) {\n for (const d of visited) {\n dirCache.set(d, cached);\n }\n return cached;\n }\n\n visited.push(dir);\n\n if (fs.existsSync(resolvePath(dir, 'package.json'))) {\n for (const d of visited) {\n dirCache.set(d, dir);\n }\n return dir;\n }\n\n const newDir = dirname(dir);\n if (newDir === dir) {\n break;\n }\n dir = newDir;\n }\n\n throw new Error(\n `No package.json found while searching for package root of ${searchDir}`,\n );\n }\n\n #dir: string;\n #rootDir: string | undefined;\n\n private constructor(dir: string) {\n this.#dir = dir;\n }\n\n get dir(): string {\n return this.#dir;\n }\n\n get rootDir(): string {\n this.#rootDir ??= findOwnRootDir(this.#dir);\n return this.#rootDir;\n }\n\n resolve = (...paths: string[]): string => {\n return resolvePath(this.#dir, ...paths);\n };\n\n resolveRoot = (...paths: string[]): string => {\n return resolvePath(this.rootDir, ...paths);\n };\n}\n\n// Finds the root of a given package\nexport function findOwnDir(searchDir: string) {\n return OwnPathsImpl.findDir(searchDir);\n}\n\n// Used by the test utility in testUtils.ts to override targetPaths\nexport let targetPathsOverride: TargetPaths | undefined;\n\n/** @internal */\nexport function setTargetPathsOverride(override: TargetPaths | undefined) {\n targetPathsOverride = override;\n}\n\nclass TargetPathsImpl implements TargetPaths {\n #cwd: string | undefined;\n #dir: string | undefined;\n #rootDir: string | undefined;\n\n get dir(): string {\n if (targetPathsOverride) {\n return targetPathsOverride.dir;\n }\n const cwd = process.cwd();\n if (this.#dir !== undefined && this.#cwd === cwd) {\n return this.#dir;\n }\n this.#cwd = cwd;\n this.#rootDir = undefined;\n // Drive letter can end up being lowercased here on Windows, bring back to uppercase for consistency\n this.#dir = fs\n .realpathSync(cwd)\n .replace(/^[a-z]:/, str => str.toLocaleUpperCase('en-US'));\n return this.#dir;\n }\n\n get rootDir(): string {\n if (targetPathsOverride) {\n return targetPathsOverride.rootDir;\n }\n // Access dir first to ensure cwd is fresh, which also invalidates rootDir on cwd change\n const dir = this.dir;\n if (this.#rootDir !== undefined) {\n return this.#rootDir;\n }\n // Lazy init to only crash commands that require a monorepo when we're not in one\n this.#rootDir =\n findRootPath(dir, path => {\n try {\n const content = fs.readFileSync(path, 'utf8');\n const data = JSON.parse(content);\n return Boolean(data.workspaces);\n } catch (error) {\n throw new Error(\n `Failed to parse package.json file while searching for root, ${error}`,\n );\n }\n }) ?? dir;\n return this.#rootDir;\n }\n\n resolve = (...paths: string[]): string => {\n if (targetPathsOverride) {\n return targetPathsOverride.resolve(...paths);\n }\n return resolvePath(this.dir, ...paths);\n };\n\n resolveRoot = (...paths: string[]): string => {\n if (targetPathsOverride) {\n return targetPathsOverride.resolveRoot(...paths);\n }\n return resolvePath(this.rootDir, ...paths);\n };\n}\n\n/**\n * Lazily resolved paths relative to the target project. Import this directly\n * for cwd-based path resolution without needing `__dirname`.\n *\n * @public\n */\nexport const targetPaths: TargetPaths = new TargetPathsImpl();\n\n/**\n * Find paths relative to the package that the calling code lives in.\n *\n * Results are cached per package root, and the package root lookup uses a\n * hierarchical directory cache so that multiple calls from different\n * subdirectories within the same package share work.\n *\n * @public\n */\nexport function findOwnPaths(searchDir: string): OwnPaths {\n return OwnPathsImpl.find(searchDir);\n}\n\n/**\n * Find paths related to a package and its execution context.\n *\n * @public\n * @deprecated Use {@link targetPaths} for cwd-based paths and\n * {@link findOwnPaths} for package-relative paths instead.\n *\n * @example\n *\n * const paths = findPaths(__dirname)\n */\nexport function findPaths(searchDir: string): Paths {\n const own = findOwnPaths(searchDir);\n return {\n get ownDir() {\n return own.dir;\n },\n get ownRoot() {\n return own.rootDir;\n },\n get targetDir() {\n return targetPaths.dir;\n },\n get targetRoot() {\n return targetPaths.rootDir;\n },\n resolveOwn: own.resolve,\n resolveOwnRoot: own.resolveRoot,\n resolveTarget: targetPaths.resolve,\n resolveTargetRoot: targetPaths.resolveRoot,\n };\n}\n\n/**\n * The name of the backstage's config file\n *\n * @public\n */\nexport const BACKSTAGE_JSON = 'backstage.json';\n"],"names":["resolvePath","fs","dirname","targetPathsOverride"],"mappings":"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"}
1
+ {"version":3,"file":"paths.cjs.js","sources":["../src/paths.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport fs from 'node:fs';\nimport { dirname, resolve as resolvePath } from 'node:path';\n\n/**\n * A function that takes a set of path fragments and resolves them into a\n * single complete path, relative to some root.\n *\n * @public\n */\nexport type ResolveFunc = (...paths: string[]) => string;\n\n/**\n * Resolved paths relative to the target project, based on `process.cwd()`.\n * Lazily initialized on first property access. Re-resolves automatically\n * when `process.cwd()` changes.\n *\n * @public\n */\nexport type TargetPaths = {\n /** The target package directory. */\n dir: string;\n\n /** The target monorepo root directory. */\n rootDir: string;\n\n /** Resolve a path relative to the target package directory. */\n resolve: ResolveFunc;\n\n /** Resolve a path relative to the target repo root. */\n resolveRoot: ResolveFunc;\n};\n\n/**\n * Resolved paths relative to a specific package in the repository.\n *\n * @public\n */\nexport type OwnPaths = {\n /** The package root directory. */\n dir: string;\n\n /** The monorepo root directory containing the package. */\n rootDir: string;\n\n /** Resolve a path relative to the package root. */\n resolve: ResolveFunc;\n\n /** Resolve a path relative to the monorepo root containing the package. */\n resolveRoot: ResolveFunc;\n};\n\n/**\n * Common paths and resolve functions used by the cli.\n * Currently assumes it is being executed within a monorepo.\n *\n * @public\n * @deprecated Use {@link targetPaths} and {@link findOwnPaths} instead.\n */\nexport type Paths = {\n ownDir: string;\n ownRoot: string;\n targetDir: string;\n targetRoot: string;\n resolveOwn: ResolveFunc;\n resolveOwnRoot: ResolveFunc;\n resolveTarget: ResolveFunc;\n resolveTargetRoot: ResolveFunc;\n};\n\n// Looks for a package.json with a workspace config to identify the root of the monorepo\nexport function findRootPath(\n searchDir: string,\n filterFunc: (pkgJsonPath: string) => boolean,\n): string | undefined {\n let path = searchDir;\n\n // Some confidence check to avoid infinite loop\n for (let i = 0; i < 1000; i++) {\n const packagePath = resolvePath(path, 'package.json');\n const exists = fs.existsSync(packagePath);\n if (exists && filterFunc(packagePath)) {\n return path;\n }\n\n const newPath = dirname(path);\n if (newPath === path) {\n return undefined;\n }\n path = newPath;\n }\n\n throw new Error(\n `Iteration limit reached when searching for root package.json at ${searchDir}`,\n );\n}\n\n// Finds the root of the monorepo that the package exists in.\nexport function findOwnRootDir(ownDir: string) {\n const isLocal = fs.existsSync(resolvePath(ownDir, 'src'));\n if (!isLocal) {\n throw new Error(\n 'Tried to access monorepo package root dir outside of Backstage repository',\n );\n }\n\n const rootDir = findRootPath(ownDir, pkgJsonPath => {\n try {\n const content = fs.readFileSync(pkgJsonPath, 'utf8');\n const data = JSON.parse(content);\n return Boolean(data.workspaces);\n } catch (error) {\n throw new Error(\n `Failed to read package.json at '${pkgJsonPath}', ${error}`,\n );\n }\n });\n\n if (!rootDir) {\n throw new Error(`No monorepo root found when searching from '${ownDir}'`);\n }\n\n return rootDir;\n}\n\n// Hierarchical directory cache shared across all OwnPathsImpl instances.\n// When we resolve a searchDir to its package root, we also cache every\n// intermediate directory, so sibling directories share work.\nconst dirCache = new Map<string, string>();\n\nclass OwnPathsImpl implements OwnPaths {\n static #instanceCache = new Map<string, OwnPathsImpl>();\n\n static find(searchDir: string): OwnPathsImpl {\n const dir = OwnPathsImpl.findDir(searchDir);\n let instance = OwnPathsImpl.#instanceCache.get(dir);\n if (!instance) {\n instance = new OwnPathsImpl(dir);\n OwnPathsImpl.#instanceCache.set(dir, instance);\n }\n return instance;\n }\n\n static findDir(searchDir: string): string {\n const visited: string[] = [];\n let dir = searchDir;\n\n for (let i = 0; i < 1000; i++) {\n const cached = dirCache.get(dir);\n if (cached !== undefined) {\n for (const d of visited) {\n dirCache.set(d, cached);\n }\n return cached;\n }\n\n visited.push(dir);\n\n if (fs.existsSync(resolvePath(dir, 'package.json'))) {\n for (const d of visited) {\n dirCache.set(d, dir);\n }\n return dir;\n }\n\n const newDir = dirname(dir);\n if (newDir === dir) {\n break;\n }\n dir = newDir;\n }\n\n throw new Error(\n `No package.json found while searching for package root of ${searchDir}`,\n );\n }\n\n #dir: string;\n #rootDir: string | undefined;\n\n private constructor(dir: string) {\n this.#dir = dir;\n }\n\n get dir(): string {\n return this.#dir;\n }\n\n get rootDir(): string {\n this.#rootDir ??= findOwnRootDir(this.#dir);\n return this.#rootDir;\n }\n\n resolve = (...paths: string[]): string => {\n return resolvePath(this.#dir, ...paths);\n };\n\n resolveRoot = (...paths: string[]): string => {\n return resolvePath(this.rootDir, ...paths);\n };\n}\n\n// Used by the test utility in testUtils.ts to override targetPaths\nexport let targetPathsOverride: TargetPaths | undefined;\n\n/** @internal */\nexport function setTargetPathsOverride(override: TargetPaths | undefined) {\n targetPathsOverride = override;\n}\n\nclass TargetPathsImpl implements TargetPaths {\n #cwd: string | undefined;\n #dir: string | undefined;\n #rootDir: string | undefined;\n\n get dir(): string {\n if (targetPathsOverride) {\n return targetPathsOverride.dir;\n }\n const cwd = process.cwd();\n if (this.#dir !== undefined && this.#cwd === cwd) {\n return this.#dir;\n }\n this.#cwd = cwd;\n this.#rootDir = undefined;\n // Drive letter can end up being lowercased here on Windows, bring back to uppercase for consistency\n this.#dir = fs\n .realpathSync(cwd)\n .replace(/^[a-z]:/, str => str.toLocaleUpperCase('en-US'));\n return this.#dir;\n }\n\n get rootDir(): string {\n if (targetPathsOverride) {\n return targetPathsOverride.rootDir;\n }\n // Access dir first to ensure cwd is fresh, which also invalidates rootDir on cwd change\n const dir = this.dir;\n if (this.#rootDir !== undefined) {\n return this.#rootDir;\n }\n // Lazy init to only crash commands that require a monorepo when we're not in one\n this.#rootDir =\n findRootPath(dir, path => {\n try {\n const content = fs.readFileSync(path, 'utf8');\n const data = JSON.parse(content);\n return Boolean(data.workspaces);\n } catch (error) {\n throw new Error(\n `Failed to parse package.json file while searching for root, ${error}`,\n );\n }\n }) ?? dir;\n return this.#rootDir;\n }\n\n resolve = (...paths: string[]): string => {\n if (targetPathsOverride) {\n return targetPathsOverride.resolve(...paths);\n }\n return resolvePath(this.dir, ...paths);\n };\n\n resolveRoot = (...paths: string[]): string => {\n if (targetPathsOverride) {\n return targetPathsOverride.resolveRoot(...paths);\n }\n return resolvePath(this.rootDir, ...paths);\n };\n}\n\n/**\n * Lazily resolved paths relative to the target project. Import this directly\n * for cwd-based path resolution without needing `__dirname`.\n *\n * @public\n */\nexport const targetPaths: TargetPaths = new TargetPathsImpl();\n\n/**\n * Find paths relative to the package that the calling code lives in.\n *\n * Results are cached per package root, and the package root lookup uses a\n * hierarchical directory cache so that multiple calls from different\n * subdirectories within the same package share work.\n *\n * @public\n */\nexport function findOwnPaths(searchDir: string): OwnPaths {\n return OwnPathsImpl.find(searchDir);\n}\n\n/**\n * Find paths related to a package and its execution context.\n *\n * @public\n * @deprecated Use {@link targetPaths} for cwd-based paths and\n * {@link findOwnPaths} for package-relative paths instead.\n *\n * @example\n *\n * const paths = findPaths(__dirname)\n */\nexport function findPaths(searchDir: string): Paths {\n const own = findOwnPaths(searchDir);\n return {\n get ownDir() {\n return own.dir;\n },\n get ownRoot() {\n return own.rootDir;\n },\n get targetDir() {\n return targetPaths.dir;\n },\n get targetRoot() {\n return targetPaths.rootDir;\n },\n resolveOwn: own.resolve,\n resolveOwnRoot: own.resolveRoot,\n resolveTarget: targetPaths.resolve,\n resolveTargetRoot: targetPaths.resolveRoot,\n };\n}\n\n/**\n * The name of the backstage's config file\n *\n * @public\n */\nexport const BACKSTAGE_JSON = 'backstage.json';\n"],"names":["resolvePath","fs","dirname","targetPathsOverride"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/cli-common",
3
- "version": "0.2.0-next.0",
3
+ "version": "0.2.0-next.1",
4
4
  "description": "Common functionality used by cli, backend, and create-app",
5
5
  "backstage": {
6
6
  "role": "node-library"
@@ -62,7 +62,7 @@
62
62
  "undici": "^7.2.3"
63
63
  },
64
64
  "devDependencies": {
65
- "@backstage/cli": "0.35.5-next.0",
65
+ "@backstage/cli": "0.36.0-next.1",
66
66
  "@types/cross-spawn": "^6.0.2",
67
67
  "@types/node": "^22.13.14"
68
68
  }