From b10d0879df958b0d632c06fe749329329d28fb45 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Mon, 13 Jan 2025 19:38:27 -0700 Subject: [PATCH 1/8] remove entrypoints and code split esm module --- d2js/js/build.js | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/d2js/js/build.js b/d2js/js/build.js index 3c1cfeec5..e0d272986 100644 --- a/d2js/js/build.js +++ b/d2js/js/build.js @@ -23,7 +23,6 @@ await writeFile( ); const commonConfig = { - splitting: false, sourcemap: "external", minify: true, }; @@ -42,40 +41,30 @@ async function buildAndCopy(buildType) { const configs = { browser: { outdir: resolve(ROOT_DIR, "dist/browser"), + splitting: false, format: "esm", target: "browser", platform: "browser", loader: { ".js": "jsx", }, - entrypoints: [ - resolve(SRC_DIR, "index.js"), - resolve(SRC_DIR, "worker.js"), - resolve(SRC_DIR, "platform.js"), - resolve(SRC_DIR, "wasm-loader.browser.js"), - ], + entrypoints: [resolve(SRC_DIR, "index.js"), resolve(SRC_DIR, "worker.js")], }, "node-esm": { outdir: resolve(ROOT_DIR, "dist/node-esm"), + splitting: true, format: "esm", target: "node", platform: "node", - entrypoints: [ - resolve(SRC_DIR, "index.js"), - resolve(SRC_DIR, "worker.js"), - resolve(SRC_DIR, "platform.js"), - ], + entrypoints: [resolve(SRC_DIR, "index.js"), resolve(SRC_DIR, "worker.js")], }, "node-cjs": { outdir: resolve(ROOT_DIR, "dist/node-cjs"), + splitting: false, format: "cjs", target: "node", platform: "node", - entrypoints: [ - resolve(SRC_DIR, "index.js"), - resolve(SRC_DIR, "worker.js"), - resolve(SRC_DIR, "platform.js"), - ], + entrypoints: [resolve(SRC_DIR, "index.js"), resolve(SRC_DIR, "worker.js")], }, }; From c32ef8a6dcf3a269ed3fa1518c989b84dd4f4a31 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Mon, 13 Jan 2025 20:07:12 -0700 Subject: [PATCH 2/8] edge friendly --- d2js/js/package.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/d2js/js/package.json b/d2js/js/package.json index 8e56af2e8..4ecefba74 100644 --- a/d2js/js/package.json +++ b/d2js/js/package.json @@ -21,10 +21,14 @@ "exports": { ".": { "browser": "./dist/browser/index.js", - "import": "./dist/node-esm/index.js", + "import": { + "browser": "./dist/browser/index.js", + "default": "./dist/node-esm/index.js" + }, "require": "./dist/node-cjs/index.js", "default": "./dist/node-esm/index.js" - } + }, + "./worker": "./dist/browser/worker.js" }, "files": [ "dist" From 45a9f58b422776a4cf2291eb1726d9302d87a3ad Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Mon, 13 Jan 2025 20:18:15 -0700 Subject: [PATCH 3/8] fix example --- d2js/js/examples/basic.html | 2 +- d2js/js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/d2js/js/examples/basic.html b/d2js/js/examples/basic.html index 3248f1b9c..caec7e428 100644 --- a/d2js/js/examples/basic.html +++ b/d2js/js/examples/basic.html @@ -30,7 +30,7 @@
+ + +
+ + + `, + { + 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}/`); From 4576d15beae2e760c9932b3feb8fa5dd1024be4a Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Mon, 13 Jan 2025 23:00:37 -0700 Subject: [PATCH 7/8] cleanup --- d2js/js/Makefile | 2 +- d2js/js/build.js | 11 ++++++----- d2js/js/package.json | 2 +- d2js/js/src/index.js | 5 +---- d2js/js/src/platform.browser.js | 28 +++------------------------- d2js/js/src/worker.browser.js | 2 +- d2js/js/src/worker.js | 2 +- d2js/js/src/worker.node.js | 2 +- d2js/js/src/worker.shared.js | 7 +++++-- 9 files changed, 20 insertions(+), 41 deletions(-) diff --git a/d2js/js/Makefile b/d2js/js/Makefile index cc098c626..1b81115e5 100644 --- a/d2js/js/Makefile +++ b/d2js/js/Makefile @@ -21,4 +21,4 @@ node_modules: .PHONY: cleanup cleanup: test - prefix "$@" git checkout -- src/platform.js + prefix "$@" git checkout -- src/platform.js src/worker.js diff --git a/d2js/js/build.js b/d2js/js/build.js index 9f0c11227..a3d3a145f 100644 --- a/d2js/js/build.js +++ b/d2js/js/build.js @@ -36,13 +36,14 @@ async function buildDynamicFiles(platform) { const platformPath = join(SRC_DIR, "platform.js"); await writeFile(platformPath, platformContent); - const workerContent = + const workerSource = platform === "node" - ? `export * from "./worker.node.js";` - : `export * from "./worker.browser.js";`; + ? join(SRC_DIR, "worker.node.js") + : join(SRC_DIR, "worker.browser.js"); - const workerPath = join(SRC_DIR, "worker.js"); - await writeFile(workerPath, workerContent); + const workerTarget = join(SRC_DIR, "worker.js"); + const workerContent = await readFile(workerSource, "utf8"); + await writeFile(workerTarget, workerContent); } async function buildAndCopy(buildType) { diff --git a/d2js/js/package.json b/d2js/js/package.json index 1212c5ff6..535b1c938 100644 --- a/d2js/js/package.json +++ b/d2js/js/package.json @@ -2,7 +2,7 @@ "name": "@terrastruct/d2", "author": "Terrastruct, Inc.", "description": "D2.js is a wrapper around the WASM build of D2, the modern text-to-diagram language.", - "version": "0.1.17", + "version": "0.1.19", "repository": { "type": "git", "url": "git+https://github.com/terrastruct/d2.git", diff --git a/d2js/js/src/index.js b/d2js/js/src/index.js index a0be58a00..880e48295 100644 --- a/d2js/js/src/index.js +++ b/d2js/js/src/index.js @@ -54,10 +54,7 @@ export class D2 { }); } else { this.worker.onerror = (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); + console.error("Worker encountered an error:", error.message || error); }; } diff --git a/d2js/js/src/platform.browser.js b/d2js/js/src/platform.browser.js index 1909b2d0a..052149742 100644 --- a/d2js/js/src/platform.browser.js +++ b/d2js/js/src/platform.browser.js @@ -18,34 +18,12 @@ export async function createWorker() { ); let workerScript = await response.text(); - // 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", - } - ); + let blob = new Blob([wasmExecJs, workerScript], { + type: "text/javascript;charset=utf-8", + }); - console.log("about to create worker"); const worker = new Worker(URL.createObjectURL(blob), { 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; } diff --git a/d2js/js/src/worker.browser.js b/d2js/js/src/worker.browser.js index 2eaed3ff0..361432b0d 100644 --- a/d2js/js/src/worker.browser.js +++ b/d2js/js/src/worker.browser.js @@ -7,4 +7,4 @@ async function initWasmBrowser(wasmBinary) { return self.d2; } -setupMessageHandler(self, initWasmBrowser); +setupMessageHandler(false, self, initWasmBrowser); diff --git a/d2js/js/src/worker.js b/d2js/js/src/worker.js index 769950402..3e2e2def0 100644 --- a/d2js/js/src/worker.js +++ b/d2js/js/src/worker.js @@ -1 +1 @@ -export * from "./worker.node.js"; \ No newline at end of file +// Replaced at build time diff --git a/d2js/js/src/worker.node.js b/d2js/js/src/worker.node.js index b9d9bc6f6..0f65334ef 100644 --- a/d2js/js/src/worker.node.js +++ b/d2js/js/src/worker.node.js @@ -8,4 +8,4 @@ async function initWasmNode(wasmBinary) { return global.d2; } -setupMessageHandler(parentPort, initWasmNode); +setupMessageHandler(true, parentPort, initWasmNode); diff --git a/d2js/js/src/worker.shared.js b/d2js/js/src/worker.shared.js index e98bf9e1e..bbd1293e8 100644 --- a/d2js/js/src/worker.shared.js +++ b/d2js/js/src/worker.shared.js @@ -1,7 +1,7 @@ let currentPort; let d2; -export function setupMessageHandler(port, initWasm) { +export function setupMessageHandler(isNode, port, initWasm) { currentPort = port; const handleMessage = async (e) => { @@ -10,6 +10,9 @@ export function setupMessageHandler(port, initWasm) { switch (type) { case "init": try { + if (isNode) { + eval(data.wasmExecContent); + } d2 = await initWasm(data.wasm); currentPort.postMessage({ type: "ready" }); } catch (err) { @@ -41,7 +44,7 @@ export function setupMessageHandler(port, initWasm) { } }; - if (typeof process !== "undefined" && process.release?.name === "node") { + if (isNode) { port.on("message", handleMessage); } else { port.onmessage = (e) => handleMessage(e.data); From b9f8fd9e5ccd8c3769693ca0af23dab6c8af7408 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Mon, 13 Jan 2025 23:15:26 -0700 Subject: [PATCH 8/8] update doc --- d2js/js/README.md | 30 ++-------- d2js/js/test-bundle.js | 133 ----------------------------------------- 2 files changed, 4 insertions(+), 159 deletions(-) delete mode 100644 d2js/js/test-bundle.js diff --git a/d2js/js/README.md b/d2js/js/README.md index 6ab63712e..0afeb230b 100644 --- a/d2js/js/README.md +++ b/d2js/js/README.md @@ -31,42 +31,20 @@ bun add @terrastruct/d2 ## Usage -### Browser +D2.js uses webworkers to call a WASM file. ```javascript +// Same for Node or browser import { D2 } from '@terrastruct/d2'; +// Or using a CDN +// import { D2 } from 'https://esm.sh/@terrastruct/d2'; const d2 = new D2(); const result = await d2.compile('x -> y'); const svg = await d2.render(result.diagram); - -const result = await d2.compile('x -> y', { - layout: 'dagre', - sketch: true -}); ``` -### Node - -```javascript -import { D2 } from '@terrastruct/d2'; - -const d2 = new D2(); - -async function createDiagram() { - const result = await d2.compile('x -> y'); - const svg = await d2.render(result.diagram); - console.log(svg); -} - -createDiagram(); -``` - -### Edge/CDN - -TODO - ## API Reference ### `new D2()` diff --git a/d2js/js/test-bundle.js b/d2js/js/test-bundle.js deleted file mode 100644 index e6fc6d08d..000000000 --- a/d2js/js/test-bundle.js +++ /dev/null @@ -1,133 +0,0 @@ -// 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( - ` - - - - D2 Test - - - -
- - - `, - { - 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}/`);