Skip to content

Commit

Permalink
Expose instantiate API from bundled package
Browse files Browse the repository at this point in the history
  • Loading branch information
kateinoigakukun committed Nov 17, 2024
1 parent 5f35b2c commit 995973a
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 128 deletions.
9 changes: 5 additions & 4 deletions Sources/CartonHelpers/StaticArchive.swift

Large diffs are not rendered by default.

97 changes: 92 additions & 5 deletions Sources/carton-frontend-slim/CartonFrontendBundleCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ struct CartonFrontendBundleCommand: AsyncParsableCommand {
topLevelResourcePaths: resources
)

terminal.write("Bundle successfully generated at \(bundleDirectory)\n", inColor: .green, bold: true)
terminal.write(
"Bundle successfully generated at \(bundleDirectory)\n", inColor: .green, bold: true)
}

func optimize(_ inputPath: AbsolutePath, outputPath: AbsolutePath, terminal: InteractiveWriter)
Expand Down Expand Up @@ -171,7 +172,10 @@ struct CartonFrontendBundleCommand: AsyncParsableCommand {
) throws {
// Rename the final binary to use a part of its hash to bust browsers and CDN caches.
let wasmFileHash = try localFileSystem.readFileContents(wasmOutputFilePath).hexChecksum
let mainModuleName = contentHash ? "\(wasmFileHash).wasm" : URL(fileURLWithPath: mainWasmPath).lastPathComponent
let mainModuleBaseName = URL(fileURLWithPath: mainWasmPath).deletingPathExtension()
.lastPathComponent
let mainModuleName =
contentHash ? "\(mainModuleBaseName).\(wasmFileHash).wasm" : mainModuleBaseName
let mainModulePath = try AbsolutePath(validating: mainModuleName, relativeTo: bundleDirectory)
try localFileSystem.move(from: wasmOutputFilePath, to: mainModulePath)

Expand All @@ -183,7 +187,7 @@ struct CartonFrontendBundleCommand: AsyncParsableCommand {
with: mainModuleName
)
)
let entrypointName = contentHash ? "\(entrypoint.hexChecksum).js" : "index.js"
let entrypointName = contentHash ? "app.\(entrypoint.hexChecksum).js" : "app.js"
try localFileSystem.writeFileContents(
AbsolutePath(validating: entrypointName, relativeTo: bundleDirectory),
bytes: entrypoint
Expand All @@ -198,7 +202,37 @@ struct CartonFrontendBundleCommand: AsyncParsableCommand {
))
)

for directoryName in try localFileSystem.resourcesDirectoryNames(relativeTo: buildDirectory) {
try localFileSystem.writeFileContents(
AbsolutePath(validating: "intrinsics.js", relativeTo: bundleDirectory),
bytes: ByteString(StaticResource.intrinsics)
)

let resourcesDirectoryNames = try localFileSystem.resourcesDirectoryNames(
relativeTo: buildDirectory)
let hasJavaScriptKitResources = resourcesDirectoryNames.contains(
"JavaScriptKit_JavaScriptKit.resources")

try localFileSystem.writeFileContents(
AbsolutePath(validating: "index.js", relativeTo: bundleDirectory),
bytes: ByteString(
encodingAsUTF8: indexJsContent(
mainModuleName: mainModuleName, hasJavaScriptKitResources: hasJavaScriptKitResources)
)
)

try localFileSystem.writeFileContents(
AbsolutePath(validating: "package.json", relativeTo: bundleDirectory),
bytes: ByteString(
encodingAsUTF8: """
{
"type": "module",
"main": "./index.js"
}
"""
)
)

for directoryName in resourcesDirectoryNames {
let resourcesPath = buildDirectory.appending(component: directoryName)
let targetDirectory = bundleDirectory.appending(component: directoryName)

Expand All @@ -212,7 +246,8 @@ struct CartonFrontendBundleCommand: AsyncParsableCommand {
validating: resourcesPath, relativeTo: localFileSystem.currentWorkingDirectory!)
for file in try localFileSystem.traverseRecursively(resourcesPath) {
let targetPath = bundleDirectory.appending(component: file.basename)
let sourcePath = bundleDirectory.appending(component: resourcesPath.basename).appending(component: file.basename)
let sourcePath = bundleDirectory.appending(component: resourcesPath.basename).appending(
component: file.basename)

guard localFileSystem.exists(sourcePath, followSymlink: true),
!localFileSystem.exists(targetPath, followSymlink: true)
Expand All @@ -223,6 +258,58 @@ struct CartonFrontendBundleCommand: AsyncParsableCommand {
}
}
}

private func indexJsContent(mainModuleName: String, hasJavaScriptKitResources: Bool) -> String {
var content = """
import { instantiate as internalInstantiate } from './intrinsics.js';
"""
if hasJavaScriptKitResources {
content += """
import { SwiftRuntime } from './JavaScriptKit_JavaScriptKit.resources/Runtime/index.mjs';
"""
}
content += """
export const wasmFileName = '\(mainModuleName)';
export async function instantiate(options, imports) {
if (!options) {
options = {};
}
const isNodeJs = (typeof process !== 'undefined') && (process.release.name === 'node');
const isWebBrowser = (typeof window !== 'undefined');
if (!options.module) {
if (isNodeJs) {
const module = await import(/* webpackIgnore: true */'node:module');
const importMeta = import.meta;
const require = module.default.createRequire(importMeta.url);
const fs = require('fs/promises');
const url = require('url');
const filePath = import.meta.resolve('./' + wasmFileName);
options.module = await WebAssembly.compile(await fs.readFile(url.fileURLToPath(filePath)));
} else if (isWebBrowser) {
options.module = await WebAssembly.compileStreaming(fetch(wasmFileName));
} else {
throw new Error('Unsupported environment to automatically load the WebAssembly module. Please provide the \"module\" option with the compiled WebAssembly module manually.');
}
}
"""
if hasJavaScriptKitResources {
content += """
options.SwiftRuntime = SwiftRuntime;
"""
}
content += """
return internalInstantiate(options, imports);
}
"""

return content
}
}

