Public: anyone can read this; the crew can change it.
A JSON Lines file (.jsonl, .ndjson) renders as a table, as CSV does #10322
Check · Review · Land · Live done — Landed and live in production.
`.jsonl` and `.ndjson` files now render as a table through the CSV view. The new tests and `tsc` pass, and `scripts/gate.ts` printed nothing. I did not run the full suite or open a file in a running preview.
shipped production is serving it deploy f21b126
.jsonl and .ndjson files now render as a table through the CSV view. The new tests and tsc pass, and scripts/gate.ts printed nothing. I did not run the full suite or open a file in a running preview.
- Kinds: both extensions are the
csvkind insrc/web/render/kinds.ts, so they get the same sort and filter script as CSV.src/web/render/csv.tssends them to a new parser,src/web/render/jsonl-parse.ts. - Columns and cells: the columns are the union of the keys of the first 1,000 objects, in first-seen order. Strings show as they are and
nullshows empty. Nested values show as compact JSON, escaped like every other cell. - Bad lines: a line that is not valid JSON, or is valid JSON but not an object, becomes one row reading
Line N: not valid JSONorLine N: not a JSON object. N is the physical line number, blank lines included. The rest of the file still renders. - Edge cases: a file with no non-blank lines falls back to the source view with "The file has no rows." If no line is an object, the table has a single column called
line. - Header: it reads "N rows × M columns · JSON Lines · size".
- Tests:
test/render-csv.test.tscovers mixed keys, a bad line, a<script>value arriving escaped, a non-object line and an empty file.test/render-kinds.test.tshas the two new extensions.
I did not add per-key type detection, so JSON numbers are not right-aligned the way CSV numbers are. Columns are not capped for JSONL; the cell cap still bounds the rows drawn, but a file with an enormous number of distinct keys would make a very wide header. There are no new dependencies.
To try it, open any .jsonl file in a repository on the preview. For example, three lines of {"a":1}, {"b":"<script>"} and oops give a 3-row table with a Line 3 row.
Two commits went in because the repo's pre-push hook wants a Try: line in a commit message. The first push was rejected for that, so the Try: line is in the second commit.
🤖 Generated with Claude Code
The change works and is tidy, but it ships without its how-to. Fix first: docs/features/csv-view.md says nothing about .jsonl/.ndjson. Add a short section (columns = union of first 1,000 objects' keys in first-seen order; nested values as compact JSON; a bad line is a row 'Line N: not valid JSON' / 'not a JSON object'; empty file falls back to source). Do not put it in README. Also: (1) parseJsonl builds a header and a full-width row per kept line for every distinct key, though csv() draws only COLUMN_CAP columns; cap the header at COLUMN_CAP in the parser (keep parsed.columns the true count so the 'Showing N of M columns' note still shows) so a file with huge key sets cannot allocate wide rows. (2) The ticked-off test does not cover a row beyond the 1,000-object key window (a key first seen on object 1,001 must not add a column); add one, since that is the behaviour the issue names. Principles 1-8 in the design: no new markup, reuses the CSV card/table, nothing to flag. Terminal door: CSV has no TUI screen either, so I do not hold this to one.
All three review items are done and pushed.
- How-to:
docs/features/csv-view.mdhas a new "JSON Lines" section. It covers the columns rule, how nested values show, theLine N: not valid JSONandLine N: not a JSON objectrows, the source fallback for an empty file, and the 200-column draw limit. The README is untouched. - Column cap:
parseJsonlnow builds the header and every row at no more thanCOLUMN_CAP(200) columns, andcsv.tspasses that cap in.columnsstill holds the true key count, so "Showing 200 of 205 columns" still appears. - Key-window test: a key first seen on object 1,001 is not a column. A second test with
COLUMN_CAP + 5keys checks the cap and the "Showing N of M columns" note.
bun test on test/render-csv.test.ts and test/render-kinds.test.ts passes, and tsc is clean. scripts/gate.ts printed nothing. I did not run the full suite.
The intermediate push, 07df0ca20, had a failing test of mine: it matched <th>a</th>, but numeric headers carry class="num". The last commit fixes the match, so the branch tip is green.
🤖 Generated with Claude Code
One bug to fix: a cell reads inherited properties. Everything else (kinds, escaping, bad-line rows, key window, column cap, how-to in docs/features/csv-view.md, no README change) looks right. I did not exercise the preview.
JSON Lines files (.jsonl, .ndjson) now show as a table through the CSV view. Each object is one row. The columns are all the keys found in the first 1,000 objects, in the order they first appear. Nested values show as compact JSON and null shows as an empty cell. A line that doesn't parse, or isn't an object, becomes one row with its line number ("Line 3: not valid JSON"), and the rest of the file still shows. The table only builds as many columns as the page can draw, and the summary still gives the full count.
This push fixes the review finding: a row that lacked a key could show a value inherited from JavaScript objects. If one line had a key like constructor, toString or __proto__, every row without that key showed function Object() { [native code] } or {} instead of an empty cell. The cell now reads the value only when the row's own object has that key (Object.hasOwn), in src/web/render/jsonl-parse.ts:61.
The new test in test/render-csv.test.ts parses {"constructor":1,"toString":2,"__proto__":3} and then {"a":1}. It checks that the second row's first three cells are empty and that the first row keeps its own values. It fails without the fix and passes with it. bun scripts/gate.ts, bun x tsc --noEmit and bun test test/render-csv.test.ts (22 pass) all pass. I did not check the preview.
To try it, open any .jsonl or .ndjson file on a blob page. docs/features/csv-view.md has the details.
🤖 Generated with Claude Code
The earlier ask is met: a missing key is now an empty cell. jsonl-parse.ts reads a value only when the row has its own key (Object.hasOwn), and the new test covers constructor, toString and __proto__. The how-to is in docs/features/csv-view.md and the README is untouched. Columns are capped at 200, escaping holds, and the preview answers 200. Next, non-blocking: (1) right-align numeric JSON columns as CSV does; (2) give a file with no object lines a clearer header than the single column called line.
.jsonland.ndjsonfiles now render as a table through the CSV view. The new tests andtscpass, andscripts/gate.tsprinted nothing. I did not run the full suite or open a file in a running preview.csvkind insrc/web/render/kinds.ts, so they get the same sort and filter script as CSV.src/web/render/csv.tssends them to a new parser,src/web/render/jsonl-parse.ts.nullshows empty. Nested values show as compact JSON, escaped like every other cell.Line N: not valid JSONorLine N: not a JSON object. N is the physical line number, blank lines included. The rest of the file still renders.line.test/render-csv.test.tscovers mixed keys, a bad line, a<script>value arriving escaped, a non-object line and an empty file.test/render-kinds.test.tshas the two new extensions.I did not add per-key type detection, so JSON numbers are not right-aligned the way CSV numbers are. Columns are not capped for JSONL; the cell cap still bounds the rows drawn, but a file with an enormous number of distinct keys would make a very wide header. There are no new dependencies.
To try it, open any
.jsonlfile in a repository on the preview. For example, three lines of{"a":1},{"b":"<script>"}andoopsgive a 3-row table with aLine 3row.Two commits went in because the repo's pre-push hook wants a
Try:line in a commit message. The first push was rejected for that, so theTry:line is in the second commit.🤖 Generated with Claude Code
Closes #10285