Skip to content

refactor: parallelize docs rendering and Shiki setup#2382

Open
trivikr wants to merge 5 commits intonpmx-dev:mainfrom
trivikr:doc-gen-parallel
Open

refactor: parallelize docs rendering and Shiki setup#2382
trivikr wants to merge 5 commits intonpmx-dev:mainfrom
trivikr:doc-gen-parallel

Conversation

@trivikr
Copy link
Copy Markdown
Contributor

@trivikr trivikr commented Apr 5, 2026

🔗 Linked issue

N/A

🧭 Context

Docs generation was doing more sequential work than necessary in the API renderer. We were awaiting kind sections one at a time, then symbols one at a time within each section, and each symbol also awaited syntax highlighting and markdown rendering serially.

For packages with large API surfaces, that made total render time closer to the sum of all individual steps instead of allowing independent work to overlap.

📚 Description

This PR parallelizes the independent async work in the docs rendering pipeline while preserving stable output order.

Changes:

  • render kind sections concurrently in renderDocNodes
  • render symbols concurrently within each kind section
  • run per-symbol signature highlighting, description markdown rendering, and JSDoc tag rendering in parallel
  • parallelize fenced code block highlighting in markdown rendering
  • cache the in-flight Shiki highlighter initialization promise so concurrent highlighting does not trigger redundant startup work

The rendered HTML order is unchanged:

  • kind sections still follow KIND_DISPLAY_ORDER
  • symbols still render in their original section order

- Render doc sections, symbols, markdown blocks, and examples concurrently
- Cache in-flight Shiki highlighter initialization to avoid duplicate work
- Add ordering tests for parallel docs rendering
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 5, 2026

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

Project Deployment Actions Updated (UTC)
npmx.dev Ready Ready Preview, Comment Apr 5, 2026 3:21am
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Preview Apr 5, 2026 3:21am
npmx-lunaria Ignored Ignored Apr 5, 2026 3:21am

Request Review

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 5, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 5, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 14c55141-f038-4708-b77f-a76feb17deb4

📥 Commits

Reviewing files that changed from the base of the PR and between e2de6db and 80295fb.

📒 Files selected for processing (2)
  • server/utils/docs/render.ts
  • server/utils/shiki.ts

📝 Walkthrough

Walkthrough

The PR parallelises documentation rendering: render.ts now processes kind sections, symbols, signatures, JSDoc descriptions, tags, deprecated messages and examples using concurrent promises instead of sequential awaits. text.ts (renderMarkdown) highlights fenced code blocks via Promise.all. shiki.ts (getShikiHighlighter) uses a shared initialisation promise to avoid concurrent startup races. Tests add helper constructors and a new ordering test to assert stable section and symbol rendering order.

🚥 Pre-merge checks | ✅ 1
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The PR description accurately and specifically describes the parallelization changes made across multiple files (renderDocNodes, renderKindSection, renderMergedSymbol, renderMarkdown, and getShikiHighlighter), directly corresponding to the changeset provided.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
server/utils/docs/text.ts (1)

138-141: Consider using forEach for type-safe iteration.

The non-null assertion highlightedCodeBlocks[i]! bypasses TypeScript's type checking. While the loop bounds guarantee valid indices, a forEach callback would be strictly type-safe without the assertion.

♻️ Suggested refactor
-  for (let i = 0; i < highlightedCodeBlocks.length; i++) {
-    const highlighted = highlightedCodeBlocks[i]!
-    result = result.replace(`__CODE_BLOCK_${i}__`, highlighted)
-  }
+  highlightedCodeBlocks.forEach((highlighted, i) => {
+    result = result.replace(`__CODE_BLOCK_${i}__`, highlighted)
+  })

As per coding guidelines: "Ensure you write strictly type-safe code, for example by ensuring you always check when accessing an array value by index".

server/utils/docs/render.ts (1)

138-161: Destructured array elements may be null despite conditional guards.

