Skip to content

Commit 5e0738c

Browse files
author
liuyu04
committed
feat:done
1 parent f79cab3 commit 5e0738c

14 files changed

+4607
-151
lines changed

.babelrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"presets": ["@babel/preset-env"]
3+
}

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# rollup-md2vue
1+
# rollup-plugin-md2vue
22

33
组件库展示,md 文件转为 vue 文件,将其中 demo 分为组件渲染和代码展示两部分
44

__test__/rollup-md2vue.test.js

+10-9
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
var rollup = require('rollup');
2-
var md = require('../src/index');
1+
const rollup = require('rollup');
2+
const md2vue = require('../build/md2vue.js');
33

4-
it('returns a module for the markdown file', async () => {
5-
const file = rollup.rollup({
6-
input: 'samples/main.js',
7-
plugins: [md()]
4+
describe("md2vue", () => {
5+
it('returns a module for the markdown file', () => {
6+
const file = rollup.rollup({
7+
input: '../examples/main.js',
8+
plugins: [md2vue()]
9+
})
810
})
9-
console.log('file: ', file)
10-
// expect(file.filename).toEqual('test.md')
11-
})
11+
});
12+

build/md2vue.js

+297
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
'use strict';
2+
3+
var require$$0$3 = require('@rollup/pluginutils');
4+
var require$$0 = require('@vue/compiler-dom');
5+
var require$$0$2 = require('highlight.js');
6+
var require$$0$1 = require('markdown-it-container');
7+
var require$$3 = require('markdown-it');
8+
9+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
10+
11+
var require$$0__default$3 = /*#__PURE__*/_interopDefaultLegacy(require$$0$3);
12+
var require$$0__default = /*#__PURE__*/_interopDefaultLegacy(require$$0);
13+
var require$$0__default$2 = /*#__PURE__*/_interopDefaultLegacy(require$$0$2);
14+
var require$$0__default$1 = /*#__PURE__*/_interopDefaultLegacy(require$$0$1);
15+
var require$$3__default = /*#__PURE__*/_interopDefaultLegacy(require$$3);
16+
17+
// const { compileTemplate } = require('@vue/component-compiler-utils');
18+
const compiler = require$$0__default["default"]; // 模板
19+
// const compiler = require('@vue/compiler-sfc') // 模板
20+
21+
22+
function stripScript$1 (content) {
23+
const result = content.match(/<(script)>([\s\S]+)<\/\1>/);
24+
return result && result[2] ? result[2].trim() : ''
25+
}
26+
27+
function stripStyle (content) {
28+
const result = content.match(/<(style)\s*>([\s\S]+)<\/\1>/);
29+
return result && result[2] ? result[2].trim() : ''
30+
}
31+
32+
// 编写例子时不一定有 template。所以采取的方案是剔除其他的内容
33+
function stripTemplate$1 (content) {
34+
content = content.trim();
35+
if (!content) {
36+
return content
37+
}
38+
content = content.replace(/<(script|style)[\s\S]+<\/\1>/g, '').trim();
39+
// 过滤<template>
40+
const ret = content.match(/<(template)\s*>([\s\S]+)<\/\1>/);
41+
return ret ? ret[2].trim() : content
42+
}
43+
44+
// function pad(source) {
45+
// return source
46+
// .split(/\r?\n/)
47+
// .map(line => ` ${line}`)
48+
// .join('\n')
49+
// }
50+
51+
function genInlineComponentText$1 (template, script) {
52+
// const finalOptions = {
53+
// source: `<div>${template}</div>`,
54+
// filename: 'inline-component', // TODO:这里有待调整
55+
// compiler
56+
// };
57+
58+
// const compiled = compiler.compile(template, { mode: "module"})
59+
const compiled = compiler.compile(template, { prefixIdentifiers: true });
60+
// const compiled = compileTemplate(finalOptions);
61+
62+
// errors
63+
// if (compiled.errors && compiled.errors.length) {
64+
// console.error(
65+
// `\n Error compiling template:\n${pad(compiled.source)}\n` +
66+
// compiled.errors.map(e => ` - ${e}`).join('\n') +
67+
// '\n'
68+
// );
69+
// }
70+
71+
const code = compiled.code.replace(/return\s+?function\s+?render/, () => {
72+
return 'function render '
73+
});
74+
75+
let demoComponentContent = `
76+
${code}
77+
`;
78+
79+
// todo: 这里采用了硬编码有待改进
80+
script = script.trim();
81+
if (script) {
82+
script = script
83+
.replace(/export\s+default/, 'const democomponentExport =')
84+
.replace(/import ([,{}\w\s]+) from (['"\w]+)/g, function (s0, s1, s2) {
85+
if (s2 === `'vue'`) {
86+
return `
87+
const ${s1} = Vue
88+
`
89+
} else if (s2 === `'element3'`) {
90+
return `
91+
const ${s1} = Element3
92+
`
93+
}
94+
});
95+
} else {
96+
script = 'const democomponentExport = {}';
97+
}
98+
demoComponentContent = `(function() {
99+
${demoComponentContent}
100+
${script}
101+
return {
102+
mounted(){
103+
this.$nextTick(()=>{
104+
const blocks = document.querySelectorAll('pre code:not(.hljs)')
105+
Array.prototype.forEach.call(blocks, hljs.highlightBlock)
106+
})
107+
},
108+
render,
109+
...democomponentExport
110+
}
111+
})()`;
112+
return demoComponentContent
113+
}
114+
115+
var util = {
116+
stripScript: stripScript$1,
117+
stripStyle,
118+
stripTemplate: stripTemplate$1,
119+
genInlineComponentText: genInlineComponentText$1
120+
};
121+
122+
// 把md中的demo部分取出来 分成两份,一份代码高亮展示,一份渲染
123+
124+
const mdContainer$1 = require$$0__default$1["default"];
125+
126+
var containers$1 = (md) => {
127+
md.use(mdContainer$1, 'demo', {
128+
validate (params) {
129+
return params.trim().match(/^demo\s*(.*)$/)
130+
},
131+
render (tokens, idx) {
132+
const m = tokens[idx].info.trim().match(/^demo\s*(.*)$/);
133+
if (tokens[idx].nesting === 1) {
134+
const description = m && m.length > 1 ? m[1] : '';
135+
const content =
136+
tokens[idx + 1].type === 'fence' ? tokens[idx + 1].content : '';
137+
return `<demo-block>
138+
${description ? `<div>${md.render(description)}</div>` : ''}
139+
<!--element-demo: ${content}:element-demo-->
140+
`
141+
}
142+
return '</demo-block>'
143+
}
144+
});
145+
146+
md.use(mdContainer$1, 'tip');
147+
md.use(mdContainer$1, 'warning');
148+
return md
149+
};
150+
151+
// 覆盖默认的 fence 渲染策略
152+
var fence = (md) => {
153+
const defaultRender = md.renderer.rules.fence;
154+
md.renderer.rules.fence = (tokens, idx, options, env, self) => {
155+
const token = tokens[idx];
156+
// 判断该 fence 是否在 :::demo 内
157+
const prevToken = tokens[idx - 1];
158+
const isInDemoContainer =
159+
prevToken &&
160+
prevToken.nesting === 1 &&
161+
prevToken.info.trim().match(/^demo\s*(.*)$/);
162+
if (token.info === 'html' && isInDemoContainer) {
163+
return `<template #highlight><pre v-pre><code class="html">${md.utils.escapeHtml(
164+
token.content
165+
)}</code></pre></template>`
166+
}
167+
return defaultRender(tokens, idx, options, env, self)
168+
};
169+
};
170+
171+
// const Config = require('markdown-it-chain') // TODO 将此webpack链式操作改为rollup支持的操作
172+
173+
const hljs = require$$0__default$2["default"];
174+
175+
const containers = containers$1;
176+
const overWriteFenceRule = fence;
177+
178+
179+
// 配置markdown-it常规代码高亮,相关文档:https://markdown-it.github.io/markdown-it/#MarkdownIt.new
180+
const highlight = (str, lang) => {
181+
if (!lang || !hljs.getLanguage(lang)) {
182+
return '<pre><code class="hljs">' + str + '</code></pre>'
183+
}
184+
const html = hljs.highlight(lang, str, true, undefined).value;
185+
return `<pre><code class="hljs language-${lang}">${html}</code></pre>`
186+
};
187+
const md$1 = require$$3__default["default"]({
188+
html: true,
189+
highlight
190+
});
191+
192+
const mdContainer = containers(md$1);
193+
overWriteFenceRule(mdContainer);
194+
// console.log(md, 'ssss')
195+
196+
var config = md$1;
197+
198+
const { createFilter } = require$$0__default$3["default"];
199+
const { stripScript, stripTemplate, genInlineComponentText } = util;
200+
const md = config;
201+
202+
const ext = /\.md$/;
203+
204+
/**
205+
* 将md->vue后文件中展示代码块,转为js组件插入到vue文件中进行展示
206+
* @param {String} vue
207+
* @returns {String} vue
208+
*/
209+
function GenerateDisplayCode (source) {
210+
console.log(source, 'source: ');
211+
const content = md.render(source);
212+
213+
const startTag = '<!--element-demo:';
214+
const startTagLen = startTag.length;
215+
const endTag = ':element-demo-->';
216+
const endTagLen = endTag.length;
217+
218+
let componenetsString = '';
219+
let id = 0; // demo 的 id
220+
const output = []; // 输出的内容
221+
let start = 0; // 字符串开始位置
222+
223+
let commentStart = content.indexOf(startTag);
224+
let commentEnd = content.indexOf(endTag, commentStart + startTagLen);
225+
while (commentStart !== -1 && commentEnd !== -1) {
226+
output.push(content.slice(start, commentStart));
227+
228+
const commentContent = content.slice(commentStart + startTagLen, commentEnd);
229+
const html = stripTemplate(commentContent);
230+
const script = stripScript(commentContent);
231+
232+
const demoComponentContent = genInlineComponentText(html, script);
233+
234+
const demoComponentName = `element-demo${id}`;
235+
output.push(`<template #source><${demoComponentName} /></template>`);
236+
componenetsString += `${JSON.stringify(
237+
demoComponentName,
238+
)}: ${demoComponentContent},`;
239+
240+
// 重新计算下一次的位置
241+
id++;
242+
start = commentEnd + endTagLen;
243+
commentStart = content.indexOf(startTag, start);
244+
commentEnd = content.indexOf(endTag, commentStart + startTagLen);
245+
}
246+
247+
// 仅允许在 demo 不存在时,才可以在 Markdown 中写 script 标签
248+
// todo: 优化这段逻辑
249+
let pageScript = '';
250+
if (componenetsString) {
251+
pageScript = `<script>
252+
import hljs from 'highlight.js'
253+
import * as Vue from "vue"
254+
export default {
255+
name: 'component-doc',
256+
components: {
257+
${componenetsString}
258+
}
259+
}
260+
</script>`;
261+
} else if (content.indexOf('<script>') === 0) {
262+
// 硬编码,有待改善
263+
start = content.indexOf('</script>') + '</script>'.length;
264+
pageScript = content.slice(0, start);
265+
}
266+
267+
output.push(content.slice(start));
268+
return `
269+
<template>
270+
<section class="content element-doc">
271+
${output.join('')}
272+
</section>
273+
</template>
274+
${pageScript}
275+
`
276+
}
277+
278+
279+
var src = function md2vue (options = {}) {
280+
const filter = createFilter(options.include || ['**/*.md'], options.exclude);
281+
return {
282+
name: 'md2vue',
283+
transform (code, id) {
284+
if (!ext.test(id)) return null;
285+
if (!filter(id)) return null;
286+
287+
const data = GenerateDisplayCode(code);
288+
return {
289+
code: `export default ${JSON.stringify(data.toString())};`,
290+
map: { mappings: '' }
291+
};
292+
}
293+
294+
}
295+
};
296+
297+
module.exports = src;

demo.vue

-11
This file was deleted.

examples/button.md

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
## webpack 配置 demo
2+
3+
- 学习 webpack 各个配置属性含义
4+
- 手写 loader
5+
6+
:::demo 这是一个说明
7+
8+
```html
9+
<template>
10+
<button type="text" @click="open">点击打开 Message Box</button>
11+
</template>
12+
13+
<script>
14+
export default {
15+
methods: {
16+
open() {
17+
alert('hello')
18+
},
19+
},
20+
}
21+
</script>
22+
```
23+
24+
:::
25+
26+
## ded

examples/main.js

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import md from './button.md'
2+
console.log('md: ', md)

index.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default require('./src/index.js');

0 commit comments

Comments
 (0)