The JSON form of a report. It carries everything the text report shows, as data, so a host, an editor or an agent tool can read it without parsing text. The schema is docs/diagnostic-report.schema.json (JSON Schema 2020-12, $id urn:oneecma:diagnostic-report:1). The writer is DiagnosticJson; the CLI prints it with oneecma check --format json <file> (on standard output; the text form goes to standard error).
Rules
- Versioned. The top-level
"version"is1. Within a version, fields are only ever added. A consumer ignores properties it does not know. A change that removes, renames or re-types a field, or changes what a value means, is a new version. - Offsets and positions.
startandendare offsets in UTF-16 code units into the source text, half-open (endis one past the last code unit).lineandcolumnare 1-based; columns count UTF-16 code units (a character outside the Basic Multilingual Plane is two columns), the same unit V8 uses inerr.stack.endLineandendColumnare the position ofend. A line ends at LF, CR, CRLF, U+2028 or U+2029. An empty span hasstart == end. - Strings. Output is UTF-8. A lone UTF-16 surrogate in the script source (legal in JavaScript, not representable in JSON) is written as U+FFFD. Offsets still count the original code units.
- Absent versus empty. Core arrays (
secondary,notes,help,fixes,recovery) are always present and may be empty. Optional members (the ones marked optional below) are left out when there is nothing to say, never written asnullor an empty array. - Deterministic. The same diagnostics give the same bytes. Property order is as listed here. Compact by default (one line);
JsonRenderOptions.Indentedgives two-space indentation (used by the corpus files).
Document
{ "version": 1, "format": "oneecma.report", "summary": { "errors": 1, "warnings": 0, "repairs": 0 }, "repairs": [ ... ], // optional: applied repairs, first to last "repairedSource": "...", // optional: present exactly when "repairs" is "diagnostics": [ ... ] }
summary.errors and summary.warnings count the diagnostics by severity; summary.repairs counts the applied repairs. Repairs are listed before diagnostics because a repaired run's report starts with what was changed.
Diagnostic
| field | type | meaning |
|---|---|---|
kind | string | error constructor name (SyntaxError, ...), or SourceMap for a problem with a source map |
code | string | stable machine-readable id such as unexpected-token; never reworded once published |
severity | "error" | "warning" | |
headline | string | word for word what V8 puts in err.message for the same mistake (for SourceMap diagnostics, our own wording) |
source | { name, length } | the source every span below refers to; length in code units |
span | span | the primary span; the --> line of the text report is its start |
label | string | the text beside the primary underline; may be empty |
secondary | [{ span, message }] | secondary labelled spans (underlined with - in text) |
notes | string[] | the = note: lines |
help | string[] | the = help: lines |
fixes | [{ description, edits }] | suggested changes; none is applied. An edit is { start, length, line, column, text }: the range [start, start+length) becomes text |
recovery | [{ action, span, text }] | what the parser assumed (insertedToken, text is the token) or skipped (skippedTokens) to continue |
mapping | object, optional | present when a source map was applied, see below |
A span is { start, end, line, column, endLine, endColumn }.
Repair
{ id, title, label, source, span, edits }: the rule id from the repair table (R001), what it did, the underline text, the original source, the first place it touched and every edit it made (offsets into the original source). Applying all repairs' edits in the order listed to the original source gives repairedSource.
mapping (optional)
Added by SourceMapReports when a mapping exists for the primary span:
"mapping": { "spansAreOriginal": true, "original": { "source": "src/app.ts", "line": 5, "column": 42, "endLine": 5, "endColumn": 43, "name": "greet" }, "generated": { "source": "dist/app.js", "line": 5, "column": 34, "endLine": 5, "endColumn": 35 }, "generatedSpan": { "start": 107, "end": 108 } }
spansAreOriginal: true: the original text was available, sosourceand every span of the diagnostic are in the original source;generatedandgeneratedSpansay where the problem is in the generated code. Suggested fixes and recovery steps (offsets into the generated text) are dropped.spansAreOriginal: false: the map has an entry but no text for the original source;sourceand spans still refer to the generated code, andoriginalsays where the problem is in the original file (the text report adds anoriginal position:note).name(optional) is the identifier the map names at that position.
Runtime explanation members (optional)
A runtime report built by a traced replay (the default for an uncaught error when recording is on) writes these members; each is present only if non-empty. Shapes are in the schema. Version 1 stays: they were defined as optional slots from the start and nothing changed shape.
| field | content |
|---|---|
stack | frames, innermost first: { function, source, span, async, locals: [{ name, value }] }. locals lists the bindings of the frame's function scope (script top level: the script's let/const/class bindings), innermost scope first, at most 24 per frame, each value a preview of at most 60 characters ({ id, lineItems, total } for an object: its first 6 own enumerable keys; Array (3 items); literals for primitives). Only present on a traced report. |
provenance | where the failing value came from, nearest step first: { source, span, description }. Each step is the span of an expression a binding or property was written from (const order = await api.getOrder(id)), followed by the earlier places the same object was stored; the chain is capped at 8 steps and a note says so when it is cut. The same spans are the report's secondary labels. |
didYouMean | candidates drawn from what the object or scope actually holds, best first: { name, description }. A candidate is a key of the object, a binding visible at the failing place, or a key whose value has the member being called (order.lineItems, result.items.map); ranking is case-insensitive equality, then substring either way, then optimal-string-alignment distance up to max(2, length/3). Never a name the object or scope does not hold. |
asyncStack | reserved, not written: frames stitched across await are in stack with async: true |
Files
testdata/golden/<case>/expected.json is the report for each syn-* case as DiagnosticJson.Render(diagnostics, Indented) plus a newline, and for map-001-* the remapped report. A test reads each back and checks its headlines, spans and counts against expected.txt and against an independent line/column count.