Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: skip ts-node register for ESM plugins #778

Merged
merged 2 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 10 additions & 23 deletions src/config/ts-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {TSConfig, Plugin} from '../interfaces'
import {settings} from '../settings'
import {isProd} from '../util'
import {Debug} from './util'
import {Config} from './config'
import {memoizedWarn} from '../errors'
// eslint-disable-next-line new-cap
const debug = Debug('ts-node')
Expand Down Expand Up @@ -110,7 +109,6 @@ function registerTSNode(root: string): TSConfig | undefined {
*/
export function tsPath(root: string, orig: string, plugin: Plugin): string
export function tsPath(root: string, orig: string | undefined, plugin?: Plugin): string | undefined
// eslint-disable-next-line complexity
export function tsPath(root: string, orig: string | undefined, plugin?: Plugin): string | undefined {
if (!orig) return orig
orig = orig.startsWith(root) ? orig : path.join(root, orig)
Expand All @@ -122,32 +120,21 @@ export function tsPath(root: string, orig: string | undefined, plugin?: Plugin):
return orig
}

// Skip ts-node registration if plugin is an ESM plugin executing from a CJS plugin
if (plugin?.moduleType === 'module' && Config.rootPlugin?.moduleType === 'commonjs') {
debug(`Skipping ts-node registration for ${root} because it's an ESM module but the root plugin is CommonJS`)
// Skip ts-node registration for ESM plugins.
// The node ecosystem is not mature enough to support auto-transpiling ESM modules at this time.
// See the following:
// - https://github.com/TypeStrong/ts-node/issues/1791#issuecomment-1149754228
// - https://github.com/nodejs/node/issues/49432
// - https://github.com/nodejs/node/pull/49407
// - https://github.com/nodejs/node/issues/34049
if (plugin?.moduleType === 'module') {
debug(`Skipping ts-node registration for ${root} because it's an ESM module`)
if (plugin.type === 'link')
memoizedWarn(`${plugin.name} is a linked ESM module and cannot be auto-compiled from a CommonJS root plugin. Existing compiled source will be used instead.`)
memoizedWarn(`${plugin.name} is a linked ESM module and cannot be auto-transpiled. Existing compiled source will be used instead.`)

return orig
}

// If plugin is an ESM plugin being executed from an ESM root plugin, check to see if ts-node/esm loader has been set
// either in the NODE_OPTIONS env var or from the exec args. If the ts-node/esm loader has NOT been loaded then we want
// to skip ts-node registration so that it falls back on the compiled source.
if (plugin?.moduleType === 'module') {
const tsNodeEsmLoaderInExecArgv = process.execArgv.includes('--loader') && process.execArgv.includes('ts-node/esm')
const tsNodeEsmLoaderInNodeOptions = process.env.NODE_OPTIONS?.includes('--loader=ts-node/esm') ?? false
if (!tsNodeEsmLoaderInExecArgv && !tsNodeEsmLoaderInNodeOptions) {
debug(`Skipping ts-node registration for ${root} because it's an ESM module but the ts-node/esm loader hasn't been run`)
debug('try setting NODE_OPTIONS="--loader ts-node/esm" in your environment.')
if (plugin.type === 'link') {
memoizedWarn(`${plugin.name} is a linked ESM module and cannot be auto-compiled without setting NODE_OPTIONS="--loader=ts-node/esm" in the environment. Existing compiled source will be used instead.`)
}

return orig
}
}

if (settings.tsnodeEnabled === undefined && isProd() && plugin?.type !== 'link') {
debug(`Skipping ts-node registration for ${root} because NODE_ENV is NOT "test" or "development"`)
return orig
Expand Down
40 changes: 20 additions & 20 deletions test/integration/esm-cjs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,34 +365,34 @@ type CleanUpOptions = {
await test('Link ESM plugin to ESM root plugin', async () => {
const plugin = PLUGINS.esm2

const linkedPlugin = await linkPlugin({executor: esmExecutor, plugin, script: 'run'})
await linkPlugin({executor: esmExecutor, plugin, script: 'run'})
// test bin/run
// NOTE: this also tests that the compiled source is used when ts-node/esm loader is not specified
await runCommand({
executor: esmExecutor,
plugin,
script: 'run',
expectStrings: [plugin.commandText, plugin.hookText],
})
// test un-compiled changes with bin/run
await modifyCommand({executor: linkedPlugin, plugin, from: 'hello', to: 'howdy'})
await runCommand({
executor: esmExecutor,
plugin,
script: 'run',
expectStrings: ['howdy', plugin.hookText],
env: {NODE_OPTIONS: '--loader=ts-node/esm'},
})

// test un-compiled changes with bin/dev
await modifyCommand({executor: linkedPlugin, plugin, from: 'howdy', to: 'cheers'})
await runCommand({
executor: esmExecutor,
plugin,
script: 'dev',
expectStrings: ['cheers', plugin.hookText],
env: {NODE_OPTIONS: '--loader=ts-node/esm'},
})
// Skipping these because we decided to not support auto-transpiling ESM plugins at this time.
// // test un-compiled changes with bin/run
// await modifyCommand({executor: linkedPlugin, plugin, from: 'hello', to: 'howdy'})
// await runCommand({
// executor: esmExecutor,
// plugin,
// script: 'run',
// expectStrings: ['howdy', plugin.hookText],
// env: {NODE_OPTIONS: '--loader=ts-node/esm'},
// })
// // test un-compiled changes with bin/dev
// await modifyCommand({executor: linkedPlugin, plugin, from: 'howdy', to: 'cheers'})
// await runCommand({
// executor: esmExecutor,
// plugin,
// script: 'dev',
// expectStrings: ['cheers', plugin.hookText],
// env: {NODE_OPTIONS: '--loader=ts-node/esm'},
// })

await cleanUp({executor: esmExecutor, plugin, script: 'run'})
})
Expand Down