extension ByteString {
Expand Down
16 changes: 8 additions & 8 deletions Sources/carton-release/HashArchive.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ struct HashArchive: AsyncParsableCommand {
let staticPath = try AbsolutePath(validating: "static", relativeTo: cwd)

var fileContent = """
import Foundation
import Foundation
public enum StaticResource {
public enum StaticResource {
"""
"""

for entrypoint in ["dev", "bundle", "test", "testNode"] {
for entrypoint in ["dev", "bundle", "test", "testNode", "intrinsics"] {
let tsFilename = "\(entrypoint).ts"
let filename = "\(entrypoint).js"
var arguments = [
Expand Down Expand Up @@ -75,15 +75,15 @@ struct HashArchive: AsyncParsableCommand {
$0.base64EncodedString()
}
fileContent += """
public static let \(entrypoint): Data = Data(base64Encoded: \"\(base64Content)\")!
public static let \(entrypoint): Data = Data(base64Encoded: \"\(base64Content)\")!
"""
"""
}

fileContent += """
}
"""
}
"""

try localFileSystem.writeFileContents(
AbsolutePath(
Expand Down
18 changes: 8 additions & 10 deletions entrypoint/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { WasmRunner } from "./common.js";
import { instantiate } from "./intrinsics.js";
import type { SwiftRuntimeConstructor } from "./JavaScriptKit_JavaScriptKit.resources/Runtime";

const startWasiTask = async () => {
// Fetch our Wasm File
const response = await fetch("REPLACE_THIS_WITH_THE_MAIN_WEBASSEMBLY_MODULE");
const responseArrayBuffer = await response.arrayBuffer();

let runtimeConstructor: SwiftRuntimeConstructor | undefined = undefined;
try {
Expand All @@ -31,22 +30,21 @@ const startWasiTask = async () => {
// JavaScriptKit module not available, running without JavaScriptKit runtime.
}

const wasmRunner = WasmRunner({
// Instantiate the WebAssembly file
await instantiate({
module: await WebAssembly.compileStreaming(response),
onStdoutLine(line) {
console.log(line);
},
onStderrLine(line) {
console.error(line);
}
}, runtimeConstructor);

// Instantiate the WebAssembly file
const wasmBytes = new Uint8Array(responseArrayBuffer).buffer;
await wasmRunner.run(wasmBytes);
},
SwiftRuntime: runtimeConstructor,
});
};

async function main(): Promise<void> {
await startWasiTask();
}

main();
main();
19 changes: 8 additions & 11 deletions entrypoint/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// limitations under the License.

import ReconnectingWebSocket from "reconnecting-websocket";
import { WasmRunner } from "./common";
import { instantiate } from "./intrinsics";
import type { SwiftRuntimeConstructor } from "./JavaScriptKit_JavaScriptKit.resources/Runtime";

const socket = new ReconnectingWebSocket(`ws://${location.host}/watcher`);
Expand All @@ -27,7 +27,6 @@ socket.addEventListener("message", (message) => {
const startWasiTask = async () => {
// Fetch our Wasm File
const response = await fetch("/main.wasm");
const responseArrayBuffer = await response.arrayBuffer();

let runtimeConstructor: SwiftRuntimeConstructor | undefined = undefined;
try {
Expand All @@ -42,8 +41,10 @@ const startWasiTask = async () => {
);
}

const wasmRunner = WasmRunner(
// Instantiate the WebAssembly file
await instantiate(
{
module: await WebAssembly.compileStreaming(response),
onStdout(chunk) {
const kindBuffer = new ArrayBuffer(2);
new DataView(kindBuffer).setUint16(0, 1001, true);
Expand All @@ -69,14 +70,10 @@ const startWasiTask = async () => {
},
onStderrLine(line) {
console.error(line);
}
},
runtimeConstructor
},
SwiftRuntime: runtimeConstructor,
}
);

// Instantiate the WebAssembly file
const wasmBytes = new Uint8Array(responseArrayBuffer).buffer;
await wasmRunner.run(wasmBytes);
};

function handleError(e: any) {
Expand Down Expand Up @@ -108,4 +105,4 @@ async function main(): Promise<void> {
}
}

main();
main();
Loading

0 comments on commit 995973a

Please sign in to comment.