-
Notifications
You must be signed in to change notification settings - Fork 1
feat: ignore <style> tag content in markdown #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,31 +1,143 @@ | ||
| # react-render-to-markdown | ||
|
|
||
| [](https://www.npmjs.com/package/react-render-to-markdown) | ||
| [](https://github.com/SoonIter/react-render-to-markdown/blob/main/LICENSE) | ||
|
|
||
| Render React components to Markdown strings — like `renderToString` in `react-dom`, but outputs **Markdown** instead of HTML. | ||
|
|
||
| Built on top of `react-reconciler`, this library creates a custom React renderer that traverses the React element tree and produces well-formatted Markdown. It follows **SSR-like behavior**: `useEffect`, `useLayoutEffect`, and `useInsertionEffect` are suppressed (as no-ops), while `useState`, `useMemo`, `useRef`, `useContext`, and other synchronous hooks work as expected. | ||
|
|
||
| ## Installation | ||
|
|
||
| The major version of `react-render-to-markdown` follows the React version. Install the one that matches your project: | ||
|
|
||
| ```bash | ||
| # React 19 | ||
| npm install react-render-to-markdown@19 | ||
|
|
||
| # React 18 | ||
| npm install react-render-to-markdown@18 | ||
| ``` | ||
|
|
||
| ## Quick Start | ||
|
|
||
| ```tsx | ||
| import { renderToMarkdownString } from 'react-render-to-markdown'; | ||
|
|
||
| const markdown = renderToMarkdownString(<h1>Hello, World!</h1>); | ||
| const markdown = await renderToMarkdownString(<h1>Hello, World!</h1>); | ||
| console.log(markdown); // # Hello, World! | ||
| ``` | ||
|
|
||
| ## Installation | ||
| ## Usage | ||
|
|
||
| ```bash | ||
| npm install react-render-to-markdown | ||
| ### Basic HTML Elements | ||
|
|
||
| ```tsx | ||
| import { renderToMarkdownString } from 'react-render-to-markdown'; | ||
|
|
||
| await renderToMarkdownString( | ||
| <div> | ||
| <strong>foo</strong> | ||
| <span>bar</span> | ||
| </div>, | ||
| ); | ||
| // Output: '**foo**bar' | ||
| ``` | ||
|
|
||
| ### React Components & Hooks | ||
|
|
||
| Synchronous hooks (`useState`, `useMemo`, `useRef`, `useContext`, etc.) work as expected. Client-side effects (`useEffect`, `useLayoutEffect`) are automatically suppressed: | ||
|
|
||
| ```tsx | ||
| import { createContext, useContext, useMemo, useState } from 'react'; | ||
| import { renderToMarkdownString } from 'react-render-to-markdown'; | ||
|
|
||
| const ThemeContext = createContext('light'); | ||
|
|
||
| const Article = () => { | ||
| const [count] = useState(42); | ||
| const theme = useContext(ThemeContext); | ||
| const doubled = useMemo(() => count * 2, [count]); | ||
|
|
||
| return ( | ||
| <> | ||
| <h1>Hello World</h1> | ||
| <p>Count: {count}, Doubled: {doubled}, Theme: {theme}</p> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| await renderToMarkdownString( | ||
| <ThemeContext.Provider value="dark"> | ||
| <Article /> | ||
| </ThemeContext.Provider>, | ||
| ); | ||
| // Output: | ||
| // # Hello World | ||
| // | ||
| // Count: 42, Doubled: 84, Theme: dark | ||
| ``` | ||
|
|
||
| ### Code Blocks | ||
|
|
||
| Fenced code blocks with language and title support: | ||
|
|
||
| ```tsx | ||
| await renderToMarkdownString( | ||
| <pre data-lang="ts" data-title="rspress.config.ts"> | ||
| <code>{'const a = 1;\n'}</code> | ||
| </pre>, | ||
| ); | ||
| // Output: | ||
| // ```ts title=rspress.config.ts | ||
| // const a = 1; | ||
| // ``` | ||
| ``` | ||
|
|
||
| For languages that may contain triple backticks (like `markdown`, `mdx`, `md`), four backticks (``````) are automatically used as delimiters. | ||
|
|
||
| ## Supported Elements | ||
|
|
||
| | HTML Element | Markdown Output | | ||
| | --- | --- | | ||
| | `<h1>` – `<h6>` | `#` – `######` headings | | ||
| | `<p>` | Paragraph with trailing newlines | | ||
| | `<strong>`, `<b>` | `**bold**` | | ||
| | `<em>`, `<i>` | `*italic*` | | ||
| | `<code>` | `` `inline code` `` | | ||
| | `<pre>` + `<code>` | Fenced code block (` ``` `) | | ||
| | `<a href="">` | `[text](url)` | | ||
| | `<img>` | `` | | ||
| | `<ul>`, `<ol>`, `<li>` | Unordered / ordered lists | | ||
| | `<blockquote>` | `> blockquote` | | ||
| | `<br>` | Line break | | ||
| | `<hr>` | `---` horizontal rule | | ||
| | `<style>` | Ignored | | ||
| | `<table>`, `<thead>`, `<tbody>`, `<tr>`, `<th>`, `<td>` | GFM table | | ||
|
|
||
| Any unrecognized elements (e.g. `<div>`, `<span>`, `<section>`) render their children as-is, acting as transparent wrappers. | ||
|
|
||
| ## How It Works | ||
|
|
||
| 1. **Custom React Reconciler** — Uses `react-reconciler` to build a lightweight tree of `MarkdownNode` objects from your React element tree. | ||
| 2. **SSR-like Hook Behavior** — Client-side effects (`useEffect`, `useLayoutEffect`, `useInsertionEffect`) are intercepted and turned into no-ops, matching React's Fizz server renderer behavior. This ensures browser-only code (e.g. `document`, `window`) in effects never runs. | ||
| 3. **Tree-to-Markdown Serialization** — The `MarkdownNode` tree is serialized to a Markdown string via a recursive `toMarkdown` function. | ||
|
|
||
| ## Requirements | ||
|
|
||
| ```json | ||
| { | ||
| "react": "^18.2.0", | ||
| "react": ">=18.2.0", | ||
| "react-reconciler": "^0.29.0" | ||
| } | ||
| ``` | ||
|
|
||
| > **Note:** React 18.2 or above is required. The effect-interception mechanism relies on React 18's internal hooks dispatcher (`__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher.current`). | ||
|
|
||
| ## Used By | ||
|
|
||
| - [Rspress SSG-MD](https://rspress.rs/guide/basic/ssg-md) — Rspress uses this library to power its SSG-MD feature, which renders documentation pages as Markdown files instead of HTML. This enables Generative Engine Optimization (GEO) by generating `llms.txt` and `llms-full.txt` for better Agent accessibility. | ||
| - [**Rspress SSG-MD**](https://rspress.rs/guide/basic/ssg-md) — Rspress uses this library to power its SSG-MD (Static Site Generation to Markdown) feature. SSG-MD renders documentation pages as Markdown files instead of HTML, generating `llms.txt` and `llms-full.txt` for [Generative Engine Optimization (GEO)](https://en.wikipedia.org/wiki/Generative_engine_optimization), enabling better accessibility for AI agents and large language models. | ||
|
|
||
| ## License | ||
|
|
||
| MIT License. | ||
| [MIT](./LICENSE) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.