Skip to content

fix: change readme md renderer behavior#1776

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

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

Conversation

@RYGRIT
Copy link
Contributor

@RYGRIT RYGRIT commented Mar 1, 2026

🔗 Linked issue

resolves #1323, #1909

🧭 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).
  • Fix inline-code heading anchors like <Text> so README self-links use #user-content-text

@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 11, 2026 3:55am
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Preview Mar 11, 2026 3:55am
npmx-lunaria Ignored Ignored Mar 11, 2026 3:55am

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

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

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: fe800b20-11c9-4402-9bc5-6aad0021b9b4

📥 Commits

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

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

📝 Walkthrough

Walkthrough

Refactors README rendering into a single-pass pipeline. Adds user-content prefix utilities (USER_CONTENT_PREFIX, withUserContentPrefix, toUserContentId, toUserContentHash) and normalizePreservedAnchorAttrs. Centralises heading processing (getHeadingPlainText, getHeadingSlugSource, processHeading, slugify) to produce idempotent user-content-prefixed IDs and consistent TOC collection across Markdown and raw HTML. Unifies link handling via processLink/resolveUrl (including '#' anchor handling and absolute-path behaviour), consolidates playground-link collection, preserves allowed HTML heading attributes, and updates transformTags and renderers to apply these changes. Extensive tests added 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, detailing specific fixes to README rendering, heading/link processing, and TOC handling in mixed markdown and HTML content.
Linked Issues check ✅ Passed The PR addresses all core coding objectives from issue #1323: single-pass rendering for consistent ordering, correct TOC generation, heading ID collision prevention, and unified link processing.
Out of Scope Changes check ✅ Passed All changes in the two modified files (readme.ts and readme.spec.ts) are directly aligned with the stated objectives of fixing README rendering for mixed markdown/HTML content and improving TOC ordering.

✏️ 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

@ghostdevv
Copy link
Contributor

is this ready for review? @RYGRIT

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
server/utils/readme.ts (1)

675-703: ⚠️ Potential issue | 🟠 Major

Prefix preserved <a id> values to match the new hash rewriting.

This branch rewrites #foo links to #user-content-foo, but it leaves legacy raw HTML anchors like <a id="foo"></a> untouched. Those links will no longer resolve after this change.

Suggested patch
       a: (tagName, attribs) => {
+        if (attribs.id) {
+          attribs.id = withUserContentPrefix(attribs.id)
+        }
         if (!attribs.href) {
           return { tagName, attribs }
         }
♻️ Duplicate comments (1)
server/utils/readme.ts (1)

331-337: ⚠️ Potential issue | 🟠 Major

Handle bare # and protocol-relative URLs before the slash fast-path.

href="#" still becomes #user-content-, and //www.npmjs.com/package/... still returns early as if it were root-relative. That changes valid README link behaviour and skips the npmjs normalisation branch.

Suggested patch
 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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 818fbd8a-d4c4-4836-8b13-0b6430f503e3

📥 Commits

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

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

@RYGRIT
Copy link
Contributor Author

RYGRIT commented Mar 11, 2026

Hi @ghostdevv, I apologize for the late reply. I think it's possible to start the review now.

@RYGRIT
Copy link
Contributor Author

RYGRIT commented Mar 11, 2026

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 11, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

2 participants