# @jiihpeeh/svgoo-wasm

[![jsDelivr hits](https://img.shields.io/jsdelivr/npm/@jiihpeeh/svgoo-wasm)](https://www.jsdelivr.com/package/npm/@jiihpeeh/svgoo-wasm)

TypeScript bindings for [svgoo] — the port of [SVGO] to Rust — compiled to
WebAssembly over a **glue-free C ABI with zero JavaScript imports**. The same
module runs in browsers, Node.js and workers, and can offload every
`optimize()` call to a dedicated thread.

## Highlights

- **SVGO, compiled.** The full SVGO 4.x plugin pipeline, ported to Rust and
  shipped as a single small `.wasm` module — no Node, no browser APIs involved.
- **Environment-agnostic core.** One loader-free engine; the `browser` /
  `node` entries (chosen via package `exports` conditions) install nothing but
  a `fetch`- or `fs`-based wasm loader.
- **Worker mode.** `optimize(svg, { worker: true })` runs on a dedicated
  worker so the calling thread stays responsive. Everything inside the worker
  is imported lazily, so the main bundle stays small.
- **Tree-shakeable.** The barrel re-exports small, side-effect-light units
  (`core`, `plugin`, `errors`); bundlers drop the parts you never import.
- **Type-safe plugins.** Plugin names are a typed enum (`PluginName`), read
  from the running engine itself so constants can't drift from the wasm.

## Install

```sh
npm install @jiihpeeh/svgoo-wasm
# pnpm add @jiihpeeh/svgoo-wasm
# yarn add @jiihpeeh/svgoo-wasm
# bun add @jiihpeeh/svgoo-wasm
```

## CDN (jsDelivr)

The package is served by [jsDelivr](https://www.jsdelivr.com) straight from npm.
Pin a version for the browser cache to hit:

```js
// ESM module bundle (esbuild/Rollup build of the browser entry)
// https://cdn.jsdelivr.net/npm/@jiihpeeh/svgoo-wasm@4.1.5/+esm

// raw assets, served directly (content-addressed, immutable):
// https://cdn.jsdelivr.net/npm/@jiihpeeh/svgoo-wasm@4.1.5/dist/browser.js
// https://cdn.jsdelivr.net/npm/@jiihpeeh/svgoo-wasm@4.1.5/dist/svg_wasm_4.1.0_fix_04.wasm
```

Because the `+esm` bundle loads the wasm through a lazy `core.js` sub-bundle (a
separate module instance, so `setWasmLoader`/`setDefaultUrl` do **not** reach the
auto-load path), fetch the pinned wasm and `load()` it explicitly:

```html
<script type="module">
  import { optimize, fetchWasmBytes, load } from "https://cdn.jsdelivr.net/npm/@jiihpeeh/svgoo-wasm@4.1.5/+esm";
  const wasm = "https://cdn.jsdelivr.net/npm/@jiihpeeh/svgoo-wasm@4.1.5/dist/svg_wasm_4.1.0_fix_04.wasm";
  await load(await fetchWasmBytes(wasm));
  const { data } = await optimize("<svg xmlns='x'><!-- hello --></svg>");
  console.log(data);
</script>
```

`worker: true` from a CDN pull needs a same-origin `Worker`, so copy
`dist/worker.js` (and the wasm) to your host before enabling it.

## Quick start — Node.js

```js
import { optimize } from "@jiihpeeh/svgoo-wasm";

const { data } = await optimize("<svg xmlns='x'><!-- hello --></svg>");
```

The compiled engine (`svg_wasm_4.1.0_fix_04.wasm`) ships inside the package; the Node entry
reads it off disk automatically.

## Quick start — browser (bundlers)

```js
import { optimize, PluginName, only } from "@jiihpeeh/svgoo-wasm";

const { data } = await optimize("<svg xmlns='x'><path d='M0 0 10 10' /></svg>", {
  plugins: only(PluginName.RemoveEmptyText),   // just this one plugin
});
```

Bundlers honoring `exports` conditions pick the browser entry, and the engine
is **delivered by default**: the browser entry points at the package's own
`.wasm` via a static `new URL(..., import.meta.url)` asset, which Vite and
webpack 5 emit into your app automatically — nothing extra to serve. For CDN
`<script type="module">` and esbuild-style setups, place `dist/svg_wasm_4.1.0_fix_04.wasm`
next to your bundle; to load from elsewhere:

```js
import { setDefaultUrl } from "@jiihpeeh/svgoo-wasm";
setDefaultUrl("/wasm/svg_wasm_4.1.0_fix_04.wasm");
```

## Worker mode

```js
const { data } = await optimize(markup, { worker: true });
```

The bindings spawn their own module worker (`dist/worker.js`) via
`new URL(..., import.meta.url)`, batch the config, and optimize there. In
Node.js this is backed by `node:worker_threads`; in browsers by a `Worker`.
If no `Worker` is available — or it fails — the call falls back to the main
thread and still succeeds.

Bundler note: Vite and webpack resolve the worker asset automatically. esbuild
and CDN `<script type="module">` users should serve `dist/worker.js` and
`dist/svg_wasm_4.1.0_fix_04.wasm` alongside their bundle.

## API

| Export | Description |
| --- | --- |
| `optimize(svg, config?)` | Asynchronously optimize an SVG string; auto-loads the engine (worker mode opt-in via `config.worker`). |
| `optimizeSync(svg, config?)` | Synchronous variant; requires a prior `load()`. |
| `plugins()` / `pluginsSync()` | Runtime-resolved plugin names in SVGO order (preset-default first). |
| `load(bytes)` / `loadUrl(url)` / `setDefaultUrl(url)` | Load / locate the wasm engine. |
| `setWasmLoader(fn)` | Install a custom bytes loader for embedded engines. |
| `isLoaded()` / `reset()` | Query load state / eject the engine. |
| `SvgoError` / `classifyError()` | Typed errors (`kind`, plugin, message) for config and engine failures. |
| `shutdownWorkers()` | Terminate idle workers explicitly. |
| `PluginName`, `ALL`, `isPluginName()` | Plugin enum, registry list, and guard. |
| `plugin(name, params?)`, `only(...)`, `drop(...)`, `preset()` | Builders for `config.plugins`. |
| `VERSION` | Package version, synced with the crate. |

Lower-level primitives (`acquire`, `consumeResult`, `instantiateWasmEngine`,
`normalizeConfig`, `EngineSession`, `WasmLoader`, `WasmExports`) are exported
for embedding / exotic loaders.

`config` mirrors SVGO's own `optimize(svg, config)`: `plugins` accepts plain
names, raw SVGO spellings and `{ name, params }` entries; an omitted `config`
selects the default `preset-default` plugin set.

## Building the wasm from source

For developers who want to rebuild the `.wasm` (only `git`, `cargo` with the
`wasm32-unknown-unknown` target, and Node are needed):

```sh
npm run build:wasm
```

`scripts/wasm-build.mjs` uses a sibling `../rs` checkout when present (the
demo layout), otherwise it clones the Rust source from git
(`SVGOO_REPO`, default `https://gitlab.com/jiiihpeeh/svgoo.git`) into
`.cache/svgoo` and updates it with `git pull`. Point `SVGOO_SRC` at a
checkout to pin an exact source tree.

The full package gate (wasm build, typecheck, Node-free-dist check, smoke
suite, example) runs with:

```sh
npm test
```

## License

MIT. `svgoo` is a translation of [SVGO] (MIT, © Kir Belevich).

[svgoo]: https://gitlab.com/jiiihpeeh/svgoo
[SVGO]: https://github.com/svg/svgo