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,
@@ -32,6 +36,7 @@ const { getOptionValue } = require('internal/options');
3236const {
3337 makeContextifyScript, runScriptInThisContext,
3438} = require ( 'internal/vm' ) ;
39+ const { emitExperimentalWarning } = require ( 'internal/util' ) ;
3540// shouldAbortOnUncaughtToggle is a typed array for faster
3641// communication with JS.
3742const { shouldAbortOnUncaughtToggle } = internalBinding ( 'util' ) ;
@@ -84,7 +89,7 @@ function evalScript(name, body, breakFirstLine, print, shouldLoadESM = false) {
8489 if ( getOptionValue ( '--experimental-detect-module' ) &&
8590 getOptionValue ( '--input-type' ) === '' &&
8691 containsModuleSyntax ( body , name , null , 'no CJS variables' ) ) {
87- return evalModuleEntryPoint ( body , print ) ;
92+ return evalTypeScriptModuleEntryPoint ( body , print ) ;
8893 }
8994
9095 const runScript = ( ) => {
@@ -238,10 +243,121 @@ function readStdin(callback) {
238243 } ) ;
239244}
240245
246+ /**
247+ *
248+ * Adds the TS message to the error stack
249+ * At the 3rd line of the stack, the message is added
250+ * @param {Error } originalStack the stack to decorate
251+ * @param {string } newMessage the message to add to the error stack
252+ * @returns {void }
253+ */
254+ function decorateCJSErrorWithTSMessage ( originalStack , newMessage ) {
255+ const lines = StringPrototypeSplit ( originalStack , '\n' ) ;
256+ ArrayPrototypeSplice ( lines , 3 , 0 , newMessage ) ;
257+ return ArrayPrototypeJoin ( lines , '\n' ) ;
258+ }
259+
260+ /**
261+ *
262+ * Wrapper of evalScript
263+ *
264+ * This function wraps the evaluation of the source code in a try-catch block.
265+ * If the source code fails to be evaluated, it will retry evaluating the source code
266+ * with the TypeScript parser.
267+ *
268+ * If the source code fails to be evaluated with the TypeScript parser,
269+ * it will rethrow the original error, adding the TypeScript error message to the stack.
270+ *
271+ * This way we don't change the behavior of the code, but we provide a better error message
272+ * in case of a typescript error.
273+ */
274+ function evalTypeScript ( name , source , breakFirstLine , print , shouldLoadESM = false ) {
275+ try {
276+ evalScript ( name , source , breakFirstLine , print , shouldLoadESM ) ;
277+ } catch ( originalError ) {
278+ try {
279+ const strippedSource = stripTypeScriptModuleTypes ( source , name , false ) ;
280+ evalScript ( name , strippedSource , breakFirstLine , print , shouldLoadESM ) ;
281+ // Throw the expiramental warning after the code was successfully evaluated.
282+ emitExperimentalWarning ( 'Type Stripping' ) ;
283+ } catch ( tsError ) {
284+ if ( tsError . code === 'ERR_INVALID_TYPESCRIPT_SYNTAX' ) {
285+ originalError . stack = decorateCJSErrorWithTSMessage ( originalError . stack , tsError . message ) ;
286+ }
287+ throw originalError ;
288+ }
289+ }
290+ }
291+
292+ /**
293+ * Wrapper of evalModuleEntryPoint
294+ *
295+ * This function wraps the evaluation of the source code in a try-catch block.
296+ * If the source code fails to be evaluated, it will retry evaluating the source code
297+ * with the TypeScript parser.
298+ *
299+ */
300+ function evalTypeScriptModuleEntryPoint ( source , print ) {
301+ if ( print ) {
302+ throw new ERR_EVAL_ESM_CANNOT_PRINT ( ) ;
303+ }
304+
305+ RegExpPrototypeExec ( / ^ / , '' ) ; // Necessary to reset RegExp statics before user code runs.
306+
307+ return require ( 'internal/modules/run_main' ) . runEntryPointWithESMLoader (
308+ async ( loader ) => {
309+ try {
310+ // Await here to catch the error and rethrow it with the typescript error message.
311+ return await loader . eval ( source , getEvalModuleUrl ( ) , true ) ;
312+ } catch ( originalError ) {
313+ try {
314+ const url = getEvalModuleUrl ( ) ;
315+ const strippedSource = stripTypeScriptModuleTypes ( source , url , false ) ;
316+ const result = await loader . eval ( strippedSource , url , true ) ;
317+ // Throw the expiramental warning after the code was successfully evaluated.
318+ emitExperimentalWarning ( 'Type Stripping' ) ;
319+ return result ;
320+ } catch ( tsError ) {
321+ if ( tsError . code === 'ERR_INVALID_TYPESCRIPT_SYNTAX' ) {
322+ originalError . stack = `${ tsError . message } \n\n${ originalError . stack } ` ;
323+ }
324+ throw originalError ;
325+ }
326+ }
327+ } ,
328+ ) ;
329+ } ;
330+
331+ /**
332+ *
333+ * Function used to shortcut when `--input-type=module-typescript` is set.
334+ * @param {string } source
335+ * @param {boolean } print
336+ */
337+ function parseAndEvalModuleTypeScript ( source , print ) {
338+ // We know its a TypeScript module, we can safely emit the experimental warning.
339+ const strippedSource = stripTypeScriptModuleTypes ( source , getEvalModuleUrl ( ) ) ;
340+ evalModuleEntryPoint ( strippedSource , print ) ;
341+ }
342+
343+ /**
344+ * Function used to shortcut when `--input-type=commonjs-typescript` is set
345+ * See evalScript signature
346+ */
347+ function parseAndEvalCommonjsTypeScript ( name , source , breakFirstLine , print , shouldLoadESM = false ) {
348+ // We know its a TypeScript module, we can safely emit the experimental warning.
349+ const strippedSource = stripTypeScriptModuleTypes ( source , getEvalModuleUrl ( ) ) ;
350+ evalScript ( name , strippedSource , breakFirstLine , print , shouldLoadESM ) ;
351+ }
352+
241353module . exports = {
354+ parseAndEvalCommonjsTypeScript,
355+ parseAndEvalModuleTypeScript,
242356 readStdin,
243357 tryGetCwd,
358+ evalTypeScriptModuleEntryPoint,
244359 evalModuleEntryPoint,
360+ evalTypeScript,
245361 evalScript,
246362 onGlobalUncaughtException : createOnGlobalUncaughtException ( ) ,
247363 setUncaughtExceptionCaptureCallback,
0 commit comments