Skip to content

Commit 959f133

Browse files
marco-ippolitoaduh95
authored andcommitted
module: support eval with ts syntax detection
PR-URL: #56285 Refs: nodejs/typescript#17 Reviewed-By: Pietro Marchini <pietro.marchini94@gmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
1 parent 96a3804 commit 959f133

File tree

10 files changed

+510
-66
lines changed

10 files changed

+510
-66
lines changed

doc/api/cli.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1381,8 +1381,23 @@ added: v12.0.0
13811381
-->
13821382

13831383
This configures Node.js to interpret `--eval` or `STDIN` input as CommonJS or
1384-
as an ES module. Valid values are `"commonjs"` or `"module"`. The default is
1385-
`"commonjs"`.
1384+
as an ES module. Valid values are `"commonjs"`, `"module"`, `"module-typescript"` and `"commonjs-typescript"`.
1385+
The `"-typescript"` values are available only in combination with the flag `--experimental-strip-types`.
1386+
The default is `"commonjs"`.
1387+
1388+
If `--experimental-strip-types` is enabled and `--input-type` is not provided,
1389+
Node.js will try to detect the syntax with the following steps:
1390+
1391+
1. Run the input as CommonJS.
1392+
2. If step 1 fails, run the input as an ES module.
1393+
3. If step 2 fails with a SyntaxError, strip the types.
1394+
4. If step 3 fails with an error code [`ERR_INVALID_TYPESCRIPT_SYNTAX`][],
1395+
throw the error from step 2, including the TypeScript error in the message,
1396+
else run as CommonJS.
1397+
5. If step 4 fails, run the input as an ES module.
1398+
1399+
To avoid the delay of multiple syntax detection passes, the `--input-type=type` flag can be used to specify
1400+
how the `--eval` input should be interpreted.
13861401

13871402
The REPL does not support this option. Usage of `--input-type=module` with
13881403
[`--print`][] will throw an error, as `--print` does not support ES module
@@ -3645,6 +3660,7 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
36453660
[`AsyncLocalStorage`]: async_context.md#class-asynclocalstorage
36463661
[`Buffer`]: buffer.md#class-buffer
36473662
[`CRYPTO_secure_malloc_init`]: https://www.openssl.org/docs/man3.0/man3/CRYPTO_secure_malloc_init.html
3663+
[`ERR_INVALID_TYPESCRIPT_SYNTAX`]: errors.md#err_invalid_typescript_syntax
36483664
[`NODE_OPTIONS`]: #node_optionsoptions
36493665
[`NO_COLOR`]: https://no-color.org
36503666
[`SlowBuffer`]: buffer.md#class-slowbuffer

lib/internal/main/eval_string.js

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,34 @@ const {
1313
prepareMainThreadExecution,
1414
markBootstrapComplete,
1515
} = require('internal/process/pre_execution');
16-
const { evalModuleEntryPoint, evalScript } = require('internal/process/execution');
16+
const {
17+
evalModuleEntryPoint,
18+
evalTypeScript,
19+
parseAndEvalCommonjsTypeScript,
20+
parseAndEvalModuleTypeScript,
21+
evalScript,
22+
} = require('internal/process/execution');
1723
const { addBuiltinLibsToObject } = require('internal/modules/helpers');
18-
const { stripTypeScriptModuleTypes } = require('internal/modules/typescript');
1924
const { getOptionValue } = require('internal/options');
2025

2126
prepareMainThreadExecution();
2227
addBuiltinLibsToObject(globalThis, '<eval>');
2328
markBootstrapComplete();
2429

2530
const code = getOptionValue('--eval');
26-
const source = getOptionValue('--experimental-strip-types') ?
27-
stripTypeScriptModuleTypes(code) :
28-
code;
2931

