2
2
3
3
const {
4
4
RegExpPrototypeExec,
5
+ StringPrototypeIndexOf,
6
+ StringPrototypeSlice,
5
7
Symbol,
6
8
globalThis,
7
9
} = primordials ;
@@ -17,6 +19,7 @@ const {
17
19
} = require ( 'internal/errors' ) ;
18
20
const { pathToFileURL } = require ( 'internal/url' ) ;
19
21
const { exitCodes : { kGenericUserError } } = internalBinding ( 'errors' ) ;
22
+ const { stripTypeScriptModuleTypes } = require ( 'internal/modules/typescript' ) ;
20
23
21
24
const {
22
25
executionAsyncId,
@@ -32,6 +35,7 @@ const { getOptionValue } = require('internal/options');
32
35
const {
33
36
makeContextifyScript, runScriptInThisContext,
34
37
} = require ( 'internal/vm' ) ;
38
+ const { emitExperimentalWarning, isError } = require ( 'internal/util' ) ;
35
39
// shouldAbortOnUncaughtToggle is a typed array for faster
36
40
// communication with JS.
37
41
const { shouldAbortOnUncaughtToggle } = internalBinding ( 'util' ) ;
@@ -84,6 +88,9 @@ function evalScript(name, body, breakFirstLine, print, shouldLoadESM = false) {
84
88
if ( getOptionValue ( '--experimental-detect-module' ) &&
85
89
getOptionValue ( '--input-type' ) === '' &&
86
90
containsModuleSyntax ( body , name , null , 'no CJS variables' ) ) {
91
+ if ( getOptionValue ( '--experimental-strip-types' ) ) {
92
+ return evalTypeScriptModuleEntryPoint ( body , print ) ;
93
+ }
87
94
return evalModuleEntryPoint ( body , print ) ;
88
95
}
89
96
@@ -238,10 +245,156 @@ function readStdin(callback) {
238
245
} ) ;
239
246
}
240
247
248
+ /**
249
+ * Adds the TS message to the error stack.
250
+ *
251
+ * At the 3rd line of the stack, the message is added.
252
+ * @param {string } originalStack The stack to decorate
253
+ * @param {string } newMessage the message to add to the error stack
254
+ * @returns {void }
255
+ */
256
+ function decorateCJSErrorWithTSMessage ( originalStack , newMessage ) {
257
+ let index ;
258
+ for ( let i = 0 ; i < 3 ; i ++ ) {
259
+ index = StringPrototypeIndexOf ( originalStack , '\n' , index + 1 ) ;
260
+ }
261
+ return StringPrototypeSlice ( originalStack , 0 , index ) +
262
+ '\n' + newMessage +
263
+ StringPrototypeSlice ( originalStack , index ) ;
264
+ }
265
+
266
+ /**
267
+ *
268
+ * Wrapper of evalScript
269
+ *
270
+ * This function wraps the evaluation of the source code in a try-catch block.
271
+ * If the source code fails to be evaluated, it will retry evaluating the source code
272
+ * with the TypeScript parser.
273
+ *
274
+ * If the source code fails to be evaluated with the TypeScript parser,
275
+ * it will rethrow the original error, adding the TypeScript error message to the stack.
276
+ *
277
+ * This way we don't change the behavior of the code, but we provide a better error message
278
+ * in case of a typescript error.
279
+ * @param {string } name The name of the file
280
+ * @param {string } source The source code to evaluate
281
+ * @param {boolean } breakFirstLine Whether to break on the first line
282
+ * @param {boolean } print If the result should be printed
283
+ * @param {boolean } shouldLoadESM If the code should be loaded as an ESM module
284
+ * @returns {void }
285
+ */
286
+ function evalTypeScript ( name , source , breakFirstLine , print , shouldLoadESM = false ) {
287
+ try {
288
+ evalScript ( name , source , breakFirstLine , print , shouldLoadESM ) ;
289
+ } catch ( originalError ) {
290
+ // If it's not a SyntaxError, rethrow it.
291
+ if ( ! isError ( originalError ) || originalError . name !== 'SyntaxError' ) {
292
+ throw originalError ;
293
+ }
294
+ try {
295
+ const strippedSource = stripTypeScriptModuleTypes ( source , name , false ) ;
296
+ evalScript ( name , strippedSource , breakFirstLine , print , shouldLoadESM ) ;
297
+ // Emit the experimental warning after the code was successfully evaluated.
298
+ emitExperimentalWarning ( 'Type Stripping' ) ;
299
+ } catch ( tsError ) {
300
+ // If its not an error, or it's not an invalid typescript syntax error, rethrow it.
301
+ if ( ! isError ( tsError ) || tsError ?. code !== 'ERR_INVALID_TYPESCRIPT_SYNTAX' ) {
302
+ throw tsError ;
303
+ }
304
+
305
+ try {
306
+ originalError . stack = decorateCJSErrorWithTSMessage ( originalError . stack , tsError . message ) ;
307
+ } catch { /* Ignore potential errors coming from `stack` getter/setter */ }
308
+ throw originalError ;
309
+ }
310
+ }
311
+ }
312
+
313
+ /**
314
+ * Wrapper of evalModuleEntryPoint
315
+ *
316
+ * This function wraps the evaluation of the source code in a try-catch block.
317
+ * If the source code fails to be evaluated, it will retry evaluating the source code
318
+ * with the TypeScript parser.
319
+ * @param {string } source The source code to evaluate
320
+ * @param {boolean } print If the result should be printed
321
+ * @returns {Promise } The module evaluation promise
322
+ */
323
+ function evalTypeScriptModuleEntryPoint ( source , print ) {
324
+ if ( print ) {
325
+ throw new ERR_EVAL_ESM_CANNOT_PRINT ( ) ;
326
+ }
327
+
328
+ RegExpPrototypeExec ( / ^ / , '' ) ; // Necessary to reset RegExp statics before user code runs.
329
+
330
+ return require ( 'internal/modules/run_main' ) . runEntryPointWithESMLoader (
331
+ async ( loader ) => {
332
+ try {
333
+ // Await here to catch the error and rethrow it with the typescript error message.
334
+ return await loader . eval ( source , getEvalModuleUrl ( ) , true ) ;
335
+ } catch ( originalError ) {
336
+ // If it's not a SyntaxError, rethrow it.
337
+ if ( ! isError ( originalError ) || originalError . name !== 'SyntaxError' ) {
338
+ throw originalError ;
339
+ }
340
+
341
+ try {
342
+ const url = getEvalModuleUrl ( ) ;
343
+ const strippedSource = stripTypeScriptModuleTypes ( source , url , false ) ;
344
+ const result = await loader . eval ( strippedSource , url , true ) ;
345
+ // Emit the experimental warning after the code was successfully evaluated.
346
+ emitExperimentalWarning ( 'Type Stripping' ) ;
347
+ return result ;
348
+ } catch ( tsError ) {
349
+ // If its not an error, or it's not an invalid typescript syntax error, rethrow it.
350
+ if ( ! isError ( tsError ) || tsError ?. code !== 'ERR_INVALID_TYPESCRIPT_SYNTAX' ) {
351
+ throw tsError ;
352
+ }
353
+
354
+ try {
355
+ originalError . stack = `${ tsError . message } \n\n${ originalError . stack } ` ;
356
+ } catch { /* Ignore potential errors coming from `stack` getter/setter */ }
357
+ throw originalError ;
358
+ }
359
+ }
360
+ } ,
361
+ ) ;
362
+ } ;
363
+
364
+ /**
365
+ *
366
+ * Function used to shortcut when `--input-type=module-typescript` is set.
367
+ * @param {string } source
368
+ * @param {boolean } print
369
+ */
370
+ function parseAndEvalModuleTypeScript ( source , print ) {
371
+ // We know its a TypeScript module, we can safely emit the experimental warning.
372
+ const strippedSource = stripTypeScriptModuleTypes ( source , getEvalModuleUrl ( ) ) ;
373
+ evalModuleEntryPoint ( strippedSource , print ) ;
374
+ }
375
+
376
+ /**
377
+ * Function used to shortcut when `--input-type=commonjs-typescript` is set
378
+ * @param {string } name The name of the file
379
+ * @param {string } source The source code to evaluate
380
+ * @param {boolean } breakFirstLine Whether to break on the first line
381
+ * @param {boolean } print If the result should be printed
382
+ * @param {boolean } shouldLoadESM If the code should be loaded as an ESM module
383
+ * @returns {void }
384
+ */
385
+ function parseAndEvalCommonjsTypeScript ( name , source , breakFirstLine , print , shouldLoadESM = false ) {
386
+ // We know its a TypeScript module, we can safely emit the experimental warning.
387
+ const strippedSource = stripTypeScriptModuleTypes ( source , getEvalModuleUrl ( ) ) ;
388
+ evalScript ( name , strippedSource , breakFirstLine , print , shouldLoadESM ) ;
389
+ }
390
+
241
391
module . exports = {
392
+ parseAndEvalCommonjsTypeScript,
393
+ parseAndEvalModuleTypeScript,
242
394
readStdin,
243
395
tryGetCwd,
244
396
evalModuleEntryPoint,
397
+ evalTypeScript,
245
398
evalScript,
246
399
onGlobalUncaughtException : createOnGlobalUncaughtException ( ) ,
247
400
setUncaughtExceptionCaptureCallback,
0 commit comments