11'use strict' ;
22
33const {
4+ ArrayPrototypeJoin,
5+ ArrayPrototypeSplice,
46 RegExpPrototypeExec,
7+ StringPrototypeSplit,
58 Symbol,
69 globalThis,
710} = primordials ;
@@ -17,6 +20,7 @@ const {
1720} = require ( 'internal/errors' ) ;
1821const { pathToFileURL } = require ( 'internal/url' ) ;
1922const { exitCodes : { kGenericUserError } } = internalBinding ( 'errors' ) ;
23+ const { stripTypeScriptModuleTypes } = require ( 'internal/modules/typescript' ) ;
2024
2125const {
2226 executionAsyncId,
@@ -84,7 +88,7 @@ function evalScript(name, body, breakFirstLine, print, shouldLoadESM = false) {
8488 if ( getOptionValue ( '--experimental-detect-module' ) &&
8589 getOptionValue ( '--input-type' ) === '' &&
8690 containsModuleSyntax ( body , name , null , 'no CJS variables' ) ) {
87- return evalModuleEntryPoint ( body , print ) ;
91+ return evalTypeScriptModuleEntryPoint ( body , print ) ;
8892 }
8993
9094 const runScript = ( ) => {
@@ -238,10 +242,114 @@ function readStdin(callback) {
238242 } ) ;
239243}
240244
245+ /**
246+ *
247+ * Adds the TS message to the error stack
248+ * At the 3rd line of the stack, the message is added
249+ * @param {Error } originalStack the stack to decorate
250+ * @param {string } newMessage the message to add to the error stack
251+ * @returns {void }
252+ */
253+ function decorateCJSErrorWithTSMessage ( originalStack , newMessage ) {
254+ const lines = StringPrototypeSplit ( originalStack , '\n' ) ;
255+ ArrayPrototypeSplice ( lines , 3 , 0 , newMessage ) ;
256+ return ArrayPrototypeJoin ( lines , '\n' ) ;
257+ }
258+
259+ /**
260+ *
261+ * Wrapper of evalScript
262+ *
263+ * This function wraps the evaluation of the source code in a try-catch block.
264+ * If the source code fails to be evaluated, it will retry evaluating the source code
265+ * with the TypeScript parser.
266+ *
267+ * If the source code fails to be evaluated with the TypeScript parser,
268+ * it will rethrow the original error, adding the TypeScript error message to the stack.
269+ *
270+ * This way we don't change the behavior of the code, but we provide a better error message
271+ * in case of a typescript error.
272+ */
273+ function evalTypeScript ( name , source , breakFirstLine , print , shouldLoadESM = false ) {
274+ try {
275+ evalScript ( name , source , breakFirstLine , print , shouldLoadESM ) ;
276+ } catch ( originalError ) {
277+ try {
278+ const strippedSource = stripTypeScriptModuleTypes ( source , name , false ) ;
279+ evalScript ( name , strippedSource , breakFirstLine , print , shouldLoadESM ) ;
280+ } catch ( tsError ) {
281+ if ( tsError . code === 'ERR_INVALID_TYPESCRIPT_SYNTAX' ) {
282+ originalError . stack = decorateCJSErrorWithTSMessage ( originalError . stack , tsError . message ) ;
283+ }
284+ throw originalError ;
285+ }
286+ }
287+ }
288+
289+ /**
290+ * Wrapper of evalModuleEntryPoint
291+ *
292+ * This function wraps the evaluation of the source code in a try-catch block.
293+ * If the source code fails to be evaluated, it will retry evaluating the source code
294+ * with the TypeScript parser.
295+ *
296+ */
297+ function evalTypeScriptModuleEntryPoint ( source , print ) {
298+ if ( print ) {
299+ throw new ERR_EVAL_ESM_CANNOT_PRINT ( ) ;
300+ }
301+
302+ RegExpPrototypeExec ( / ^ / , '' ) ; // Necessary to reset RegExp statics before user code runs.
303+
304+ return require ( 'internal/modules/run_main' ) . runEntryPointWithESMLoader (
305+ async ( loader ) => {
306+ try {
307+ // Await here to catch the error and rethrow it with the typescript error message
308+ return await loader . eval ( source , getEvalModuleUrl ( ) , true ) ;
309+ } catch ( originalError ) {
310+ try {
311+ const url = getEvalModuleUrl ( ) ;
312+ const strippedSource = stripTypeScriptModuleTypes ( source , url , false ) ;
313+ return loader . eval ( strippedSource , url , true ) ;
314+ } catch ( tsError ) {
315+ if ( tsError . code === 'ERR_INVALID_TYPESCRIPT_SYNTAX' ) {
316+ originalError . stack = `${ tsError . message } \n\n${ originalError . stack } ` ;
317+ }
318+ throw originalError ;
319+ }
320+ }
321+ } ,
322+ ) ;
323+ } ;
324+
325+ /**
326+ *
327+ * Function used to shortcut when `--input-type=module-typescript` is set
328+ * @param {string } source
329+ * @param {boolean } print
330+ */
331+ function parseAndEvalModuleTypeScript ( source , print ) {
332+ const strippedSource = stripTypeScriptModuleTypes ( source , getEvalModuleUrl ( ) , false ) ;
333+ evalModuleEntryPoint ( strippedSource , print ) ;
334+ }
335+
336+ /**
337+ * Function used to shortcut when `--input-type=commonjs-typescript` is set
338+ * See evalScript signature
339+ */
340+ function parseAndEvalCommonjsTypeScript ( name , source , breakFirstLine , print , shouldLoadESM = false ) {
341+ const strippedSource = stripTypeScriptModuleTypes ( source , getEvalModuleUrl ( ) , false ) ;
342+ evalScript ( name , strippedSource , breakFirstLine , print , shouldLoadESM ) ;
343+ }
344+
241345module . exports = {
346+ parseAndEvalCommonjsTypeScript,
347+ parseAndEvalModuleTypeScript,
242348 readStdin,
243349 tryGetCwd,
350+ evalTypeScriptModuleEntryPoint,
244351 evalModuleEntryPoint,
352+ evalTypeScript,
245353 evalScript,
246354 onGlobalUncaughtException : createOnGlobalUncaughtException ( ) ,
247355 setUncaughtExceptionCaptureCallback,
0 commit comments