Skip to content

fix: change readme md renderer behavior#1776

Open
RYGRIT wants to merge 12 commits intonpmx-dev:mainfrom
RYGRIT:fix/issue-1323
Open

fix: change readme md renderer behavior#1776
RYGRIT wants to merge 12 commits intonpmx-dev:mainfrom
RYGRIT:fix/issue-1323

Conversation

@RYGRIT
Copy link
Contributor

@RYGRIT RYGRIT commented Mar 1, 2026

🔗 Linked issue

resolves #1323

🧭 Context

📚 Description

This PR fixes README rendering issues caused by split processing between marked and sanitizeHtml, and makes heading/link handling more consistent in mixed markdown + raw HTML content.

What changed:

  • Processed headings and links in a more consistent single-pass flow during marked rendering.
  • Fixed TOC/slug ordering for mixed markdown and raw HTML headings.
  • Prevented heading ID collisions when heading text already includes user-content-.
  • Preserved supported attributes during raw HTML rewrites:
    • headings keep supported attrs (e.g. align)
    • anchors preserve allowlisted attrs (including title, even when placed before href)
    • rewritten anchors normalize href and enforce safe rel/target without duplicates
  • Added/updated unit tests for mixed heading order, duplicate slug/ID behavior, and raw HTML anchor/heading attribute preservation (including renderer.html-path cases).

@vercel
Copy link

vercel bot commented Mar 1, 2026

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

Project Deployment Actions Updated (UTC)
npmx.dev Ready Ready Preview, Comment Mar 6, 2026 2:00pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Preview Mar 6, 2026 2:00pm
npmx-lunaria Ignored Ignored Mar 6, 2026 2:00pm

Request Review

@codecov
Copy link

codecov bot commented Mar 1, 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
Contributor

coderabbitai bot commented Mar 1, 2026

📝 Walkthrough

Walkthrough

Refactors README rendering into a single-pass pipeline. Adds user-content naming utilities (USER_CONTENT_PREFIX, withUserContentPrefix, toUserContentId, toUserContentHash) and normalizePreservedAnchorAttrs. Centralises heading processing (semantic level, slug generation, TOC collection) and unifies Markdown and raw-HTML heading handling so IDs use user-content prefixes and duplicate slugs reflect document order. Consolidates URL resolution, playground-link collection, and security attribute application via processLink/resolveUrl with de-duplication. Renderer changes intercept raw HTML headings and anchors, normalise preserved attrs, ensure idempotent anchor/hash prefixing, and update transformTags to preserve data-level semantics. Tests expanded for TOC ordering, slug collisions, ID/anchor consistency, link resolution and single-pass behaviour.

Possibly related PRs

Suggested reviewers

  • danielroe
  • alexdln
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The PR description clearly relates to the changeset by explaining the refactoring of README rendering, heading/link processing, and fixes for mixed markdown and HTML content handling.
Linked Issues check ✅ Passed The PR implements all key objectives from issue #1323: single-pass processing during marked rendering, correct TOC ordering for mixed headings, duplicate ID handling, attribute preservation for HTML elements, and link collection consolidation.
Out of Scope Changes check ✅ Passed All changes are directly related to the objectives in issue #1323. The readme.ts refactoring addresses heading/link processing consolidation, and test additions comprehensively validate the new single-pass rendering behaviour.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
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: 4

🧹 Nitpick comments (1)
test/unit/server/utils/readme.spec.ts (1)

579-584: Add a regression case for user-content- slug collisions.

This duplicate-slug test only covers identical heading text. Please add a case like # Title plus # user-content-title to ensure generated IDs remain unique.

🧪 Suggested additional test
   it('handles duplicate raw HTML heading slugs', async () => {
     const md = '<h2>API</h2>\n\n<h2>API</h2>'
     const result = await renderReadmeHtml(md, 'test-pkg')
     expect(result.html).toContain('id="user-content-api"')
     expect(result.html).toContain('id="user-content-api-1"')
   })
+
+  it('keeps IDs unique when a heading slug already starts with user-content-', async () => {
+    const md = '# Title\n\n# user-content-title'
+    const result = await renderReadmeHtml(md, 'test-pkg')
+    const ids = result.toc.map(t => t.id)
+    expect(new Set(ids).size).toBe(ids.length)
+  })
 })

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f18d64e and a4d07f7.

📒 Files selected for processing (2)
  • server/utils/readme.ts
  • test/unit/server/utils/readme.spec.ts

@RYGRIT RYGRIT marked this pull request as draft March 1, 2026 07:37
Copy link
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

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a4d07f7 and dcda67f.

📒 Files selected for processing (2)
  • server/utils/readme.ts
  • test/unit/server/utils/readme.spec.ts

Copy link
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

♻️ Duplicate comments (1)
server/utils/readme.ts (1)

490-495: ⚠️ Potential issue | 🟠 Major

Preserve explicit heading ids when normalising raw HTML headings.

Lines [490-495] only carry align forward. A heading like <h2 id="custom-anchor">Install</h2> is rewritten with a generated id based on Install, while resolveUrl('#custom-anchor') still points links at #user-content-custom-anchor. That leaves no matching target and breaks existing in-document links. If you keep custom ids here, make sure they also participate in the duplicate-id tracking so later generated slugs still suffix in document order.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9cedcc33-23a5-4b86-88f3-9a02da10ce8d

📥 Commits

Reviewing files that changed from the base of the PR and between 8f8e3c0 and c735405.

📒 Files selected for processing (1)
  • server/utils/readme.ts

Comment on lines 323 to +329
if (url.startsWith('#')) {
// Prefix anchor links to match heading IDs (avoids collision with page IDs)
return `#user-content-${url.slice(1)}`
// Idempotent: don't double-prefix if already prefixed
return toUserContentHash(url.slice(1))
}
// Absolute paths (e.g. /package/foo from a previous npmjs redirect) are already resolved
if (url.startsWith('/')) return url
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don’t fold # and //… into the new fast-path.

Lines [323-329] now turn href="#" into #user-content-, and protocol-relative URLs like //www.npmjs.com/package/foo return early as if they were root-relative. That breaks valid README links and skips the npmjs normalisation branch for //… URLs.

Possible fix
 function resolveUrl(url: string, packageName: string, repoInfo?: RepositoryInfo): string {
   if (!url) return url
+  if (url === '#') return url
   if (url.startsWith('#')) {
     // Prefix anchor links to match heading IDs (avoids collision with page IDs)
     // Idempotent: don't double-prefix if already prefixed
     return toUserContentHash(url.slice(1))
   }
   // Absolute paths (e.g. /package/foo from a previous npmjs redirect) are already resolved
-  if (url.startsWith('/')) return url
+  if (url.startsWith('/') && !url.startsWith('//')) return url
   if (hasProtocol(url, { acceptRelative: true })) {

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.

fix: change markdown renderer behavior

1 participant