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

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

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

opened this

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.ts names package.json a kind of its own, package. Only the exact name counts, so package-lock.json and tsconfig.json stay config.
  • render/package-json.ts draws 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 makefile and workflow kinds that clashed with package in kinds.ts and renderers.ts. I kept both sides in each conflict.

Checks

  • scripts/gate.ts and tsc --noEmit are clean.
  • All 27 render-* test files pass (275 tests), and so do test/render-package-json.test.ts and test/tui-workspace.test.ts (48 tests).
  • design-lint is clean.
  • scripts/fast.ts exited 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, optionalDependencies and workspaces are not drawn; they remain in source.

Try it

  • Open any package.json in a repository, for example this repository's own, and use Source to see the raw file.

Closes #11208

commented

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.ts names package.json a kind of its own, package. Only the exact name counts, so package-lock.json and tsconfig.json stay config.
  • render/package-json.ts draws 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 makefile and workflow kinds that clashed with package in kinds.ts and renderers.ts. I kept both sides in each conflict.

Checks

  • scripts/gate.ts and tsc --noEmit are clean.
  • All 27 render-* test files pass (275 tests), and so do test/render-package-json.test.ts and test/tui-workspace.test.ts (48 tests).
  • design-lint is clean.
  • scripts/fast.ts exited 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, optionalDependencies and workspaces are not drawn; they remain in source.

Try it

  • Open any package.json in a repository, for example this repository's own, and use Source to see the raw file.
Changes requested — before the latest push

The feature does what #11208 asks and the tests cover it, but four things should change in one round.

  1. 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 let td code wrap, using an existing token or class, not new CSS.
  2. renderers.ts now parses the manifest twice, once in rendererFor to decide whether to draw and again in packageJson. 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.
  3. The BOM strip uses a literal invisible character, which no one can review. Write \uFEFF.
  4. 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).

commented

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 code with the existing break class, which wraps anywhere. Each table sits in the existing .table-scroll wrapper, so a long command like bun run build && bun test --coverage … cannot widen the page. No new CSS.
  • Version ranges: these are now code, left-aligned, instead of class="num".
  • Single parse: the package condition is gone from rendererFor, which no longer parses the file, and its remaining condition is a named declined value. packageJson parses once and, when there is nothing to draw, returns its existing fallback. 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: parsePackage now 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 packageManager facts, the peer and optional sections, and the code/table-scroll markup 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.

Changes requested — before the latest push

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.

commented

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, rendererFor has to know whether the file is a manifest. readPackage in src/web/render/package-json.ts caches the parse by the file's bytes. rendererFor hands 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.ts renders the whole file page through renderBlob. 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 after isPackageName.

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

Approved

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.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs