@oclif/core 3.19.2-qa.0 → 3.19.3-dev.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/lib/cache.d.ts CHANGED
@@ -2,15 +2,23 @@ import { PJSON, Plugin } from './interfaces';
2
2
  type CacheContents = {
3
3
  rootPlugin: Plugin;
4
4
  exitCodes: PJSON.Plugin['oclif']['exitCodes'];
5
+ '@oclif/core': OclifCoreInfo;
5
6
  };
6
7
  type ValueOf<T> = T[keyof T];
8
+ type OclifCoreInfo = {
9
+ name: string;
10
+ version: string;
11
+ };
7
12
  /**
8
13
  * A simple cache for storing values that need to be accessed globally.
9
14
  */
10
15
  export default class Cache extends Map<keyof CacheContents, ValueOf<CacheContents>> {
11
16
  static instance: Cache;
17
+ constructor();
12
18
  static getInstance(): Cache;
19
+ get(key: '@oclif/core'): OclifCoreInfo;
13
20
  get(key: 'rootPlugin'): Plugin | undefined;
14
21
  get(key: 'exitCodes'): PJSON.Plugin['oclif']['exitCodes'] | undefined;
22
+ private getOclifCoreMeta;
15
23
  }
16
24
  export {};
package/lib/cache.js CHANGED
@@ -1,10 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const node_fs_1 = require("node:fs");
4
+ const node_path_1 = require("node:path");
3
5
  /**
4
6
  * A simple cache for storing values that need to be accessed globally.
5
7
  */
6
8
  class Cache extends Map {
7
9
  static instance;
10
+ constructor() {
11
+ super();
12
+ this.set('@oclif/core', this.getOclifCoreMeta());
13
+ }
8
14
  static getInstance() {
9
15
  if (!Cache.instance) {
10
16
  Cache.instance = new Cache();
@@ -14,5 +20,22 @@ class Cache extends Map {
14
20
  get(key) {
15
21
  return super.get(key);
16
22
  }
23
+ getOclifCoreMeta() {
24
+ try {
25
+ // eslint-disable-next-line node/no-extraneous-require
26
+ return { name: '@oclif/core', version: require('@oclif/core/package.json').version };
27
+ }
28
+ catch {
29
+ try {
30
+ return {
31
+ name: '@oclif/core',
32
+ version: JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, '..', 'package.json'), 'utf8')),
33
+ };
34
+ }
35
+ catch {
36
+ return { name: '@oclif/core', version: 'unknown' };
37
+ }
38
+ }
39
+ }
17
40
  }
18
41
  exports.default = Cache;
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.config = exports.Config = void 0;
7
- const fs_1 = require("../util/fs");
7
+ const cache_1 = __importDefault(require("../cache"));
8
8
  const simple_1 = __importDefault(require("./action/simple"));
9
9
  const spinner_1 = __importDefault(require("./action/spinner"));
10
10
  const g = global;
@@ -35,7 +35,8 @@ class Config {
35
35
  }
36
36
  exports.Config = Config;
