save
This commit is contained in:
parent
f50f02b9c5
commit
0b7b6cf79c
9 changed files with 247 additions and 100 deletions
|
|
@ -27,7 +27,7 @@ const commonConfig = {
|
||||||
minify: true,
|
minify: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
async function buildPlatformFile(platform) {
|
async function buildDynamicFiles(platform) {
|
||||||
const platformContent =
|
const platformContent =
|
||||||
platform === "node"
|
platform === "node"
|
||||||
? `export * from "./platform.node.js";`
|
? `export * from "./platform.node.js";`
|
||||||
|
|
@ -35,6 +35,14 @@ async function buildPlatformFile(platform) {
|
||||||
|
|
||||||
const platformPath = join(SRC_DIR, "platform.js");
|
const platformPath = join(SRC_DIR, "platform.js");
|
||||||
await writeFile(platformPath, platformContent);
|
await writeFile(platformPath, platformContent);
|
||||||
|
|
||||||
|
const workerContent =
|
||||||
|
platform === "node"
|
||||||
|
? `export * from "./worker.node.js";`
|
||||||
|
: `export * from "./worker.browser.js";`;
|
||||||
|
|
||||||
|
const workerPath = join(SRC_DIR, "worker.js");
|
||||||
|
await writeFile(workerPath, workerContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildAndCopy(buildType) {
|
async function buildAndCopy(buildType) {
|
||||||
|
|
@ -69,7 +77,7 @@ async function buildAndCopy(buildType) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const config = configs[buildType];
|
const config = configs[buildType];
|
||||||
await buildPlatformFile(config.platform);
|
await buildDynamicFiles(config.platform);
|
||||||
|
|
||||||
const result = await build({
|
const result = await build({
|
||||||
...commonConfig,
|
...commonConfig,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "@terrastruct/d2",
|
"name": "@terrastruct/d2",
|
||||||
"author": "Terrastruct, Inc.",
|
"author": "Terrastruct, Inc.",
|
||||||
"description": "D2.js is a wrapper around the WASM build of D2, the modern text-to-diagram language.",
|
"description": "D2.js is a wrapper around the WASM build of D2, the modern text-to-diagram language.",
|
||||||
"version": "0.1.12",
|
"version": "0.1.17",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/terrastruct/d2.git",
|
"url": "git+https://github.com/terrastruct/d2.git",
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,10 @@ export class D2 {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.worker.onerror = (error) => {
|
this.worker.onerror = (error) => {
|
||||||
console.error("Worker encountered an error:", error.message || error);
|
console.error("Worker detailed error:", error);
|
||||||
|
console.error("Error message:", error.message);
|
||||||
|
console.error("Error filename:", error.filename);
|
||||||
|
console.error("Error lineno:", error.lineno);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,34 @@ export async function createWorker() {
|
||||||
);
|
);
|
||||||
let workerScript = await response.text();
|
let workerScript = await response.text();
|
||||||
|
|
||||||
let blob = new Blob(["(() => {", wasmExecJs, "})();", workerScript], {
|
// Create global Go without IIFE in module context
|
||||||
|
let blob = new Blob(
|
||||||
|
[
|
||||||
|
// First establish Go in global scope
|
||||||
|
wasmExecJs,
|
||||||
|
// Then the module code
|
||||||
|
workerScript,
|
||||||
|
],
|
||||||
|
{
|
||||||
type: "text/javascript;charset=utf-8",
|
type: "text/javascript;charset=utf-8",
|
||||||
});
|
}
|
||||||
return new Worker(URL.createObjectURL(blob), {
|
);
|
||||||
|
|
||||||
|
console.log("about to create worker");
|
||||||
|
const worker = new Worker(URL.createObjectURL(blob), {
|
||||||
type: "module",
|
type: "module",
|
||||||
});
|
});
|
||||||
|
console.log("worker", worker);
|
||||||
|
|
||||||
|
// Add error handler to see initialization errors
|
||||||
|
worker.onerror = (error) => {
|
||||||
|
console.error("Worker initialization error:", {
|
||||||
|
message: error.message,
|
||||||
|
filename: error.filename,
|
||||||
|
lineno: error.lineno,
|
||||||
|
error: error.error,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return worker;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
d2js/js/src/worker.browser.js
Normal file
10
d2js/js/src/worker.browser.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { setupMessageHandler } from "./worker.shared.js";
|
||||||
|
|
||||||
|
async function initWasmBrowser(wasmBinary) {
|
||||||
|
const go = new Go();
|
||||||
|
const result = await WebAssembly.instantiate(wasmBinary, go.importObject);
|
||||||
|
go.run(result.instance);
|
||||||
|
return self.d2;
|
||||||
|
}
|
||||||
|
|
||||||
|
setupMessageHandler(self, initWasmBrowser);
|
||||||
|
|
@ -1,92 +1 @@
|
||||||
const isNode = typeof process !== "undefined" && process.release?.name === "node";
|
export * from "./worker.node.js";
|
||||||
let currentPort;
|
|
||||||
let wasm;
|
|
||||||
let d2;
|
|
||||||
|
|
||||||
async function initWasm(wasmBinary) {
|
|
||||||
const go = new Go();
|
|
||||||
const result = await WebAssembly.instantiate(wasmBinary, go.importObject);
|
|
||||||
go.run(result.instance);
|
|
||||||
return isNode ? global.d2 : self.d2;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupMessageHandler(port) {
|
|
||||||
currentPort = port;
|
|
||||||
if (isNode) {
|
|
||||||
port.on("message", handleMessage);
|
|
||||||
} else {
|
|
||||||
port.onmessage = (e) => handleMessage(e.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleMessage(e) {
|
|
||||||
const { type, data } = e;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case "init":
|
|
||||||
try {
|
|
||||||
if (isNode) {
|
|
||||||
eval(data.wasmExecContent);
|
|
||||||
}
|
|
||||||
d2 = await initWasm(data.wasm);
|
|
||||||
currentPort.postMessage({ type: "ready" });
|
|
||||||
} catch (err) {
|
|
||||||
currentPort.postMessage({
|
|
||||||
type: "error",
|
|
||||||
error: err.message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "compile":
|
|
||||||
try {
|
|
||||||
const result = await d2.compile(JSON.stringify(data));
|
|
||||||
const response = JSON.parse(result);
|
|
||||||
if (response.error) {
|
|
||||||
throw new Error(response.error.message);
|
|
||||||
}
|
|
||||||
currentPort.postMessage({
|
|
||||||
type: "result",
|
|
||||||
data: response.data,
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
currentPort.postMessage({
|
|
||||||
type: "error",
|
|
||||||
error: err.message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "render":
|
|
||||||
try {
|
|
||||||
const result = await d2.render(JSON.stringify(data));
|
|
||||||
const response = JSON.parse(result);
|
|
||||||
if (response.error) {
|
|
||||||
throw new Error(response.error.message);
|
|
||||||
}
|
|
||||||
currentPort.postMessage({
|
|
||||||
type: "result",
|
|
||||||
data: atob(response.data),
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
currentPort.postMessage({
|
|
||||||
type: "error",
|
|
||||||
error: err.message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function init() {
|
|
||||||
if (isNode) {
|
|
||||||
const { parentPort } = await import("node:worker_threads");
|
|
||||||
setupMessageHandler(parentPort);
|
|
||||||
} else {
|
|
||||||
setupMessageHandler(self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init().catch((err) => {
|
|
||||||
console.error("Initialization error:", err);
|
|
||||||
});
|
|
||||||
11
d2js/js/src/worker.node.js
Normal file
11
d2js/js/src/worker.node.js
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { parentPort } from "node:worker_threads";
|
||||||
|
import { setupMessageHandler } from "./worker.shared.js";
|
||||||
|
|
||||||
|
async function initWasmNode(wasmBinary) {
|
||||||
|
const go = new Go();
|
||||||
|
const result = await WebAssembly.instantiate(wasmBinary, go.importObject);
|
||||||
|
go.run(result.instance);
|
||||||
|
return global.d2;
|
||||||
|
}
|
||||||
|
|
||||||
|
setupMessageHandler(parentPort, initWasmNode);
|
||||||
49
d2js/js/src/worker.shared.js
Normal file
49
d2js/js/src/worker.shared.js
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
let currentPort;
|
||||||
|
let d2;
|
||||||
|
|
||||||
|
export function setupMessageHandler(port, initWasm) {
|
||||||
|
currentPort = port;
|
||||||
|
|
||||||
|
const handleMessage = async (e) => {
|
||||||
|
const { type, data } = e;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case "init":
|
||||||
|
try {
|
||||||
|
d2 = await initWasm(data.wasm);
|
||||||
|
currentPort.postMessage({ type: "ready" });
|
||||||
|
} catch (err) {
|
||||||
|
currentPort.postMessage({ type: "error", error: err.message });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "compile":
|
||||||
|
try {
|
||||||
|
const result = await d2.compile(JSON.stringify(data));
|
||||||
|
const response = JSON.parse(result);
|
||||||
|
if (response.error) throw new Error(response.error.message);
|
||||||
|
currentPort.postMessage({ type: "result", data: response.data });
|
||||||
|
} catch (err) {
|
||||||
|
currentPort.postMessage({ type: "error", error: err.message });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "render":
|
||||||
|
try {
|
||||||
|
const result = await d2.render(JSON.stringify(data));
|
||||||
|
const response = JSON.parse(result);
|
||||||
|
if (response.error) throw new Error(response.error.message);
|
||||||
|
currentPort.postMessage({ type: "result", data: atob(response.data) });
|
||||||
|
} catch (err) {
|
||||||
|
currentPort.postMessage({ type: "error", error: err.message });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof process !== "undefined" && process.release?.name === "node") {
|
||||||
|
port.on("message", handleMessage);
|
||||||
|
} else {
|
||||||
|
port.onmessage = (e) => handleMessage(e.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
133
d2js/js/test-bundle.js
Normal file
133
d2js/js/test-bundle.js
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
// test-bundle.js
|
||||||
|
import { build } from "bun";
|
||||||
|
import { mkdir, writeFile } from "node:fs/promises";
|
||||||
|
import { join } from "node:path";
|
||||||
|
|
||||||
|
// Ensure output directory exists
|
||||||
|
await mkdir("./test-dist", { recursive: true });
|
||||||
|
|
||||||
|
// First, write a temporary platform.js that uses browser code
|
||||||
|
const platformContent = `export * from "./platform.browser.js";`;
|
||||||
|
await writeFile("./src/platform.js", platformContent);
|
||||||
|
|
||||||
|
console.log("Building main bundle...");
|
||||||
|
const result = await build({
|
||||||
|
entrypoints: ["./src/index.js"],
|
||||||
|
outdir: "./test-dist",
|
||||||
|
format: "esm",
|
||||||
|
target: "browser",
|
||||||
|
platform: "browser",
|
||||||
|
minify: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
|
console.error("Main bundle build failed:", result.logs);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Building worker bundle...");
|
||||||
|
const workerResult = await build({
|
||||||
|
entrypoints: ["./src/worker.js"],
|
||||||
|
outdir: "./test-dist",
|
||||||
|
format: "esm",
|
||||||
|
target: "browser",
|
||||||
|
platform: "browser",
|
||||||
|
minify: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!workerResult.success) {
|
||||||
|
console.error("Worker bundle build failed:", workerResult.logs);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Builds complete");
|
||||||
|
|
||||||
|
// Create a simple server to serve the bundles
|
||||||
|
const server = Bun.serve({
|
||||||
|
port: 3001,
|
||||||
|
async fetch(req) {
|
||||||
|
const url = new URL(req.url);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Serve main bundle
|
||||||
|
if (url.pathname === "/d2.mjs") {
|
||||||
|
const file = await Bun.file("./test-dist/index.js").text();
|
||||||
|
return new Response(file, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/javascript",
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve worker bundle
|
||||||
|
if (url.pathname === "/worker.js") {
|
||||||
|
const file = await Bun.file("./test-dist/worker.js").text();
|
||||||
|
return new Response(file, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/javascript",
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve test page
|
||||||
|
if (url.pathname === "/") {
|
||||||
|
return new Response(
|
||||||
|
`
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>D2 Test</title>
|
||||||
|
<script type="module">
|
||||||
|
import { D2 } from 'http://localhost:3001/d2.mjs';
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
try {
|
||||||
|
console.log("Creating D2");
|
||||||
|
const d2 = new D2();
|
||||||
|
console.log("D2 created:", d2);
|
||||||
|
|
||||||
|
await d2.ready;
|
||||||
|
console.log("D2 ready");
|
||||||
|
|
||||||
|
const result = await d2.compile("x -> y");
|
||||||
|
console.log("Compile result:", result);
|
||||||
|
|
||||||
|
const svg = await d2.render(result.diagram);
|
||||||
|
console.log("Render result:", svg);
|
||||||
|
document.getElementById('output').innerHTML = svg;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="output"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "text/html",
|
||||||
|
"Content-Security-Policy":
|
||||||
|
"script-src 'unsafe-inline' 'wasm-unsafe-eval' http://localhost:3001; " +
|
||||||
|
"worker-src 'self' blob:; " +
|
||||||
|
"script-src-elem 'unsafe-inline' http://localhost:3001 blob:",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response("Not found", { status: 404 });
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error serving ${url.pathname}:`, error);
|
||||||
|
return new Response("Server Error", { status: 500 });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Test server running at http://localhost:${server.port}/`);
|
||||||
Loading…
Reference in a new issue