# svg-parser — SVG Parser and Serializer
_(no submodules)_
## Purpose
`parseSvg` and `serializeSvg` are the low-level I/O layer for `@suaveplan/compress-svg`. The parser is a pure-TypeScript push parser that processes SVG strings character-by-character without requiring `DOMParser`, making it safe to use in Node.js, Bun, edge runtimes, and workers. `serializeSvg` produces compact, whitespace-free output optimised for minimum byte count.
## Features
- `parseSvg(svg)` — converts an SVG string to an `SvgNode` tree; no DOM dependency; handles element, text, comment, CDATA, and doctype nodes
- `serializeSvg(node)` — converts an `SvgNode` tree back to a compact SVG string; self-closing tags for empty elements
- Handles XML declarations (``) by silently skipping them (irrelevant to the SVG tree)
- Decodes HTML/XML character entities (`&`, `<`, `>`, `"`, `'`, numeric `...;` and `...;`)
- Throws `SvgCompressError(SVG_COMPRESS_PARSE_FAILED)` with a descriptive message on malformed input
- Supports both double-quoted and single-quoted attribute values
- Supports boolean (value-less) attributes
## Usage: Basic Example
```typescript
import { parseSvg, serializeSvg } from '@suaveplan/compress-svg';
const node = parseSvg('');
console.log(node.name); // 'svg'
console.log(node.children![0]!.name); // 'circle'
const output = serializeSvg(node);
// ''
```
## Usage: Advanced Example
```typescript
import { parseSvg, serializeSvg } from '@suaveplan/compress-svg';
import { SvgCompressError, SVG_COMPRESS_CODES } from '@suaveplan/compress-svg';
function safeRoundTrip(svg: string): string | null {
try {
return serializeSvg(parseSvg(svg));
} catch (e) {
if (e instanceof SvgCompressError && e.code === SVG_COMPRESS_CODES.PARSE_FAILED) {
return null;
}
throw e;
}
}
// Handles comments, CDATA, and nested structures
const svg = `
`;
const output = safeRoundTrip(svg.trim());
// ''
```
## API Reference
### `parseSvg(svg: string): SvgNode`
Parses an SVG string into a tree of `SvgNode` objects. The returned node is always the root element. XML declarations are silently skipped. Doctype nodes are captured as children of the virtual root but not returned as the root element — the root is always the first element node.
**Throws:** `SvgCompressError(SVG_COMPRESS_PARSE_FAILED)` when:
- Input is empty or all whitespace
- A tag is left unclosed
- A closing tag does not match the open tag
- There is no root element in the input
### `serializeSvg(node: SvgNode): string`
Serializes a single `SvgNode` (and all its descendants) to a compact SVG string. Empty element nodes (no children) are serialized as self-closing (``). No whitespace or newlines are added between elements.
| Node type | Output format |
|------------|---------------------------------------|
| `element` | `children` or `` |
| `text` | Raw text value |
| `comment` | `` |
| `cdata` | `` |
| `doctype` | `` |
## Implementation Notes
The parser is a single-pass, index-advancing loop over the input string. It maintains an explicit stack of open elements rather than recursing, so deeply nested SVGs do not risk stack overflow. Attribute parsing uses a single regex that correctly handles double-quoted, single-quoted, and unquoted values. The `findTagEnd` helper handles `>` characters inside attribute values by tracking quote state. Entity decoding covers the five XML predefined entities plus numeric decimal and hexadecimal code points. The serializer uses a switch statement over node type, making it branch-exhaustive with TypeScript's `never` check at compile time.