npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

rct-md-loader

v1.0.2

Published

## setup

Readme

rct-md-loader

setup

npm install -D rct-md-loader

usage

Webpack 配置

在你的 webpack 配置中为 .md 类型文件添加以下配置:

{
    test: /\.md$/,
    use: [
        // 由于 rct-md-loader 会将 md 文件转成一个 react 组件,因此需要对它再次使用一个 babel-loader 进行处理
        {
            loader: 'babel-loader'
        },
        {
            loader: 'rct-md-loader',
            options: {
                // 对 md 中的可运行 demo 进行配置
                codeBlock: {
                    // 可运行 demo 只有一个要求需要返回一个 react 组件,其他的是不限制的。
                    // 因此为了保证这个 demo 能被正确的转译,可以通过指定它的 loader。
                    loader: 'babel-loader',
                    // 可运行 demo 的默认属性
                    props: {
                        className: 'markdown-body'
                    }
                },
                // 以下两个属性是供 `markdown-it` 使用的参数,与 `markdown-it` 的 options 一致。
                preset: 'default'
                use: []
            }
        }
    ]
}

markdown 文档的扩展

嵌入组件

我们可以在 markdown 中直接嵌入某些 React 组件。但受到 markdown 语法的限制,没办法提供完事的 jsx 功能。你只可以这样使用组件:

## 一个标题

<MyTestComponent name="test" />

所有使用 jsx 中的 {} 都是不支持的。例如下边这种就不行:

## 一个标题

<MyTestComponent
    name="test"
    age={10}
    onClick={() => alert()} />

可运行代码块

rct-md-loader 支持在 markdown 文件中添加可运行的 demo。可运行的 demo 的语法是基于代码块的:以 react 标题**为起始,以 ** 为结束。


## 标题

一些描述文字,下边是示例:

\`\`\`react 可运行文档的标题
import React from 'react';
export default () => {
    return (
        <div>这里是一个可以运行的demo</div>
    );
}
\`\`\`

这里 ```react 表示这个代码块输出的是一个 react 组件;标题 是额外提供的 meta 信息,可以用来生成提示或者锚点。

标题是必须添加的,否则会被当成普通代码块处理。

在 js 中使用 markdown 文档

接下来就可以在代码里边使用 .md 文件了:

import MyDoc from './Test.md';
import React from 'react';
import ReactDOM from 'react-dom';

function MyCodeBlock(props) {
    let {title, content, children} = props;
    return (
        <div>
            <h4>{title}</h4>
            <pre><code>{content}</code></pre>
            <div>{children}</div>
        </div>
    );
}

function MyTestComponent() {
    return (<div>my test</div>);
}

ReactDOM.render(
    <MyDoc ReactCodeBlock={MyCodeBlock} MyTestComponent={} />,
    document.getElementById('app')
);

请注意两种情况:

  1. 如果使用了嵌入组件,则必须为 markdown 组件提供相应的属性。

    上边的示例中的 MyTestComponent 会被在 markdown 文件中被渲染出来。

  2. 如果使用了可运行代码块,那么必须提供一个 ReactCodeBlock 的属性。

    这个属性需要是一个 React 组件。这个组件主要是用来展现可运行代码块的源码的。它会得到 titlecontentchildren 三个属性,分别是标题代码块源代码可运行代码块vnode

    请注意:content 属性为了保持 jsx 中的 \n,因此对它做了 JSON.stringify 操作,需要对它进行一次解析

    例如: content = JSON.parse('{"content":"' + content + '"}').content;

开发

解决方案:

  1. 使用 markdown-it 将 markdown 文档渲染成 html

    其中,我们添加了对可运行代码块的语法扩展插件,会把可运行代码块渲染成一个嵌入组件 <ReactDemoCodeBlock />

  2. 把上一步生成的 html 解析成 ast,利用自定义的序列化过程将 ast 转成 jsx

    在这个过程中,我们会所有嵌入组件收集起来,也就是类似 <Text><<ReactDemoCodeBlock>也包含在内)

  3. 最后把 jsx 生成为一个 react 组件。

    这里会把上一步收集到的嵌入组件生成为依赖。分两类:

    1. 普通嵌入组件:这种依赖需要通过 props 传递进来,在 propTypes 中标识它为 required,并在 render 中把它从 props 给提取出来。

    2. 可运行代码块:

      这个为成两层:<ReactDemoCodeBlock/> 和真实的代码内部的组件。前者是渲染的外层容器,通过 props 传递进来的。后者是生成出来的。

      对于可运行代码块内部的代码,我们要求它是一个 js 文件,并且输出一个react组件。所以,实际上这就是一个标准的 js 模块,只不过它隐藏在 markdown 文档中。

      所以我们的 markdown 可以解析出多个模块:除了主模块是文档模块,还有多个可运行代码块模块。即解析的结果是:

      {
          main: '主文档模块',
          1: '可运行代码块1',
          2: '可运行代码块2'
          // ...
      }

      因此,它们之间的关系则为:主模块引入了这些可运行代码块模块。这种情况我们可以通过 webpack 的 loader 参数来区别 main 和可运行代码块。

      所以我们给主模块添加依赖 import ReactCodeBlock_1 from '!!rct-md-loader?id=1!/path/to/markdown.md';

      这里示意的 loader string 是不完整的,还要加上 codeBlock.loader 指定的前缀

      最后在主文档生成 jsx 时,把 ReactCodeBlock_1 填给 ReactDemoCodeBlock 当作 children 使用即可。