The variables highlightedSignature, renderedDescription, and renderedJsDocTags are typed as string | null. The conditionals at lines 144, 154, and 159 mirror the conditions used to create the promises, but TypeScript cannot correlate these checks.

The runtime behaviour is correct, but for stricter type safety, consider using truthiness checks directly on the resolved values (which you already do at lines 154 and 159) or adding explicit null checks.

♻️ Minor type-safe adjustment for line 145
   if (signatures.length > 0) {
-    lines.push(`<div class="docs-signature">${highlightedSignature}</div>`)
+    lines.push(`<div class="docs-signature">${highlightedSignature ?? ''}</div>`)

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b64ac82b-b369-41a2-b097-ee16c3a7f693

📥 Commits

Reviewing files that changed from the base of the PR and between eb4d862 and ed78b73.

📒 Files selected for processing (4)
  • server/utils/docs/render.ts
  • server/utils/docs/text.ts
  • server/utils/shiki.ts
  • test/unit/server/utils/docs/render.spec.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bd79fef8-b292-4e38-9c0d-e632936bf7fc

📥 Commits

Reviewing files that changed from the base of the PR and between ed78b73 and e2de6db.

📒 Files selected for processing (2)
  • server/utils/docs/render.ts
  • server/utils/docs/text.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • server/utils/docs/render.ts

Comment on lines +138 to +140
highlightedCodeBlocks.forEach((highlighted, i) => {
result = result.replace(`__CODE_BLOCK_${i}__`, highlighted)
}
})
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Apr 5, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

In JavaScript String.prototype.replace, when the search value is a plain string, are replacement patterns like $&, $, and $' interpreted in the replacement string?`

💡 Result:

Yes, in JavaScript String.prototype.replace, when the search value is a plain string, replacement patterns like $&amp;, $, and $' are interpreted in the replacement string. The GetSubstitution abstract operation, used for both RegExp and string search values, processes these patterns using the matched substring (which is the plain string found), the full string, and position. For example, 'ABC'.replace('B', '$') returns 'AAC' because $ inserts the portion before the match ('A'), and 'ABC'.replace('B', "$'") returns 'BBC' because $' inserts the portion after ('C'). This behavior is consistent across engines and documented in MDN, with examples confirming interpretation even for plain string searches. To insert literal $&amp;, use a function replacer or escape as '$$&'.

Citations:


🏁 Script executed:

cat -n server/utils/docs/text.ts | head -160 | tail -40

Repository: npmx-dev/npmx.dev

Length of output: 1718


🏁 Script executed:

# Find the complete function containing lines 138-140
sed -n '100,200p' server/utils/docs/text.ts

Repository: npmx-dev/npmx.dev

Length of output: 1639


🏁 Script executed:

# Look for where highlightedCodeBlocks is populated to understand what it contains
rg -n "highlightedCodeBlocks" server/utils/docs/text.ts -A 2 -B 2

Repository: npmx-dev/npmx.dev

Length of output: 390


Use a function replacer to prevent $ substitution artefacts in highlighted HTML.

At line 139, passing highlighted directly as the replacement string can cause output mangling when the highlighted HTML incidentally contains special patterns like $&, $, or $'. Although syntax highlighters rarely produce such patterns, using a callback replacer is the defensive approach to ensure the HTML is inserted verbatim without interpretation.

Suggested fix
 highlightedCodeBlocks.forEach((highlighted, i) => {
-  result = result.replace(`__CODE_BLOCK_${i}__`, highlighted)
+  result = result.replace(`__CODE_BLOCK_${i}__`, () => highlighted)
 })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
highlightedCodeBlocks.forEach((highlighted, i) => {
result = result.replace(`__CODE_BLOCK_${i}__`, highlighted)
}
})
highlightedCodeBlocks.forEach((highlighted, i) => {
result = result.replace(`__CODE_BLOCK_${i}__`, () => highlighted)
})

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.

cc: @trivikr

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.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

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.

2 participants