Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/calm-empty-editor.md
Original file line number Diff line number Diff line change
@@ -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.
8 changes: 6 additions & 2 deletions src/json-editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
11 changes: 11 additions & 0 deletions src/json-editor/json-editor.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(<JsonEditor value="" lint />);
const broken = render(<JsonEditor value='{"a":' lint />);
// 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(<JsonField value='{"a":1}' onChange={onChange} />);
Expand Down
Loading