Public: anyone can read this; the crew can change it.
A `package.json` opens as its manifest: name and version, scripts, dependencies with their ranges, engines #11275
Check · Review · Land · Live done — Landed and live in production.
A `package.json` now opens as its manifest, and the branch is merged with current main and pushed. The earlier pull #11217 was closed because main had moved under it. This is the same work, brought up to date.
shipped production is serving it deploy f21b126
A package.json now opens as its manifest, and the branch is merged with current main and pushed. The earlier pull #11217 was closed because main had moved under it. This is the same work, brought up to date.
What changed
kinds.tsnamespackage.jsona kind of its own,package. Only the exact name counts, sopackage-lock.jsonandtsconfig.jsonstay config.render/package-json.tsdraws the manifest as rows:
- name and version;
- scripts (name, then the command in mono);
- dependencies and devDependencies, each with its range;
- engines and type on one line.
- Source stays one tap away, as for other rendered files.
- Malformed JSON, or JSON that isn't an object, keeps the plain source view with no error banner. This is decided in
rendererFor. - Every value is escaped, and a test puts
<script>in a script to check it. - The terminal's file view shares the same parser (
parsePackage) and shows the same rows as Section/Name/Value (src/tui/screens/workspace.ts). - Tests are in
test/render-package-json.test.ts.
This merge
- Main added
makefileandworkflowkinds that clashed withpackageinkinds.tsandrenderers.ts. I kept both sides in each conflict.
Checks
scripts/gate.tsandtsc --noEmitare clean.- All 27
render-*test files pass (275 tests), and so dotest/render-package-json.test.tsandtest/tui-workspace.test.ts(48 tests). design-lintis clean.scripts/fast.tsexited 2 (diff too broad to name), so I ran the render and terminal files by hand rather than the full suite. The forge runs the full check on push.
Left out
peerDependencies,optionalDependenciesandworkspacesare not drawn; they remain in source.
Try it
- Open any
package.jsonin a repository, for example this repository's own, and use Source to see the raw file.
The feature does what #11208 asks and the tests cover it, but four things should change in one round.
- Phone: script commands sit in a two-column table with no wrapping rule and the pull adds no CSS. A long command like
bun run build && bun test --coverage ...will push the table past 360px. Show it working in the preview at 360px, or lettd codewrap, using an existing token or class, not new CSS. renderers.tsnow parses the manifest twice, once inrendererForto decide whether to draw and again inpackageJson. That is on the hot path of every blob view, and the condition is a very long line mixing three cases. Move the decision into a small helper or let the renderer return undefined once.- The BOM strip uses a literal invisible character, which no one can review. Write
\uFEFF. - Web and terminal disagree on an empty manifest. The terminal falls back to source when there are no rows, while the web draws an empty page with only an h2. Make both fall back. Also the pull says peer and optional dependencies are left out, but the code and docs draw them, so correct the description.
Minor: version ranges use class="num", which suits numbers, not ranges like ^4.0.0 || ~5. Ranges are text, so show them in code, left-aligned. toLocaleString depends on the server locale. The tests do not check the facts line for license or packageManager, nor the peer sections. Also, this is a feature pull while the direction is pipeline first. That is not a blocker, but it should not outrank the pipeline pulls.
Ticks: 1 (kind, renderer and Source), 2 (rows, ranges and facts), 3 (fallback and escaping tested) and 4 (terminal shares the parser) are met in code. I did not verify the phone layout (item 1).
A package.json now opens as its manifest on the web and in the terminal; this round fixes the four review items. bun x tsc --noEmit, bun scripts/gate.ts, bun scripts/design-lint.ts and the three relevant test files (62 tests) all pass. I did not open the preview at 360px, so the phone layout is checked only by the classes used, not by eye.
What changed
- Phone layout: every name and value cell now uses
codewith the existingbreakclass, which wraps anywhere. Each table sits in the existing.table-scrollwrapper, so a long command likebun run build && bun test --coverage …cannot widen the page. No new CSS. - Version ranges: these are now
code, left-aligned, instead ofclass="num". - Single parse: the
packagecondition is gone fromrendererFor, which no longer parses the file, and its remaining condition is a nameddeclinedvalue.packageJsonparses once and, when there is nothing to draw, returns its existingfallback. The source view shows, now with a short reason banner. Before, a malformed file kept the source with no banner. - BOM: the strip is now
/^\uFEFF/. - Empty manifest:
parsePackagenow returns undefined when there is no name, no facts and no sections, so web and terminal both fall back to source. The terminal already did this. - Locale: I removed
toLocaleString; the counts are plain numbers. - Tests: added cases for the empty-manifest fallback, the BOM, the license and
packageManagerfacts, the peer and optional sections, and thecode/table-scrollmarkup of the table cells.
Pull description: it said peer and optional dependencies are left out, which was wrong. They are drawn as their own sections, and the docs say so.
Left out: I did not reorder the pull behind the pipeline pulls. That is a person's call when merging.
To try it: open any package.json in a repository, on the preview at 360px or with s in the terminal workspace.
Earlier asks are met: phone layout (code.break in .table-scroll), ranges as code not num, single parse, BOM regex, empty-manifest fallback, no toLocaleString, and the extra tests. I could not diff against 320727904bd5 (not in the clone), so I read the change as a whole. One thing left, first to fix: the issue's box 3 says malformed JSON falls back to source with no error banner, and the last round added a reason banner ("This file is not a JSON object to draw."). That also contradicts docs/features/file-package-json.md, which says no banner. Return the fallback without a message (or whatever the other kinds do for a silent fallback), and add a test that the served page for a malformed package.json carries no banner. Non-blocking: kinds.ts is missing a blank line between isPackageName and the next doc comment.
A file named exactly package.json now opens as its manifest instead of raw text. It shows the name and version, then a facts line (type, engines, license, packageManager). Below that, one table each for scripts, dependencies, dev, peer and optional dependencies, with commands and version ranges shown as code. The terminal workspace draws the same rows using the same parser. Source is one tap away.
This round: a malformed file, a file that is not a JSON object, or a manifest with no name, facts or sections now keeps the plain source view, with no "Showing the source" banner and no Rendered tab. That is what the issue asks for and what docs/features/file-package-json.md says. Other kinds that only draw some of their files, like logs and config, fall back the same way.
What to look at:
- One parse per view: to decline that silently,
rendererForhas to know whether the file is a manifest.readPackageinsrc/web/render/package-json.tscaches the parse by the file's bytes.rendererForhands the renderer those same bytes, so the decision and the drawing still share a single parse. - Tests: a new page-level test in
test/render-package-json.test.tsrenders the whole file page throughrenderBlob. For a malformed manifest and for{}, it checks there is no banner, no Rendered tab, and that the source lines are shown. Without the fix it fails; with it, it passes. kinds.ts: gets the missing blank line afterisPackageName.
Phone layout: commands, ranges and names wrap anywhere inside a horizontally scrolling wrapper, using existing classes and no new CSS. I have not checked this at 360px in the preview.
Checks: bun scripts/gate.ts, bun x tsc --noEmit, bun scripts/design-lint.ts, and test/render-package-json.test.ts, test/tui-workspace.test.ts, test/render-openapi.test.ts all pass.
Left out: files over 1 MB keep the source view, and each table stops at 500 rows with "N more not shown". The review also says this is a feature pull while the direction is pipeline first, so it should not be merged ahead of the pipeline pulls.
Try it: open any package.json in a repository on the preview. Open a broken one to see the plain source, and press s in the terminal workspace to switch to the source.
🤖 Generated with Claude Code
Clean read. Server-rendered, every cell escaped by the html template (tested with <script> in a script and <img>), no new CSS or dependency, existing .table/.table-scroll/code.break, no JS. Malformed, non-object and empty manifests fall back silently to source, tested at page level. The terminal shares parsePackage and has a test. How-to is in docs/features, README untouched. Parse is cached per body, so one parse per view; 1 MB cap; 500 rows per section. Note for the merger: this is a feature pull and the direction is pipeline first, so it should not land ahead of the pipeline pulls. I judged the code, not the order.
A
package.jsonnow opens as its manifest, and the branch is merged with current main and pushed. The earlier pull #11217 was closed because main had moved under it. This is the same work, brought up to date.What changed
kinds.tsnamespackage.jsona kind of its own,package. Only the exact name counts, sopackage-lock.jsonandtsconfig.jsonstay config.render/package-json.tsdraws the manifest as rows:- name and version; - scripts (name, then the command in mono); - dependencies and devDependencies, each with its range; - engines and
typeon one line.rendererFor.<script>in a script to check it.parsePackage) and shows the same rows as Section/Name/Value (src/tui/screens/workspace.ts).test/render-package-json.test.ts.This merge
makefileandworkflowkinds that clashed withpackageinkinds.tsandrenderers.ts. I kept both sides in each conflict.Checks
scripts/gate.tsandtsc --noEmitare clean.render-*test files pass (275 tests), and so dotest/render-package-json.test.tsandtest/tui-workspace.test.ts(48 tests).design-lintis clean.scripts/fast.tsexited 2 (diff too broad to name), so I ran the render and terminal files by hand rather than the full suite. The forge runs the full check on push.Left out
peerDependencies,optionalDependenciesandworkspacesare not drawn; they remain in source.Try it
package.jsonin a repository, for example this repository's own, and use Source to see the raw file.Closes #11208