@lawsafrica/bluebell-wasm
v5.0.0
Published
WebAssembly bindings for the Bluebell Akoma Ntoso parser
Downloads
418
Readme
bluebell-wasm
WebAssembly bindings for the Bluebell parser: parse Bluebell markup into Akoma Ntoso XML from browser or Node.js JavaScript.
This crate is a thin wasm-bindgen wrapper around bluebell-core. It is not
published to crates.io; it exists only to produce the
@lawsafrica/bluebell-wasm npm package.
API
// Parse Bluebell markup into an Akoma Ntoso XML string.
// root is a Python-compatible grammar root such as "act", "statement",
// "hier_element_block", "attachment", or "block_list_item".
// Throws an Error for an unknown root, an invalid FRBR URI, or a parse failure.
function parseToXml(
text: string,
root: string,
frbr_uri: string,
eid_prefix?: string,
source?: { showAs: string; eid: string; href: string },
): string;
// The crate/package version, e.g. "4.0.0".
function version(): string;The XML is returned as a string. The call signature matches Python Bluebell's
top-level parse_to_xml(text, root, frbr_uri, eid_prefix="", source=None) API.
The optional source config controls generated AKN metadata; when omitted it
defaults to cobalt metadata. Document roots return a full <akomaNtoso>
document; fragment roots return the fragment element. In the browser, feed the
returned XML to DOMParser if you need a document tree:
const doc = new DOMParser().parseFromString(xml, "text/xml");Prerequisites
- A rustup-managed Rust toolchain with the wasm target:
rustup target add wasm32-unknown-unknown - wasm-pack:
cargo install wasm-packorbrew install wasm-pack - Node.js (to run the tests)
Note: a Homebrew-installed Rust (brew install rust) does not ship the
wasm32 standard library and cannot build this crate. If both are installed,
make sure the rustup toolchain's cargo is first on your PATH when running
the commands below, e.g.
export PATH="$HOME/.rustup/toolchains/stable-aarch64-apple-darwin/bin:$PATH".
Building
The published npm package is built with --target web (via the build-wasm
poe task, see below), because the docs site loads it directly in the browser
with no bundler, and a --target web build also works when consumed through
a bundler. The other targets remain available for local builds aimed at a
specific consumer:
From this directory (crates/bluebell-wasm):
# published target: plain browser ES modules, also works with bundlers
wasm-pack build --target web --scope lawsafrica
# for bundlers (webpack, vite, rollup) only
wasm-pack build --target bundler --scope lawsafrica
# for Node.js
wasm-pack build --target nodejs --scope lawsafricaEach build writes an npm package to pkg/ (git-ignored) named
@lawsafrica/bluebell-wasm, including TypeScript definitions. The --scope
flag is what puts the package in the @lawsafrica npm scope; wasm-pack
derives the rest of the name from the crate name.
Optimised release builds are the default for wasm-pack build; add --dev
for faster, unoptimised builds while developing.
From the repository root, poe build-wasm runs the same --target web build
into crates/bluebell-wasm/pkg (see pyproject.toml).
Testing
Tests live in tests/wasm.rs and use wasm-bindgen-test, which compiles the
tests to wasm and executes them in a real JavaScript runtime — this is the
standard way to test wasm-bindgen crates. From this directory:
# run in Node (no browser needed)
wasm-pack test --node
# or in a headless browser
wasm-pack test --headless --chromeA plain cargo test -p bluebell-wasm from the repo root compiles the crate
natively but runs no wasm tests; the real coverage of the parse logic itself
lives in bluebell-core's test suite, so these tests only need to cover the
JS binding surface (root mapping, error conversion, string round-tripping).
Using the package
The published @lawsafrica/bluebell-wasm package is a --target web build,
so its default export is the loader function that fetches and
instantiates the .wasm file; call it (and await it) once before using any
other export.
Note: the module also has a separate named export called init — that's
the crate's own #[wasm_bindgen(start)] function (it forwards Rust panics
to the console) and runs automatically on load, so you never need to call it
yourself. When you import init, { parseToXml } from "...", the local name
init refers to the default export (the loader), not this named one — the
same name, two different functions. Naming the local default-import binding
something like initWasm avoids the ambiguity if it's confusing.
Plain browser, no bundler (the published package)
<script type="module">
import initWasm, { parseToXml } from "https://cdn.jsdelivr.net/npm/@lawsafrica/bluebell-wasm@4/bluebell_wasm.js";
// initWasm() fetches and instantiates the .wasm file; call it once before
// using any other export.
await initWasm();
const xml = parseToXml(
"SEC 1. - Heading\n\n Some content.",
"act",
"/akn/za/act/2022/1",
);
const doc = new DOMParser().parseFromString(xml, "text/xml");
</script>The same code works after npm install @lawsafrica/bluebell-wasm, importing
from @lawsafrica/bluebell-wasm/bluebell_wasm.js instead of the CDN URL —
most modern bundlers accept a --target web build directly, awaiting the
default export the same way.
With a bundler that needs a bundler-target package
If a bundler cannot consume the --target web output directly (some webpack
configurations, for example), build a --target bundler package locally
instead of using the published one — see "Building" above — and consume it
without an explicit loader call:
import { parseToXml, version } from "@lawsafrica/bluebell-wasm";
const xml = parseToXml(
"SEC 1. - Heading\n\n Some content.",
"act",
"/akn/za/act/2022/1",
);Node.js (--target nodejs build)
Build locally with --target nodejs (the published package does not target
Node):
const { parseToXml } = require("@lawsafrica/bluebell-wasm");Errors
parseToXml throws a JavaScript Error whose message describes the problem:
an unsupported root name, an invalid FRBR URI, or a Bluebell parse failure.
Rust panics (which should not happen) are reported to the browser console via
console_error_panic_hook instead of an opaque wasm trap.
Testing the demo locally
The MkDocs live demo (docs/demo.md / docs/js/demo.js) loads
@lawsafrica/bluebell-wasm from jsDelivr. Since the package isn't published
yet (and even once it is, you'll want to test local changes before a
release), the demo also supports loading a locally built copy: visit the
demo page with a ?local-wasm query parameter and it loads the module from
docs/js/bluebell-wasm/ (git-ignored) instead of the CDN.
poe build-wasm # builds --target web into crates/bluebell-wasm/pkg
cp -r crates/bluebell-wasm/pkg docs/js/bluebell-wasm
mkdocs serve # then open http://127.0.0.1:8000/bluebell/demo/?local-wasm