@backstage/cli-common 0.1.16 → 0.1.18-next.0

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,19 @@
1
1
  # @backstage/cli-common
2
2
 
3
+ ## 0.1.18-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 7455dae: Use node prefix on native imports
8
+ - Updated dependencies
9
+ - @backstage/errors@1.2.7
10
+
11
+ ## 0.1.17
12
+
13
+ ### Patch Changes
14
+
15
+ - ae4dd5d: Move some of the symlink resolution to `isChildPath`
16
+
3
17
  ## 0.1.16
4
18
 
5
19
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { SpawnOptions, ChildProcess } from 'child_process';
1
+ import { SpawnOptions, ChildProcess } from 'node:child_process';
2
2
  import { CustomErrorBase } from '@backstage/errors';
3
3
 
4
4
  /**
@@ -1,14 +1,41 @@
1
1
  'use strict';
2
2
 
3
- var path = require('path');
3
+ var node_path = require('node:path');
4
+ var fs = require('node:fs');
4
5
 
5
- function isChildPath(base, path$1) {
6
- const relativePath = path.relative(base, path$1);
6
+ function resolveRealPath(path) {
7
+ try {
8
+ return fs.realpathSync(path);
9
+ } catch (ex) {
10
+ if (ex.code !== "ENOENT") {
11
+ throw ex;
12
+ }
13
+ }
14
+ try {
15
+ if (fs.lstatSync(path).isSymbolicLink()) {
16
+ const target = node_path.resolve(node_path.dirname(path), fs.readlinkSync(path));
17
+ return resolveRealPath(target);
18
+ }
19
+ } catch (ex) {
20
+ if (ex.code !== "ENOENT") {
21
+ throw ex;
22
+ }
23
+ }
24
+ const parent = node_path.dirname(path);
25
+ if (parent === path) {
26
+ return path;
27
+ }
28
+ return node_path.resolve(resolveRealPath(parent), node_path.basename(path));
29
+ }
30
+ function isChildPath(base, path) {
31
+ const resolvedBase = resolveRealPath(base);
32
+ const resolvedPath = resolveRealPath(path);
33
+ const relativePath = node_path.relative(resolvedBase, resolvedPath);
7
34
  if (relativePath === "") {
8
35
  return true;
9
36
  }
10
37
  const outsideBase = relativePath.startsWith("..");
11
- const differentDrive = path.isAbsolute(relativePath);
38
+ const differentDrive = node_path.isAbsolute(relativePath);
12
39
  return !outsideBase && !differentDrive;
13
40
  }
14
41
 
@@ -1 +1 @@
1
- {"version":3,"file":"isChildPath.cjs.js","sources":["../src/isChildPath.ts"],"sourcesContent":["/*\n * Copyright 2021 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 { relative, isAbsolute } from 'path';\n\n/**\n * Checks if path is the same as or a child path of base.\n *\n * @public\n */\nexport function isChildPath(base: string, path: string): boolean {\n const relativePath = relative(base, path);\n if (relativePath === '') {\n // The same directory\n return true;\n }\n\n const outsideBase = relativePath.startsWith('..'); // not outside base\n const differentDrive = isAbsolute(relativePath); // on Windows, this means dir is on a different drive from base.\n\n return !outsideBase && !differentDrive;\n}\n"],"names":["path","relative","isAbsolute"],"mappings":";;;;AAuBO,SAAS,WAAA,CAAY,MAAcA,MAAA,EAAuB;AAC/D,EAAA,MAAM,YAAA,GAAeC,aAAA,CAAS,IAAA,EAAMD,MAAI,CAAA;AACxC,EAAA,IAAI,iBAAiB,EAAA,EAAI;AAEvB,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,WAAA,GAAc,YAAA,CAAa,UAAA,CAAW,IAAI,CAAA;AAChD,EAAA,MAAM,cAAA,GAAiBE,gBAAW,YAAY,CAAA;AAE9C,EAAA,OAAO,CAAC,eAAe,CAAC,cAAA;AAC1B;;;;"}
1
+ {"version":3,"file":"isChildPath.cjs.js","sources":["../src/isChildPath.ts"],"sourcesContent":["/*\n * Copyright 2021 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 {\n relative,\n isAbsolute,\n resolve as resolvePath,\n dirname,\n basename,\n} from 'node:path';\nimport { realpathSync, lstatSync, readlinkSync } from 'node:fs';\n\n// Resolves a path to its real location, following symlinks.\n// Handles cases where the final target doesn't exist by recursively\n// resolving parent directories.\nfunction resolveRealPath(path: string): string {\n try {\n return realpathSync(path);\n } catch (ex) {\n if (ex.code !== 'ENOENT') {\n throw ex;\n }\n }\n\n // Check if path itself is a dangling symlink - recursively resolve the target\n // to handle symlink chains (e.g., link1 -> link2 -> /outside)\n try {\n if (lstatSync(path).isSymbolicLink()) {\n const target = resolvePath(dirname(path), readlinkSync(path));\n return resolveRealPath(target);\n }\n } catch (ex) {\n if (ex.code !== 'ENOENT') {\n throw ex;\n }\n }\n\n // Path doesn't exist - walk up the tree until we find an existing path,\n // resolve it, then rebuild the non-existent portion on top\n const parent = dirname(path);\n if (parent === path) {\n return path; // Hit filesystem root\n }\n\n return resolvePath(resolveRealPath(parent), basename(path));\n}\n\n/**\n * Checks if path is the same as or a child path of base.\n *\n * @public\n */\nexport function isChildPath(base: string, path: string): boolean {\n const resolvedBase = resolveRealPath(base);\n const resolvedPath = resolveRealPath(path);\n\n const relativePath = relative(resolvedBase, resolvedPath);\n if (relativePath === '') {\n // The same directory\n return true;\n }\n\n const outsideBase = relativePath.startsWith('..'); // not outside base\n const differentDrive = isAbsolute(relativePath); // on Windows, this means dir is on a different drive from base.\n\n return !outsideBase && !differentDrive;\n}\n"],"names":["realpathSync","lstatSync","resolvePath","dirname","readlinkSync","basename","relative","isAbsolute"],"mappings":";;;;;AA4BA,SAAS,gBAAgB,IAAA,EAAsB;AAC7C,EAAA,IAAI;AACF,IAAA,OAAOA,gBAAa,IAAI,CAAA;AAAA,EAC1B,SAAS,EAAA,EAAI;AACX,IAAA,IAAI,EAAA,CAAG,SAAS,QAAA,EAAU;AACxB,MAAA,MAAM,EAAA;AAAA,IACR;AAAA,EACF;AAIA,EAAA,IAAI;AACF,IAAA,IAAIC,YAAA,CAAU,IAAI,CAAA,CAAE,cAAA,EAAe,EAAG;AACpC,MAAA,MAAM,SAASC,iBAAA,CAAYC,iBAAA,CAAQ,IAAI,CAAA,EAAGC,eAAA,CAAa,IAAI,CAAC,CAAA;AAC5D,MAAA,OAAO,gBAAgB,MAAM,CAAA;AAAA,IAC/B;AAAA,EACF,SAAS,EAAA,EAAI;AACX,IAAA,IAAI,EAAA,CAAG,SAAS,QAAA,EAAU;AACxB,MAAA,MAAM,EAAA;AAAA,IACR;AAAA,EACF;AAIA,EAAA,MAAM,MAAA,GAASD,kBAAQ,IAAI,CAAA;AAC3B,EAAA,IAAI,WAAW,IAAA,EAAM;AACnB,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,OAAOD,kBAAY,eAAA,CAAgB,MAAM,CAAA,EAAGG,kBAAA,CAAS,IAAI,CAAC,CAAA;AAC5D;AAOO,SAAS,WAAA,CAAY,MAAc,IAAA,EAAuB;AAC/D,EAAA,MAAM,YAAA,GAAe,gBAAgB,IAAI,CAAA;AACzC,EAAA,MAAM,YAAA,GAAe,gBAAgB,IAAI,CAAA;AAEzC,EAAA,MAAM,YAAA,GAAeC,kBAAA,CAAS,YAAA,EAAc,YAAY,CAAA;AACxD,EAAA,IAAI,iBAAiB,EAAA,EAAI;AAEvB,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,WAAA,GAAc,YAAA,CAAa,UAAA,CAAW,IAAI,CAAA;AAChD,EAAA,MAAM,cAAA,GAAiBC,qBAAW,YAAY,CAAA;AAE9C,EAAA,OAAO,CAAC,eAAe,CAAC,cAAA;AAC1B;;;;"}
package/dist/paths.cjs.js CHANGED
@@ -1,25 +1,25 @@
1
1
  'use strict';
2
2
 
3
- var fs = require('fs');
4
- var path = require('path');
3
+ var fs = require('node:fs');
4
+ var node_path = require('node:path');
5
5
 
6
6
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
7
7
 
8
8
  var fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
9
9
 
10
10
  function findRootPath(searchDir, filterFunc) {
11
- let path$1 = searchDir;
11
+ let path = searchDir;
12
12
  for (let i = 0; i < 1e3; i++) {
13
- const packagePath = path.resolve(path$1, "package.json");
13
+ const packagePath = node_path.resolve(path, "package.json");
14
14
  const exists = fs__default.default.existsSync(packagePath);
15
15
  if (exists && filterFunc(packagePath)) {
16
- return path$1;
16
+ return path;
17
17
  }
18
- const newPath = path.dirname(path$1);
19
- if (newPath === path$1) {
18
+ const newPath = node_path.dirname(path);
19
+ if (newPath === path) {
20
20
  return void 0;
21
21
  }
22
- path$1 = newPath;
22
+ path = newPath;
23
23
  }
24
24
  throw new Error(
25
25
  `Iteration limit reached when searching for root package.json at ${searchDir}`
@@ -35,13 +35,13 @@ function findOwnDir(searchDir) {
35
35
  return path;
36
36
  }
37
37
  function findOwnRootDir(ownDir) {
38
- const isLocal = fs__default.default.existsSync(path.resolve(ownDir, "src"));
38
+ const isLocal = fs__default.default.existsSync(node_path.resolve(ownDir, "src"));
39
39
  if (!isLocal) {
40
40
  throw new Error(
41
41
  "Tried to access monorepo package root dir outside of Backstage repository"
42
42
  );
43
43
  }
44
- return path.resolve(ownDir, "../..");
44
+ return node_path.resolve(ownDir, "../..");
45
45
  }
46
46
  function findPaths(searchDir) {
47
47
  const ownDir = findOwnDir(searchDir);
@@ -79,10 +79,10 @@ function findPaths(searchDir) {
79
79
  get targetRoot() {
80
80
  return getTargetRoot();
81
81
  },
82
- resolveOwn: (...paths) => path.resolve(ownDir, ...paths),
83
- resolveOwnRoot: (...paths) => path.resolve(getOwnRoot(), ...paths),
84
- resolveTarget: (...paths) => path.resolve(targetDir, ...paths),
85
- resolveTargetRoot: (...paths) => path.resolve(getTargetRoot(), ...paths)
82
+ resolveOwn: (...paths) => node_path.resolve(ownDir, ...paths),
83
+ resolveOwnRoot: (...paths) => node_path.resolve(getOwnRoot(), ...paths),
84
+ resolveTarget: (...paths) => node_path.resolve(targetDir, ...paths),
85
+ resolveTargetRoot: (...paths) => node_path.resolve(getTargetRoot(), ...paths)
86
86
  };
87
87
  }
88
88
  const BACKSTAGE_JSON = "backstage.json";
@@ -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 'fs';\nimport { dirname, resolve as resolvePath } from '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 * Common paths and resolve functions used by the cli.\n * Currently assumes it is being executed within a monorepo.\n *\n * @public\n */\nexport type Paths = {\n // Root dir of the cli itself, containing package.json\n ownDir: string;\n\n // Monorepo root dir of the cli itself. Only accessible when running inside Backstage repo.\n ownRoot: string;\n\n // The location of the app that the cli is being executed in\n targetDir: string;\n\n // The monorepo root package of the app that the cli is being executed in.\n targetRoot: string;\n\n // Resolve a path relative to own repo\n resolveOwn: ResolveFunc;\n\n // Resolve a path relative to own monorepo root. Only accessible when running inside Backstage repo.\n resolveOwnRoot: ResolveFunc;\n\n // Resolve a path relative to the app\n resolveTarget: ResolveFunc;\n\n // Resolve a path relative to the app repo root\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 a given package\nexport function findOwnDir(searchDir: string) {\n const path = findRootPath(searchDir, () => true);\n if (!path) {\n throw new Error(\n `No package.json found while searching for package root of ${searchDir}`,\n );\n }\n return path;\n}\n\n// Finds the root of the monorepo that the package exists in. Only accessible when running inside Backstage repo.\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/**\n * Find paths related to a package and its execution context.\n *\n * @public\n * @example\n *\n * const paths = findPaths(__dirname)\n */\nexport function findPaths(searchDir: string): Paths {\n const ownDir = findOwnDir(searchDir);\n // Drive letter can end up being lowercased here on Windows, bring back to uppercase for consistency\n const targetDir = fs\n .realpathSync(process.cwd())\n .replace(/^[a-z]:/, str => str.toLocaleUpperCase('en-US'));\n\n // Lazy load this as it will throw an error if we're not inside the Backstage repo.\n let ownRoot = '';\n const getOwnRoot = () => {\n if (!ownRoot) {\n ownRoot = findOwnRootDir(ownDir);\n }\n return ownRoot;\n };\n\n // We're not always running in a monorepo, so we lazy init this to only crash commands\n // that require a monorepo when we're not in one.\n let targetRoot = '';\n const getTargetRoot = () => {\n if (!targetRoot) {\n targetRoot =\n findRootPath(targetDir, 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 }) ?? targetDir; // We didn't find any root package.json, assume we're not in a monorepo\n }\n return targetRoot;\n };\n\n return {\n ownDir,\n get ownRoot() {\n return getOwnRoot();\n },\n targetDir,\n get targetRoot() {\n return getTargetRoot();\n },\n resolveOwn: (...paths) => resolvePath(ownDir, ...paths),\n resolveOwnRoot: (...paths) => resolvePath(getOwnRoot(), ...paths),\n resolveTarget: (...paths) => resolvePath(targetDir, ...paths),\n resolveTargetRoot: (...paths) => resolvePath(getTargetRoot(), ...paths),\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":["path","resolvePath","fs","dirname"],"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 * Common paths and resolve functions used by the cli.\n * Currently assumes it is being executed within a monorepo.\n *\n * @public\n */\nexport type Paths = {\n // Root dir of the cli itself, containing package.json\n ownDir: string;\n\n // Monorepo root dir of the cli itself. Only accessible when running inside Backstage repo.\n ownRoot: string;\n\n // The location of the app that the cli is being executed in\n targetDir: string;\n\n // The monorepo root package of the app that the cli is being executed in.\n targetRoot: string;\n\n // Resolve a path relative to own repo\n resolveOwn: ResolveFunc;\n\n // Resolve a path relative to own monorepo root. Only accessible when running inside Backstage repo.\n resolveOwnRoot: ResolveFunc;\n\n // Resolve a path relative to the app\n resolveTarget: ResolveFunc;\n\n // Resolve a path relative to the app repo root\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 a given package\nexport function findOwnDir(searchDir: string) {\n const path = findRootPath(searchDir, () => true);\n if (!path) {\n throw new Error(\n `No package.json found while searching for package root of ${searchDir}`,\n );\n }\n return path;\n}\n\n// Finds the root of the monorepo that the package exists in. Only accessible when running inside Backstage repo.\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/**\n * Find paths related to a package and its execution context.\n *\n * @public\n * @example\n *\n * const paths = findPaths(__dirname)\n */\nexport function findPaths(searchDir: string): Paths {\n const ownDir = findOwnDir(searchDir);\n // Drive letter can end up being lowercased here on Windows, bring back to uppercase for consistency\n const targetDir = fs\n .realpathSync(process.cwd())\n .replace(/^[a-z]:/, str => str.toLocaleUpperCase('en-US'));\n\n // Lazy load this as it will throw an error if we're not inside the Backstage repo.\n let ownRoot = '';\n const getOwnRoot = () => {\n if (!ownRoot) {\n ownRoot = findOwnRootDir(ownDir);\n }\n return ownRoot;\n };\n\n // We're not always running in a monorepo, so we lazy init this to only crash commands\n // that require a monorepo when we're not in one.\n let targetRoot = '';\n const getTargetRoot = () => {\n if (!targetRoot) {\n targetRoot =\n findRootPath(targetDir, 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 }) ?? targetDir; // We didn't find any root package.json, assume we're not in a monorepo\n }\n return targetRoot;\n };\n\n return {\n ownDir,\n get ownRoot() {\n return getOwnRoot();\n },\n targetDir,\n get targetRoot() {\n return getTargetRoot();\n },\n resolveOwn: (...paths) => resolvePath(ownDir, ...paths),\n resolveOwnRoot: (...paths) => resolvePath(getOwnRoot(), ...paths),\n resolveTarget: (...paths) => resolvePath(targetDir, ...paths),\n resolveTargetRoot: (...paths) => resolvePath(getTargetRoot(), ...paths),\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"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"run.cjs.js","sources":["../src/run.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 { ChildProcess, SpawnOptions } from 'child_process';\nimport spawn from 'cross-spawn';\nimport { ExitCodeError } from './errors';\nimport { assertError } from '@backstage/errors';\n\n/**\n * Callback function that can be used to receive stdout or stderr data from a child process.\n *\n * @public\n */\nexport type RunOnOutput = (data: Buffer) => void;\n\n/**\n * Options for running a child process with {@link run} or related functions.\n *\n * @public\n */\nexport type RunOptions = Omit<SpawnOptions, 'env'> & {\n env?: Partial<NodeJS.ProcessEnv>;\n onStdout?: RunOnOutput;\n onStderr?: RunOnOutput;\n stdio?: SpawnOptions['stdio'];\n};\n\n/**\n * Child process handle returned by {@link run}.\n *\n * @public\n */\nexport interface RunChildProcess extends ChildProcess {\n /**\n * Waits for the child process to exit.\n *\n * @remarks\n *\n * Resolves when the process exits successfully (exit code 0) or is terminated by a signal.\n * If the process exits with a non-zero exit code, the promise is rejected with an {@link ExitCodeError}.\n *\n * @returns A promise that resolves when the process exits successfully or is terminated by a signal, or rejects on error.\n */\n waitForExit(): Promise<void>;\n}\n\n/**\n * Runs a command and returns a child process handle.\n *\n * @public\n */\nexport function run(args: string[], options: RunOptions = {}): RunChildProcess {\n if (args.length === 0) {\n throw new Error('run requires at least one argument');\n }\n\n const [name, ...cmdArgs] = args;\n\n const { onStdout, onStderr, stdio: customStdio, ...spawnOptions } = options;\n const env: NodeJS.ProcessEnv = {\n ...process.env,\n FORCE_COLOR: 'true',\n ...(options.env ?? {}),\n };\n\n const stdio =\n customStdio ??\n ([\n 'inherit',\n onStdout ? 'pipe' : 'inherit',\n onStderr ? 'pipe' : 'inherit',\n ] as ('inherit' | 'pipe')[]);\n\n const child = spawn(name, cmdArgs, {\n ...spawnOptions,\n stdio,\n env,\n }) as RunChildProcess;\n\n if (onStdout && child.stdout) {\n child.stdout.on('data', onStdout);\n }\n if (onStderr && child.stderr) {\n child.stderr.on('data', onStderr);\n }\n\n const commandName = args.join(' ');\n\n let waitPromise: Promise<void> | undefined;\n\n child.waitForExit = async (): Promise<void> => {\n if (waitPromise) {\n return waitPromise;\n }\n\n waitPromise = new Promise<void>((resolve, reject) => {\n if (typeof child.exitCode === 'number') {\n if (child.exitCode) {\n reject(new ExitCodeError(child.exitCode, commandName));\n } else {\n resolve();\n }\n return;\n }\n\n function onError(error: Error) {\n cleanup();\n reject(error);\n }\n\n function onExit(code: number | null) {\n cleanup();\n if (code) {\n reject(new ExitCodeError(code, commandName));\n } else {\n resolve();\n }\n }\n\n function onSignal() {\n if (!child.killed && child.exitCode === null) {\n child.kill();\n }\n }\n\n function cleanup() {\n for (const signal of ['SIGINT', 'SIGTERM'] as const) {\n process.removeListener(signal, onSignal);\n }\n child.removeListener('error', onError);\n child.removeListener('exit', onExit);\n }\n\n child.once('error', onError);\n child.once('exit', onExit);\n\n for (const signal of ['SIGINT', 'SIGTERM'] as const) {\n process.addListener(signal, onSignal);\n }\n });\n\n return waitPromise;\n };\n\n return child;\n}\n\n/**\n * Runs a command and returns the stdout.\n *\n * @remarks\n *\n * On error, both stdout and stderr are attached to the error object as properties.\n *\n * @public\n */\nexport async function runOutput(\n args: string[],\n options?: RunOptions,\n): Promise<string> {\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n\n if (args.length === 0) {\n throw new Error('runOutput requires at least one argument');\n }\n\n try {\n await run(args, {\n ...options,\n onStdout: data => {\n stdoutChunks.push(data);\n options?.onStdout?.(data);\n },\n onStderr: data => {\n stderrChunks.push(data);\n options?.onStderr?.(data);\n },\n }).waitForExit();\n\n return Buffer.concat(stdoutChunks).toString().trim();\n } catch (error) {\n assertError(error);\n\n (error as Error & { stdout?: string }).stdout =\n Buffer.concat(stdoutChunks).toString();\n (error as Error & { stderr?: string }).stderr =\n Buffer.concat(stderrChunks).toString();\n\n throw error;\n }\n}\n\n/**\n * Runs a command and returns true if it exits with code 0, false otherwise.\n *\n * @public\n */\nexport async function runCheck(args: string[]): Promise<boolean> {\n try {\n await run(args).waitForExit();\n return true;\n } catch {\n return false;\n }\n}\n"],"names":["spawn","ExitCodeError","assertError"],"mappings":"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"}
1
+ {"version":3,"file":"run.cjs.js","sources":["../src/run.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 { ChildProcess, SpawnOptions } from 'node:child_process';\nimport spawn from 'cross-spawn';\nimport { ExitCodeError } from './errors';\nimport { assertError } from '@backstage/errors';\n\n/**\n * Callback function that can be used to receive stdout or stderr data from a child process.\n *\n * @public\n */\nexport type RunOnOutput = (data: Buffer) => void;\n\n/**\n * Options for running a child process with {@link run} or related functions.\n *\n * @public\n */\nexport type RunOptions = Omit<SpawnOptions, 'env'> & {\n env?: Partial<NodeJS.ProcessEnv>;\n onStdout?: RunOnOutput;\n onStderr?: RunOnOutput;\n stdio?: SpawnOptions['stdio'];\n};\n\n/**\n * Child process handle returned by {@link run}.\n *\n * @public\n */\nexport interface RunChildProcess extends ChildProcess {\n /**\n * Waits for the child process to exit.\n *\n * @remarks\n *\n * Resolves when the process exits successfully (exit code 0) or is terminated by a signal.\n * If the process exits with a non-zero exit code, the promise is rejected with an {@link ExitCodeError}.\n *\n * @returns A promise that resolves when the process exits successfully or is terminated by a signal, or rejects on error.\n */\n waitForExit(): Promise<void>;\n}\n\n/**\n * Runs a command and returns a child process handle.\n *\n * @public\n */\nexport function run(args: string[], options: RunOptions = {}): RunChildProcess {\n if (args.length === 0) {\n throw new Error('run requires at least one argument');\n }\n\n const [name, ...cmdArgs] = args;\n\n const { onStdout, onStderr, stdio: customStdio, ...spawnOptions } = options;\n const env: NodeJS.ProcessEnv = {\n ...process.env,\n FORCE_COLOR: 'true',\n ...(options.env ?? {}),\n };\n\n const stdio =\n customStdio ??\n ([\n 'inherit',\n onStdout ? 'pipe' : 'inherit',\n onStderr ? 'pipe' : 'inherit',\n ] as ('inherit' | 'pipe')[]);\n\n const child = spawn(name, cmdArgs, {\n ...spawnOptions,\n stdio,\n env,\n }) as RunChildProcess;\n\n if (onStdout && child.stdout) {\n child.stdout.on('data', onStdout);\n }\n if (onStderr && child.stderr) {\n child.stderr.on('data', onStderr);\n }\n\n const commandName = args.join(' ');\n\n let waitPromise: Promise<void> | undefined;\n\n child.waitForExit = async (): Promise<void> => {\n if (waitPromise) {\n return waitPromise;\n }\n\n waitPromise = new Promise<void>((resolve, reject) => {\n if (typeof child.exitCode === 'number') {\n if (child.exitCode) {\n reject(new ExitCodeError(child.exitCode, commandName));\n } else {\n resolve();\n }\n return;\n }\n\n function onError(error: Error) {\n cleanup();\n reject(error);\n }\n\n function onExit(code: number | null) {\n cleanup();\n if (code) {\n reject(new ExitCodeError(code, commandName));\n } else {\n resolve();\n }\n }\n\n function onSignal() {\n if (!child.killed && child.exitCode === null) {\n child.kill();\n }\n }\n\n function cleanup() {\n for (const signal of ['SIGINT', 'SIGTERM'] as const) {\n process.removeListener(signal, onSignal);\n }\n child.removeListener('error', onError);\n child.removeListener('exit', onExit);\n }\n\n child.once('error', onError);\n child.once('exit', onExit);\n\n for (const signal of ['SIGINT', 'SIGTERM'] as const) {\n process.addListener(signal, onSignal);\n }\n });\n\n return waitPromise;\n };\n\n return child;\n}\n\n/**\n * Runs a command and returns the stdout.\n *\n * @remarks\n *\n * On error, both stdout and stderr are attached to the error object as properties.\n *\n * @public\n */\nexport async function runOutput(\n args: string[],\n options?: RunOptions,\n): Promise<string> {\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n\n if (args.length === 0) {\n throw new Error('runOutput requires at least one argument');\n }\n\n try {\n await run(args, {\n ...options,\n onStdout: data => {\n stdoutChunks.push(data);\n options?.onStdout?.(data);\n },\n onStderr: data => {\n stderrChunks.push(data);\n options?.onStderr?.(data);\n },\n }).waitForExit();\n\n return Buffer.concat(stdoutChunks).toString().trim();\n } catch (error) {\n assertError(error);\n\n (error as Error & { stdout?: string }).stdout =\n Buffer.concat(stdoutChunks).toString();\n (error as Error & { stderr?: string }).stderr =\n Buffer.concat(stderrChunks).toString();\n\n throw error;\n }\n}\n\n/**\n * Runs a command and returns true if it exits with code 0, false otherwise.\n *\n * @public\n */\nexport async function runCheck(args: string[]): Promise<boolean> {\n try {\n await run(args).waitForExit();\n return true;\n } catch {\n return false;\n }\n}\n"],"names":["spawn","ExitCodeError","assertError"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/cli-common",
3
- "version": "0.1.16",
3
+ "version": "0.1.18-next.0",
4
4
  "description": "Common functionality used by cli, backend, and create-app",
5
5
  "backstage": {
6
6
  "role": "node-library"
@@ -35,13 +35,13 @@
35
35
  "test": "backstage-cli package test"
36
36
  },
37
37
  "dependencies": {
38
- "@backstage/errors": "^1.2.7",
38
+ "@backstage/errors": "1.2.7",
39
39
  "cross-spawn": "^7.0.3",
40
40
  "global-agent": "^3.0.0",
41
41
  "undici": "^7.2.3"
42
42
  },
43
43
  "devDependencies": {
44
- "@backstage/cli": "^0.35.0",
44
+ "@backstage/cli": "0.35.3-next.0",
45
45
  "@types/cross-spawn": "^6.0.2",
46
46
  "@types/node": "^22.13.14"
47
47
  },