Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@ jobs:
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: actions/setup-node@v6
with:
targets: wasm32-unknown-unknown
- run: cargo install wasm-bindgen-cli --version 0.2.128
node-version: '24'
- run: npm install
- run: npm run build
working-directory: wasm
- run: npm test
working-directory: wasm

build:
needs: test
Expand Down
13 changes: 9 additions & 4 deletions DEV.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,19 @@ The repository is a Cargo workspace with one published crate and one binding cra

`wasm/` is `mdhtml-wasm`, the `wasm-bindgen` glue for the browser, and `wasm/package.json` is the npm package `@answerdotai/mdhtml` around it. The package is private until its first publish. Its `version` field is a copy that `ship-bump` keeps in step through `[tool.fastship].version-files`.

Building needs two one-time installs. The CLI version must match the crate version pinned in `wasm/Cargo.toml`.
WASM builds require Rust managed by rustup and Node/npm. The project's npm dependency `wasm-pack` installs the WASM target when missing and downloads or builds the CLI matching Cargo's resolved `wasm-bindgen` version. No separate target or CLI installation is needed.

In an aai-ws workspace, `ws-sync` installs the npm dependencies, links local packages, and runs the WASM build. For a standalone checkout, install and build from the repository root:

```bash
rustup target add wasm32-unknown-unknown
cargo install wasm-bindgen-cli --version 0.2.128
npm install
npm run build --workspace wasm
npm test --workspace wasm
```

`npm run build` in `wasm/` compiles with the `wasm` profile (`dist` at `opt-level = "z"`, for size) and runs `wasm-bindgen` into the ignored `wasm/pkg/`: the `.wasm`, the JavaScript glue, and type declarations. That is the `maturin develop` of the JavaScript side. In the browser the output of `md2mdhtml` goes straight into the DOM, and the browser's own parser does the tree construction that fast5ever does for Python.
`npm run build` in `wasm/` compiles with the `wasm` profile (`dist` at `opt-level = "z"`, for size) and generates the ignored `wasm/pkg/`: the `.wasm`, the JavaScript glue, and type declarations. It keeps the hand-maintained npm manifest and does not run an additional wasm-opt pass. That is the `maturin develop` of the JavaScript side. In the browser the output of `md2mdhtml` goes straight into the DOM, and the browser's own parser does the tree construction that fast5ever does for Python.

`npm test` in `wasm/` runs Node's built-in test runner against that generated browser-targeted package, checking rendering and Unicode string transfer through the real WASM module. Build first after Rust changes; the test does not rebuild. CI installs, builds, and tests through the same npm commands.

A binding crate can only reach the library's public surface, so anything a binding needs is exported from `src/lib.rs`. The Python glue needs six items beyond the documented API (`render_inlines`, `plain`, `code_block_open`, `CODE_BLOCK_CLOSE`, `trailing_attr_span`, `highlight_md`), exported by name so the modules that hold them stay private.

Expand Down
2 changes: 1 addition & 1 deletion wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ crate-type = ["cdylib"]

[dependencies]
mdhtml-crate = { path = ".." }
wasm-bindgen = "=0.2.128"
wasm-bindgen = ">=0.2.128"
6 changes: 5 additions & 1 deletion wasm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@
"main": "pkg/mdhtml_wasm.js",
"types": "pkg/mdhtml_wasm.d.ts",
"files": ["pkg"],
"devDependencies": {
"wasm-pack": ">=0.15.0"
},
"scripts": {
"build": "cargo build -p mdhtml-wasm --target wasm32-unknown-unknown --profile wasm && wasm-bindgen --target web --out-dir pkg ../target/wasm32-unknown-unknown/wasm/mdhtml_wasm.wasm"
"build": "wasm-pack build --target web --profile wasm --no-pack --no-opt",
"test": "node --test test.js"
}
}
17 changes: 17 additions & 0 deletions wasm/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import test from 'node:test';
import init, { md2mdhtml } from '@answerdotai/mdhtml';

await init({ module_or_path: readFileSync(new URL('./pkg/mdhtml_wasm_bg.wasm', import.meta.url)) });

test('renders Markdown through the generated JavaScript bindings', () => {
assert.equal(md2mdhtml('# Hello\n\n**world**'), '<h1>Hello</h1>\n<p><strong>world</strong></p>\n');
assert.equal(md2mdhtml(''), '');
});

test('preserves Unicode across repeated calls and memory growth', () => {
for (const text of ['café 日本語 🦀', '🦀'.repeat(100000), 'café 日本語 🦀']) {
assert.equal(md2mdhtml(text), `<p>${text}</p>\n`);
}
});
Loading