Files
suaveplan/packages/devtools/docs/compress-svg/README.md
2026-05-27 23:29:53 +01:00

117 lines
15 KiB
Markdown

# @suaveplan/compress-svg - SVG optimisation and minification: whitespace removal, redundant attribute stripping, path data precision reduction, element collapsing, ID mangling, and SVGO-compatible plugin pipeline
## Summary
SVG optimisation and minification for TypeScript. Pure TypeScript — no DOMParser, no external SVG library. Runs in Bun, Node.js, browsers, and edge runtimes.
<!-- begin:auto-docs -->
## Documentation tree
- [compress-svg](./src/core/compress-svg/compress-svg.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [compress-svg](./src/core/compress-svg/compress-svg.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [svg](./src/parser/svg/svg.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [svg](./src/parser/svg/svg.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [cleanup-attrs](./src/plugins/cleanup-attrs/cleanup-attrs.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [cleanup-attrs](./src/plugins/cleanup-attrs/cleanup-attrs.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [collapse-groups](./src/plugins/collapse-groups/collapse-groups.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [collapse-groups](./src/plugins/collapse-groups/collapse-groups.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [convert-path-data](./src/plugins/convert-path-data/convert-path-data.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [convert-path-data](./src/plugins/convert-path-data/convert-path-data.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [convert-transforms](./src/plugins/convert-transforms/convert-transforms.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [convert-transforms](./src/plugins/convert-transforms/convert-transforms.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [mangle-ids](./src/plugins/mangle-ids/mangle-ids.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [mangle-ids](./src/plugins/mangle-ids/mangle-ids.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [remove-comments](./src/plugins/remove-comments/remove-comments.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [remove-comments](./src/plugins/remove-comments/remove-comments.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [remove-doctype](./src/plugins/remove-doctype/remove-doctype.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [remove-doctype](./src/plugins/remove-doctype/remove-doctype.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [remove-metadata](./src/plugins/remove-metadata/remove-metadata.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [remove-metadata](./src/plugins/remove-metadata/remove-metadata.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [types](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [common](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
<!-- end:auto-docs --> - [compress-svg](./src/core/compress-svg/compress-svg.md) — <!-- begin:auto-docs --> _(no submodules)_- [svg](./src/parser/svg/svg.md) — <!-- begin:auto-docs --> _(no submodules)_- [svg](./src/parser/svg/svg.md) — <!-- begin:auto-docs --> _(no submodules)_- [cleanup-attrs](./src/plugins/cleanup-attrs/cleanup-attrs.md) — <!-- begin:auto-docs --> _(no submodules)_- [cleanup-attrs](./src/plugins/cleanup-attrs/cleanup-attrs.md) — <!-- begin:auto-docs --> _(no submodules)_- [collapse-groups](./src/plugins/collapse-groups/collapse-groups.md) — <!-- begin:auto-docs --> _(no submodules)_- [collapse-groups](./src/plugins/collapse-groups/collapse-groups.md) — <!-- begin:auto-docs --> _(no submodules)_- [convert-path-data](./src/plugins/convert-path-data/convert-path-data.md) — <!-- begin:auto-docs --> _(no submodules)_- [convert-path-data](./src/plugins/convert-path-data/convert-path-data.md) — <!-- begin:auto-docs --> _(no submodules)_- [convert-transforms](./src/plugins/convert-transforms/convert-transforms.md) — <!-- begin:auto-docs --> _(no submodules)_- [convert-transforms](./src/plugins/convert-transforms/convert-transforms.md) — <!-- begin:auto-docs --> _(no submodules)_- [mangle-ids](./src/plugins/mangle-ids/mangle-ids.md) — <!-- begin:auto-docs --> _(no submodules)_- [mangle-ids](./src/plugins/mangle-ids/mangle-ids.md) — <!-- begin:auto-docs --> _(no submodules)_- [remove-comments](./src/plugins/remove-comments/remove-comments.md) — <!-- begin:auto-docs --> _(no submodules)_- [remove-comments](./src/plugins/remove-comments/remove-comments.md) — <!-- begin:auto-docs --> _(no submodules)_- [remove-doctype](./src/plugins/remove-doctype/remove-doctype.md) — <!-- begin:auto-docs --> _(no submodules)_- [remove-doctype](./src/plugins/remove-doctype/remove-doctype.md) — <!-- begin:auto-docs --> _(no submodules)_- [remove-metadata](./src/plugins/remove-metadata/remove-metadata.md) — <!-- begin:auto-docs --> _(no submodules)_- [remove-metadata](./src/plugins/remove-metadata/remove-metadata.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_- [common](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_- [compress-svg](./src/core/compress-svg/compress-svg.md) — <!-- begin:auto-docs --> _(no submodules)_- [svg](./src/parser/svg/svg.md) — <!-- begin:auto-docs --> _(no submodules)_- [svg](./src/parser/svg/svg.md) — <!-- begin:auto-docs --> _(no submodules)_- [cleanup-attrs](./src/plugins/cleanup-attrs/cleanup-attrs.md) — <!-- begin:auto-docs --> _(no submodules)_- [cleanup-attrs](./src/plugins/cleanup-attrs/cleanup-attrs.md) — <!-- begin:auto-docs --> _(no submodules)_- [collapse-groups](./src/plugins/collapse-groups/collapse-groups.md) — <!-- begin:auto-docs --> _(no submodules)_- [collapse-groups](./src/plugins/collapse-groups/collapse-groups.md) — <!-- begin:auto-docs --> _(no submodules)_- [convert-path-data](./src/plugins/convert-path-data/convert-path-data.md) — <!-- begin:auto-docs --> _(no submodules)_- [convert-path-data](./src/plugins/convert-path-data/convert-path-data.md) — <!-- begin:auto-docs --> _(no submodules)_- [convert-transforms](./src/plugins/convert-transforms/convert-transforms.md) — <!-- begin:auto-docs --> _(no submodules)_- [convert-transforms](./src/plugins/convert-transforms/convert-transforms.md) — <!-- begin:auto-docs --> _(no submodules)_- [mangle-ids](./src/plugins/mangle-ids/mangle-ids.md) — <!-- begin:auto-docs --> _(no submodules)_- [mangle-ids](./src/plugins/mangle-ids/mangle-ids.md) — <!-- begin:auto-docs --> _(no submodules)_- [remove-comments](./src/plugins/remove-comments/remove-comments.md) — <!-- begin:auto-docs --> _(no submodules)_- [remove-comments](./src/plugins/remove-comments/remove-comments.md) — <!-- begin:auto-docs --> _(no submodules)_- [remove-doctype](./src/plugins/remove-doctype/remove-doctype.md) — <!-- begin:auto-docs --> _(no submodules)_- [remove-doctype](./src/plugins/remove-doctype/remove-doctype.md) — <!-- begin:auto-docs --> _(no submodules)_- [remove-metadata](./src/plugins/remove-metadata/remove-metadata.md) — <!-- begin:auto-docs --> _(no submodules)_- [remove-metadata](./src/plugins/remove-metadata/remove-metadata.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_- [common](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_- [compress-svg](./src/core/compress-svg/compress-svg.md) — <!-- begin:auto-docs --> _(no submodules)_- [svg](./src/parser/svg/svg.md) — <!-- begin:auto-docs --> _(no submodules)_- [svg](./src/parser/svg/svg.md) — <!-- begin:auto-docs --> _(no submodules)_- [cleanup-attrs](./src/plugins/cleanup-attrs/cleanup-attrs.md) — <!-- begin:auto-docs --> _(no submodules)_- [cleanup-attrs](./src/plugins/cleanup-attrs/cleanup-attrs.md) — <!-- begin:auto-docs --> _(no submodules)_- [collapse-groups](./src/plugins/collapse-groups/collapse-groups.md) — <!-- begin:auto-docs --> _(no submodules)_- [collapse-groups](./src/plugins/collapse-groups/collapse-groups.md) — <!-- begin:auto-docs --> _(no submodules)_- [convert-path-data](./src/plugins/convert-path-data/convert-path-data.md) — <!-- begin:auto-docs --> _(no submodules)_- [convert-path-data](./src/plugins/convert-path-data/convert-path-data.md) — <!-- begin:auto-docs --> _(no submodules)_- [convert-transforms](./src/plugins/convert-transforms/convert-transforms.md) — <!-- begin:auto-docs --> _(no submodules)_- [convert-transforms](./src/plugins/convert-transforms/convert-transforms.md) — <!-- begin:auto-docs --> _(no submodules)_- [mangle-ids](./src/plugins/mangle-ids/mangle-ids.md) — <!-- begin:auto-docs --> _(no submodules)_- [mangle-ids](./src/plugins/mangle-ids/mangle-ids.md) — <!-- begin:auto-docs --> _(no submodules)_- [remove-comments](./src/plugins/remove-comments/remove-comments.md) — <!-- begin:auto-docs --> _(no submodules)_- [remove-comments](./src/plugins/remove-comments/remove-comments.md) — <!-- begin:auto-docs --> _(no submodules)_- [remove-doctype](./src/plugins/remove-doctype/remove-doctype.md) — <!-- begin:auto-docs --> _(no submodules)_- [remove-doctype](./src/plugins/remove-doctype/remove-doctype.md) — <!-- begin:auto-docs --> _(no submodules)_- [remove-metadata](./src/plugins/remove-metadata/remove-metadata.md) — <!-- begin:auto-docs --> _(no submodules)_- [remove-metadata](./src/plugins/remove-metadata/remove-metadata.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_- [common](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_- [compress-svg](./src/core/compress-svg/compress-svg.md) — <!-- begin:auto-docs --> _(no submodules)_- [svg](./src/parser/svg/svg.md) — <!-- begin:auto-docs --> _(no submodules)_ - [svg](./src/parser/svg/svg.md) — <!-- begin:auto-docs --> _(no submodules)_- [cleanup-attrs](./src/plugins/cleanup-attrs/cleanup-attrs.md) — <!-- begin:auto-docs --> _(no submodules)_ - [cleanup-attrs](./src/plugins/cleanup-attrs/cleanup-attrs.md) — <!-- begin:auto-docs --> _(no submodules)_- [collapse-groups](./src/plugins/collapse-groups/collapse-groups.md) — <!-- begin:auto-docs --> _(no submodules)_ - [collapse-groups](./src/plugins/collapse-groups/collapse-groups.md) — <!-- begin:auto-docs --> _(no submodules)_- [convert-path-data](./src/plugins/convert-path-data/convert-path-data.md) — <!-- begin:auto-docs --> _(no submodules)_ - [convert-path-data](./src/plugins/convert-path-data/convert-path-data.md) — <!-- begin:auto-docs --> _(no submodules)_- [convert-transforms](./src/plugins/convert-transforms/convert-transforms.md) — <!-- begin:auto-docs --> _(no submodules)_ - [convert-transforms](./src/plugins/convert-transforms/convert-transforms.md) — <!-- begin:auto-docs --> _(no submodules)_- [mangle-ids](./src/plugins/mangle-ids/mangle-ids.md) — <!-- begin:auto-docs --> _(no submodules)_ - [mangle-ids](./src/plugins/mangle-ids/mangle-ids.md) — <!-- begin:auto-docs --> _(no submodules)_- [remove-comments](./src/plugins/remove-comments/remove-comments.md) — <!-- begin:auto-docs --> _(no submodules)_ - [remove-comments](./src/plugins/remove-comments/remove-comments.md) — <!-- begin:auto-docs --> _(no submodules)_- [remove-doctype](./src/plugins/remove-doctype/remove-doctype.md) — <!-- begin:auto-docs --> _(no submodules)_ - [remove-doctype](./src/plugins/remove-doctype/remove-doctype.md) — <!-- begin:auto-docs --> _(no submodules)_- [remove-metadata](./src/plugins/remove-metadata/remove-metadata.md) — <!-- begin:auto-docs --> _(no submodules)_ - [remove-metadata](./src/plugins/remove-metadata/remove-metadata.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_ - [common](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_
## Features
- **`compressSvg`** — parse → plugin pipeline → multipass loop → serialize → CompressResult with size metrics
- **`parseSvg` / `serializeSvg`** — standalone push-parser and compact serializer
- **8 built-in plugins** — removeComments, removeMetadata, removeDoctype, collapseGroups, cleanupAttrs, convertPathData, convertTransforms, mangleIds
- **3 presets** — PRESET_DEFAULT, PRESET_SAFE, PRESET_AGGRESSIVE
- **Plugin API** — single `apply(node, options): SvgNode | null` contract; compose any plugins
- **OTel instrumentation** — counter + duration + ratio histograms via `@opentelemetry/api`
- **Property-based tests** — idempotency, parse↔serialize round-trip, ratio invariants
## Installation
```bash
bun add @suaveplan/compress-svg
```
## Quick Start
```typescript
import { compressSvg } from '@suaveplan/compress-svg';
const result = compressSvg(svgString);
console.log(result.data); // optimised SVG
console.log(result.ratio); // e.g. 0.78
console.log(result.passCount); // multipass iterations used
```
## Presets
```typescript
import { compressSvg, PRESET_DEFAULT, PRESET_SAFE, PRESET_AGGRESSIVE } from '@suaveplan/compress-svg';
// Default: removeComments + metadata + doctype + collapseGroups + cleanupAttrs + convertPathData
compressSvg(svg, { plugins: PRESET_DEFAULT });
// Safe: no ID mangling, no transform conversion
compressSvg(svg, { plugins: PRESET_SAFE });
// Aggressive: all plugins including mangleIds and convertTransforms
compressSvg(svg, { plugins: PRESET_AGGRESSIVE });
```
## Custom Plugin
```typescript
import type { SvgPlugin } from '@suaveplan/compress-svg';
const removeTitle: SvgPlugin = {
name: 'removeTitle',
apply(node) {
return node.type === 'element' && node.name === 'title' ? null : node;
},
};
compressSvg(svg, { plugins: [removeTitle, ...PRESET_DEFAULT] });
```
## Options
```typescript
compressSvg(svg, {
plugins: PRESET_DEFAULT, // override the full pipeline
floatPrecision: 2, // decimal places for path coords (default: 3)
transformPrecision: 3, // decimal places for transforms (default: 5)
multipass: true, // re-run until output is stable (default: true)
});
```
## Modules
| Import path | Contents |
|---|---|
| `@suaveplan/compress-svg` | All exports |
| `@suaveplan/compress-svg/core/compress-svg` | `compressSvg` |
| `@suaveplan/compress-svg/parser/svg-parser` | `parseSvg`, `serializeSvg` |
| `@suaveplan/compress-svg/plugins/remove-comments` | `removeComments` |
| `@suaveplan/compress-svg/plugins/remove-metadata` | `removeMetadata` |
| `@suaveplan/compress-svg/plugins/remove-doctype` | `removeDoctype` |
| `@suaveplan/compress-svg/plugins/collapse-groups` | `collapseGroups` |
| `@suaveplan/compress-svg/plugins/cleanup-attrs` | `cleanupAttrs` |
| `@suaveplan/compress-svg/plugins/convert-path-data` | `convertPathData` |
| `@suaveplan/compress-svg/plugins/convert-transforms` | `convertTransforms` |
| `@suaveplan/compress-svg/plugins/mangle-ids` | `mangleIds`, `createMangleIds` |
| `@suaveplan/compress-svg/presets` | `PRESET_DEFAULT`, `PRESET_SAFE`, `PRESET_AGGRESSIVE` |