From ec9470c122bf7ffed0834a13e5c848340858ec36 Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Mon, 17 Mar 2025 07:21:42 +0000
Subject: [PATCH 1/3] PackageToJS: Fetch module from the default location if
 `init` is called without options

---
 .gitignore                                    |  2 +-
 Examples/ActorOnWebWorker/index.html          |  2 +-
 Examples/Basic/.gitignore                     |  5 ----
 Examples/Basic/index.html                     |  2 +-
 Examples/Embedded/.gitignore                  |  6 -----
 Examples/Embedded/index.html                  |  2 +-
 Examples/Multithreading/.gitignore            |  8 ------
 Examples/Multithreading/index.html            |  2 +-
 Examples/OffscrenCanvas/.gitignore            |  8 ------
 Examples/OffscrenCanvas/index.html            |  2 +-
 Plugins/PackageToJS/Templates/index.d.ts      | 26 +++++++------------
 Plugins/PackageToJS/Templates/index.js        | 12 ++++++---
 .../Resources/hello-world-2-2-index-html.html |  2 +-
 13 files changed, 24 insertions(+), 55 deletions(-)
 delete mode 100644 Examples/Basic/.gitignore
 delete mode 100644 Examples/Embedded/.gitignore
 delete mode 100644 Examples/Multithreading/.gitignore
 delete mode 100644 Examples/OffscrenCanvas/.gitignore

diff --git a/.gitignore b/.gitignore
index 2fb37cb48..232ea1145 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,4 +9,4 @@ xcuserdata/
 .vscode
 Examples/*/Bundle
 Examples/*/package-lock.json
-/Package.resolved
+Package.resolved
diff --git a/Examples/ActorOnWebWorker/index.html b/Examples/ActorOnWebWorker/index.html
index 2797702e1..4a16f16a0 100644
--- a/Examples/ActorOnWebWorker/index.html
+++ b/Examples/ActorOnWebWorker/index.html
@@ -8,7 +8,7 @@
 <body>
   <script type="module">
     import { init } from "./Bundle/index.js"
-    init(fetch(new URL("./Bundle/main.wasm", import.meta.url)));
+    init();
   </script>
   <h1>Full-text Search with Actor on Web Worker</h1>
 
diff --git a/Examples/Basic/.gitignore b/Examples/Basic/.gitignore
deleted file mode 100644
index 95c432091..000000000
--- a/Examples/Basic/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-.DS_Store
-/.build
-/Packages
-/*.xcodeproj
-xcuserdata/
diff --git a/Examples/Basic/index.html b/Examples/Basic/index.html
index a674baca1..93868214d 100644
--- a/Examples/Basic/index.html
+++ b/Examples/Basic/index.html
@@ -8,7 +8,7 @@
 <body>
   <script type="module">
     import { init } from "./.build/plugins/PackageToJS/outputs/Package/index.js";
-    await init(fetch("./.build/plugins/PackageToJS/outputs/Package/main.wasm"));
+    init();
   </script>
 </body>
 
diff --git a/Examples/Embedded/.gitignore b/Examples/Embedded/.gitignore
deleted file mode 100644
index 31492b35d..000000000
--- a/Examples/Embedded/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-.DS_Store
-/.build
-/Packages
-/*.xcodeproj
-xcuserdata/
-Package.resolved
\ No newline at end of file
diff --git a/Examples/Embedded/index.html b/Examples/Embedded/index.html
index a674baca1..93868214d 100644
--- a/Examples/Embedded/index.html
+++ b/Examples/Embedded/index.html
@@ -8,7 +8,7 @@
 <body>
   <script type="module">
     import { init } from "./.build/plugins/PackageToJS/outputs/Package/index.js";
-    await init(fetch("./.build/plugins/PackageToJS/outputs/Package/main.wasm"));
+    init();
   </script>
 </body>
 
diff --git a/Examples/Multithreading/.gitignore b/Examples/Multithreading/.gitignore
deleted file mode 100644
index 0023a5340..000000000
--- a/Examples/Multithreading/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-.DS_Store
-/.build
-/Packages
-xcuserdata/
-DerivedData/
-.swiftpm/configuration/registries.json
-.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
-.netrc
diff --git a/Examples/Multithreading/index.html b/Examples/Multithreading/index.html
index 74ba8cfed..20696d83a 100644
--- a/Examples/Multithreading/index.html
+++ b/Examples/Multithreading/index.html
@@ -29,7 +29,7 @@
 <body>
   <script type="module">
     import { init } from "./Bundle/index.js"
-    init(fetch(new URL("./Bundle/main.wasm", import.meta.url)));
+    init();
   </script>
   <h1>Threading Example</h1>
   <p>
diff --git a/Examples/OffscrenCanvas/.gitignore b/Examples/OffscrenCanvas/.gitignore
deleted file mode 100644
index 0023a5340..000000000
--- a/Examples/OffscrenCanvas/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-.DS_Store
-/.build
-/Packages
-xcuserdata/
-DerivedData/
-.swiftpm/configuration/registries.json
-.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
-.netrc
diff --git a/Examples/OffscrenCanvas/index.html b/Examples/OffscrenCanvas/index.html
index 1202807a0..dd101b765 100644
--- a/Examples/OffscrenCanvas/index.html
+++ b/Examples/OffscrenCanvas/index.html
@@ -70,7 +70,7 @@
 <body>
   <script type="module">
     import { init } from "./Bundle/index.js"
-    init(fetch(new URL("./Bundle/main.wasm", import.meta.url)));
+    init();
   </script>
   <h1>OffscreenCanvas Example</h1>
   <p>
diff --git a/Plugins/PackageToJS/Templates/index.d.ts b/Plugins/PackageToJS/Templates/index.d.ts
index 4a1074c14..11d5908c2 100644
--- a/Plugins/PackageToJS/Templates/index.d.ts
+++ b/Plugins/PackageToJS/Templates/index.d.ts
@@ -1,29 +1,21 @@
-import type { Import, Export } from './instantiate.js'
+import type { Export, ModuleSource } from './instantiate.js'
 
 export type Options = {
     /**
-     * The CLI arguments to pass to the WebAssembly module
+     * The WebAssembly module to instantiate
+     *
+     * If not provided, the module will be fetched from the default path.
      */
-    args?: string[]
-/* #if USE_SHARED_MEMORY */
-    /**
-     * The WebAssembly memory to use (must be 'shared')
-     */
-    memory: WebAssembly.Memory
-/* #endif */
+    module?: ModuleSource
 }
 
 /**
- * Initialize the given WebAssembly module
+ * Instantiate and initialize the module
  *
- * This is a convenience function that creates an instantiator and instantiates the module.
- * @param moduleSource - The WebAssembly module to instantiate
- * @param imports - The imports to add
- * @param options - The options
+ * This is a convenience function for browser environments.
+ * If you need a more flexible API, see `instantiate`.
  */
-export declare function init(
-    moduleSource: WebAssembly.Module | ArrayBufferView | ArrayBuffer | Response | PromiseLike<Response>
-): Promise<{
+export declare function init(options?: Options): Promise<{
     instance: WebAssembly.Instance,
     exports: Export
 }>
diff --git a/Plugins/PackageToJS/Templates/index.js b/Plugins/PackageToJS/Templates/index.js
index d0d28569f..4b8d90f6b 100644
--- a/Plugins/PackageToJS/Templates/index.js
+++ b/Plugins/PackageToJS/Templates/index.js
@@ -3,12 +3,16 @@ import { instantiate } from './instantiate.js';
 import { defaultBrowserSetup /* #if USE_SHARED_MEMORY */, createDefaultWorkerFactory /* #endif */} from './platforms/browser.js';
 
 /** @type {import('./index.d').init} */
-export async function init(moduleSource) {
-    const options = await defaultBrowserSetup({
-        module: moduleSource,
+export async function init(options = {}) {
+    let module = options.module;
+    if (!module) {
+        module = fetch(new URL("@PACKAGE_TO_JS_MODULE_PATH@", import.meta.url))
+    }
+    const instantiateOptions = await defaultBrowserSetup({
+        module,
 /* #if USE_SHARED_MEMORY */
         spawnWorker: createDefaultWorkerFactory()
 /* #endif */
     })
-    return await instantiate(options);
+    return await instantiate(instantiateOptions);
 }
diff --git a/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-2-2-index-html.html b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-2-2-index-html.html
index 84a3aa15e..c75dd927a 100644
--- a/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-2-2-index-html.html
+++ b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-2-2-index-html.html
@@ -5,7 +5,7 @@
     <title>Swift Web App</title>
     <script type="module">
         import { init } from "./.build/plugins/PackageToJS/outputs/Package/index.js";
-        await init(fetch("./.build/plugins/PackageToJS/outputs/Package/main.wasm"));
+        init();
     </script>
 </head>
 <body>

From 53d1a470c54be990cfee1e6c9f32e963ce6c719c Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Mon, 17 Mar 2025 07:38:22 +0000
Subject: [PATCH 2/3] PackageToJS: Use the actual wasm filename in the final
 product

---
 Plugins/PackageToJS/Sources/PackageToJS.swift         | 4 +++-
 Plugins/PackageToJS/Sources/PackageToJSPlugin.swift   | 1 +
 Plugins/PackageToJS/Tests/PackagingPlannerTests.swift | 2 ++
 3 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/Plugins/PackageToJS/Sources/PackageToJS.swift b/Plugins/PackageToJS/Sources/PackageToJS.swift
index cc0c02182..c766995d2 100644
--- a/Plugins/PackageToJS/Sources/PackageToJS.swift
+++ b/Plugins/PackageToJS/Sources/PackageToJS.swift
@@ -357,7 +357,7 @@ struct PackagingPlanner {
     /// The directory for intermediate files
     let intermediatesDir: BuildPath
     /// The filename of the .wasm file
-    let wasmFilename = "main.wasm"
+    let wasmFilename: String
     /// The path to the .wasm product artifact
     let wasmProductArtifact: BuildPath
     /// The build configuration
@@ -374,6 +374,7 @@ struct PackagingPlanner {
         selfPackageDir: BuildPath,
         outputDir: BuildPath,
         wasmProductArtifact: BuildPath,
+        wasmFilename: String,
         configuration: String,
         triple: String,
         selfPath: BuildPath = BuildPath(absolute: #filePath),
@@ -384,6 +385,7 @@ struct PackagingPlanner {
         self.selfPackageDir = selfPackageDir
         self.outputDir = outputDir
         self.intermediatesDir = intermediatesDir
+        self.wasmFilename = wasmFilename
         self.selfPath = selfPath
         self.wasmProductArtifact = wasmProductArtifact
         self.configuration = configuration
diff --git a/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift b/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift
index 4bf6a1106..a4fd58d29 100644
--- a/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift
+++ b/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift
@@ -498,6 +498,7 @@ extension PackagingPlanner {
             selfPackageDir: BuildPath(absolute: selfPackage.directoryURL.path),
             outputDir: BuildPath(absolute: outputDir.path),
             wasmProductArtifact: BuildPath(absolute: wasmProductArtifact.path),
+            wasmFilename: wasmProductArtifact.lastPathComponent,
             configuration: configuration,
             triple: triple,
             system: system
diff --git a/Plugins/PackageToJS/Tests/PackagingPlannerTests.swift b/Plugins/PackageToJS/Tests/PackagingPlannerTests.swift
index 1b1eb1abf..6392ca664 100644
--- a/Plugins/PackageToJS/Tests/PackagingPlannerTests.swift
+++ b/Plugins/PackageToJS/Tests/PackagingPlannerTests.swift
@@ -53,6 +53,7 @@ import Testing
             selfPackageDir: BuildPath(prefix: "SELF_PACKAGE"),
             outputDir: BuildPath(prefix: "OUTPUT"),
             wasmProductArtifact: BuildPath(prefix: "WASM_PRODUCT_ARTIFACT"),
+            wasmFilename: "main.wasm",
             configuration: configuration,
             triple: "wasm32-unknown-wasi",
             selfPath: BuildPath(prefix: "PLANNER_SOURCE_PATH"),
@@ -81,6 +82,7 @@ import Testing
             selfPackageDir: BuildPath(prefix: "SELF_PACKAGE"),
             outputDir: BuildPath(prefix: "OUTPUT"),
             wasmProductArtifact: BuildPath(prefix: "WASM_PRODUCT_ARTIFACT"),
+            wasmFilename: "main.wasm",
             configuration: "debug",
             triple: "wasm32-unknown-wasi",
             selfPath: BuildPath(prefix: "PLANNER_SOURCE_PATH"),

From 81fe6c8033030cb9aaaaf203c40a8b85c235d1de Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@gmail.com>
Date: Mon, 17 Mar 2025 07:55:26 +0000
Subject: [PATCH 3/3] PackageToJS: Fix browser tests with non-.wasm product

---
 .../Sources/PackageToJSPlugin.swift            | 18 ++++++++++++++----
 .../PackageToJS/Templates/test.browser.html    |  3 ++-
 Plugins/PackageToJS/Tests/ExampleTests.swift   |  2 +-
 3 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift b/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift
index a4fd58d29..2844d52ec 100644
--- a/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift
+++ b/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift
@@ -119,7 +119,9 @@ struct PackageToJSPlugin: CommandPlugin {
         )
         let planner = PackagingPlanner(
             options: buildOptions.packageOptions, context: context, selfPackage: selfPackage,
-            outputDir: outputDir, wasmProductArtifact: productArtifact)
+            outputDir: outputDir, wasmProductArtifact: productArtifact,
+            wasmFilename: productArtifact.lastPathComponent
+        )
         let rootTask = try planner.planBuild(
             make: &make, buildOptions: buildOptions)
         cleanIfBuildGraphChanged(root: rootTask, make: make, context: context)
@@ -193,7 +195,14 @@ struct PackageToJSPlugin: CommandPlugin {
         )
         let planner = PackagingPlanner(
             options: testOptions.packageOptions, context: context, selfPackage: selfPackage,
-            outputDir: outputDir, wasmProductArtifact: productArtifact)
+            outputDir: outputDir, wasmProductArtifact: productArtifact,
+            // If the product artifact doesn't have a .wasm extension, add it
+            // to deliver it with the correct MIME type when serving the test
+            // files for browser tests.
+            wasmFilename: productArtifact.lastPathComponent.hasSuffix(".wasm")
+                ? productArtifact.lastPathComponent
+                : productArtifact.lastPathComponent + ".wasm"
+        )
         let (rootTask, binDir) = try planner.planTestBuild(
             make: &make)
         cleanIfBuildGraphChanged(root: rootTask, make: make, context: context)
@@ -486,7 +495,8 @@ extension PackagingPlanner {
         context: PluginContext,
         selfPackage: Package,
         outputDir: URL,
-        wasmProductArtifact: URL
+        wasmProductArtifact: URL,
+        wasmFilename: String
     ) {
         let outputBaseName = outputDir.lastPathComponent
         let (configuration, triple) = PackageToJS.deriveBuildConfiguration(wasmProductArtifact: wasmProductArtifact)
@@ -498,7 +508,7 @@ extension PackagingPlanner {
             selfPackageDir: BuildPath(absolute: selfPackage.directoryURL.path),
             outputDir: BuildPath(absolute: outputDir.path),
             wasmProductArtifact: BuildPath(absolute: wasmProductArtifact.path),
-            wasmFilename: wasmProductArtifact.lastPathComponent,
+            wasmFilename: wasmFilename,
             configuration: configuration,
             triple: triple,
             system: system
diff --git a/Plugins/PackageToJS/Templates/test.browser.html b/Plugins/PackageToJS/Templates/test.browser.html
index 27bfd25fc..35a37c943 100644
--- a/Plugins/PackageToJS/Templates/test.browser.html
+++ b/Plugins/PackageToJS/Templates/test.browser.html
@@ -4,6 +4,7 @@
 <body>
     <script type="module">
         import { testBrowserInPage } from "./test.js";
+        import { MODULE_PATH } from "./instantiate.js";
         import { defaultBrowserSetup /* #if USE_SHARED_MEMORY */, createDefaultWorkerFactory /* #endif */} from './platforms/browser.js';
 
         const logElement = document.createElement("pre");
@@ -11,7 +12,7 @@
 
         const processInfo = await fetch("/process-info.json").then((response) => response.json());
         const options = await defaultBrowserSetup({
-            module: await fetch("./main.wasm"),
+            module: await fetch(new URL(MODULE_PATH, import.meta.url)),
             args: processInfo.args,
             onStdoutLine: (line) => {
                 console.log(line);
diff --git a/Plugins/PackageToJS/Tests/ExampleTests.swift b/Plugins/PackageToJS/Tests/ExampleTests.swift
index 90a20e5a4..508062297 100644
--- a/Plugins/PackageToJS/Tests/ExampleTests.swift
+++ b/Plugins/PackageToJS/Tests/ExampleTests.swift
@@ -182,7 +182,7 @@ extension Trait where Self == ConditionTrait {
                 let process = Process()
                 process.executableURL = llvmCov
                 let profdata = packageDir.appending(path: ".build/plugins/PackageToJS/outputs/PackageTests/default.profdata")
-                let wasm = packageDir.appending(path: ".build/plugins/PackageToJS/outputs/PackageTests/main.wasm")
+                let wasm = packageDir.appending(path: ".build/plugins/PackageToJS/outputs/PackageTests/TestingPackageTests.wasm")
                 process.arguments = ["report", "-instr-profile", profdata.path, wasm.path]
                 process.standardOutput = FileHandle.nullDevice
                 try process.run()