Add compact JSON formatter with format-on-paste (§03A)

This commit is contained in:
2026-06-07 18:45:17 +03:00
parent a62e2c6128
commit 9f7bf27b7a
6 changed files with 148 additions and 0 deletions
+41
View File
@@ -0,0 +1,41 @@
/**
* JSON pretty-printer for Vega-Lite specs (spec §03A; docs/architecture/08 →
* "compact formatter").
*
* Uses `json-stringify-pretty-compact` for Vega's house style — arrays/objects
* stay on one line until they exceed a width budget, then wrap — which reads far
* better for VL specs than `JSON.stringify(…, null, 2)`. Pure and portable: no
* Monaco, no browser. The Monaco formatting adapter
* (`infrastructure/monaco-format`) is a thin wrapper over this.
*/
import stringify from 'json-stringify-pretty-compact';
/** Width budget before an array/object wraps onto multiple lines. */
export const DEFAULT_MAX_LINE = 80;
/** Default indent (spaces) — matches the editor's default tab size (spec §07). */
const DEFAULT_INDENT = 2;
/**
* Reformat `text` as consistently-indented JSON, or return `null` when it is not
* valid JSON. Returning `null` (rather than throwing) lets callers skip a no-op
* rather than mangle an in-progress edit, mirroring auto-save's "skip when
* unparseable" rule (spec §03B). The result is idempotent: formatting
* already-formatted text returns it unchanged.
*/
export function formatJson(
text: string,
opts: { indent?: number; maxLength?: number } = {},
): string | null {
let parsed: unknown;
try {
parsed = JSON.parse(text);
} catch {
return null;
}
return stringify(parsed, {
indent: opts.indent ?? DEFAULT_INDENT,
maxLength: opts.maxLength ?? DEFAULT_MAX_LINE,
});
}