# 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 `&#x...;`) - 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 = ` raw `; const output = safeRoundTrip(svg.trim()); // 'raw' ``` ## 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.