@@ -16,6 +16,7 @@ import { WASI, File, OpenFile, ConsoleStdout, PreopenDirectory, WASIProcExit, In
16
16
import type { SwiftRuntime , SwiftRuntimeConstructor } from "./JavaScriptKit_JavaScriptKit.resources/Runtime/index" ;
17
17
import { polyfill as polyfillWebAssemblyTypeReflection } from "wasm-imports-parser/polyfill" ;
18
18
import type { ImportEntry } from "wasm-imports-parser" ;
19
+ import { AddressInfo } from "node:net" ;
19
20
20
21
// Apply polyfill for WebAssembly Type Reflection JS API to inspect imported memory info.
21
22
// https://github.com/WebAssembly/js-types/blob/main/proposals/js-types/Overview.md
@@ -65,7 +66,14 @@ export async function instantiate(rawOptions: InstantiationOptions, extraWasmImp
65
66
66
67
let swift : SwiftRuntime | undefined = options . swift ;
67
68
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 } ) ;
69
77
}
70
78
71
79
let stdoutLine : LineDecoder | undefined = undefined ;
@@ -242,10 +250,54 @@ async function extractAndSaveFile(rootFs: Map<string, Inode>, path: string): Pro
242
250
return false ;
243
251
}
244
252
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
+ ) {
246
261
if ( inPage ) {
247
262
return await testBrowserInPage ( instantiate , wasmFileName , args ) ;
248
263
}
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
+
249
301
const playwright = await ( async ( ) => {
250
302
try {
251
303
// @ts -ignore
@@ -263,29 +315,16 @@ Please run the following command to install it:
263
315
const browser = await playwright . chromium . launch ( ) ;
264
316
const context = await browser . newContext ( ) ;
265
317
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 ) ;
270
318
271
319
// Forward console messages in the page to the Node.js console
272
320
page . on ( "console" , ( message : any ) => {
273
321
console . log ( message . text ( ) ) ;
274
322
} ) ;
275
323
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
- } ) ;
285
324
const onExit = new Promise < number > ( ( resolve ) => {
286
325
page . exposeFunction ( "exitTest" , resolve ) ;
287
326
} ) ;
288
- await page . goto ( " http://example.com /test.browser.html" ) ;
327
+ await page . goto ( ` http://localhost: ${ address . port } /test.browser.html` ) ;
289
328
const exitCode = await onExit ;
290
329
await browser . close ( ) ;
291
330
process . exit ( exitCode ) ;
0 commit comments