diff --git a/dist-raw/node-esm-resolve-implementation.js b/dist-raw/node-esm-resolve-implementation.js index 1a5846673..a3da2f9a0 100644 --- a/dist-raw/node-esm-resolve-implementation.js +++ b/dist-raw/node-esm-resolve-implementation.js @@ -50,37 +50,7 @@ const { Stats, } = require('fs'); // const { getOptionValue } = require('internal/options'); -const { getOptionValue } = (() => { - let options; - function parseOptions() { - if (!options) { - options = { - '--preserve-symlinks': false, - '--preserve-symlinks-main': false, - '--input-type': undefined, - '--experimental-specifier-resolution': 'explicit', - ...parseExecArgv() - } - } - }; - function parseExecArgv () { - return require('arg')({ - '--preserve-symlinks': Boolean, - '--preserve-symlinks-main': Boolean, - '--input-type': String, - '--experimental-specifier-resolution': String - }, { - argv: process.execArgv, - permissive: true - }); - } - return { - getOptionValue: (opt) => { - parseOptions(); - return options[opt]; - } - }; -})(); +const { getOptionValue } = require('./node-options'); const { sep } = require('path'); const preserveSymlinks = getOptionValue('--preserve-symlinks'); diff --git a/dist-raw/node-options.js b/dist-raw/node-options.js new file mode 100644 index 000000000..f27a44689 --- /dev/null +++ b/dist-raw/node-options.js @@ -0,0 +1,83 @@ +// Replacement for node's internal 'internal/options' module + +exports.getOptionValue = getOptionValue; +function getOptionValue(opt) { + parseOptions(); + return options[opt]; +} + +let options; +function parseOptions() { + if (!options) { + options = { + '--preserve-symlinks': false, + '--preserve-symlinks-main': false, + '--input-type': undefined, + '--experimental-specifier-resolution': 'explicit', + ...parseArgv(getNodeOptionsEnvArgv()), + ...parseArgv(process.execArgv) + } + } +} + +function parseArgv(argv) { + return require('arg')({ + '--preserve-symlinks': Boolean, + '--preserve-symlinks-main': Boolean, + '--input-type': String, + '--experimental-specifier-resolution': String + }, { + argv, + permissive: true + }); +} + +function getNodeOptionsEnvArgv() { + const errors = []; + const envArgv = ParseNodeOptionsEnvVar(process.env.NODE_OPTIONS || '', errors); + if (errors.length !== 0) { + // TODO: handle errors somehow + } + return envArgv; +} + +// Direct JS port of C implementation: https://github.com/nodejs/node/blob/67ba825037b4082d5d16f922fb9ce54516b4a869/src/node_options.cc#L1024-L1063 +function ParseNodeOptionsEnvVar(node_options, errors) { + const env_argv = []; + + let is_in_string = false; + let will_start_new_arg = true; + for (let index = 0; index < node_options.length; ++index) { + let c = node_options[index]; + + // Backslashes escape the following character + if (c === '\\' && is_in_string) { + if (index + 1 === node_options.length) { + errors.push("invalid value for NODE_OPTIONS " + + "(invalid escape)\n"); + return env_argv; + } else { + c = node_options[++index]; + } + } else if (c === ' ' && !is_in_string) { + will_start_new_arg = true; + continue; + } else if (c === '"') { + is_in_string = !is_in_string; + continue; + } + + if (will_start_new_arg) { + env_argv.push(c); + will_start_new_arg = false; + } else { + env_argv[env_argv.length - 1] += c; + } + } + + if (is_in_string) { + errors.push("invalid value for NODE_OPTIONS " + + "(unterminated string)\n"); + } + return env_argv; +} diff --git a/src/index.spec.ts b/src/index.spec.ts index 3e64030ae..9c0a37d34 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -767,8 +767,33 @@ describe('ts-node', function () { return done() }) + }) + + describe('supports experimental-specifier-resolution=node', () => { + it('via --experimental-specifier-resolution', (done) => { + exec(`${cmd} --experimental-specifier-resolution=node index.ts`, { cwd: join(__dirname, '../tests/esm-node-resolver') }, function (err, stdout) { + expect(err).to.equal(null) + expect(stdout).to.equal('foo bar baz biff\n') + + return done() + }) + }) + it('via NODE_OPTIONS', (done) => { + exec(`${cmd} index.ts`, { + cwd: join(__dirname, '../tests/esm-node-resolver'), + env: { + ...process.env, + NODE_OPTIONS: '--experimental-specifier-resolution=node' + } + }, function (err, stdout) { + expect(err).to.equal(null) + expect(stdout).to.equal('foo bar baz biff\n') + return done() + }) + }) }) + it('throws ERR_REQUIRE_ESM when attempting to require() an ESM script while ESM loader is enabled', function (done) { exec(`${cmd} ./index.js`, { cwd: join(__dirname, '../tests/esm-err-require-esm') }, function (err, stdout, stderr) { expect(err).to.not.equal(null)