@oclif/core 3.5.0 → 3.6.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.
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Plugin = void 0;
4
- /* eslint-disable no-await-in-loop */
5
4
  const globby_1 = require("globby");
6
5
  const node_path_1 = require("node:path");
7
6
  const node_util_1 = require("node:util");
@@ -9,6 +8,7 @@ const errors_1 = require("../errors");
9
8
  const module_loader_1 = require("../module-loader");
10
9
  const performance_1 = require("../performance");
11
10
  const cache_command_1 = require("../util/cache-command");
11
+ const find_root_1 = require("../util/find-root");
12
12
  const fs_1 = require("../util/fs");
13
13
  const util_1 = require("../util/util");
14
14
  const ts_node_1 = require("./ts-node");
@@ -26,79 +26,6 @@ function topicsToArray(input, base) {
26
26
  return [{ ...input[k], name: `${base}${k}` }, ...topicsToArray(input[k].subtopics, `${base}${input[k].name}`)];
27
27
  });
28
28
  }
29
- // essentially just "cd .."
30
- function* up(from) {
31
- while ((0, node_path_1.dirname)(from) !== from) {
32
- yield from;
33
- from = (0, node_path_1.dirname)(from);
34
- }
35
- yield from;
36
- }
37
- async function findSourcesRoot(root, name) {
38
- // If we know the plugin name then we just need to traverse the file
39
- // system until we find the directory that matches the plugin name.
40
- if (name) {
41
- for (const next of up(root)) {
42
- if (next.endsWith((0, node_path_1.basename)(name)))
43
- return next;
44
- }
45
- }
46
- // If there's no plugin name (typically just the root plugin), then we need
47
- // to traverse the file system until we find a directory with a package.json
48
- for (const next of up(root)) {
49
- // Skip the bin directory
50
- if ((0, node_path_1.basename)((0, node_path_1.dirname)(next)) === 'bin' &&
51
- ['dev', 'dev.cmd', 'dev.js', 'run', 'run.cmd', 'run.js'].includes((0, node_path_1.basename)(next))) {
52
- continue;
53
- }
54
- try {
55
- const cur = (0, node_path_1.join)(next, 'package.json');
56
- if (await (0, fs_1.safeReadJson)(cur))
57
- return (0, node_path_1.dirname)(cur);
58
- }
59
- catch { }
60
- }
61
- }
62
- /**
63
- * Find package root for packages installed into node_modules. This will go up directories
64
- * until it finds a node_modules directory with the plugin installed into it
65
- *
66
- * This is needed because some oclif plugins do not declare the `main` field in their package.json
67
- * https://github.com/oclif/config/pull/289#issuecomment-983904051
68
- *
69
- * @returns string
70
- * @param name string
71
- * @param root string
72
- */
73
- async function findRootLegacy(name, root) {
74
- for (const next of up(root)) {
75
- let cur;
76
- if (name) {
77
- cur = (0, node_path_1.join)(next, 'node_modules', name, 'package.json');
78
- if (await (0, fs_1.safeReadJson)(cur))
79
- return (0, node_path_1.dirname)(cur);
80
- const pkg = await (0, fs_1.safeReadJson)((0, node_path_1.join)(next, 'package.json'));
81
- if (pkg?.name === name)
82
- return next;
83
- }
84
- else {
85
- cur = (0, node_path_1.join)(next, 'package.json');
86
- if (await (0, fs_1.safeReadJson)(cur))
87
- return (0, node_path_1.dirname)(cur);
88
- }
89
- }
90
- }
91
- async function findRoot(name, root) {
92
- if (name) {
93
- let pkgPath;
94
- try {
95
- pkgPath = (0, util_2.resolvePackage)(name, { paths: [root] });
96
- }
97
- catch { }
98
- return pkgPath ? findSourcesRoot((0, node_path_1.dirname)(pkgPath), name) : findRootLegacy(name, root);
99
- }
100
- return findSourcesRoot(root);
101
- }
102
29
  const cachedCommandCanBeUsed = (manifest, id) => Boolean(manifest?.commands[id] && 'isESM' in manifest.commands[id] && 'relativePath' in manifest.commands[id]);
