migrate to TypeScript

This commit is contained in:
omochimetaru 2024-04-18 00:04:43 +09:00 committed by Yuta Saito
parent f286203b0d
commit 3abdbd2386
11 changed files with 158 additions and 37 deletions

File diff suppressed because one or more lines are too long

View File

@ -47,9 +47,10 @@ struct HashArchive: AsyncParsableCommand {
try localFileSystem.createDirectory(dotFilesStaticPath, recursive: true)
var hashes: [(String, String)] = []
for entrypoint in ["dev", "bundle", "test", "testNode"] {
let tsFilename = "\(entrypoint).ts"
let filename = "\(entrypoint).js"
var arguments = [
"esbuild", "--bundle", "entrypoint/\(filename)", "--outfile=static/\(filename)",
"esbuild", "--bundle", "entrypoint/\(tsFilename)", "--outfile=static/\(filename)",
]
if entrypoint == "testNode" {

View File

@ -0,0 +1,6 @@
export class SwiftRuntime {
setInstance(instance: WebAssembly.Instance): void;
readonly wasmImports: ImportedFunctions;
}
export type SwiftRuntimeConstructor = typeof SwiftRuntime;
export interface ImportedFunctions { }

View File

@ -13,15 +13,17 @@
// limitations under the License.
import { WasmRunner } from "./common.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;
let runtimeConstructor: SwiftRuntimeConstructor | undefined = undefined;
try {
const { SwiftRuntime } = await import(
// @ts-ignore
"./JavaScriptKit_JavaScriptKit.resources/Runtime/index.mjs"
);
runtimeConstructor = SwiftRuntime;
@ -36,7 +38,7 @@ const startWasiTask = async () => {
await wasmRunner.run(wasmBytes);
};
function handleError(e) {
function handleError(e: any) {
console.error(e);
if (e instanceof WebAssembly.RuntimeError) {
console.log(e.stack);

View File

@ -15,11 +15,22 @@
import { WASI } from "@wasmer/wasi";
import { WasmFs } from "@wasmer/wasmfs";
import * as path from "path-browserify";
import type { SwiftRuntime, SwiftRuntimeConstructor } from "./JavaScriptKit_JavaScriptKit.resources/Runtime";
export const WasmRunner = (rawOptions, SwiftRuntime) => {
const options = defaultRunnerOptions(rawOptions);
export type Options = {
args?: string[];
onStdout?: (text: string) => void;
onStderr?: (text: string) => void;
};
let swift;
export type WasmRunner = {
run(wasmBytes: ArrayBufferLike, extraWasmImports?: WebAssembly.Imports): Promise<void>
};
export const WasmRunner = (rawOptions: Options | false, SwiftRuntime: SwiftRuntimeConstructor | undefined): WasmRunner => {
const options: Options = defaultRunnerOptions(rawOptions);
let swift: SwiftRuntime;
if (SwiftRuntime) {
swift = new SwiftRuntime();
}
@ -27,11 +38,11 @@ export const WasmRunner = (rawOptions, SwiftRuntime) => {
const wasmFs = createWasmFS(
(stdout) => {
console.log(stdout);
options.onStdout(stdout);
options.onStdout?.call(undefined, stdout);
},
(stderr) => {
console.error(stderr);
options.onStderr(stderr);
options.onStderr?.call(undefined, stderr);
}
);
@ -50,13 +61,16 @@ export const WasmRunner = (rawOptions, SwiftRuntime) => {
},
});
const createWasmImportObject = (extraWasmImports, wasmModule) => {
const importObject = {
const createWasmImportObject = (
extraWasmImports: WebAssembly.Imports,
wasmModule: WebAssembly.Module
): WebAssembly.Imports => {
const importObject: WebAssembly.Imports = {
wasi_snapshot_preview1: wrapWASI(wasi, wasmModule),
};
if (swift) {
importObject.javascript_kit = swift.wasmImports;
importObject.javascript_kit = swift.wasmImports as unknown as WebAssembly.ModuleImports;
}
if (extraWasmImports) {
@ -70,7 +84,7 @@ export const WasmRunner = (rawOptions, SwiftRuntime) => {
};
return {
async run(wasmBytes, extraWasmImports) {
async run(wasmBytes: ArrayBufferLike, extraWasmImports?: WebAssembly.Imports) {
if (!extraWasmImports) {
extraWasmImports = {};
}
@ -91,7 +105,7 @@ export const WasmRunner = (rawOptions, SwiftRuntime) => {
wasi.start(instance);
// Initialize and start Reactor
if (instance.exports._initialize) {
if (typeof instance.exports._initialize == "function") {
instance.exports._initialize();
if (typeof instance.exports.main === "function") {
instance.exports.main();
@ -104,7 +118,7 @@ export const WasmRunner = (rawOptions, SwiftRuntime) => {
};
};
const defaultRunnerOptions = (options) => {
const defaultRunnerOptions = (options: Options | false): Options => {
if (!options) return defaultRunnerOptions({});
if (!options.onStdout) {
options.onStdout = () => { };
@ -118,13 +132,19 @@ const defaultRunnerOptions = (options) => {
return options;
};
const createWasmFS = (onStdout, onStderr) => {
const createWasmFS = (
onStdout: (text: string) => void,
onStderr: (text: string) => void
): WasmFs => {
// Instantiate a new WASI Instance
const wasmFs = new WasmFs();
// Output stdout and stderr to console
const originalWriteSync = wasmFs.fs.writeSync;
wasmFs.fs.writeSync = (fd, buffer, offset, length, position) => {
(wasmFs.fs as any).writeSync = (
fd: number, buffer: Buffer | Uint8Array,
offset?: number, length?: number, position?: number
): number => {
const text = new TextDecoder("utf-8").decode(buffer);
if (text !== "\n") {
switch (fd) {
@ -142,7 +162,7 @@ const createWasmFS = (onStdout, onStderr) => {
return wasmFs;
};
const wrapWASI = (wasiObject, wasmModule) => {
const wrapWASI = (wasiObject: WASI, wasmModule: WebAssembly.Module): WebAssembly.ModuleImports => {
// PATCH: @wasmer-js/wasi@0.x forgets to call `refreshMemory` in `clock_res_get`,
// which writes its result to memory view. Without the refresh the memory view,
// it accesses a detached array buffer if the memory is grown by malloc.
@ -154,7 +174,7 @@ const wrapWASI = (wasiObject, wasmModule) => {
// Reference: https://github.com/wasmerio/wasmer-js/blob/55fa8c17c56348c312a8bd23c69054b1aa633891/packages/wasi/src/index.ts#L557
const original_clock_res_get = wasiObject.wasiImport["clock_res_get"];
wasiObject.wasiImport["clock_res_get"] = (clockId, resolution) => {
wasiObject.wasiImport["clock_res_get"] = (clockId: number, resolution: number) => {
wasiObject.refreshMemory();
return original_clock_res_get(clockId, resolution);
};

View File

@ -13,7 +13,8 @@
// limitations under the License.
import ReconnectingWebSocket from "reconnecting-websocket";
import { WasmRunner } from "./common.js";
import { WasmRunner } from "./common";
import type { SwiftRuntimeConstructor } from "./JavaScriptKit_JavaScriptKit.resources/Runtime";
const socket = new ReconnectingWebSocket(`ws://${location.host}/watcher`);
@ -28,9 +29,10 @@ const startWasiTask = async () => {
const response = await fetch("/main.wasm");
const responseArrayBuffer = await response.arrayBuffer();
let runtimeConstructor;
let runtimeConstructor: SwiftRuntimeConstructor | undefined = undefined;
try {
const { SwiftRuntime } = await import(
// @ts-ignore
"./JavaScriptKit_JavaScriptKit.resources/Runtime/index.mjs"
);
runtimeConstructor = SwiftRuntime;
@ -62,7 +64,7 @@ const startWasiTask = async () => {
await wasmRunner.run(wasmBytes);
};
function handleError(e) {
function handleError(e: any) {
console.error(e);
if (e instanceof WebAssembly.RuntimeError) {
console.log(e.stack);

View File

@ -15,6 +15,7 @@
import ReconnectingWebSocket from "reconnecting-websocket";
import { WASIExitError } from "@wasmer/wasi";
import { WasmRunner } from "./common.js";
import type { SwiftRuntimeConstructor } from "./JavaScriptKit_JavaScriptKit.resources/Runtime";
const socket = new ReconnectingWebSocket(`ws://${location.host}/watcher`);
socket.addEventListener("message", (message) => {
@ -28,9 +29,10 @@ const startWasiTask = async () => {
const response = await fetch("/main.wasm");
const responseArrayBuffer = await response.arrayBuffer();
let runtimeConstructor;
let runtimeConstructor: SwiftRuntimeConstructor | undefined = undefined;
try {
const { SwiftRuntime } = await import(
// @ts-ignore
"./JavaScriptKit_JavaScriptKit.resources/Runtime/index.mjs"
);
runtimeConstructor = SwiftRuntime;
@ -69,7 +71,7 @@ const startWasiTask = async () => {
// 5. Crash by throwing JS exception synchronously
// 6. Crash by throwing JS exception asynchronously
const handleExitOrError = (error) => {
const handleExitOrError = (error: any) => {
// XCTest always calls `exit` at the end when no crash
if (error instanceof WASIExitError) {
// pass the output to the server in any case
@ -105,7 +107,7 @@ const startWasiTask = async () => {
// reachable here without catch (case 3, 4, 6)
};
function handleError(e) {
function handleError(e: any) {
console.error(e);
if (e instanceof WebAssembly.RuntimeError) {
console.log(e.stack);

View File

@ -14,6 +14,7 @@
import fs from "fs/promises";
import { WasmRunner } from "./common.js";
import type { SwiftRuntimeConstructor } from "./JavaScriptKit_JavaScriptKit.resources/Runtime";
const args = [...process.argv];
args.shift();
@ -27,9 +28,10 @@ if (!wasmFile) {
const startWasiTask = async () => {
const wasmBytes = await fs.readFile(wasmFile);
let runtimeConstructor;
let runtimeConstructor: SwiftRuntimeConstructor | undefined = undefined;
try {
const { SwiftRuntime } = await import(
// @ts-ignore
"./JavaScriptKit_JavaScriptKit.resources/Runtime/index.mjs"
);

66
package-lock.json generated
View File

@ -9,14 +9,32 @@
"version": "1.0.2",
"license": "Apache-2.0",
"devDependencies": {
"@types/node": "^20.12.7",
"@types/path-browserify": "^1.0.2",
"@wasmer/wasi": "^0.12.0",
"@wasmer/wasmfs": "^0.12.0",
"esbuild": "^0.14.38",
"npm-run-all": "^4.1.5",
"path-browserify": "^1.0.1",
"reconnecting-websocket": "^4.4.0"
"reconnecting-websocket": "^4.4.0",
"typescript": "^5.4.5"
}
},
"node_modules/@types/node": {
"version": "20.12.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz",
"integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/path-browserify": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@types/path-browserify/-/path-browserify-1.0.2.tgz",
"integrity": "sha512-ZkC5IUqqIFPXx3ASTTybTzmQdwHwe2C0u3eL75ldQ6T9E9IWFJodn6hIfbZGab73DfyiHN4Xw15gNxUq2FbvBA==",
"dev": true
},
"node_modules/@wasmer/wasi": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/@wasmer/wasi/-/wasi-0.12.0.tgz",
@ -1466,6 +1484,19 @@
"node": ">=6"
}
},
"node_modules/typescript": {
"version": "5.4.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/unbox-primitive": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
@ -1481,6 +1512,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -1533,6 +1570,21 @@
}
},
"dependencies": {
"@types/node": {
"version": "20.12.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz",
"integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==",
"dev": true,
"requires": {
"undici-types": "~5.26.4"
}
},
"@types/path-browserify": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@types/path-browserify/-/path-browserify-1.0.2.tgz",
"integrity": "sha512-ZkC5IUqqIFPXx3ASTTybTzmQdwHwe2C0u3eL75ldQ6T9E9IWFJodn6hIfbZGab73DfyiHN4Xw15gNxUq2FbvBA==",
"dev": true
},
"@wasmer/wasi": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/@wasmer/wasi/-/wasi-0.12.0.tgz",
@ -2518,6 +2570,12 @@
"readable-stream": "^3.1.1"
}
},
"typescript": {
"version": "5.4.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
"dev": true
},
"unbox-primitive": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
@ -2530,6 +2588,12 @@
"which-boxed-primitive": "^1.0.2"
}
},
"undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",

View File

@ -20,11 +20,14 @@
},
"homepage": "https://github.com/swiftwasm/carton#readme",
"devDependencies": {
"@types/node": "^20.12.7",
"@types/path-browserify": "^1.0.2",
"@wasmer/wasi": "^0.12.0",
"@wasmer/wasmfs": "^0.12.0",
"path-browserify": "^1.0.1",
"esbuild": "^0.14.38",
"npm-run-all": "^4.1.5",
"reconnecting-websocket": "^4.4.0"
"path-browserify": "^1.0.1",
"reconnecting-websocket": "^4.4.0",
"typescript": "^5.4.5"
}
}

19
tsconfig.json Normal file
View File

@ -0,0 +1,19 @@
{
"compilerOptions": {
"module": "CommonJS",
"strict": true,
"target": "ESNext",
"esModuleInterop": true,
"lib": [
"DOM",
"ES2020",
"ES2021.WeakRef"
],
"noEmit": true,
"isolatedModules": true,
"sourceMap": true
},
"include": [
"entrypoint/**/*.ts"
]
}