Skip to content

fix: do not render headings with no content - #944

Merged
bjohansebas merged 2 commits into
nodejs:mainfrom
moshams272:feat/optional-heading-render
Aug 22, 2026
Merged

fix: do not render headings with no content#944
bjohansebas merged 2 commits into
nodejs:mainfrom
moshams272:feat/optional-heading-render

Conversation

@moshams272

@moshams272 moshams272 commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Description

Currently, if an MDX page lacks an ATX H1 (e.g., when the h1 is built into a custom React component), doc-kit automatically injects an empty h1 to satisfy AST structural requirements and metadata generation.

However, this leads to an issue for MDX: if it renders its own custom h1 in a React component, the final HTML ends up with two h1 tags (one from doc-kit, one from the component), breaking SEO and Accessibility.

The current workaround involves forcing a # Heading in markdown and visually hiding it via CSS (sr-only), which is a hack.

This PR still injects an empty h1, but it doesn't render the empty headings in general to DOM.

Validation

Tested on webpack-doc-kit:

Before:
image

After:
image

Related Issues

None

Check List

  • I have read the Contributing Guidelines and made commit messages that follow the guideline.
  • I have run node --run test and all tests passed.
  • I have check code formatting with node --run format & node --run lint.
  • I've covered new added functionality with unit tests if necessary.

@moshams272
moshams272 requested a review from a team as a code owner July 26, 2026 20:36
@vercel

vercel Bot commented Jul 26, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
api-docs-tooling Ready Ready Preview Aug 22, 2026 5:27pm

Request Review

@vercel

vercel Bot commented Jul 26, 2026

Copy link
Copy Markdown

@moshams272 is attempting to deploy a commit to the OpenJS Foundation Team on Vercel.

A member of the Team first needs to authorize it.

@cursor

cursor Bot commented Jul 26, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
Rendering-only guard and documentation tweaks; metadata injection behavior is unchanged.

Overview
Empty headings no longer appear in generated HTML, which fixes duplicate <h1> on MDX pages that supply their own title in React while the parser still injects a structural depth-1 heading for metadata.

createHeadingElement returns an empty text node when a heading has no children, so the placeholder from parse.mjs (unchanged behavior: still added when a file has content but no headings) stays in the AST for parsing but is omitted from the DOM.

The spec now treats the depth-1 title as OPTIONAL, RECOMMENDED instead of REQUIRED, and depth-1 semantics use SHOULD rather than MUST for “exactly one” heading.

Reviewed by Cursor Bugbot for commit 66b522f. Bugbot is set up for automated code reviews on this repo. Configure here.

@codecov

codecov Bot commented Jul 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 50.00000% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.56%. Comparing base (81b6224) to head (114b02d).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
packages/react/src/jsx-ast/utils/buildContent.mjs 40.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #944      +/-   ##
==========================================
- Coverage   89.57%   89.56%   -0.02%     
==========================================
  Files         203      202       -1     
  Lines       19126    18997     -129     
  Branches     1791     1773      -18     
==========================================
- Hits        17132    17014     -118     
+ Misses       1986     1975      -11     
  Partials        8        8              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a0c54a2. Configure here.

Comment thread src/generators/jsx-ast/utils/buildContent.mjs Outdated
@avivkeller

Copy link
Copy Markdown
Member

Can you:

  1. Make this not an option, we shouldn't render elements with no content in any case
  2. Patch the docs + spec to remove the note about titleless pages?

@moshams272

Copy link
Copy Markdown
Contributor Author

Can you:

  1. Make this not an option, we shouldn't render elements with no content in any case
  2. Patch the docs + spec to remove the note about titleless pages?

you mean for headings only, right?

@avivkeller

Copy link
Copy Markdown
Member

yes sorry

@moshams272
moshams272 force-pushed the feat/optional-heading-render branch from a0c54a2 to bee146c Compare July 26, 2026 22:04
@moshams272 moshams272 changed the title feat: support hide_title in frontmatter to skip the default h1 created fix: do not render headings with no content Jul 26, 2026
Comment thread packages/core/src/generators/metadata/utils/parse.mjs
@bmuenzenmeyer

