Merge pull request #2283 from alixander/d2js-split
d2js: remove entrypoints and code split esm module
This commit is contained in:
commit
a416253a05
10 changed files with 117 additions and 148 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -31,36 +31,18 @@ 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();
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
|
@ -87,7 +69,7 @@ D2.js uses Bun, so install this first.
|
|||
```bash
|
||||
git clone https://github.com/terrastruct/d2.git
|
||||
cd d2/d2js/js
|
||||
./make.sh
|
||||
./make.sh all
|
||||
```
|
||||
|
||||
If you change the main D2 source code, you should regenerate the WASM file:
|
||||
|
|
@ -97,6 +79,8 @@ If you change the main D2 source code, you should regenerate the WASM file:
|
|||
|
||||
### Running the Development Server
|
||||
|
||||
Make sure you've built already, then run:
|
||||
|
||||
```bash
|
||||
bun run dev
|
||||
```
|
||||
|
|
|
|||
|
|
@ -23,12 +23,11 @@ await writeFile(
|
|||
);
|
||||
|
||||
const commonConfig = {
|
||||
splitting: false,
|
||||
sourcemap: "external",
|
||||
minify: true,
|
||||
};
|
||||
|
||||
async function buildPlatformFile(platform) {
|
||||
async function buildDynamicFiles(platform) {
|
||||
const platformContent =
|
||||
platform === "node"
|
||||
? `export * from "./platform.node.js";`
|
||||
|
|
@ -36,51 +35,50 @@ async function buildPlatformFile(platform) {
|
|||
|
||||
const platformPath = join(SRC_DIR, "platform.js");
|
||||
await writeFile(platformPath, platformContent);
|
||||
|
||||
const workerSource =
|
||||
platform === "node"
|
||||
? join(SRC_DIR, "worker.node.js")
|
||||
: join(SRC_DIR, "worker.browser.js");
|
||||
|
||||
const workerTarget = join(SRC_DIR, "worker.js");
|
||||
const workerContent = await readFile(workerSource, "utf8");
|
||||
await writeFile(workerTarget, workerContent);
|
||||
}
|
||||
|
||||
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")],
|
||||
},
|
||||
};
|
||||
|
||||
const config = configs[buildType];
|
||||
await buildPlatformFile(config.platform);
|
||||
await buildDynamicFiles(config.platform);
|
||||
|
||||
const result = await build({
|
||||
...commonConfig,
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
</div>
|
||||
<div id="output"></div>
|
||||
<script type="module">
|
||||
import { D2 } from "../src/index.js";
|
||||
import { D2 } from "../dist/browser/index.js";
|
||||
const d2 = new D2();
|
||||
window.compile = async () => {
|
||||
const input = document.getElementById("input").value;
|
||||
|
|
|
|||
|
|
@ -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.11",
|
||||
"version": "0.1.19",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/terrastruct/d2.git",
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -11,18 +11,19 @@ export async function loadFile(path) {
|
|||
}
|
||||
|
||||
export async function createWorker() {
|
||||
// Combine wasmExecJs with worker script
|
||||
const workerResponse = await fetch(new URL("./worker.js", import.meta.url));
|
||||
if (!workerResponse.ok) {
|
||||
let response = await fetch(new URL("./worker.js", import.meta.url));
|
||||
if (!response.ok)
|
||||
throw new Error(
|
||||
`Failed to load worker.js: ${workerResponse.status} ${workerResponse.statusText}`
|
||||
`Failed to load worker.js: ${response.status} ${response.statusText}`
|
||||
);
|
||||
}
|
||||
const workerJs = await workerResponse.text();
|
||||
let workerScript = await response.text();
|
||||
|
||||
const blob = new Blob(["(() => {", wasmExecJs, "})();", workerJs], {
|
||||
type: "application/javascript",
|
||||
let blob = new Blob([wasmExecJs, workerScript], {
|
||||
type: "text/javascript;charset=utf-8",
|
||||
});
|
||||
|
||||
return new Worker(URL.createObjectURL(blob));
|
||||
const worker = new Worker(URL.createObjectURL(blob), {
|
||||
type: "module",
|
||||
});
|
||||
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(false, self, initWasmBrowser);
|
||||
|
|
@ -1,92 +1 @@
|
|||
const isNode = typeof process !== "undefined" && process.release?.name === "node";
|
||||
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);
|
||||
});
|
||||
// Replaced at build time
|
||||
|
|
|
|||
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(true, parentPort, initWasmNode);
|
||||
52
d2js/js/src/worker.shared.js
Normal file
52
d2js/js/src/worker.shared.js
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
let currentPort;
|
||||
let d2;
|
||||
|
||||
export function setupMessageHandler(isNode, port, initWasm) {
|
||||
currentPort = port;
|
||||
|
||||
const handleMessage = async (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;
|
||||
}
|
||||
};
|
||||
|
||||
if (isNode) {
|
||||
port.on("message", handleMessage);
|
||||
} else {
|
||||
port.onmessage = (e) => handleMessage(e.data);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue