$npx -y skills add rstackjs/agent-skills --skill mdx-to-markdownConvert .mdx to portable .md, cleaning MDX components, anchors, code titles, table-of-contents links, and root-relative docs links.
| 1 | # MDX to Markdown |
| 2 | |
| 3 | ## Overview |
| 4 | |
| 5 | Use this skill to produce plain Markdown from MDX without requiring the target renderer to understand MDX imports, JSX components, Rspress anchors, or code-block metadata. |
| 6 | |
| 7 | ## Workflow |
| 8 | |
| 9 | 1. Identify the source `.mdx` and target `.md` path. If the user gives only a source path, default the output to the same path with `.md`. |
| 10 | 2. Inspect the MDX for non-Markdown constructs: |
| 11 | - YAML frontmatter. |
| 12 | - top-level `import` lines. |
| 13 | - custom components such as `<BlogAuthors />` and `<PackageManagerTabs />`. |
| 14 | - heading anchors like `### Title \{#title}`. |
| 15 | - standalone HTML anchors like `<a id="title"></a>`. |
| 16 | - fenced code block titles such as a JavaScript fence with `title="env.js"`. |
| 17 | - HTML image wrappers such as centered `<div><img ... /></div>`. |
| 18 | 3. Run the helper script when the requested conversion matches the default rules: |
| 19 | |
| 20 | ```bash |
| 21 | node /path/to/skills/mdx-to-markdown/scripts/convert_mdx_to_markdown.mjs input.mdx output.md |
| 22 | ``` |
| 23 | |
| 24 | 4. Review the result with `rg` and a short diff. Confirm that no MDX-only syntax remains. |
| 25 | 5. Apply any user-requested follow-up edits, such as removing a generated table of contents or changing link policy. |
| 26 | |
| 27 | ## Default Conversion Rules |
| 28 | |
| 29 | - Drop YAML frontmatter and top-level MDX imports. |
| 30 | - Remove `<BlogAuthors />`. |
| 31 | - Expand `<PackageManagerTabs command="add pkg -D" />` to a `bash` code block using `pnpm add pkg -D`. |
| 32 | - Convert centered HTML image blocks into Markdown image syntax. |
| 33 | - Remove Rspress heading anchor suffixes while keeping the heading text. |
| 34 | - Remove standalone `<a id="..."></a>` anchor lines. |
| 35 | - Remove table-of-contents links that target in-page anchors, preserving the list text. |
| 36 | - Convert code-fence titles or standalone bold filename captions into the first line of the code block as a comment, for example: |
| 37 | |
| 38 | ```js |
| 39 | // hello.js |
| 40 | export function greet(name) { |
| 41 | return `Hello, ${name}!`; |
| 42 | } |
| 43 | |
| 44 | console.log(greet('world')); |
| 45 | ``` |
| 46 | |
| 47 | - Convert code blocks that contain markers such as `// [!code highlight]` to `diff` code blocks, remove the marker, and prefix highlighted lines with `+`. |
| 48 | - Prefix Markdown links whose target starts with `/` using the current repository's site domain. Infer the product from the input file's Git repository, remote, package name, or path segment: |
| 49 | - `rspack` -> `rspack.rs` |
| 50 | - `rsbuild` -> `rsbuild.rs` |
| 51 | - `rspress` -> `rspress.rs` |
| 52 | - `rslib` -> `rslib.rs` |
| 53 | - `rsdoctor` -> `rsdoctor.rs` |
| 54 | - `rstest` -> `rstest.rs` |
| 55 | - Add `/zh` to the prefix when the MDX path contains a `zh` path segment. For example, a Chinese Rspack document converts `/guide/optimization/tree-shaking` to `https://rspack.rs/zh/guide/optimization/tree-shaking`. |
| 56 | - Use `--link-prefix <prefix>` only when the inferred domain is wrong or the user explicitly requests a custom prefix. |
| 57 | |
| 58 | ## Validation Checklist |
| 59 | |
| 60 | Run targeted checks after conversion: |
| 61 | |
| 62 | ````bash |
| 63 | rg -n '<BlogAuthors|PackageManagerTabs|title="|\\{#|^<a id="|\\]\(/|!code highlight|^\*\*[^*]+\*\*$' output.md |
| 64 | rg -n '^```' output.md | wc -l |
| 65 | ```` |
| 66 | |
| 67 | The first command should return no matches unless the user intentionally kept that syntax. The fence count should be even. |
| 68 | |
| 69 | ## Resources |
| 70 | |
| 71 | - `scripts/convert_mdx_to_markdown.mjs`: deterministic converter for the common Rspack/Rstack release-blog MDX pattern. |