Parser
.gg source → DiagramDef in three stages, each exposed as a separate function you can call directly:
.gg source
│
▼
parseGg(source) → { def, errors, icons }
│ (includes integrity checks)
▼
buildIconContext({ … }) → IconContext (gridgram/node)
│ (filesystem / network reads)
▼
resolveDiagramIcons(def, ctx) → { def, diagnostics }
│
▼
renderDiagram(def) → { svg, diagnostics }Browser vs Node: parseGg, resolveDiagramIcons, checkIntegrity, formatError, and the icon classification helpers (isPathRef, collectPathRefs, stripSvgWrapper) are all pure and exported from 'gridgram' — they work in every ESM host. The filesystem / HTTP icon loader (buildIconContext and friends) lives on the 'gridgram/node' subpath and is Node-only.
parseGg(source)
import { parseGg } from 'gridgram'
interface ParseResult {
def: DiagramDef
errors: GgError[]
icons?: Record<string, string> // from `doc { icons: … }`
}
function parseGg(source: string): ParseResultTokenizes the source, folds doc/icon/region/note/connector statements into a DiagramDef, and runs integrity checks (connector refs, note targets, region spans). Three categories of error end up in errors:
source | Meaning |
|---|---|
'dsl' | Tokenizer / statement-parser issue — unknown statement, malformed attr. |
'json' | The JSON5 parser rejected a doc { } or icons: body. |
'check' | Integrity: unknown connector target, disjoint region, etc. |
Parse errors are surfaced before the def is assembled; an errored def may still be partially usable for quick-look previews, but don't render one in production without handling errors.
GgError shape
interface GgError {
message: string
line: number // 1-based
source: 'dsl' | 'json' | 'check' | 'icon'
snippet?: string // the offending source line
related?: { line: number; source: GgErrorSource; snippet?: string }
}Use formatError for a readable string:
import { formatError } from 'gridgram'
for (const e of errors) console.error(formatError(e, filename))
// Error: Unknown statement "icn"
// at diagram.gg:3 (DSL: icn :a @A1 "hello")The icons field
A .gg file can register inline icon sources directly:
doc {
icons: {
logo: 'https://example.com/logo.svg',
brand: './assets/brand.svg',
},
}
icon @A1 logo "Us"
icon @B1 brand "You"parseGg returns those as icons: Record<string, string> alongside the def. The resolver consumes it via IconContext.inline (for raw SVG strings) or via buildIconContext's jsonIconsMap for URL / file / dataURL values that need loading. Keeps the source-time icon references off the DiagramDef type (which models the rendered diagram, not its provenance).
resolveDiagramIcons(def, ctx)
import { resolveDiagramIcons } from 'gridgram'
function resolveDiagramIcons(
def: DiagramDef,
ctx: IconContext,
): { def: DiagramDef; diagnostics: PlacementDiagnostic[] }Replaces every string-valued node.src with its resolved SVG fragment. Nodes whose src couldn't be resolved get iconError: true and their src deleted. Each of those failures emits a PlacementDiagnostic with:
kind: 'icon-unresolved'iconSrc: the original DSL identifiericonReason:'not-found'(tabler miss / unregistered) or'load-failed'(the loader tried and errored)
Callers usually concat these with the render-time diagnostics to hand an agent one stream:
const iconResolve = resolveDiagramIcons(rawDef, ctx)
const rendered = renderDiagram(iconResolve.def)
const allDiagnostics = [...iconResolve.diagnostics, ...rendered.diagnostics]Pure, synchronous, browser-safe. ctx is just a plain object — you can construct it by hand (typical for in-browser use where only Tabler built-ins and pre-loaded inline SVG are in play) or let buildIconContext populate it for you in Node.
IconContext
interface IconContext {
inline?: Record<string, string> // from doc.icons / --icons map
dir?: Record<string, string> // from --icons <dir> basename
aliases?: Record<string, string> // from --alias / project config
paths?: Record<string, string> // pre-loaded external refs
errors?: GgError[] // non-fatal loader issues
failedSources?: Map<string, string> // identifier → reason
}Every field is optional. The simplest valid context — {} — resolves Tabler built-ins only and flags every other src as iconError.
buildIconContext(opts) — Node only
import { buildIconContext } from 'gridgram/node'
function buildIconContext(opts: {
iconsDir?: string
jsonIconsMap?: Record<string, string>
aliases?: Record<string, string>
def?: DiagramDef
docDir: string // for cwd-relative paths AND for the icons map
aliasDir?: string // defaults to docDir
}): Promise<IconContext>Walks the def for every external-path icon reference and pre-reads it (filesystem or HTTP). Returns an IconContext the resolver consumes. Non-fatal per-icon failures end up on ctx.errors (as GgError) and ctx.failedSources (map keyed by the DSL identifier).
import { buildIconContext } from 'gridgram/node'
const ctx = await buildIconContext({
iconsDir: settings.iconsDir, // --icons <dir>
jsonIconsMap: parseResult.icons, // from parseGg
aliases: settings.assetAliases, // from project config
def: rawDef,
docDir: dirname(sourcePath),
aliasDir: process.cwd(),
})
// Surface per-icon loader errors early
for (const err of ctx.errors ?? []) console.error(formatError(err, sourcePath))This import fails in browser bundles. In the browser, either skip external icon references entirely (Tabler only) or pre-load them in your host code and hand the result to resolveDiagramIcons via ctx.inline / ctx.paths.
Path reference resolution
| DSL form | Where it resolves |
|---|---|
'@brand/aws.svg' | aliases.brand + '/aws.svg' (absolute if aliases.brand is absolute; else joined with aliasDir) |
'./foo.svg' | docDir + '/foo.svg' |
'/abs/path.svg' | as-is |
'foo.svg' | docDir + '/foo.svg' |
checkIntegrity(def)
import { checkIntegrity } from 'gridgram'
function checkIntegrity(def: DiagramDef): GgError[]Post-parse checks that parseGg runs for you automatically:
- Connector
from/toreference known node ids - Note
targetsreference known node / connector ids - Region spans fit within
columns × rows - Region spans form a single 4-connected shape
- Coordinate malformations (bad A1, col < 1, …)
Exposed separately so programmatic callers who build a DiagramDef from scratch (skipping parseGg) can still validate it. Error messages use 1-based A1 coordinates — "A1-J10 exceeds A1-B2 grid" — so agents don't need to decode the internal 0-based form.
Icon classification helpers
Also exported from 'gridgram' and safe in the browser:
import { isPathRef, collectPathRefs, stripSvgWrapper } from 'gridgram'
isPathRef('@brand/aws.svg') // true — needs external loading
isPathRef('tabler/user') // false — Tabler built-in
isPathRef('logo') // false — bare name → icon map
collectPathRefs(def) // string[] of every node.src / badge.icon
// that looks like an external path
stripSvgWrapper('<svg …>…</svg>') // '…' — keep the inner fragmentcollectPathRefs is what buildIconContext uses to discover every external file it needs to pre-read. Reach for it if you're implementing your own async loader (e.g. resolving paths through a storage adapter instead of fs).
See also
- Types —
DiagramDefand its members. - Diagnostics — consumer-side view of the icon-unresolved stream.
- User Guide: CLI — same parsing from the CLI side.