diff --git a/.changeset/calm-empty-editor.md b/.changeset/calm-empty-editor.md new file mode 100644 index 0000000..6e7a73c --- /dev/null +++ b/.changeset/calm-empty-editor.md @@ -0,0 +1,11 @@ +--- +"@qorpe/ui": patch +--- + +fix(json-editor): an empty document is never a lint error + +The JSON linter flagged an empty editor as a parse error — a red dot on line 1 of +nothing. A blank editor is an invitation, not a mistake; the linter now answers no +diagnostics for a zero-length document and stays exactly as strict the moment content +appears. Consumers no longer need to toggle `lint` off for the empty state — which +also recreated the editor mid-typing, since `lint` participates in the setup effect. diff --git a/src/json-editor/index.tsx b/src/json-editor/index.tsx index a34b4a8..6cd1736 100644 --- a/src/json-editor/index.tsx +++ b/src/json-editor/index.tsx @@ -51,8 +51,12 @@ const extensions = (readOnly: boolean, onChange: ((v: string) => void) | undefin bracketMatching(), keymap.of([...defaultKeymap, ...historyKeymap, ...foldKeymap, indentWithTab]), json(), - // Bodies may hold template syntax ({{…}}), so linting is opt-out — no false parse errors. - ...(lint ? [linter(jsonParseLinter()), lintGutter()] : []), + // Bodies may hold template syntax ({{…}}), so linting is opt-out — no false parse + // errors. An EMPTY document is never an error either: a blank editor is an invitation, + // and a red parse dot on line 1 of nothing teaches only confusion. + ...(lint + ? [linter((view) => (view.state.doc.length === 0 ? [] : jsonParseLinter()(view))), lintGutter()] + : []), syntaxHighlighting(highlight), theme, EditorState.readOnly.of(readOnly), diff --git a/src/json-editor/json-editor.test.tsx b/src/json-editor/json-editor.test.tsx index b1ca402..b8bf366 100644 --- a/src/json-editor/json-editor.test.tsx +++ b/src/json-editor/json-editor.test.tsx @@ -17,6 +17,17 @@ describe("the json editor subpath (RFC D6) — CodeMirror stays out of the main expect(container.textContent).not.toContain('"a"'); }); + it("an EMPTY document is never a lint error — broken JSON still is", async () => { + // A blank editor is an invitation, not a mistake: no red dot on line 1 of nothing. + const empty = render(); + const broken = render(); + // CodeMirror's linter runs on a delay; the broken editor's marker is the clock. + await vi.waitFor(() => { + expect(broken.container.querySelector(".cm-lint-marker")).toBeTruthy(); + }, { timeout: 3000 }); + expect(empty.container.querySelector(".cm-lint-marker")).toBeNull(); + }); + it("Beautify pretty-prints valid JSON and leaves templated bodies untouched", async () => { const onChange = vi.fn(); render();