3032
const print = getOptionValue('--print');
3133
const shouldLoadESM = getOptionValue('--import').length > 0 || getOptionValue('--experimental-loader').length > 0;
32-
if (getOptionValue('--input-type') === 'module') {
33-
evalModuleEntryPoint(source, print);
34+
const inputType = getOptionValue('--input-type');
35+
const tsEnabled = getOptionValue('--experimental-strip-types');
36+
if (inputType === 'module') {
37+
evalModuleEntryPoint(code, print);
38+
} else if (inputType === 'module-typescript' && tsEnabled) {
39+
parseAndEvalModuleTypeScript(code, print);
3440
} else {
3541
// For backward compatibility, we want the identifier crypto to be the
3642
// `node:crypto` module rather than WebCrypto.
37-
const isUsingCryptoIdentifier = RegExpPrototypeExec(/\bcrypto\b/, source) !== null;
43+
const isUsingCryptoIdentifier = RegExpPrototypeExec(/\bcrypto\b/, code) !== null;
3844
const shouldDefineCrypto = isUsingCryptoIdentifier && internalBinding('config').hasOpenSSL;
3945

4046
if (isUsingCryptoIdentifier && !shouldDefineCrypto) {
@@ -49,11 +55,24 @@ if (getOptionValue('--input-type') === 'module') {
4955
};
5056
ObjectDefineProperty(object, name, { __proto__: null, set: setReal });
5157
}
52-
evalScript('[eval]',
53-
shouldDefineCrypto ? (
54-
print ? `let crypto=require("node:crypto");{${source}}` : `(crypto=>{{${source}}})(require('node:crypto'))`
55-
) : source,
56-
getOptionValue('--inspect-brk'),
57-
print,
58-
shouldLoadESM);
58+
59+
let evalFunction;
60+
if (inputType === 'commonjs') {
61+
evalFunction = evalScript;
62+
} else if (inputType === 'commonjs-typescript' && tsEnabled) {
63+
evalFunction = parseAndEvalCommonjsTypeScript;
64+
} else if (tsEnabled) {
65+
evalFunction = evalTypeScript;
66+
} else {
67+
// Default to commonjs.
68+
evalFunction = evalScript;
69+
}
70+
71+
evalFunction('[eval]',
72+
shouldDefineCrypto ? (
73+
print ? `let crypto=require("node:crypto");{${code}}` : `(crypto=>{{${code}}})(require('node:crypto'))`
74+
) : code,
75+
getOptionValue('--inspect-brk'),
76+
print,
77+
shouldLoadESM);
5978
}

lib/internal/modules/cjs/loader.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,6 @@ function initializeCJS() {
449449

450450
const tsEnabled = getOptionValue('--experimental-strip-types');
451451
if (tsEnabled) {
452-
emitExperimentalWarning('Type Stripping');
453452
Module._extensions['.cts'] = loadCTS;
454453
Module._extensions['.ts'] = loadTS;
455454
}

lib/internal/modules/esm/loader.js

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,25 @@ class ModuleLoader {
213213
}
214214
}
215215

216-
async eval(source, url, isEntryPoint = false) {
216+
/**
217+
*
218+
* @param {string} source Source code of the module.
219+
* @param {string} url URL of the module.
220+
* @returns {object} The module wrap object.
221+
*/
222+
createModuleWrap(source, url) {
223+
return compileSourceTextModule(url, source, this);
224+
}
225+
226+
/**
227+
*
228+
* @param {string} url URL of the module.
229+
* @param {object} wrap Module wrap object.
230+
* @param {boolean} isEntryPoint Whether the module is the entry point.
231+
* @returns {Promise<object>} The module object.
232+
*/
233+
async executeModuleJob(url, wrap, isEntryPoint = false) {
217234
const { ModuleJob } = require('internal/modules/esm/module_job');
218-
const wrap = compileSourceTextModule(url, source, this);
219235
const module = await onImport.tracePromise(async () => {
220236
const job = new ModuleJob(
221237
this, url, undefined, wrap, false, false);
@@ -235,6 +251,18 @@ class ModuleLoader {
235251
};
236252
}
237253

254+
/**
255+
*
256+
* @param {string} source Source code of the module.
257+
* @param {string} url URL of the module.
258+
* @param {boolean} isEntryPoint Whether the module is the entry point.
259+
* @returns {Promise<object>} The module object.
260+
*/
261+
eval(source, url, isEntryPoint = false) {
262+
const wrap = this.createModuleWrap(source, url);
263+
return this.executeModuleJob(url, wrap, isEntryPoint);
264+
}
265+
238266
/**
239267
* Get a (possibly not yet fully linked) module job from the cache, or create one and return its Promise.
240268
* @param {string} specifier The module request of the module to be resolved. Typically, what's

lib/internal/modules/esm/translators.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,6 @@ translators.set('require-commonjs', (url, source, isMain) => {
291291
// Handle CommonJS modules referenced by `require` calls.
292292
// This translator function must be sync, as `require` is sync.
293293
translators.set('require-commonjs-typescript', (url, source, isMain) => {
294-
emitExperimentalWarning('Type Stripping');
295294
assert(cjsParse);
296295
const code = stripTypeScriptModuleTypes(stringify(source), url);
297296
return createCJSModuleWrap(url, code, isMain, 'commonjs-typescript');
@@ -536,7 +535,6 @@ translators.set('addon', function translateAddon(url, source, isMain) {
536535

537536
// Strategy for loading a commonjs TypeScript module
538537
translators.set('commonjs-typescript', function(url, source) {
539-
emitExperimentalWarning('Type Stripping');
540538
assertBufferSource(source, true, 'load');
541539
const code = stripTypeScriptModuleTypes(stringify(source), url);
542540
debug(`Translating TypeScript ${url}`);
@@ -545,7 +543,6 @@ translators.set('commonjs-typescript', function(url, source) {
545543

546544
// Strategy for loading an esm TypeScript module
547545
translators.set('module-typescript', function(url, source) {
548-
emitExperimentalWarning('Type Stripping');
549546
assertBufferSource(source, true, 'load');
550547
const code = stripTypeScriptModuleTypes(stringify(source), url);
551548
debug(`Translating TypeScript ${url}`);

lib/internal/modules/typescript.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,13 @@ function processTypeScriptCode(code, options) {
113113
* It is used by internal loaders.
114114
* @param {string} source TypeScript code to parse.
115115
* @param {string} filename The filename of the source code.
116+
* @param {boolean} emitWarning Whether to emit a warning.
116117
* @returns {TransformOutput} The stripped TypeScript code.
117118
*/
118-
function stripTypeScriptModuleTypes(source, filename) {
119+
function stripTypeScriptModuleTypes(source, filename, emitWarning = true) {
120+
if (emitWarning) {
121+
emitExperimentalWarning('Type Stripping');
122+
}
119123
assert(typeof source === 'string');
120124
if (isUnderNodeModules(filename)) {
121125
throw new ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING(filename);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy