Skip to content

feat: render docs for multi-entry packages#2964

Open
maraisr wants to merge 12 commits into
npmx-dev:mainfrom
maraisr:maraisr/multi-entry-package-docs
Open

feat: render docs for multi-entry packages#2964
maraisr wants to merge 12 commits into
npmx-dev:mainfrom
maraisr:maraisr/multi-entry-package-docs

Conversation

@maraisr

@maraisr maraisr commented Jun 26, 2026

Copy link
Copy Markdown

🔗 Linked issue

None yet, happy to open a tracking issue if that's preferred.

🧭 Context

Some packages don't ship a single root entry. My library tctx is one of them. It only exposes submodule exports:

"exports": {
  "./traceparent": { "types": "./traceparent.d.mts", "default": "./traceparent.mjs" },
  "./tracestate": { "types": "./tracestate.d.mts", "default": "./tracestate.mjs" },
  "./package.json": "./package.json"
}

Because the docs pipeline assumes a single root (. / index) entry, packages like this fall through and render nothing on the Docs tab. You can see it live today on npmx.dev/package-docs/tctx:

empty docs for tctx

📚 Description

This PR is really my attempt at enhancing the docs generator to resolve and render multiple entry points instead of assuming one root module.

I can now see my docs rendering correctly! 🎉

With this, tctx now has real docs. one group per submodule (traceparent, tracestate):

tctx Docs tab with this PR — grouped submodule docs

It also handles the common case of a package that exports a root and submodules together, eg unstorage (. + ./server):

unstorage Docs tab. root + ./server grouped

Scope & follow-ups

I deliberately kept this slice small so it's easy to reason about:

  • Wildcard submodules (e.g. "./foo/*") aren't solved yet. I'd love to iterate toward that, but wanted to land something useful first rather than block on the harder cases.

And a small sneak peek of what I've got working locally for a follow-up PR, rendering @module docs too:

@module description + example rendered above an entry's symbols

A note on landing this

I genuinely don't know what the right rollout or shape here is, and I'm very open to feedback on the approach, the entry resolution, and especially the grouped-docs design (headings, ordering, how multi-entry should feel). Happy to chat it through and iterate; please push back wherever it makes sense. 🙏

@vercel

vercel Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
npmx.dev Ready Ready Preview, Comment Jun 26, 2026 1:47pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Preview Jun 26, 2026 1:47pm
npmx-lunaria Ignored Ignored Jun 26, 2026 1:47pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Documentation generation now resolves package entry points, fetches doc nodes per entry, and renders grouped documentation sections and TOCs with prefix-aware anchors. Shared entry/result types, slugging helpers, and unit tests were updated for the per-entry output shape.

Changes

Grouped package docs

Layer / File(s) Summary
Entry contract and resolution
shared/types/deno-doc.ts, server/utils/docs/client.ts, test/unit/server/utils/docs/client.spec.ts
DenoDocResult now exposes entries, and getModules() / getDocNodes() resolve package exports into per-entry type URLs before fetching docs per entry. The client tests cover the resolution and fallback cases.
Prefix-aware ids
server/utils/docs/types.ts, server/utils/docs/text.ts, server/utils/docs/processing.ts, test/unit/server/utils/docs/text.spec.ts
ProcessedEntry now carries per-entry nodes, symbols, and lookups; createSymbolId() accepts an optional prefix, entrySlug() derives entry slugs, and buildSymbolLookup() uses the prefix-aware IDs. The text tests cover the updated ID and slug helpers.
Grouped rendering and generation
server/utils/docs/render.ts, server/utils/docs/index.ts, app/pages/package-docs/[...path].vue, test/unit/server/utils/docs/render.spec.ts
Rendering and TOC generation now use prefix-aware section and symbol IDs, wrap non-root entries in grouped sections, and switch generateDocsWithDeno() to flat or grouped rendering based on the number of processed entries. The render tests cover grouped output and anchor links, and the page stylesheet adds spacing for group titles.

Suggested reviewers

  • danielroe
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarises the main change: rendering docs for packages with multiple entry points.
Description check ✅ Passed The description is clearly related and explains the multi-entry docs rendering change and its scope.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

Hello! Thank you for opening your first PR to npmx, @maraisr! 🚀

Here’s what will happen next:

  1. Our GitHub bots will run to check your changes.
    If they spot any issues you will see some error messages on this PR.
    Don’t hesitate to ask any questions if you’re not sure what these mean!

  2. In a few minutes, you’ll be able to see a preview of your changes on Vercel

  3. One or more of our maintainers will take a look and may ask you to make changes.
    We try to be responsive, but don’t worry if this takes a few days.

@codecov

codecov Bot commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 60.86957% with 36 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
server/utils/docs/client.ts 27.02% 18 Missing and 9 partials ⚠️
server/utils/docs/render.ts 83.33% 2 Missing and 4 partials ⚠️
server/utils/docs/text.ts 83.33% 0 Missing and 3 partials ⚠️

📢 Thoughts on this report? Let us know!

@maraisr maraisr marked this pull request as ready for review June 26, 2026 03:51
@coderabbitai coderabbitai Bot added the 007 This PR *may* not follow our code of conduct regarding AI usage. label Jun 26, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/pages/package-docs/`[...path].vue:
- Around line 289-295: The grouped TOC targets created by renderGroupedToc() are
missing the sticky-header scroll offset, so clicking a group anchor can hide the
title under the header. Update the docs page styles for the group target
selectors used by docs-group-title / `#group-`* so they receive the same offset
behavior already applied to .docs-symbol:target, while keeping the existing
first-group spacing override intact.

In `@server/utils/docs/client.ts`:
- Around line 29-64: The per-entry work in the docs client is unbounded:
resolveEntryPoints() then Promise.all() over every export can trigger too many
concurrent HEAD/doc() calls. Update client.ts to cap concurrency or process
entryPoints in small batches inside the main mapping flow, keeping the existing
entryPoint/typesUrl/doc() logic but limiting simultaneous upstream requests and
WASM parses. Ensure the final filtering of DocEntry results still behaves the
same.

In `@server/utils/docs/render.ts`:
- Around line 529-558: renderGroupedToc currently wraps each group with
renderToc(), which creates nested <nav> landmarks inside the top-level Table of
contents nav. Update renderGroupedToc and the renderToc helper so the grouped
view uses a non-nav inner list wrapper for entries, while keeping only the outer
renderGroupedToc landmark; use the existing renderToc symbol as the place to
split out a wrapper-free list renderer.
- Around line 74-99: The grouped-doc rendering flow is using a lossy entry
prefix from entrySlug(), which can make different entry points generate the same
ids and broken anchors. Update renderGroupedDocNodes and the downstream grouped
rendering helpers (including renderDocNodes and any section/symbol id generation
they call) to use a collision-safe, injective entry prefix instead of deriving
ids from the raw slug. Prefer computing a unique prefix once per ProcessedEntry
and threading it through the grouped render path so group, section, and symbol
anchors remain distinct.

In `@server/utils/docs/text.ts`:
- Around line 65-71: Wildcard entry points are being normalized by entrySlug()
into the same slug as concrete paths, which can create duplicate group, section,
and anchor IDs for unsupported exports. Update entrySlug() to either reject any
entryPoint containing “*” before slugging or preserve the wildcard in a distinct
slug form so these inputs fail explicitly instead of collapsing to the same
output as a non-wildcard path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7240fd3e-6f8d-41dd-bfcf-0d3a48ffaa31

📥 Commits

Reviewing files that changed from the base of the PR and between 15c4966 and 9cccfb8.

📒 Files selected for processing (10)
  • app/pages/package-docs/[...path].vue
  • server/utils/docs/client.ts
  • server/utils/docs/index.ts
  • server/utils/docs/processing.ts
  • server/utils/docs/render.ts
  • server/utils/docs/text.ts
  • server/utils/docs/types.ts
  • shared/types/deno-doc.ts
  • test/unit/server/utils/docs/render.spec.ts
  • test/unit/server/utils/docs/text.spec.ts

Comment thread app/pages/package-docs/[...path].vue
Comment thread server/utils/docs/client.ts
Comment thread server/utils/docs/render.ts
Comment thread server/utils/docs/render.ts
Comment thread server/utils/docs/text.ts
@danielroe

Copy link
Copy Markdown
Member

this is nice!

I wonder if we could distinguish these 'files' headings a bit, or make it more obvious that it's a subpath export:

image

e.g. tctx/tracestate as the heading?

@maraisr

maraisr commented Jun 26, 2026

Copy link
Copy Markdown
Author

Hey thanks for taking a look! ✨ I'll have another play, but have you got ideas? I didn't really like what jsr did with indentation, but perhaps simply some top margin there is enough to distinguish? I also trimmed the "./". Which maybe we can put back to better indicate that its a "submodule". Thoughts, ideas?

@gameroman gameroman removed the 007 This PR *may* not follow our code of conduct regarding AI usage. label Jun 26, 2026
@maraisr

maraisr commented Jun 26, 2026

Copy link
Copy Markdown
Author

What about something like this @danielroe?

CleanShot 2026-06-26 at 23 16 35@2x

Think the text mono on the headings is a little whimsical and fits better with the vibe of the site? Thoughts?

Added this too, so that it ellipsis, and on the left. Thinking long submodules like ./foo/bar/baz, the baz part is possibly a little more important. But we can play with it iteratively, like maybe we prune things like ./foo/bar/baz/index → ...bar/baz. Lets do that in a follow-up though, thoughts?

CleanShot 2026-06-26 at 23 37 17@2x

@danielroe

Copy link
Copy Markdown
Member

yes, I think that looks better ... it doesn't seem to show up in the preview yet though, if you've pushed it

@maraisr

maraisr commented Jun 26, 2026

Copy link
Copy Markdown
Author

I've definitely pushed. The latest preview deployment shows up for me 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants