diff --git a/doc/api/cli.md b/doc/api/cli.md
index 15da9004b0e7b6..ad916576654072 100644
--- a/doc/api/cli.md
+++ b/doc/api/cli.md
@@ -1414,8 +1414,23 @@ added: v12.0.0
-->
This configures Node.js to interpret `--eval` or `STDIN` input as CommonJS or
-as an ES module. Valid values are `"commonjs"` or `"module"`. The default is
-`"commonjs"` unless [`--experimental-default-type=module`][] is used.
+as an ES module. Valid values are `"commonjs"`, `"module"`, `"module-typescript"` and `"commonjs-typescript"`.
+The `"-typescript"` values are available only in combination with the flag `--experimental-strip-types`.
+The default is `"commonjs"` unless [`--experimental-default-type=module`][] is used.
+If `--experimental-strip-types` is enabled and `--input-type` is not provided,
+Node.js will try to detect the syntax with the following steps:
+
+1. Run the input as CommonJS.
+2. If step 1 fails, run the input as an ES module.
+3. If step 2 fails with a SyntaxError, strip the types.
+4. If step 3 fails with an error code [`ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX`][]
+ or [`ERR_INVALID_TYPESCRIPT_SYNTAX`][],
+ throw the error from step 2, including the TypeScript error in the message,
+ else run as CommonJS.
+5. If step 4 fails, run the input as an ES module.
+
+To avoid the delay of multiple syntax detection passes, the `--input-type=type` flag can be used to specify
+how the `--eval` input should be interpreted.
The REPL does not support this option. Usage of `--input-type=module` with
[`--print`][] will throw an error, as `--print` does not support ES module
@@ -3712,6 +3727,8 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
[`Atomics.wait()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics/wait
[`Buffer`]: buffer.md#class-buffer
[`CRYPTO_secure_malloc_init`]: https://www.openssl.org/docs/man3.0/man3/CRYPTO_secure_malloc_init.html
+[`ERR_INVALID_TYPESCRIPT_SYNTAX`]: errors.md#err_invalid_typescript_syntax
+[`ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX`]: errors.md#err_unsupported_typescript_syntax
[`NODE_OPTIONS`]: #node_optionsoptions
[`NO_COLOR`]: https://no-color.org
[`SlowBuffer`]: buffer.md#class-slowbuffer
diff --git a/doc/api/errors.md b/doc/api/errors.md
index 3c0158457b3563..af0b0cb57094ec 100644
--- a/doc/api/errors.md
+++ b/doc/api/errors.md
@@ -2106,11 +2106,13 @@ does not consist of exactly two elements.
-The provided TypeScript syntax is not valid or unsupported.
-This could happen when using TypeScript syntax that requires
-transformation with [type-stripping][].
+The provided TypeScript syntax is not valid.
@@ -3103,6 +3105,18 @@ try {
}
```
+
+
+### `ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX`
+
+
+
+The provided TypeScript syntax is unsupported.
+This could happen when using TypeScript syntax that requires
+transformation with [type-stripping][].
+
### `ERR_USE_AFTER_CLOSE`
diff --git a/doc/api/typescript.md b/doc/api/typescript.md
index d2680670a5f316..4d90f5c285f525 100644
--- a/doc/api/typescript.md
+++ b/doc/api/typescript.md
@@ -153,10 +153,10 @@ import { fn, FnParams } from './fn.ts';
### Non-file forms of input
-Type stripping can be enabled for `--eval`. The module system
+Type stripping can be enabled for `--eval` and STDIN. The module system
will be determined by `--input-type`, as it is for JavaScript.
-TypeScript syntax is unsupported in the REPL, STDIN input, `--print`, `--check`, and
+TypeScript syntax is unsupported in the REPL, `--check`, and
`inspect`.
### Source maps
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index de880fedd03515..356ed1fd94ec1b 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -1841,6 +1841,7 @@ E('ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING',
E('ERR_UNSUPPORTED_RESOLVE_REQUEST',
'Failed to resolve module specifier "%s" from "%s": Invalid relative URL or base scheme is not hierarchical.',
TypeError);
+E('ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX', '%s', SyntaxError);
E('ERR_USE_AFTER_CLOSE', '%s was closed', Error);
// This should probably be a `TypeError`.
diff --git a/lib/internal/main/eval_stdin.js b/lib/internal/main/eval_stdin.js
index 3ee4bcdb1d853b..2a0ecf9a4aba5e 100644
--- a/lib/internal/main/eval_stdin.js
+++ b/lib/internal/main/eval_stdin.js
@@ -11,6 +11,9 @@ const { getOptionValue } = require('internal/options');
const {
evalModuleEntryPoint,
+ evalTypeScript,
+ parseAndEvalCommonjsTypeScript,
+ parseAndEvalModuleTypeScript,
evalScript,
readStdin,
} = require('internal/process/execution');
@@ -25,14 +28,32 @@ readStdin((code) => {
const print = getOptionValue('--print');
const shouldLoadESM = getOptionValue('--import').length > 0;
- if (getOptionValue('--input-type') === 'module' ||
- (getOptionValue('--experimental-default-type') === 'module' && getOptionValue('--input-type') !== 'commonjs')) {
+ const inputType = getOptionValue('--input-type');
+ const tsEnabled = getOptionValue('--experimental-strip-types');
+ if (inputType === 'module' ||
+ (getOptionValue('--experimental-default-type') === 'module' &&
+ inputType !== 'commonjs')) {
evalModuleEntryPoint(code, print);
+ } else if (inputType === 'module-typescript' && tsEnabled) {
+ parseAndEvalModuleTypeScript(code, print);
} else {
- evalScript('[stdin]',
- code,
- getOptionValue('--inspect-brk'),
- print,
- shouldLoadESM);
+
+ let evalFunction;
+ if (inputType === 'commonjs') {
+ evalFunction = evalScript;
+ } else if (inputType === 'commonjs-typescript' && tsEnabled) {
+ evalFunction = parseAndEvalCommonjsTypeScript;
+ } else if (tsEnabled) {
+ evalFunction = evalTypeScript;
+ } else {
+ // Default to commonjs.
+ evalFunction = evalScript;
+ }
+
+ evalFunction('[stdin]',
+ code,
+ getOptionValue('--inspect-brk'),
+ print,
+ shouldLoadESM);
}
});
diff --git a/lib/internal/main/eval_string.js b/lib/internal/main/eval_string.js
index a3be08551425b1..171ab3bae86f16 100644
--- a/lib/internal/main/eval_string.js
+++ b/lib/internal/main/eval_string.js
@@ -13,9 +13,14 @@ const {
prepareMainThreadExecution,
markBootstrapComplete,
} = require('internal/process/pre_execution');
-const { evalModuleEntryPoint, evalScript } = require('internal/process/execution');
+const {
+ evalModuleEntryPoint,
+ evalTypeScript,
+ parseAndEvalCommonjsTypeScript,
+ parseAndEvalModuleTypeScript,
+ evalScript,
+} = require('internal/process/execution');
const { addBuiltinLibsToObject } = require('internal/modules/helpers');
-const { stripTypeScriptModuleTypes } = require('internal/modules/typescript');
const { getOptionValue } = require('internal/options');
prepareMainThreadExecution();
@@ -23,21 +28,22 @@ addBuiltinLibsToObject(globalThis, '');
markBootstrapComplete();
const code = getOptionValue('--eval');
-const source = getOptionValue('--experimental-strip-types') ?
- stripTypeScriptModuleTypes(code) :
- code;
const print = getOptionValue('--print');
const shouldLoadESM = getOptionValue('--import').length > 0 || getOptionValue('--experimental-loader').length > 0;
-if (getOptionValue('--input-type') === 'module' ||
- (getOptionValue('--experimental-default-type') === 'module' && getOptionValue('--input-type') !== 'commonjs')) {
- evalModuleEntryPoint(source, print);
+const inputType = getOptionValue('--input-type');
+const tsEnabled = getOptionValue('--experimental-strip-types');
+if (inputType === 'module' ||
+ (getOptionValue('--experimental-default-type') === 'module' && inputType !== 'commonjs')) {
+ evalModuleEntryPoint(code, print);
+} else if (inputType === 'module-typescript' && tsEnabled) {
+ parseAndEvalModuleTypeScript(code, print);
} else {
// For backward compatibility, we want the identifier crypto to be the
// `node:crypto` module rather than WebCrypto.
const isUsingCryptoIdentifier =
- getOptionValue('--experimental-global-webcrypto') &&
- RegExpPrototypeExec(/\bcrypto\b/, source) !== null;
+ getOptionValue('--experimental-global-webcrypto') &&
+ RegExpPrototypeExec(/\bcrypto\b/, code) !== null;
const shouldDefineCrypto = isUsingCryptoIdentifier && internalBinding('config').hasOpenSSL;
if (isUsingCryptoIdentifier && !shouldDefineCrypto) {
@@ -52,11 +58,24 @@ if (getOptionValue('--input-type') === 'module' ||
};
ObjectDefineProperty(object, name, { __proto__: null, set: setReal });
}
- evalScript('[eval]',
- shouldDefineCrypto ? (
- print ? `let crypto=require("node:crypto");{${source}}` : `(crypto=>{{${source}}})(require('node:crypto'))`
- ) : source,
- getOptionValue('--inspect-brk'),
- print,
- shouldLoadESM);
+
+ let evalFunction;
+ if (inputType === 'commonjs') {
+ evalFunction = evalScript;
+ } else if (inputType === 'commonjs-typescript' && tsEnabled) {
+ evalFunction = parseAndEvalCommonjsTypeScript;
+ } else if (tsEnabled) {
+ evalFunction = evalTypeScript;
+ } else {
+ // Default to commonjs.
+ evalFunction = evalScript;
+ }
+
+ evalFunction('[eval]',
+ shouldDefineCrypto ? (
+ print ? `let crypto=require("node:crypto");{${code}}` : `(crypto=>{{${code}}})(require('node:crypto'))`
+ ) : code,
+ getOptionValue('--inspect-brk'),
+ print,
+ shouldLoadESM);
}
diff --git a/lib/internal/main/worker_thread.js b/lib/internal/main/worker_thread.js
index f8c410b5b25cb0..caf64728b754cc 100644
--- a/lib/internal/main/worker_thread.js
+++ b/lib/internal/main/worker_thread.js
@@ -49,7 +49,10 @@ const { setupMainThreadPort } = require('internal/worker/messaging');
const {
onGlobalUncaughtException,
evalScript,
+ evalTypeScript,
evalModuleEntryPoint,
+ parseAndEvalCommonjsTypeScript,
+ parseAndEvalModuleTypeScript,
} = require('internal/process/execution');
let debug = require('internal/util/debuglog').debuglog('worker', (fn) => {
@@ -166,7 +169,29 @@ port.on('message', (message) => {
value: filename,
});
ArrayPrototypeSplice(process.argv, 1, 0, name);
- evalScript(name, filename);
+ const tsEnabled = getOptionValue('--experimental-strip-types');
+ const inputType = getOptionValue('--input-type');
+
+ if (inputType === 'module-typescript' && tsEnabled) {
+ // This is a special case where we want to parse and eval the
+ // TypeScript code as a module
+ parseAndEvalModuleTypeScript(filename, false);
+ break;
+ }
+
+ let evalFunction;
+ if (inputType === 'commonjs') {
+ evalFunction = evalScript;
+ } else if (inputType === 'commonjs-typescript' && tsEnabled) {
+ evalFunction = parseAndEvalCommonjsTypeScript;
+ } else if (tsEnabled) {
+ evalFunction = evalTypeScript;
+ } else {
+ // Default to commonjs.
+ evalFunction = evalScript;
+ }
+
+ evalFunction(name, filename);
break;
}
diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js
index 455166d3612aa3..62c33730ed17cb 100644
--- a/lib/internal/modules/cjs/loader.js
+++ b/lib/internal/modules/cjs/loader.js
@@ -435,7 +435,6 @@ function initializeCJS() {
const tsEnabled = getOptionValue('--experimental-strip-types');
if (tsEnabled) {
- emitExperimentalWarning('Type Stripping');
Module._extensions['.cts'] = loadCTS;
Module._extensions['.ts'] = loadTS;
}
diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js
index e8c7b97d23c62a..fdecd0c926c953 100644
--- a/lib/internal/modules/esm/loader.js
+++ b/lib/internal/modules/esm/loader.js
@@ -210,9 +210,25 @@ class ModuleLoader {
}
}
- async eval(source, url, isEntryPoint = false) {
+ /**
+ *
+ * @param {string} source Source code of the module.
+ * @param {string} url URL of the module.
+ * @returns {object} The module wrap object.
+ */
+ createModuleWrap(source, url) {
+ return compileSourceTextModule(url, source, this);
+ }
+
+ /**
+ *
+ * @param {string} url URL of the module.
+ * @param {object} wrap Module wrap object.
+ * @param {boolean} isEntryPoint Whether the module is the entry point.
+ * @returns {Promise