Expat for WebAssembly
v2.8.5WebAssemblyExpat 2.8.5 for browsers, Node.js and edge runtimes, precompiled for wasm32, single-threaded and multi-threaded as @crossbind/port-expat-wasm.
Install
crossbind itself arrives with your bundler plugin, or with a new project from npm create crossbind@beta; Bundlers has Vite, Webpack, Rspack and Rollup.
Usage
Each example runs here, in this tab, and prints what the site build checked.
Each example also has a JavaScript only tab: the same task with no C++ file, calling Expat's own headers from @crossbind/port-expat directly. All 4 work that way.
Turn XML into JavaScript objects
The calls most Expat code makes: XML_ParserCreate, an element handler and a character data handler, then XML_Parse. A malformed document throws with the line and column where Expat stopped.
Handlers are JavaScript functions, which cannot be sent to a worker, so the module runs on the page's thread and each handler is freed with releaseCallback (64 can be registered at a time). Attributes arrive as a handle to walk with readPointerAt, and character data as a string that runs past its length bytes to the next NUL byte, so the handler keeps only those bytes; XML_Parse wants the UTF-8 byte count too, xml.length stops one byte short here and fails with unclosed token. XML_ExpatVersionInfo returns its struct without fields, so the version comes from the XML_ExpatVersion string.
Stream a file through Expat
For documents you should not hold in one string: XML_GetBuffer hands out Expat's own buffer, fread fills it and XML_ParseBuffer parses it, 64 KiB at a time.
fread is gone: JavaScript cuts the document into 64 KiB pieces and copies each one into the buffer XML_GetBuffer hands out with writeBytes, which takes one character per byte, so this ASCII sitemap goes in as it is and other text would have to become a byte string first. The start-element handler is a JavaScript function, so the module runs on the page's thread and the handler is freed with releaseCallback along with the parser.
Read namespaced XML whatever the prefixes
XML_ParserCreateNS resolves every prefix to its namespace URI before your handlers see a name, and XML_SetStartNamespaceDeclHandler reports the declarations themselves.
The namespace separator is a C char, so it crosses as a number, '|'.charCodeAt(0): the string '|' is accepted without an error but arrives as 0, and Expat then joins URI and local name with nothing between them. The handlers run on the page's thread as in the tree example, the default namespace's prefix arrives as null, and the text inside hr is cut to the byte count Expat reports.
Expand entities without a billion laughs
Expat expands the entities a document declares and stops the ones that blow up: XML_SetBillionLaughsAttackProtectionMaximumAmplification and …ActivationThreshold set how far.
XML_SetBillionLaughsAttackProtectionMaximumAmplification and ...ActivationThreshold are declared in expat.h only if XML_DTD is defined or XML_GE is 1, which the library's build sets in expat_config.h, a header the binding never reads, so importing either fails with MISSING_EXPORT although the library contains both. The protection still runs with the defaults the C++ passes (100 times, from 8 MiB), so the first three lines match and the 64 KiB threshold of the fourth cannot be set. Each piece of character data is a call into JavaScript that cuts its string down to length bytes, 867,703 of them before Expat stops the 9-level document, so this version runs nearly 20 times longer than the C++ one.
What is different on WebAssembly
- In a browser the module runs in a Worker by default (
useWorker), so every call returns a promise:awaitcalls and constructors alike. - The module has its own filesystem:
m.FSwrites files,m.getFileBytesreads them back andm.autoMountFilesmountsFileobjects from an<input type=file>./memfslives in memory;/opfspersists across reloads and needs the Worker. See Filesystem. - In Node.js,
m.FSis the real disk, so use real paths there. - Multi-threaded builds (
runtime: 'mt') need COOP and COEP headers in production. See Threading.
Other platforms
- Expat overview: the apps, every platform's setup and the packages.
- Expat for Android: React Native apps on Android.
- Expat for iOS: React Native apps on iOS.
- Expat for WASI: command-line programs under wasmtime.
Facts on this page come from the port manifests in the repository and from what npm served on beta when the site was built. See the Libraries guide for the full consumer flow.