# How to Contribute
## Reporting Issues
- Search [GitHub Issues](https://github.com/jameslan/libxml2-wasm/issues) first to avoid duplicates
- Provide sufficient information: version, environment, steps to reproduce
- Include failing test cases when possible
## Pull Requests
The CI build verifies tests, coverage, and code style. Run locally with `npm test`.
**Requirements:**
- **Documentation:** Every exported API needs [TypeDoc](https://typedoc.org/guides/doccomments/) comments
- **Code Coverage:** 98% lines, 90% branches - check `coverage/lcov-report/index.html`
- **Linting:** Must pass ESLint (Airbnb TypeScript config)
## Environment Setup
### Prerequisites
- **Node.js 18+**
- **Emscripten SDK** (compiles libxml2 C to WebAssembly)
- **C toolchain:** autoconf, automake, libtool, pkg-config
### Option 1: DevContainer (Recommended for Windows)
Provides pre-configured environment with all dependencies.
**VS Code:**
1. Install Docker Desktop
and [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
2. Clone and open repository:
```bash
git clone https://github.com/jameslan/libxml2-wasm.git
code libxml2-wasm
```
3. Press `F1` → "Dev Containers: Reopen in Container"
4. Wait for setup to complete (auto-initializes submodules and installs dependencies)
**WebStorm/IntelliJ:** See [DevContainer docs](https://www.jetbrains.com/help/webstorm/connect-to-devcontainer.html)
### Option 2: Native Setup
**Install C toolchain:**
- **macOS:** `brew install autoconf automake libtool pkg-config`
- **Ubuntu/Debian:** `sudo apt-get install autoconf automake libtool pkg-config libatomic1`
- **Fedora/Enterprise Linux:** `sudo dnf install autoconf automake libtool pkg-config libatomic`
**Install Emscripten:**
```bash
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
```
**Build:**
```bash
git clone https://github.com/jameslan/libxml2-wasm.git
cd libxml2-wasm
git submodule update --init --recursive
npm install
npm run build
```
**Windows:** Use DevContainer (Option 1) or WSL 2 with Linux instructions. Native Windows is not supported due to shell
command differences.
## Development Workflows
### Build
Run the full build after cloning or when changing libxml2 configuration:
```bash
npm run build # Full build: WASM + TypeScript
npm test # Verify everything works
```
This compiles libxml2 C code into WebAssembly and TypeScript into JavaScript.
---
### TypeScript Development
**Most common workflow** - editing `src/*.mts` or `test/**/*.mts`:
```bash
npm run watch # Auto-recompile TypeScript on save
# OR
npm run tsc # Compile TypeScript once
# and always
npm test # Tests + coverage + linting
```
---
## Exporting libxml2 Functions
Two files control WASM exports:
### `binding/exported-functions.txt`
Lists libxml2 C functions (with underscore prefix per Emscripten convention):
```
_xmlNewDoc
_xmlAddChild
_xmlFreeDoc
...
```
### `binding/exported-runtime-functions.txt`
Lists Emscripten runtime functions for memory management:
```
HEAP32
UTF8ToString
addFunction
...
```
**Adding new functions:**
1. Find the function in [libxml2 docs](https://gnome.pages.gitlab.gnome.org/libxml2/html/index.html)
2. Add to `binding/exported-functions.txt` with underscore: `_xmlFunctionName`
3. `npm run link` to relink WASM
4. Add TypeScript wrapper in `src/libxml2.mts`
5. Write tests in `test/`
6. Build and run tests to verify everything works
## Debugging & Best Practices
### Never Create Multiple WASM Module Instances
Each call to `moduleLoader()` creates a new WASM module instance with its own separate memory space. Pointers from one
instance won't work in another.
**Incorrect approach:**
```typescript
// -- mynewfeature.mts --
import {xmlSaveDoc} from './libxml2.mjs';
import moduleLoader from "./libxml2raw.mjs";
import {XmlDocument} from './document.mjs';
const libxml2 = await moduleLoader(); // Creates a NEW instance of libxml2 with its own memory space
const ptr = libxml2._malloc(100); // Memory in NEW instance
const doc = XmlDocument.fromString(''); // This uses the "global" instance of libxml2
xmlSaveDoc(ptr, doc._ptr); // FAIL: the ptr and the doc are from different instances
```
---
### TypeScript Debugging
**VS Code:** Three launch configurations are available in `.vscode/launch.json`:
1. **Mocha: All Tests** - Debug all tests
2. **Mocha: Current File** - Debug a single test file
3. **Mocha: Grep Tests** - Debug specific tests by name pattern
**WebStorm/IntelliJ:** Works out of the box with the built-in debugger.
---
### WASM/C Debugging
To debug the libxml2 C code build the debug version:
```bash
npm run build:debug # Build WASM with debug symbols (-g) and no optimizations (-O0)
```
**VS Code DevContainer:** Works by default. You can step through and set breakpoints in C code directly.
**Local VS Code setup:** Install
the [WebAssembly DWARF Debugging](https://marketplace.visualstudio.com/items?itemName=ms-vscode.wasm-dwarf-debugging)
extension for C code debugging support.
**WebStorm/IntelliJ:** Not supported at this time.
---
### Prevent Memory Leaks
Always use `using` keyword or call `.dispose()`:
```typescript
using doc = XmlDocument.fromString('');
// Automatically disposed
```
## Troubleshooting
**"emcc: command not found"**
→ Activate Emscripten: `source /path/to/emsdk/emsdk_env.sh`
**`libxml2` submodule out of sync**
The submodule is checked out at a different commit than the repo records. This happens after pulling a change that bumps the submodule ref.
Building against the mismatched source can fail (e.g. `wasm-ld: symbol ... not found`) or silently produce a stale WASM. Resync, then rebuild:
```bash
git submodule update --checkout libxml2
npm run build
```
---
Questions? [GitHub Discussions](https://github.com/jameslan/libxml2-wasm/discussions) |
Bugs? [GitHub Issues](https://github.com/jameslan/libxml2-wasm/issues)