@@ -16,6 +16,7 @@ import { WASI, File, OpenFile, ConsoleStdout, PreopenDirectory, WASIProcExit, In
1616import type { SwiftRuntime , SwiftRuntimeConstructor } from "./JavaScriptKit_JavaScriptKit.resources/Runtime/index" ;
1717import { polyfill as polyfillWebAssemblyTypeReflection } from "wasm-imports-parser/polyfill" ;
1818import type { ImportEntry } from "wasm-imports-parser" ;
19+ import { AddressInfo } from "node:net" ;
1920
2021// Apply polyfill for WebAssembly Type Reflection JS API to inspect imported memory info.
2122// https://github.com/WebAssembly/js-types/blob/main/proposals/js-types/Overview.md
@@ -65,7 +66,14 @@ export async function instantiate(rawOptions: InstantiationOptions, extraWasmImp
6566
6667 let swift : SwiftRuntime | undefined = options . swift ;
6768 if ( ! swift && options . SwiftRuntime ) {
68- swift = new options . SwiftRuntime ( ) ;
69+ let sharedMemory = false ;
70+ for ( const importEntry of WebAssembly . Module . imports ( options . module ) ) {
71+ if ( importEntry . module === "env" && importEntry . name === "memory" && importEntry . kind === "memory" ) {
72+ sharedMemory = true ;
73+ break ;
74+ }
75+ }
76+ swift = new options . SwiftRuntime ( { sharedMemory } ) ;
6977 }
7078
7179 let stdoutLine : LineDecoder | undefined = undefined ;
@@ -242,10 +250,54 @@ async function extractAndSaveFile(rootFs: Map<string, Inode>, path: string): Pro
242250 return false ;
243251}
244252
245- export async function testBrowser ( instantiate : Instantiate , wasmFileName : string , args : string [ ] , indexJsUrl : string , inPage : boolean ) {
253+ export async function testBrowser (
254+ instantiate : Instantiate ,
255+ wasmFileName : string ,
256+ args : string [ ] ,
257+ indexJsUrl : string ,
258+ options : { contentTypes ?: ( fileName : string ) => string } = { } ,
259+ inPage : boolean = false
260+ ) {
246261 if ( inPage ) {
247262 return await testBrowserInPage ( instantiate , wasmFileName , args ) ;
248263 }
264+
265+ const { fileURLToPath } = await import ( "node:url" ) ;
266+ const path = await import ( "node:path" ) ;
267+ const fs = await import ( "node:fs/promises" ) ;
268+ const { existsSync } = await import ( "node:fs" ) ;
269+ const indexJsPath = fileURLToPath ( indexJsUrl ) ;
270+ const webRoot = path . dirname ( indexJsPath ) ;
271+
272+ const http = await import ( "node:http" ) ;
273+ const defaultContentTypes : Record < string , string > = {
274+ ".html" : "text/html" ,
275+ ".js" : "text/javascript" ,
276+ ".mjs" : "text/javascript" ,
277+ ".wasm" : "application/wasm" ,
278+ } ;
279+ const server = http . createServer ( async ( req , res ) => {
280+ const url = new URL ( req . url ! , `http://${ req . headers . host } ` ) ;
281+ const pathname = url . pathname ;
282+ const filePath = path . join ( webRoot , pathname ) ;
283+ if ( existsSync ( filePath ) && ( await fs . stat ( filePath ) ) . isFile ( ) ) {
284+ const data = await fs . readFile ( filePath ) ;
285+ const ext = pathname . slice ( pathname . lastIndexOf ( "." ) ) ;
286+ const contentType = options . contentTypes ?.( pathname ) || defaultContentTypes [ ext ] || "text/plain" ;
287+ res . writeHead ( 200 , { "Content-Type" : contentType } ) ;
288+ res . end ( data ) ;
289+ } else if ( pathname === "/process-info.json" ) {
290+ res . writeHead ( 200 , { "Content-Type" : "application/json" } ) ;
291+ res . end ( JSON . stringify ( { env : process . env } ) ) ;
292+ } else {
293+ res . writeHead ( 404 ) ;
294+ res . end ( ) ;
295+ }
296+ } ) ;
297+
298+ await new Promise < void > ( ( resolve ) => server . listen ( { host : "localhost" , port : 0 } , ( ) => resolve ( ) ) ) ;
299+ const address = server . address ( ) as AddressInfo ;
300+
249301 const playwright = await ( async ( ) => {
250302 try {
251303 // @ts -ignore
@@ -263,29 +315,16 @@ Please run the following command to install it:
263315 const browser = await playwright . chromium . launch ( ) ;
264316 const context = await browser . newContext ( ) ;
265317 const page = await context . newPage ( ) ;
266- const { fileURLToPath } = await import ( "node:url" ) ;
267- const path = await import ( "node:path" ) ;
268- const indexJsPath = fileURLToPath ( indexJsUrl ) ;
269- const webRoot = path . dirname ( indexJsPath ) ;
270318
271319 // Forward console messages in the page to the Node.js console
272320 page . on ( "console" , ( message : any ) => {
273321 console . log ( message . text ( ) ) ;
274322 } ) ;
275323
276- await page . route ( "http://example.com/**/*" , async ( route : any ) => {
277- const url = route . request ( ) . url ( ) ;
278- const urlPath = new URL ( url ) . pathname ;
279- if ( urlPath === "/process-info.json" ) {
280- route . fulfill ( { body : JSON . stringify ( { env : process . env } ) } ) ;
281- return ;
282- }
283- route . fulfill ( { path : path . join ( webRoot , urlPath . slice ( 1 ) ) } ) ;
284- } ) ;
285324 const onExit = new Promise < number > ( ( resolve ) => {
286325 page . exposeFunction ( "exitTest" , resolve ) ;
287326 } ) ;
288- await page . goto ( " http://example.com /test.browser.html" ) ;
327+ await page . goto ( ` http://localhost: ${ address . port } /test.browser.html` ) ;
289328 const exitCode = await onExit ;
290329 await browser . close ( ) ;
291330 process . exit ( exitCode ) ;
0 commit comments