37
37
  function fetch() {
38
- const major = (0, fs_1.requireJson)(__dirname, '..', '..', 'package.json').version.split('.')[0];
38
+ const core = cache_1.default.getInstance().get('@oclif/core');
39
+ const major = core?.version.split('.')[0] || 'unknown';
39
40
  if (globals[major])
40
41
  return globals[major];
41
42
  globals[major] = new Config();
@@ -28,28 +28,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.anykey = exports.confirm = exports.prompt = void 0;
30
30
  const chalk_1 = __importDefault(require("chalk"));
31
+ const node_readline_1 = __importDefault(require("node:readline"));
31
32
  const Errors = __importStar(require("../errors"));
32
33
  const config_1 = require("./config");
33
34
  function normal(options, retries = 100) {
34
35
  if (retries < 0)
35
36
  throw new Error('no input');
37
+ const ac = new AbortController();
38
+ const { signal } = ac;
36
39
  return new Promise((resolve, reject) => {
37
- let timer;
38
- if (options.timeout) {
39
- timer = setTimeout(() => {
40
- process.stdin.pause();
41
- reject(new Error('Prompt timeout'));
42
- }, options.timeout);
43
- timer.unref();
44
- }
45
- process.stdin.setEncoding('utf8');
46
- process.stderr.write(options.prompt);
47
- process.stdin.resume();
48
- process.stdin.once('data', (b) => {
49
- if (timer)
50
- clearTimeout(timer);
51
- process.stdin.pause();
52
- const data = (typeof b === 'string' ? b : b.toString()).trim();
40
+ const rl = node_readline_1.default.createInterface({
41
+ input: process.stdin,
42
+ output: process.stdout,
43
+ });
44
+ rl.question(options.prompt, { signal }, (answer) => {
45
+ rl.close();
46
+ const data = answer.trim();
53
47
  if (!options.default && options.required && data === '') {
54
48
  resolve(normal(options, retries - 1));
55
49
  }
@@ -57,6 +51,12 @@ function normal(options, retries = 100) {
57
51
  resolve(data || options.default);
58
52
  }
59
53
  });
54
+ if (options.timeout) {
55
+ signal.addEventListener('abort', () => {
56
+ reject(new Error('Prompt timeout'));
57
+ }, { once: true });
58
+ setTimeout(() => ac.abort(), options.timeout);
59
+ }
60
60
  });
61
61
  }
62
62
  function getPrompt(name, type, defaultValue) {
package/lib/command.js CHANGED
@@ -30,16 +30,16 @@ exports.Command = void 0;
30
30
  const chalk_1 = __importDefault(require("chalk"));
31
31
  const node_url_1 = require("node:url");
32
32
  const node_util_1 = require("node:util");
33
+ const cache_1 = __importDefault(require("./cache"));
33
34
  const cli_ux_1 = require("./cli-ux");
34
35
  const config_1 = require("./config");
35
36
  const Errors = __importStar(require("./errors"));
36
37
  const util_1 = require("./help/util");
37
38
  const Parser = __importStar(require("./parser"));
38
39
  const aggregate_flags_1 = require("./util/aggregate-flags");
39
- const fs_1 = require("./util/fs");
40
40
  const ids_1 = require("./util/ids");
41
41
  const util_2 = require("./util/util");
42
- const pjson = (0, fs_1.requireJson)(__dirname, '..', 'package.json');
42
+ const pjson = cache_1.default.getInstance().get('@oclif/core');
43
43
  /**
44
44
  * swallows stdout epipe errors
45
45
  * this occurs when stdout closes such as when piping to head
@@ -48,7 +48,7 @@ const ts_node_1 = require("./ts-node");
48
48
  const util_3 = require("./util");
49
49
  // eslint-disable-next-line new-cap
50
50
  const debug = (0, util_3.Debug)();
51
- const _pjson = (0, fs_1.requireJson)(__dirname, '..', '..', 'package.json');
51
+ const _pjson = cache_1.default.getInstance().get('@oclif/core');
52
52
  const BASE = `${_pjson.name}@${_pjson.version}`;
53
53
  function channelFromVersion(version) {
54
54
  const m = version.match(/[^-]+(?:-([^.]+))?/);
@@ -487,14 +487,15 @@ class Config {
487
487
  };
488
488
  const hooks = p.hooks[event] || [];
489
489
  for (const hook of hooks) {
490
- const marker = performance_1.Performance.mark(performance_1.OCLIF_MARKER_OWNER, `config.runHook#${p.name}(${hook})`);
490
+ const marker = performance_1.Performance.mark(performance_1.OCLIF_MARKER_OWNER, `config.runHook#${p.name}(${hook.target})`);
491
491
  try {
492
492
  /* eslint-disable no-await-in-loop */
493
- const { filePath, isESM, module } = await (0, module_loader_1.loadWithData)(p, await (0, ts_node_1.tsPath)(p.root, hook, p));
493
+ const { filePath, isESM, module } = await (0, module_loader_1.loadWithData)(p, await (0, ts_node_1.tsPath)(p.root, hook.target, p));
494
494
  debug('start', isESM ? '(import)' : '(require)', filePath);
495
+ const hookFn = module[hook.identifier] ?? search(module);
495
496
  const result = timeout
496
- ? await withTimeout(timeout, search(module).call(context, { ...opts, config: this, context }))
497
- : await search(module).call(context, { ...opts, config: this, context });
497
+ ? await withTimeout(timeout, hookFn.call(context, { ...opts, config: this, context }))
498
+ : await hookFn.call(context, { ...opts, config: this, context });
498
499
  final.successes.push({ plugin: p, result });
499
500
  if (p.name === '@oclif/plugin-legacy' && event === 'init') {
500
501
  this.insertLegacyPlugins(result);
@@ -516,7 +517,7 @@ class Config {
516
517
  }
517
518
  marker?.addDetails({
518
519
  event,
519
- hook,
520
+ hook: hook.target,
520
521
  plugin: p.name,
521
522
  });
522
523
  marker?.stop();
@@ -1,6 +1,6 @@
1
1
  import { Command } from '../command';
2
2
  import { Manifest } from '../interfaces/manifest';
3
- import { PJSON } from '../interfaces/pjson';
3
+ import { HookOptions, PJSON } from '../interfaces/pjson';
4
4
  import { Plugin as IPlugin, PluginOptions } from '../interfaces/plugin';
5
5
  import { Topic } from '../interfaces/topic';
6
6
  export declare class Plugin implements IPlugin {
@@ -13,7 +13,7 @@ export declare class Plugin implements IPlugin {
13
13
  commandsDir: string | undefined;
14
14
  hasManifest: boolean;
15
15
  hooks: {
16
- [k: string]: string[];
16
+ [key: string]: HookOptions[];
17
17
  };
18
18
  isRoot: boolean;
19
19
  manifest: Manifest;
@@ -7,6 +7,7 @@ exports.Plugin = void 0;
7
7
  const globby_1 = __importDefault(require("globby"));
8
8
  const node_path_1 = require("node:path");
9
9
  const node_util_1 = require("node:util");
10
+ const cache_1 = __importDefault(require("../cache"));
10
11
  const errors_1 = require("../errors");
11
12
  const module_loader_1 = require("../module-loader");
12
13
  const performance_1 = require("../performance");
@@ -17,7 +18,7 @@ const fs_1 = require("../util/fs");
17
18
  const util_1 = require("../util/util");
18
19
  const ts_node_1 = require("./ts-node");
19
20
  const util_2 = require("./util");
20
- const _pjson = (0, fs_1.requireJson)(__dirname, '..', '..', 'package.json');
21
+ const _pjson = cache_1.default.getInstance().get('@oclif/core');
21
22
  function topicsToArray(input, base) {
22
23
  if (!input)
23
24
  return [];
@@ -64,8 +65,18 @@ function determineCommandDiscoveryOptions(commandDiscovery, defaultCmdId) {
64
65
  throw new errors_1.CLIError('`oclif.commandDiscovery.target` is required.');
65
66
  if (!commandDiscovery.strategy)
66
67
  throw new errors_1.CLIError('`oclif.commandDiscovery.strategy` is required.');
68
+ if (commandDiscovery.strategy === 'explicit' && !commandDiscovery.identifier) {
69
+ commandDiscovery.identifier = 'default';
70
+ }
67
71
  return commandDiscovery;
68
72
  }
73
+ function determineHookOptions(hook) {
74
+ if (typeof hook === 'string')
75
+ return { identifier: 'default', target: hook };
76
+ if (!hook.identifier)
77
+ return { ...hook, identifier: 'default' };
78
+ return hook;
79
+ }
69
80
  class Plugin {
70
81
  options;
71
82
  alias;
@@ -182,7 +193,10 @@ class Plugin {
182
193
  else {
183
194
  this.pjson.oclif = this.pjson['cli-engine'] || {};
184
195
  }
185
- this.hooks = Object.fromEntries(Object.entries(this.pjson.oclif.hooks ?? {}).map(([k, v]) => [k, (0, util_1.castArray)(v)]));
196
+ this.hooks = Object.fromEntries(Object.entries(this.pjson.oclif.hooks ?? {}).map(([k, v]) => [
197
+ k,
198
+ (0, util_1.castArray)(v).map((v) => determineHookOptions(v)),
199
+ ]));
186
200
  this.commandDiscoveryOpts = determineCommandDiscoveryOptions(this.pjson.oclif?.commands, this.pjson.oclif?.default);
187
201
  this._debug('command discovery options', this.commandDiscoveryOpts);
188
202
  this.manifest = await this._manifest();
@@ -333,7 +347,7 @@ class Plugin {
333
347
  if (this.commandDiscoveryOpts?.strategy === 'explicit' && this.commandDiscoveryOpts.target) {
334
348
  const filePath = await (0, ts_node_1.tsPath)(this.root, this.commandDiscoveryOpts.target, this);
335
349
  const module = await (0, module_loader_1.load)(this, filePath);
336
- this.commandCache = module.default ?? {};
350
+ this.commandCache = module[this.commandDiscoveryOpts?.identifier ?? 'default'] ?? {};
337
351
  return this.commandCache;
338
352
  }
339
353
  if (this.commandDiscoveryOpts?.strategy === 'single' && this.commandDiscoveryOpts.target) {
package/lib/execute.d.ts CHANGED
@@ -8,23 +8,17 @@ import { LoadOptions } from './interfaces';
8
8
  * @example For ESM dev.js
9
9
  * ```
10
10
  * #!/usr/bin/env -S node --loader ts-node/esm --no-warnings=ExperimentalWarning
11
- * async function main() {
12
- * const oclif = await import('@oclif/core')
13
- * await oclif.execute({development: true, dir: import.meta.url})
14
- * }
11
+ * import { execute } from '@oclif/core'
15
12
  *
16
- * await main()
13
+ * await execute({development: true, dir: import.meta.url})
17
14
  * ```
18
15
  *
19
16
  * @example For ESM run.js
20
17
  * ```
21
18
  * #!/usr/bin/env node
22
- * async function main() {
23
- * const oclif = await import('@oclif/core')
24
- * await oclif.execute({dir: import.meta.url})
25
- * }
19
+ * import { execute } from '@oclif/core'
26
20
  *
27
- * await main()
21
+ * await execute({dir: import.meta.url})
28
22
  * ```
29
23
  *
30
24
  * @example For CJS dev.js
package/lib/execute.js CHANGED
@@ -14,23 +14,17 @@ const settings_1 = require("./settings");
14
14
  * @example For ESM dev.js
15
15
  * ```
16
16
  * #!/usr/bin/env -S node --loader ts-node/esm --no-warnings=ExperimentalWarning
17
- * async function main() {
18
- * const oclif = await import('@oclif/core')
19
- * await oclif.execute({development: true, dir: import.meta.url})
20
- * }
17
+ * import { execute } from '@oclif/core'
21
18
  *
22
- * await main()
19
+ * await execute({development: true, dir: import.meta.url})
23
20
  * ```
24
21
  *
25
22
  * @example For ESM run.js
26
23
  * ```
27
24
  * #!/usr/bin/env node
28
- * async function main() {
29
- * const oclif = await import('@oclif/core')
30
- * await oclif.execute({dir: import.meta.url})
31
- * }
25
+ * import { execute } from '@oclif/core'
32
26
  *
33
- * await main()
27
+ * await execute({dir: import.meta.url})
34
28
  * ```
35
29
  *
36
30
  * @example For CJS dev.js
@@ -20,9 +20,9 @@ export type CommandDiscovery = {
20
20
  /**
21
21
  * The strategy to use for loading commands.
22
22
  *
23
- * - `pattern` will use glob patterns to find command files in the specified `directory`.
23
+ * - `pattern` will use glob patterns to find command files in the specified `target`.
24
24
  * - `explicit` will use `import` (or `require` for CJS) to load the commands from the
25
- * specified `file`.
25
+ * specified `target`.
26
26
  * - `single` will use the `target` which should export a command class. This is for CLIs that
27
27
  * only have a single command.
28
28
  *
@@ -32,9 +32,9 @@ export type CommandDiscovery = {
32
32
  /**
33
33
  * If the `strategy` is `pattern`, this is the **directory** to use to find command files.
34
34
  *
35
- * If the `strategy` is `explicit`, this is the **file** that default exports the commands.
36
- * - This export must be the default export and an object with keys that are the command names
37
- * and values that are the command classes.
35
+ * If the `strategy` is `explicit`, this is the **file** that exports the commands.
36
+ * - This export must be an object with keys that are the command names and values that are the command classes.
37
+ * - Unless `identifier` is specified, the default export will be used.
38
38
  *
39
39
  * @example
40
40
  * ```typescript
@@ -54,6 +54,45 @@ export type CommandDiscovery = {
54
54
  * This is only used when `strategy` is `pattern`.
55
55
  */
56
56
  globPatterns?: string[];
57
+ /**
58
+ * The name of the export to used when loading the command object from the `target` file. Only
59
+ * used when `strategy` is `explicit`. Defaults to `default`.
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * // in src/commands.ts
64
+ * import Hello from './commands/hello/index.js'
65
+ * import HelloWorld from './commands/hello/world.js'
66
+ *
67
+ * export const MY_COMMANDS = {
68
+ * hello: Hello,
69
+ * 'hello:world': HelloWorld,
70
+ * }
71
+ * ```
72
+ *
73
+ * In the package.json:
74
+ * ```json
75
+ * {
76
+ * "oclif": {
77
+ * "commands": {
78
+ * "strategy": "explicit",
79
+ * "target": "./dist/index.js",
80
+ * "identifier": "MY_COMMANDS"
81
+ * }
82
+ * }
83
+ * ```
84
+ */
85
+ identifier?: string;
86
+ };
87
+ export type HookOptions = {
88
+ /**
89
+ * The file path containing hook.
90
+ */
91
+ target: string;
92
+ /**
93
+ * The name of the export to use when loading the hook function from the `target` file. Defaults to `default`.
94
+ */
95
+ identifier: string;
57
96
  };
58
97
  export declare namespace PJSON {
59
98
  interface Plugin extends PJSON {
@@ -87,7 +126,7 @@ export declare namespace PJSON {
87
126
  helpClass?: string;
88
127
  helpOptions?: HelpOptions;
89
128
  hooks?: {
90
- [name: string]: string | string[];
129
+ [name: string]: string | string[] | HookOptions | HookOptions[];
91
130
  };
92
131
  jitPlugins?: Record<string, string>;
93
132
  macos?: {
@@ -1,5 +1,5 @@
1
1
  import { Command } from '../command';
2
- import { PJSON } from './pjson';
2
+ import { HookOptions, PJSON } from './pjson';
3
3
  import { Topic } from './topic';
4
4
  export interface PluginOptions {
5
5
  children?: Plugin[];
@@ -44,7 +44,7 @@ export interface Plugin {
44
44
  }): Promise<Command.Class> | undefined;
45
45
  readonly hasManifest: boolean;
46
46
  hooks: {
47
- [k: string]: string[];
47
+ [key: string]: HookOptions[];
48
48
  };
49
49
  /**
50
50
  * True if the plugin is the root plugin.
package/lib/util/fs.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- export declare function requireJson<T>(...pathParts: string[]): T;
2
1
  /**
3
2
  * Parser for Args.directory and Flags.directory. Checks that the provided path
4
3
  * exists and is a directory.
package/lib/util/fs.js CHANGED
@@ -1,13 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.existsSync = exports.safeReadJson = exports.readJsonSync = exports.readJson = exports.fileExists = exports.dirExists = exports.requireJson = void 0;
3
+ exports.existsSync = exports.safeReadJson = exports.readJsonSync = exports.readJson = exports.fileExists = exports.dirExists = void 0;
4
4
  const node_fs_1 = require("node:fs");
5
5
  const promises_1 = require("node:fs/promises");
6
- const node_path_1 = require("node:path");
7
- function requireJson(...pathParts) {
8
- return JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(...pathParts), 'utf8'));
9
- }
10
- exports.requireJson = requireJson;
11
6
  /**
12
7
  * Parser for Args.directory and Flags.directory. Checks that the provided path
13
8
  * exists and is a directory.
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.19.2-qa.0",
4
+ "version": "3.19.3-dev.0",
5
5
  "author": "Salesforce",
6
6
  "bugs": "https://github.com/oclif/core/issues",
7
7
  "dependencies": {
@@ -38,7 +38,7 @@
38
38
  "@oclif/plugin-help": "^6",
39
39
  "@oclif/plugin-plugins": "^4",
40
40
  "@oclif/prettier-config": "^0.2.1",
41
- "@oclif/test": "^3.1.12",
41
+ "@oclif/test": "^3.1.14",
42
42
  "@types/ansi-styles": "^3.2.1",
43
43
  "@types/benchmark": "^2.1.5",
44
44
  "@types/chai": "^4.3.11",
@@ -72,9 +72,9 @@
72
72
  "husky": "^8",
73
73
  "lint-staged": "^14.0.1",
74
74
  "madge": "^6.1.0",
75
- "mocha": "^10.2.0",
75
+ "mocha": "^10.3.0",
76
76
  "nyc": "^15.1.0",
77
- "prettier": "^3.2.4",
77
+ "prettier": "^3.2.5",
78
78
  "shx": "^0.3.4",
79
79
  "sinon": "^16.1.3",
80
80
  "ts-node": "^10.9.2",