forked from jd-opensource/nutui
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
603 additions
and
250 deletions.
There are no files selected for viewing
This file contains 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 |
---|---|---|
@@ -0,0 +1 @@ | ||
/src/packages/**/*.md |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains 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 |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/* eslint-disable @typescript-eslint/no-var-requires */ | ||
const Config = require('markdown-it-chain'); | ||
const anchorPlugin = require('markdown-it-anchor'); | ||
const slugify = require('transliteration').slugify; | ||
const hljs = require('highlight.js'); | ||
const containers = require('./containers'); | ||
const overWriteFenceRule = require('./fence'); | ||
|
||
const config = new Config(); | ||
|
||
const highlight = (str, lang) => { | ||
if (!lang || !hljs.getLanguage(lang)) { | ||
return '<pre><code class="hljs">' + str + '</code></pre>'; | ||
} | ||
const html = hljs.highlight(lang, str, true, undefined).value; | ||
return `<pre><code class="hljs language-${lang}">${html}</code></pre>`; | ||
}; | ||
|
||
config.options | ||
.html(true) | ||
.highlight(highlight) | ||
.end() | ||
|
||
// .plugin('anchor').use(anchorPlugin, [ | ||
// { | ||
// level: 2, | ||
// slugify: slugify, | ||
// permalink: false, | ||
// permalinkBefore: false, | ||
// }, | ||
// ]).end() | ||
|
||
.plugin('containers') | ||
.use(containers) | ||
.end(); | ||
|
||
const md = config.toMd(); | ||
overWriteFenceRule(md); | ||
|
||
module.exports = md; |
This file contains 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 |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/* eslint-disable @typescript-eslint/no-var-requires */ | ||
const mdContainer = require('markdown-it-container'); | ||
|
||
module.exports = md => { | ||
md.use(mdContainer, 'demo', { | ||
validate(params) { | ||
return params.trim().match(/^demo\s*(.*)$/); | ||
}, | ||
render(tokens, idx) { | ||
const m = tokens[idx].info.trim().match(/^demo\s*(.*)$/); | ||
if (tokens[idx].nesting === 1) { | ||
const description = m && m.length > 1 ? m[1] : ''; | ||
const content = tokens[idx + 1].type === 'fence' ? tokens[idx + 1].content : ''; | ||
return `<demo-block> | ||
${description ? `<div>${md.render(description)}</div>` : ''} | ||
<!--nutui-demo: ${content}:nutui-demo--> | ||
`; | ||
} | ||
return '</demo-block>'; | ||
} | ||
}); | ||
|
||
md.use(mdContainer, 'tip'); | ||
md.use(mdContainer, 'warning'); | ||
}; |
This file contains 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 |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// 覆盖默认的 fence 渲染策略 | ||
module.exports = md => { | ||
const defaultRender = md.renderer.rules.fence; | ||
md.renderer.rules.fence = (tokens, idx, options, env, self) => { | ||
const token = tokens[idx]; | ||
// 判断该 fence 是否在 :::demo 内 | ||
const prevToken = tokens[idx - 1]; | ||
const isInDemoContainer = prevToken && prevToken.nesting === 1 && prevToken.info.trim().match(/^demo\s*(.*)$/); | ||
if (token.info === 'html' && isInDemoContainer) { | ||
return `<template #highlight><pre v-pre><code class="html">${md.utils.escapeHtml(token.content)}</code></pre></template>`; | ||
} | ||
return defaultRender(tokens, idx, options, env, self); | ||
}; | ||
}; |
This file was deleted.
Oops, something went wrong.
This file contains 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,80 +1,68 @@ | ||
const loaderUtils = require('loader-utils'); //:loader-utils 是一个npm i loader-utils -D 安装的插件,便于获取webpack.config.js 中配置loader的options; | ||
const MarkdownIt = require('markdown-it'); //渲染 markdown 基本语法 | ||
const markdownItAnchor = require('markdown-it-anchor'); //为各级标题添加锚点 | ||
const frontMatter = require('front-matter'); //就是md文档最上面的内容 类似于docz中的路由/标题的设置部分 | ||
const highlight = require('./highlight'); | ||
const linkOpen = require('./link-open'); | ||
const cardWrapper = require('./card-wrapper'); | ||
const codeWrapper = require('./code-wrapper'); | ||
/* eslint-disable @typescript-eslint/no-var-requires */ | ||
const { stripScript, stripTemplate, genInlineComponentText } = require('./util'); | ||
const md = require('./config'); | ||
|
||
const { slugify } = require('transliteration'); | ||
|
||
function wrapper(content) { | ||
content = cardWrapper(content); | ||
content = codeWrapper(content); | ||
content = escape(content); | ||
|
||
return ` | ||
import { h } from 'vue'; | ||
module.exports = function(source) { | ||
const content = md.render(source); | ||
|
||
const content = unescape(\`${content}\`); | ||
const startTag = '<!--nutui-demo:'; | ||
const startTagLen = startTag.length; | ||
const endTag = ':nutui-demo-->'; | ||
const endTagLen = endTag.length; | ||
|
||
export default { | ||
mounted() { | ||
const anchors = [].slice.call(this.$el.querySelectorAll('h2, h3, h4, h5')); | ||
let componenetsString = ''; | ||
let id = 0; // demo 的 id | ||
let output = []; // 输出的内容 | ||
let start = 0; // 字符串开始位置 | ||
|
||
anchors.forEach(anchor => { | ||
anchor.addEventListener('click', this.scrollToAnchor); | ||
}); | ||
}, | ||
let commentStart = content.indexOf(startTag); | ||
let commentEnd = content.indexOf(endTag, commentStart + startTagLen); | ||
while (commentStart !== -1 && commentEnd !== -1) { | ||
output.push(content.slice(start, commentStart)); | ||
|
||
methods: { | ||
scrollToAnchor(event) { | ||
if (event.target.id) { | ||
this.$router.push({ | ||
path: this.$route.path, | ||
hash: '#'+event.target.id | ||
}) | ||
} | ||
} | ||
}, | ||
const commentContent = content.slice(commentStart + startTagLen, commentEnd); | ||
const html = stripTemplate(commentContent); | ||
const script = stripScript(commentContent); | ||
let demoComponentContent = genInlineComponentText(html, script); | ||
const demoComponentName = `nutui-demo${id}`; | ||
output.push(`<template #source><${demoComponentName} /></template>`); | ||
componenetsString += `${JSON.stringify(demoComponentName)}: ${demoComponentContent},`; | ||
|
||
render() { | ||
return h('section', { innerHTML: content }); | ||
// 重新计算下一次的位置 | ||
id++; | ||
start = commentEnd + endTagLen; | ||
commentStart = content.indexOf(startTag, start); | ||
commentEnd = content.indexOf(endTag, commentStart + startTagLen); | ||
} | ||
}; | ||
`; | ||
} | ||
|
||
const parser = new MarkdownIt({ | ||
html: true, | ||
linkify: true, | ||
highlight | ||
}).use(markdownItAnchor, { | ||
level: 2, // 添加超链接锚点的最小标题级别, 如: #标题 不会添加锚点 | ||
slugify // 自定义slugify, 我们使用的是将中文转为汉语拼音,最终生成为标题id属性 | ||
}); | ||
|
||
module.exports = function(source) { | ||
let options = loaderUtils.getOptions(this) || {}; // 获取loader的参数 | ||
this.cacheable && this.cacheable(); | ||
|
||
options = { | ||
wrapper, | ||
linkOpen: true, | ||
...options | ||
}; | ||
|
||
let fm; | ||
// 仅允许在 demo 不存在时,才可以在 Markdown 中写 script 标签 | ||
// todo: 优化这段逻辑 | ||
|
||
if (options.enableMetaData) { | ||
fm = frontMatter(source); | ||
source = fm.body; | ||
} | ||
|
||
if (options.linkOpen) { | ||
linkOpen(parser); | ||
let pageScript = ''; | ||
if (componenetsString) { | ||
pageScript = `<script lang="ts"> | ||
import * as Vue from 'vue'; | ||
export default { | ||
name: 'component-doc', | ||
components: { | ||
${componenetsString} | ||
} | ||
} | ||
</script>`; | ||
} else if (content.indexOf('<script>') === 0) { | ||
// 硬编码,有待改善 | ||
start = content.indexOf('</script>') + '</script>'.length; | ||
pageScript = content.slice(0, start); | ||
} | ||
|
||
return options.wrapper(parser.render(source), fm); | ||
output.push(content.slice(start)); | ||
const result = ` | ||
<template> | ||
<section class="content nutui-doc"> | ||
${output.join('')} | ||
</section> | ||
</template> | ||
${pageScript} | ||
`; | ||
return result; | ||
}; |
This file was deleted.
Oops, something went wrong.
This file contains 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 |
---|---|---|
@@ -0,0 +1,82 @@ | ||
/* eslint-disable @typescript-eslint/no-var-requires */ | ||
const { compileTemplate, TemplateCompiler } = require('@vue/compiler-sfc'); | ||
|
||
function stripScript(content) { | ||
const result = content.match(/<(script)>([\s\S]+)<\/\1>/); | ||
return result && result[2] ? result[2].trim() : ''; | ||
} | ||
|
||
function stripStyle(content) { | ||
const result = content.match(/<(style)\s*>([\s\S]+)<\/\1>/); | ||
return result && result[2] ? result[2].trim() : ''; | ||
} | ||
|
||
// 编写例子时不一定有 template。所以采取的方案是剔除其他的内容 | ||
function stripTemplate(content) { | ||
content = content.trim(); | ||
if (!content) { | ||
return content; | ||
} | ||
return content.replace(/<(script|style)[\s\S]+<\/\1>/g, '').trim(); | ||
} | ||
|
||
function pad(source) { | ||
return source | ||
.split(/\r?\n/) | ||
.map(line => ` ${line}`) | ||
.join('\n'); | ||
} | ||
|
||
const templateReplaceRegex = /<template>([\s\S]+)<\/template>/g; | ||
function genInlineComponentText(template, script) { | ||
// https://github.com/vuejs/vue-loader/blob/423b8341ab368c2117931e909e2da9af74503635/lib/loaders/templateLoader.js#L46 | ||
let source = template; | ||
if (templateReplaceRegex.test(source)) { | ||
source = source.replace(templateReplaceRegex, '$1'); | ||
} | ||
const finalOptions = { | ||
source: `<div>${source}</div>`, | ||
filename: 'inline-component', // TODO:这里有待调整 | ||
compiler: TemplateCompiler, | ||
compilerOptions: { | ||
mode: 'function' | ||
} | ||
}; | ||
const compiled = compileTemplate(finalOptions); | ||
// tips | ||
if (compiled.tips && compiled.tips.length) { | ||
compiled.tips.forEach(tip => { | ||
console.warn(tip); | ||
}); | ||
} | ||
// errors | ||
if (compiled.errors && compiled.errors.length) { | ||
console.error(`\n Error compiling template:\n${pad(compiled.source)}\n` + compiled.errors.map(e => ` - ${e}`).join('\n') + '\n'); | ||
} | ||
let demoComponentContent = ` | ||
${compiled.code.replace('return function render', 'function render')} | ||
`; | ||
// todo: 这里采用了硬编码有待改进 | ||
script = script.trim(); | ||
if (script) { | ||
script = script.replace(/export\s+default/, 'const democomponentExport =').replace(/import ({.*}) from 'vue'/g, (s, s1) => `const ${s1} = Vue`); | ||
} else { | ||
script = 'const democomponentExport = {}'; | ||
} | ||
demoComponentContent = `(function() { | ||
${demoComponentContent} | ||
${script} | ||
return { | ||
render, | ||
...democomponentExport | ||
} | ||
})()`; | ||
return demoComponentContent; | ||
} | ||
|
||
module.exports = { | ||
stripScript, | ||
stripStyle, | ||
stripTemplate, | ||
genInlineComponentText | ||
}; |
Oops, something went wrong.