├── .babelrc ├── .gitignore ├── .npmignore ├── README.md ├── docs ├── default.css ├── index.html └── index.js ├── index.css ├── index.js ├── package.json └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "react", 4 | "es2015", 5 | "stage-0" 6 | ] 7 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea 2 | /docs -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | imports: 3 | import moment from 'moment'; 4 | --- 5 | 6 | # markdown-it-react-loader 7 | 8 | 用Markdown提供一份直观的React文档,有可运行的示例,有示例源代码,有示例的说明。 9 | 10 | 这样用户看起来直观,编写者写起来也直观,维护成本低。 11 | 12 | 经过几番尝试,结合 React 的特点。写了一套处理 Markdown 文件的 webpack loader,可以将 Markdown 转成 React 文件。 13 | 14 | 本md对应生成的文档是[readme.md](https://liyatang.github.io/markdown-it-react-loader/) 15 | 16 | --- 17 | 18 | ## Install 19 | 20 | `npm install markdown-it-react-loader` 21 | 22 | 在 webpack 中加入 loader 23 | 24 | ```js 25 | { 26 | test: /\.md$/, 27 | loader: 'babel!markdown-it-react-loader' 28 | } 29 | ``` 30 | 31 | 随后把md文件当成一个react component去使用即可。比如本工程中的demo 32 | 33 | ```js 34 | import ReadMe from '../README.md'; 35 | ``` 36 | 37 | 如需运行demo `npm install; npm start;` 打开 http://localhost:8080 38 | 39 | ### 样式 40 | 41 | 提供样式文件`index.css`,可直接引入或自定义。 42 | 43 | --- 44 | 45 | ## 语法介绍 46 | 47 | 正常的Markdown语法不影响。有几个需要注意的地方: 48 | 49 | ### 使用示例 50 | 51 | #### 纯渲染 52 | 53 | ::: demo 这是描述这是**描述**,点三角可展开代码。也可以不提供 54 | ```jsx 55 | 56 | ``` 57 | ::: 58 | 59 | ``` 60 | ::: demo 这是描述这是**描述**,点三角可展开代码 61 | ```jsx 62 | 63 | ``` 64 | ::: 65 | ``` 66 | 67 | 注意:渲染到页面的代码语言必须写`jsx`,因为loaders会把语言为`jsx`放入render的jsx内 68 | 69 | #### 引入其他库 70 | 71 | ::: demo 72 | ```jsx 73 |
', '')
47 |
48 | return highlight.fixMarkup(highlightedContent)
49 | }
50 | })
51 |
52 | const formatModule = (imports, js, jsx) => {
53 | let moduleText = `
54 | ${imports}
55 |
56 | ${js}
57 |
58 | class MarkdownItReactComponent extends React.Component {
59 | constructor(props){
60 | super(props);
61 | this.state = {};
62 | }
63 | handleToggleCode(flag){
64 | const state = {};
65 | state['showCode' + flag] = !this.state['showCode' + flag];
66 | this.setState(state);
67 | }
68 |
69 | render(){
70 | return (
71 |
72 | ${jsx}
73 |
74 | );
75 | }
76 | };
77 |
78 | export default MarkdownItReactComponent;`
79 |
80 | return moduleText
81 | }
82 |
83 | const formatOpening = (code, description, flag) => {
84 |
85 | const desc = description ? `${description}` : ''
86 |
87 | return (
88 | `
89 |
90 | Example
91 | ${code}
92 |
93 |
94 | ${desc}
95 | `)
96 | }
97 |
98 | const formatClosing = (flag) => {
99 | return (
100 | `
101 |
102 |
103 |
104 |
105 | `)
106 | }
107 |
108 | module.exports = function (source) {
109 | this.cacheable()
110 |
111 | // init options
112 | // TODO
113 | // Object.assign(options, this.options.markdownItReact ? this.options.markdownItReact() : {})
114 |
115 | const { body, attributes: { imports: importMap } } = frontMatter(source)
116 | const imports = 'import React from \'react\'; ' + importMap
117 |
118 | let moduleJS = []
119 |
120 | // 放在这里应该没有问题, 反正是顺序执行的
121 | let flag = ''
122 |
123 | md.use(mdContainer, 'demo', {
124 | validate: function (params) {
125 | return params.trim().match(/^demo\s*(.*)$/)
126 | },
127 | render: function (tokens, idx) {
128 | // container 从开头到结尾把之间的token跑一遍,其中idx定位到具体的位置
129 |
130 | // 获取描述
131 | const m = tokens[idx].info.trim().match(/^demo\s*(.*)$/)
132 |
133 | // 有此标记代表 ::: 开始
134 | if (tokens[idx].nesting === 1) {
135 | flag = idx
136 |
137 | let jsx = '', i = 1
138 |
139 | // 从 ::: 下一个token开始
140 | let token = tokens[idx + i]
141 |
142 | // 如果没有到结尾
143 | while (token.markup !== ':::') {
144 | // 只认```,其他忽略
145 | if (token.markup === '```') {
146 | if (token.info === 'js') {
147 | // 插入到import后,component前
148 | moduleJS.push(token.content)
149 | } else if (token.info === 'jsx') {
150 | // 插入render内
151 | jsx = token.content
152 | }
153 | }
154 | i++
155 | token = tokens[idx + i]
156 | }
157 |
158 | // 描述也执行md
159 | return formatOpening(jsx, md.render(m[1]), flag)
160 | }
161 | return formatClosing(flag)
162 | }
163 | })
164 |
165 | // md 处理过后的字符串含有 class 和 style ,需要再次处理给到react
166 | let content = md.render(body)
167 | .replace(/
/g, '
')
168 | .replace(/
/g, '
')
169 | .replace(/class=/g, 'className=')
170 |
171 | return formatModule(imports, moduleJS.join('\n'), content)
172 | }
173 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "markdown-it-react-loader",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "NODE_ENV=development webpack-dev-server --colors",
8 | "gh-pages": "webpack --color; git add --all; git commit -m 'gh-pages';git push origin master:master;"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/liyatang/markdown-it-react-loader.git"
13 | },
14 | "author": "",
15 | "license": "ISC",
16 | "bugs": {
17 | "url": "https://github.com/liyatang/markdown-it-react-loader/issues"
18 | },
19 | "homepage": "https://github.com/liyatang/markdown-it-react-loader#readme",
20 | "dependencies": {
21 | "front-matter": "^3.0.1",
22 | "highlight.js": "^9.13.1",
23 | "markdown-it": "^8.4.2",
24 | "markdown-it-anchor": "^5.0.2",
25 | "markdown-it-container": "^2.0.0",
26 | "markdown-it-regexp": "^0.4.0",
27 | "transliteration": "^2.1.2"
28 | },
29 | "devDependencies": {
30 | "babel-core": "^6.26.3",
31 | "babel-loader": "^7.1.5",
32 | "babel-preset-es2015": "^6.24.1",
33 | "babel-preset-react": "^6.24.1",
34 | "babel-preset-stage-0": "^6.24.1",
35 | "moment": "^2.24.0",
36 | "path": "^0.12.7",
37 | "react": "^16.7.0",
38 | "react-dom": "^16.7.0",
39 | "webpack": "^4.29.0",
40 | "webpack-cli": "^3.2.1",
41 | "webpack-dev-server": "^3.1.14"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | module.exports = {
4 | mode: process.env.NODE_ENV,
5 | entry: './docs/index.js',
6 | output: {
7 | path: path.join(__dirname, 'docs/build'),
8 | filename: '[name].bundle.js',
9 | publicPath: '/markdown-it-react-loader/docs/build/'
10 | },
11 | module: {
12 | rules: [{
13 | test: /\.js$/,
14 | loader: 'babel-loader'
15 | }, {
16 | test: /\.md$/,
17 | loader: 'babel-loader!./index.js'
18 | }]
19 | }
20 | }
21 |
--------------------------------------------------------------------------------