11import fs from "node:fs" ;
22import path from "node:path" ;
33import { fileURLToPath } from "node:url" ;
4+ import { Script } from "node:vm" ;
45import { JSDOM , ResourceLoader , VirtualConsole } from "jsdom" ;
56import { escapeSep } from "../../helper" ;
67import EventSource from "../../helper/legacy/EventSourceForNode" ;
78import urlToRelativePath from "../../helper/legacy/urlToRelativePath" ;
89import type { ECompilerType , TRunnerFile , TRunnerRequirer } from "../../type" ;
910import { type INodeRunnerOptions , NodeRunner } from "../node" ;
1011
12+ const EVAL_LOCATION_REGEX = / < a n o n y m o u s > : ( \d + ) / ;
1113export interface IWebRunnerOptions <
1214 T extends ECompilerType = ECompilerType . Rspack
1315> extends INodeRunnerOptions < T > {
@@ -236,6 +238,41 @@ export class WebRunner<
236238 }
237239 }
238240
241+ const createLocatedError = ( e : Error , file : TRunnerFile ) => {
242+ const match = ( e . stack || e . message ) . match ( EVAL_LOCATION_REGEX ) ;
243+ if ( match ) {
244+ const [ , line ] = match ;
245+ const realLine = Number ( line ) - 34 ;
246+ const codeLines = file . content . split ( "\n" ) ;
247+ const lineContents = [
248+ ...codeLines
249+ . slice ( Math . max ( 0 , realLine - 3 ) , Math . max ( 0 , realLine - 1 ) )
250+ . map ( line => `│ ${ line } ` ) ,
251+ `│> ${ codeLines [ realLine - 1 ] } ` ,
252+ ...codeLines . slice ( realLine , realLine + 2 ) . map ( line => `│ ${ line } ` )
253+ ] ;
254+ const message = `Error in JSDOM when running file '${ file . path } ' at line ${ realLine } : ${ e . message } \n${ lineContents . join ( "\n" ) } ` ;
255+ const finalError = new Error ( message ) ;
256+ finalError . stack = undefined ;
257+ return finalError ;
258+ } else {
259+ return e ;
260+ }
261+ } ;
262+ const originIt = currentModuleScope . it ;
263+ currentModuleScope . it = (
264+ description : string ,
265+ fn : ( ) => Promise < void > | void
266+ ) => {
267+ originIt ( description , async ( ) => {
268+ try {
269+ await fn ( ) ;
270+ } catch ( err ) {
271+ throw createLocatedError ( err as Error , file ) ;
272+ }
273+ } ) ;
274+ } ;
275+
239276 const scopeKey = escapeSep ( file ! . path ) ;
240277 const args = Object . keys ( currentModuleScope ) . filter (
241278 arg => ! [ "window" , "self" , "globalThis" , "console" ] . includes ( arg )
@@ -245,10 +282,12 @@ export class WebRunner<
245282 . join ( ", " ) ;
246283 this . dom . window [ scopeKey ] = currentModuleScope ;
247284 this . dom . window [ "__GLOBAL_SHARED__" ] = this . globalContext ;
285+ this . dom . window [ "__FILE__" ] = file ;
286+ this . dom . window [ "__CREATE_LOCATED_ERROR__" ] = createLocatedError ;
287+
248288 return [
249289 m ,
250- `
251- // hijack document.currentScript for auto public path
290+ `// hijack document.currentScript for auto public path
252291 var $$g$$ = new Proxy(window, {
253292 get(target, prop, receiver) {
254293 if (prop === "document") {
@@ -281,9 +320,12 @@ export class WebRunner<
281320 }
282321 });
283322 (function(window, self, globalThis, console, ${ args . join ( ", " ) } ) {
284- ${ file . content }
285- })($$g$$, $$self$$, $$g$$, window["console"], ${ argValues } );
286- `
323+ try {
324+ ${ file . content }
325+ } catch (err) {
326+ throw __CREATE_LOCATED_ERROR__(err, __FILE__);
327+ }
328+ })($$g$$, $$self$$, $$g$$, window["console"], ${ argValues } );`
287329 ] ;
288330 }
289331
@@ -301,7 +343,11 @@ export class WebRunner<
301343 const [ m , code ] = this . getModuleContent ( file ) ;
302344
303345 this . preExecute ( code , file ) ;
304- this . dom . window . eval ( code ) ;
346+
347+ const script = new Script ( code ) ;
348+ const vmContext = this . dom . getInternalVMContext ( ) ;
349+ script . runInContext ( vmContext ) ;
350+
305351 this . postExecute ( m , file ) ;
306352
307353 this . requireCache [ file . path ] = m ;
0 commit comments