@oclif/core 3.0.5 → 3.0.6

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/README.md CHANGED
@@ -1,5 +1,4 @@
1
- @oclif/core
2
- ===========
1
+ # @oclif/core
3
2
 
4
3
  base library for oclif CLIs
5
4
 
@@ -7,9 +6,7 @@ base library for oclif CLIs
7
6
  [![Downloads/week](https://img.shields.io/npm/dw/@oclif/core.svg)](https://npmjs.org/package/@oclif/core)
8
7
  [![License](https://img.shields.io/npm/l/@oclif/core.svg)](https://github.com/oclif/core/blob/main/package.json)
9
8
 
10
-
11
- Migrating
12
- =====
9
+ # Migrating
13
10
 
14
11
  See the [v3 migration guide](./guides/V3_MIGRATION.md) for an overview of breaking changes that occurred between v2 and v3.
15
12
 
@@ -17,19 +14,18 @@ See the [v2 migration guide](./guides/V2_MIGRATION.md) for an overview of breaki
17
14
 
18
15
  Migrating from `@oclif/config` and `@oclif/command`? See the [v1 migration guide](./guides/PRE_CORE_MIGRATION.md).
19
16
 
20
- CLI UX
21
- =====
17
+ # CLI UX
22
18
 
23
19
  The [ux README](./src/cli-ux/README.md) contains detailed usage examples of using the `ux` export.
24
20
 
25
- Usage
26
- =====
21
+ # Usage
27
22
 
28
23
  We strongly encourage you generate an oclif CLI using the [oclif cli](https://github.com/oclif/oclif). The generator will generate an npm package with `@oclif/core` as a dependency.
29
24
 
30
25
  You can, however, use `@oclif/core` in a standalone script like this:
26
+
31
27
  ```typescript
32
- #!/usr/bin/env ts-node
28
+ #!/usr/bin/env -S node --loader ts-node/esm --no-warnings=ExperimentalWarning
33
29
 
34
30
  import * as fs from 'fs'
35
31
  import {Command, Flags, flush, handle} from '@oclif/core'
@@ -54,11 +50,14 @@ class LS extends Command {
54
50
  }
55
51
  }
56
52
 
57
- LS.run().then(async () => {
58
- await flush()
59
- }, async (err) => {
60
- await handle(err)
61
- })
53
+ LS.run().then(
54
+ async () => {
55
+ await flush()
56
+ },
57
+ async (err) => {
58
+ await handle(err)
59
+ },
60
+ )
62
61
  ```
63
62
 
64
63
  Then run it like this:
@@ -86,6 +86,10 @@ function registerTSNode(root) {
86
86
  sourceMap: tsconfig.compilerOptions.sourceMap ?? true,
87
87
  target: tsconfig.compilerOptions.target ?? 'es2019',
88
88
  typeRoots,
89
+ ...(tsconfig.compilerOptions.moduleResolution
90
+ ? { moduleResolution: tsconfig.compilerOptions.moduleResolution }
91
+ : {}),
92
+ ...(tsconfig.compilerOptions.jsx ? { jsx: tsconfig.compilerOptions.jsx } : {}),
89
93
  },
90
94
  cwd: root,
91
95
  esm: tsconfig['ts-node']?.esm ?? true,
@@ -95,18 +99,67 @@ function registerTSNode(root) {
95
99
  skipProject: true,
96
100
  transpileOnly: true,
97
101
  };
98
- if (tsconfig.compilerOptions.moduleResolution) {
99
- // @ts-expect-error TSNode.RegisterOptions.compilerOptions is typed as a plain object
100
- conf.compilerOptions.moduleResolution = tsconfig.compilerOptions.moduleResolution;
101
- }
102
- if (tsconfig.compilerOptions.jsx) {
103
- // @ts-expect-error TSNode.RegisterOptions.compilerOptions is typed as a plain object
104
- conf.compilerOptions.jsx = tsconfig.compilerOptions.jsx;
105
- }
106
102
  tsNode.register(conf);
107
103
  REGISTERED.add(root);
108
104
  return tsconfig;
109
105
  }
106
+ /**
107
+ * Skip ts-node registration for ESM plugins in production.
108
+ * The node ecosystem is not mature enough to support auto-transpiling ESM modules at this time.
109
+ * See the following:
110
+ * - https://github.com/TypeStrong/ts-node/issues/1791#issuecomment-1149754228
111
+ * - https://github.com/nodejs/node/issues/49432
112
+ * - https://github.com/nodejs/node/pull/49407
113
+ * - https://github.com/nodejs/node/issues/34049
114
+ *
115
+ * We still register ts-node for ESM plugins when NODE_ENV is "test" or "development" and root plugin is also ESM
116
+ * since that allows plugins to be auto-transpiled when developing locally using `bin/dev.js`.
117
+ */
118
+ function cannotTranspileEsm(root, plugin, isProduction) {
119
+ return (isProduction || ROOT_PLUGIN?.moduleType === 'commonjs') && plugin?.moduleType === 'module';
120
+ }
121
+ /**
122
+ * If the dev script is run with ts-node for an ESM plugin, skip ts-node registration
123
+ * and fall back on compiled source since ts-node executable cannot transpile ESM in Node 20+
124
+ *
125
+ * See the following:
126
+ * https://nodejs.org/en/blog/announcements/v20-release-announce#custom-esm-loader-hooks-nearing-stable
127
+ * https://github.com/oclif/core/issues/817
128
+ * https://github.com/TypeStrong/ts-node/issues/1997
129
+ */
130
+ function cannotUseTsNode(root, plugin, isProduction) {
131
+ if (plugin?.moduleType !== 'module' || isProduction)
132
+ return false;
133
+ const nodeMajor = Number.parseInt(process.version.replace('v', '').split('.')[0], 10);
134
+ const tsNodeExecIsUsed = process.execArgv[0] === '--require' && process.execArgv[1].split(node_path_1.sep).includes(`ts-node`);
135
+ return tsNodeExecIsUsed && nodeMajor >= 20;
136
+ }
137
+ /**
138
+ * Determine the path to the source file from the compiled ./lib files
139
+ */
140
+ function determinePath(root, orig) {
141
+ const tsconfig = registerTSNode(root);
142
+ if (!tsconfig)
143
+ return orig;
144
+ const { outDir, rootDir, rootDirs } = tsconfig.compilerOptions;
145
+ const rootDirPath = rootDir || (rootDirs || [])[0];
146
+ if (!rootDirPath || !outDir)
147
+ return orig;
148
+ // rewrite path from ./lib/foo to ./src/foo
149
+ const lib = (0, node_path_1.join)(root, outDir); // ./lib
150
+ const src = (0, node_path_1.join)(root, rootDirPath); // ./src
151
+ const relative = (0, node_path_1.relative)(lib, orig); // ./commands
152
+ // For hooks, it might point to a js file, not a module. Something like "./hooks/myhook.js" which doesn't need the js.
153
+ const out = (0, node_path_1.join)(src, relative).replace(/\.js$/, ''); // ./src/commands
154
+ // this can be a directory of commands or point to a hook file
155
+ // if it's a directory, we check if the path exists. If so, return the path to the directory.
156
+ // For hooks, it might point to a module, not a file. Something like "./hooks/myhook"
157
+ // That file doesn't exist, and the real file is "./hooks/myhook.ts"
158
+ // In that case we attempt to resolve to the filename. If it fails it will revert back to the lib path
159
+ if ((0, node_fs_1.existsSync)(out) || (0, node_fs_1.existsSync)(out + '.ts'))
160
+ return out;
161
+ return orig;
162
+ }
110
163
  function tsPath(root, orig, plugin) {
111
164
  if (plugin?.isRoot)
112
165
  ROOT_PLUGIN = plugin;
@@ -119,51 +172,24 @@ function tsPath(root, orig, plugin) {
119
172
  return orig;
120
173
  }
121
174
  const isProduction = (0, util_1.isProd)();
122
- /**
123
- * Skip ts-node registration for ESM plugins.
124
- * The node ecosystem is not mature enough to support auto-transpiling ESM modules at this time.
125
- * See the following:
126
- * - https://github.com/TypeStrong/ts-node/issues/1791#issuecomment-1149754228
127
- * - https://github.com/nodejs/node/issues/49432
128
- * - https://github.com/nodejs/node/pull/49407
129
- * - https://github.com/nodejs/node/issues/34049
130
- *
131
- * We still register ts-node for ESM plugins when NODE_ENV is "test" or "development" and root plugin is also ESM.
132
- * In other words, this allows plugins to be auto-transpiled when developing locally using `bin/dev.js`.
133
- */
134
- if ((isProduction || ROOT_PLUGIN?.moduleType === 'commonjs') && plugin?.moduleType === 'module') {
175
+ if (cannotTranspileEsm(root, plugin, isProduction)) {
135
176
  debug(`Skipping ts-node registration for ${root} because it's an ESM module (NODE_ENV: ${process.env.NODE_ENV}, root plugin module type: ${ROOT_PLUGIN?.moduleType})))`);
136
- if (plugin.type === 'link')
137
- (0, errors_1.memoizedWarn)(`${plugin.name} is a linked ESM module and cannot be auto-transpiled. Existing compiled source will be used instead.`);
177
+ if (plugin?.type === 'link')
178
+ (0, errors_1.memoizedWarn)(`${plugin?.name} is a linked ESM module and cannot be auto-transpiled. Existing compiled source will be used instead.`);
138
179
  return orig;
139
180
  }
140
181
  if (settings_1.settings.tsnodeEnabled === undefined && isProduction && plugin?.type !== 'link') {
141
182
  debug(`Skipping ts-node registration for ${root} because NODE_ENV is NOT "test" or "development"`);
142
183
  return orig;
143
184
  }
144
- try {
145
- const tsconfig = registerTSNode(root);
146
- if (!tsconfig)
147
- return orig;
148
- const { outDir, rootDir, rootDirs } = tsconfig.compilerOptions;
149
- const rootDirPath = rootDir || (rootDirs || [])[0];
150
- if (!rootDirPath || !outDir)
151
- return orig;
152
- // rewrite path from ./lib/foo to ./src/foo
153
- const lib = (0, node_path_1.join)(root, outDir); // ./lib
154
- const src = (0, node_path_1.join)(root, rootDirPath); // ./src
155
- const relative = (0, node_path_1.relative)(lib, orig); // ./commands
156
- // For hooks, it might point to a js file, not a module. Something like "./hooks/myhook.js" which doesn't need the js.
157
- const out = (0, node_path_1.join)(src, relative).replace(/\.js$/, ''); // ./src/commands
158
- // this can be a directory of commands or point to a hook file
159
- // if it's a directory, we check if the path exists. If so, return the path to the directory.
160
- // For hooks, it might point to a module, not a file. Something like "./hooks/myhook"
161
- // That file doesn't exist, and the real file is "./hooks/myhook.ts"
162
- // In that case we attempt to resolve to the filename. If it fails it will revert back to the lib path
163
- if ((0, node_fs_1.existsSync)(out) || (0, node_fs_1.existsSync)(out + '.ts'))
164
- return out;
185
+ if (cannotUseTsNode(root, plugin, isProduction)) {
186
+ debug(`Skipping ts-node registration for ${root} because ts-node is run in node version ${process.version}"`);
187
+ (0, errors_1.memoizedWarn)(`ts-node executable cannot transpile ESM in Node 20. Existing compiled source will be used instead. See https://github.com/oclif/core/issues/817.`);
165
188
  return orig;
166
189
  }
190
+ try {
191
+ return determinePath(root, orig);
192
+ }
167
193
  catch (error) {
168
194
  debug(error);
169
195
  return orig;
package/lib/execute.d.ts CHANGED
@@ -7,7 +7,7 @@ import { LoadOptions } from './interfaces';
7
7
  *
8
8
  * @example For ESM dev.js
9
9
  * ```
10
- * #!/usr/bin/env ts-node
10
+ * #!/usr/bin/env -S node --loader ts-node/esm --no-warnings=ExperimentalWarning
11
11
  * async function main() {
12
12
  * const oclif = await import('@oclif/core')
13
13
  * await oclif.execute({development: true, dir: import.meta.url})
package/lib/execute.js CHANGED
@@ -13,7 +13,7 @@ const settings_1 = require("./settings");
13
13
  *
14
14
  * @example For ESM dev.js
15
15
  * ```
16
- * #!/usr/bin/env ts-node
16
+ * #!/usr/bin/env -S node --loader ts-node/esm --no-warnings=ExperimentalWarning
17
17
  * async function main() {
18
18
  * const oclif = await import('@oclif/core')
19
19
  * await oclif.execute({development: true, dir: import.meta.url})
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.0.5",
4
+ "version": "3.0.6",
5
5
  "author": "Salesforce",
6
6
  "bugs": "https://github.com/oclif/core/issues",
7
7
  "dependencies": {