103
30
  const search = (cmd) => {
104
31
  if (typeof cmd.run === 'function')
@@ -212,7 +139,7 @@ class Plugin {
212
139
  // Linked plugins already have a root so there's no need to search for it.
213
140
  // However there could be child plugins nested inside the linked plugin, in which
214
141
  // case we still need to search for the child plugin's root.
215
- const root = this.type === 'link' && !this.parent ? this.options.root : await findRoot(this.options.name, this.options.root);
142
+ const root = this.type === 'link' && !this.parent ? this.options.root : await (0, find_root_1.findRoot)(this.options.name, this.options.root);
216
143
  if (!root)
217
144
  throw new errors_1.CLIError(`could not find package.json with ${(0, node_util_1.inspect)(this.options)}`);
218
145
  this.root = root;
@@ -25,7 +25,7 @@ function loadTSConfig(root) {
25
25
  }
26
26
  catch {
27
27
  try {
28
- typescript = require((0, node_path_1.join)(root, 'node_modules', 'typescript'));
28
+ typescript = require(require.resolve('typescript', { paths: [root, __dirname] }));
29
29
  }
30
30
  catch {
31
31
  debug(`Could not find typescript dependency. Skipping ts-node registration for ${root}.`);
@@ -1,6 +1,3 @@
1
- export declare function resolvePackage(id: string, paths: {
2
- paths: string[];
3
- }): string;
4
1
  export declare function Debug(...scope: string[]): (..._: any) => void;
5
2
  export declare function getPermutations(arr: string[]): Array<string[]>;
6
3
  export declare function getCommandIdPermutations(commandId: string): string[];
@@ -1,11 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.collectUsableIds = exports.getCommandIdPermutations = exports.getPermutations = exports.Debug = exports.resolvePackage = void 0;
3
+ exports.collectUsableIds = exports.getCommandIdPermutations = exports.getPermutations = exports.Debug = void 0;
4
4
  const debug = require('debug');
5
- function resolvePackage(id, paths) {
6
- return require.resolve(id, paths);
7
- }
8
- exports.resolvePackage = resolvePackage;
9
5
  function displayWarnings() {
10
6
  if (process.listenerCount('warning') > 1)
11
7
  return;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Returns the root directory of the plugin.
3
+ *
4
+ * It first attempts to use require.resolve to find the plugin root.
5
+ * If that returns a path, it will `cd` up the file system until if finds the package.json for the plugin
6
+ * Example: node_modules/@oclif/plugin-version/dist/index.js -> node_modules/@oclif/plugin-version
7
+ *
8
+ * If require.resolve throws an error, it will attempt to find the plugin root by traversing the file system.
9
+ * If we're in a PnP environment (determined by process.versions.pnp), it will use the pnpapi module to
10
+ * traverse the dependency tree. Otherwise, it will traverse the node_modules until it finds a package.json
11
+ * with a matching name.
12
+ *
13
+ * If no path is found, undefined is returned which will eventually result in a thrown Error from Plugin.
14
+ */
15
+ export declare function findRoot(name: string | undefined, root: string): Promise<string | undefined>;
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findRoot = void 0;
4
+ const node_path_1 = require("node:path");
5
+ const fs_1 = require("./fs");
6
+ // essentially just "cd .."
7
+ function* up(from) {
8
+ while ((0, node_path_1.dirname)(from) !== from) {
9
+ yield from;
10
+ from = (0, node_path_1.dirname)(from);
11
+ }
12
+ yield from;
13
+ }
14
+ /**
15
+ * Return the plugin root directory from a given file. This will `cd` up the file system until it finds
16
+ * a package.json and then return the dirname of that path.
17
+ *
18
+ * Example: node_modules/@oclif/plugin-version/dist/index.js -> node_modules/@oclif/plugin-version
19
+ */
20
+ async function findPluginRoot(root, name) {
21
+ // If we know the plugin name then we just need to traverse the file
22
+ // system until we find the directory that matches the plugin name.
23
+ if (name) {
24
+ for (const next of up(root)) {
25
+ if (next.endsWith((0, node_path_1.basename)(name)))
26
+ return next;
27
+ }
28
+ }
29
+ // If there's no plugin name (typically just the root plugin), then we need
30
+ // to traverse the file system until we find a directory with a package.json
31
+ for (const next of up(root)) {
32
+ // Skip the bin directory
33
+ if ((0, node_path_1.basename)((0, node_path_1.dirname)(next)) === 'bin' &&
34
+ ['dev', 'dev.cmd', 'dev.js', 'run', 'run.cmd', 'run.js'].includes((0, node_path_1.basename)(next))) {
35
+ continue;
36
+ }
37
+ try {
38
+ const cur = (0, node_path_1.join)(next, 'package.json');
39
+ if (await (0, fs_1.safeReadJson)(cur))
40
+ return (0, node_path_1.dirname)(cur);
41
+ }
42
+ catch { }
43
+ }
44
+ }
45
+ /**
46
+ * Find plugin root directory for plugins installed into node_modules that don't have a `main` or `export`.
47
+ * This will go up directories until it finds a directory with the plugin installed into it.
48
+ *
49
+ * See https://github.com/oclif/config/pull/289#issuecomment-983904051
50
+ */
51
+ async function findRootLegacy(name, root) {
52
+ for (const next of up(root)) {
53
+ let cur;
54
+ if (name) {
55
+ cur = (0, node_path_1.join)(next, 'node_modules', name, 'package.json');
56
+ if (await (0, fs_1.safeReadJson)(cur))
57
+ return (0, node_path_1.dirname)(cur);
58
+ const pkg = await (0, fs_1.safeReadJson)((0, node_path_1.join)(next, 'package.json'));
59
+ if (pkg?.name === name)
60
+ return next;
61
+ }
62
+ else {
63
+ cur = (0, node_path_1.join)(next, 'package.json');
64
+ if (await (0, fs_1.safeReadJson)(cur))
65
+ return (0, node_path_1.dirname)(cur);
66
+ }
67
+ }
68
+ }
69
+ let pnp;
70
+ /**
71
+ * The pnpapi module is only available if running in a pnp environment. Because of that
72
+ * we have to require it from the plugin.
73
+ *
74
+ * Solution taken from here: https://github.com/yarnpkg/berry/issues/1467#issuecomment-642869600
75
+ */
76
+ function maybeRequirePnpApi(root) {
77
+ if (pnp)
78
+ return pnp;
79
+ try {
80
+ // eslint-disable-next-line node/no-missing-require
81
+ pnp = require(require.resolve('pnpapi', { paths: [root] }));
82
+ return pnp;
83
+ }
84
+ catch { }
85
+ }
86
+ const getKey = (locator) => JSON.stringify(locator);
87
+ const isPeerDependency = (pkg, parentPkg, name) => getKey(pkg?.packageDependencies.get(name)) === getKey(parentPkg?.packageDependencies.get(name));
88
+ /**
89
+ * Traverse PnP dependency tree to find plugin root directory.
90
+ *
91
+ * Implementation adapted from https://yarnpkg.com/advanced/pnpapi#traversing-the-dependency-tree
92
+ */
93
+ function findPnpRoot(name, root) {
94
+ maybeRequirePnpApi(root);
95
+ if (!pnp)
96
+ return;
97
+ const seen = new Set();
98
+ const traverseDependencyTree = (locator, parentPkg) => {
99
+ // Prevent infinite recursion when A depends on B which depends on A
100
+ const key = getKey(locator);
101
+ if (seen.has(key))
102
+ return;
103
+ const pkg = pnp.getPackageInformation(locator);
104
+ if (locator.name === name) {
105
+ return pkg.packageLocation;
106
+ }
107
+ seen.add(key);
108
+ for (const [name, referencish] of pkg.packageDependencies) {
109
+ // Unmet peer dependencies
110
+ if (referencish === null)
111
+ continue;
112
+ // Avoid iterating on peer dependencies - very expensive
113
+ if (parentPkg !== null && isPeerDependency(pkg, parentPkg, name))
114
+ continue;
115
+ const childLocator = pnp.getLocator(name, referencish);
116
+ const foundSomething = traverseDependencyTree(childLocator, pkg);
117
+ if (foundSomething)
118
+ return foundSomething;
119
+ }
120
+ // Important: This `delete` here causes the traversal to go over nodes even
121
+ // if they have already been traversed in another branch. If you don't need
122
+ // that, remove this line for a hefty speed increase.
123
+ seen.delete(key);
124
+ };
125
+ // Iterate on each workspace
126
+ for (const locator of pnp.getDependencyTreeRoots()) {
127
+ const foundSomething = traverseDependencyTree(locator);
128
+ if (foundSomething)
129
+ return foundSomething;
130
+ }
131
+ }
132
+ /**
133
+ * Returns the root directory of the plugin.
134
+ *
135
+ * It first attempts to use require.resolve to find the plugin root.
136
+ * If that returns a path, it will `cd` up the file system until if finds the package.json for the plugin
137
+ * Example: node_modules/@oclif/plugin-version/dist/index.js -> node_modules/@oclif/plugin-version
138
+ *
139
+ * If require.resolve throws an error, it will attempt to find the plugin root by traversing the file system.
140
+ * If we're in a PnP environment (determined by process.versions.pnp), it will use the pnpapi module to
141
+ * traverse the dependency tree. Otherwise, it will traverse the node_modules until it finds a package.json
142
+ * with a matching name.
143
+ *
144
+ * If no path is found, undefined is returned which will eventually result in a thrown Error from Plugin.
145
+ */
146
+ async function findRoot(name, root) {
147
+ if (name) {
148
+ let pkgPath;
149
+ try {
150
+ pkgPath = require.resolve(name, { paths: [root] });
151
+ }
152
+ catch { }
153
+ if (pkgPath)
154
+ return findPluginRoot((0, node_path_1.dirname)(pkgPath), name);
155
+ return process.versions.pnp ? findPnpRoot(name, root) : findRootLegacy(name, root);
156
+ }
157
+ return findPluginRoot(root);
158
+ }
159
+ exports.findRoot = findRoot;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@oclif/core",
3
3
  "description": "base library for oclif CLIs",
4
- "version": "3.5.0",
4
+ "version": "3.6.0",
5
5
  "author": "Salesforce",
6
6
  "bugs": "https://github.com/oclif/core/issues",
7
7
  "dependencies": {
@@ -49,6 +49,7 @@
49
49
  "@types/mocha": "^10.0.2",
50
50
  "@types/node": "^18",
51
51
  "@types/node-notifier": "^8.0.2",
52
+ "@types/pnpapi": "^0.0.4",
52
53
  "@types/slice-ansi": "^4.0.0",
53
54
  "@types/strip-ansi": "^5.2.1",
54
55
  "@types/supports-color": "^8.1.1",