Copy link
Copy Markdown
Contributor

this feels very related to notions within #931

@avivkeller

Copy link
Copy Markdown
Member

this feels very related to notions within #931

Sort of, yes. We have one page without a heading, since we add the heading via JSX

Comment thread docs/specification.md Outdated
@avivkeller
avivkeller requested a review from bmuenzenmeyer July 29, 2026 15:25
@moshams272

Copy link
Copy Markdown
Contributor Author

Could u tell me what the Vercel problem is 😅

@ovflowd

ovflowd commented Aug 21, 2026

Copy link
Copy Markdown
Member

Could u tell me what the Vercel problem is 😅

No idea, seems just authorization issue. Probably because needs rebase. @moshams272 could you rebase please?

@github-actions

Copy link
Copy Markdown
Contributor

api-links Generator

Output: 1 file differs

apilinks.json
Expected values to be strictly deep-equal:
+ actual - expected
... Skipped lines

  {
    'Agent.defaultMaxSockets': 'https://github.com/{repository}/blob/HEAD/lib/_http_agent.js#L300',
    'Buffer.alloc': 'https://github.com/{repository}/blob/HEAD/lib/buffer.js#L450',
    'Buffer.allocUnsafe': 'https://github.com/{repository}/blob/HEAD/lib/buffer.js#L469',
    'Buffer.allocUnsafeSlow': 'https://github.com/{repository}/blob/HEAD/lib/buffer.js#L492',
...
    'agent.addRequest': 'https://github.com/{repository}/blob/HEAD/lib/_http_agent.js#L365',
+   'agent.createConnection': 'https://github.com/{repository}/blob/HEAD/lib/https.js#L332',
-   'agent.createConnection': 'https://github.com/{repository}/blob/HEAD/lib/_http_agent.js#L304',
    'agent.createSocket': 'https://github.com/{repository}/blob/HEAD/lib/_http_agent.js#L446',
    'agent.destroy': 'https://github.com/{repository}/blob/HEAD/lib/_http_agent.js#L679',
+   'agent.getName': 'https://github.com/{repository}/blob/HEAD/lib/https.js#L515',
+   'agent.keepSocketAlive': 'https://github.com/{repository}/blob/HEAD/lib/https.js#L484',
-   'agent.getName': 'https://github.com/{repository}/blob/HEAD/lib/_http_agent.js#L334',
-   'agent.keepSocketAlive': 'https://github.com/{repository}/blob/HEAD/lib/_http_agent.js#L635',
    'agent.removeSocket': 'https://github.com/{repository}/blob/HEAD/lib/_http_agent.js#L574',
    'agent.reuseSocket': 'https://github.com/{repository}/blob/HEAD/lib/_http_agent.js#L671',
    'assert.assert': 'https://github.com/{repository}/blob/HEAD/lib/assert.js#L185',
    'asyncResource.asyncId': 'https://github.com/{repository}/blob/HEAD/lib/async_hooks.js#L243',
    'asyncResource.bind': 'https://github.com/{repository}/blob/HEAD/lib/async_hooks.js#L275',
...
    'server.address': 'https://github.com/{repository}/blob/HEAD/lib/net.js#L2699',
+   'server.close': 'https://github.com/{repository}/blob/HEAD/lib/net.js#L2832',
+   'server.closeAllConnections': 'https://github.com/{repository}/blob/HEAD/lib/https.js#L124',
+   'server.closeIdleConnections': 'https://github.com/{repository}/blob/HEAD/lib/https.js#L126',
-   'server.close': 'https://github.com/{repository}/blob/HEAD/lib/_http_server.js#L681',
-   'server.closeAllConnections': 'https://github.com/{repository}/blob/HEAD/lib/_http_server.js#L691',
-   'server.closeIdleConnections': 'https://github.com/{repository}/blob/HEAD/lib/_http_server.js#L703',
    'server.getConnections': 'https://github.com/{repository}/blob/HEAD/lib/net.js#L2794',
    'server.listen': 'https://github.com/{repository}/blob/HEAD/lib/net.js#L2496',
    'server.ref': 'https://github.com/{repository}/blob/HEAD/lib/net.js#L2945',
+   'server.setTimeout': 'https://github.com/{repository}/blob/HEAD/lib/https.js#L128',
-   'server.setTimeout': 'https://github.com/{repository}/blob/HEAD/lib/_http_server.js#L719',
    'server.unref': 'https://github.com/{repository}/blob/HEAD/lib/net.js#L2954',
+   'server[SymbolAsyncDispose]': 'https://github.com/{repository}/blob/HEAD/lib/net.js#L2872',
-   'server[SymbolAsyncDispose]': 'https://github.com/{repository}/blob/HEAD/lib/_http_server.js#L687',
    'server[SymbolAsyncIterator]': 'https://github.com/{repository}/blob/HEAD/lib/net.js#L2879',
    'server[kDeserialize]': 'https://github.com/{repository}/blob/HEAD/lib/net.js#L2419',
    'server[kTransferList]': 'https://github.com/{repository}/blob/HEAD/lib/net.js#L2392',
    'server[kTransfer]': 'https://github.com/{repository}/blob/HEAD/lib/net.js#L2397',
+   'server[undefined]': 'https://github.com/{repository}/blob/HEAD/lib/net.js#L2909',
-   'server[undefined]': 'https://github.com/{repository}/blob/HEAD/lib/_http_server.js#L726',
    'serverresponse._finish': 'https://github.com/{repository}/blob/HEAD/lib/_http_server.js#L254',
    'serverresponse._implicitHeader': 'https://github.com/{repository}/blob/HEAD/lib/_http_server.js#L411',
    'serverresponse.assignSocket': 'https://github.com/{repository}/blob/HEAD/lib/_http_server.js#L304',
    'serverresponse.detachSocket': 'https://github.com/{repository}/blob/HEAD/lib/_http_server.js#L315',
    'serverresponse.statusCode': 'https://github.com/{repository}/blob/HEAD/lib/_http_server.js#L277',

Performance estimate (single CI run)

  • Generation time: 2.9% faster (1.37 s → 1.33 s)
  • Peak memory: 0.8% higher (353.76 MB → 356.61 MB)

legacy-html Generator

Performance estimate (single CI run)

  • Generation time: 8.6% slower (19.29 s → 20.94 s)
  • Peak memory: 0.2% higher (2.30 GB → 2.30 GB)

legacy-json Generator

Performance estimate (single CI run)

  • Generation time: 22.2% slower (6.93 s → 8.47 s)
  • Peak memory: 2.5% lower (1.83 GB → 1.79 GB)

llms-txt Generator

Performance estimate (single CI run)

  • Generation time: 1.0% slower (8.28 s → 8.36 s)
  • Peak memory: 1.3% lower (1.77 GB → 1.75 GB)

orama-db Generator

Output size: 1 file changed · net -1.00 B

File size details
File Main PR Change
orama-db.json 9.14 MB 9.14 MB -1.00 B (-0.0%)

Performance estimate (single CI run)

  • Generation time: 1.7% faster (8.76 s → 8.61 s)
  • Peak memory: 4.9% lower (1.91 GB → 1.82 GB)

web Generator

Output size: 1 file changed · net -208.00 B

File size details
File Main PR Change
all.html 31.74 MB 31.74 MB -208.00 B (-0.0%)

Performance estimate (single CI run)

  • Generation time: 1.9% faster (86.68 s → 84.99 s)
  • Peak memory: 9.8% higher (4.81 GB → 5.29 GB)

@moshams272

Copy link
Copy Markdown
Contributor Author

No idea, seems just authorization issue. Probably because needs rebase. @moshams272 could you rebase please?

Github things, I thought the deploy failed :)

@bjohansebas
bjohansebas merged commit b910a23 into nodejs:main Aug 22, 2026
23 checks passed
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.

6 participants