From 6a054ca0e3dd1970a674d38e55c1a06f420f4c2c Mon Sep 17 00:00:00 2001 From: chenjiahan Date: Thu, 30 Sep 2021 10:32:29 +0800 Subject: [PATCH] Revert "chore: remove @vant/markdown-loader package" This reverts commit 6eec7f3c2f9207227c178baedf767f3031e040f8. --- packages/vant-markdown-loader/README.md | 30 +++ packages/vant-markdown-loader/package.json | 20 ++ .../vant-markdown-loader/src/card-wrapper.js | 16 ++ .../vant-markdown-loader/src/extract-demo.js | 42 ++++ .../vant-markdown-loader/src/highlight.js | 10 + packages/vant-markdown-loader/src/index.js | 117 +++++++++ .../vant-markdown-loader/src/link-open.js | 18 ++ .../vant-markdown-loader/src/md-parser.js | 14 ++ .../src/side-effect-tags.js | 14 ++ packages/vant-markdown-loader/yarn.lock | 227 ++++++++++++++++++ 10 files changed, 508 insertions(+) create mode 100644 packages/vant-markdown-loader/README.md create mode 100644 packages/vant-markdown-loader/package.json create mode 100644 packages/vant-markdown-loader/src/card-wrapper.js create mode 100644 packages/vant-markdown-loader/src/extract-demo.js create mode 100644 packages/vant-markdown-loader/src/highlight.js create mode 100644 packages/vant-markdown-loader/src/index.js create mode 100644 packages/vant-markdown-loader/src/link-open.js create mode 100644 packages/vant-markdown-loader/src/md-parser.js create mode 100644 packages/vant-markdown-loader/src/side-effect-tags.js create mode 100644 packages/vant-markdown-loader/yarn.lock diff --git a/packages/vant-markdown-loader/README.md b/packages/vant-markdown-loader/README.md new file mode 100644 index 000000000..5536263c3 --- /dev/null +++ b/packages/vant-markdown-loader/README.md @@ -0,0 +1,30 @@ +# vant-markdown-loader + +Simple and fast vue markdown loader, transform markdown to vue component. + +## Install + +### NPM + +```shell +npm i @vant/markdown-loader -S +``` + +### YARN + +```shell +yarn add @vant/markdown-loader +``` + +## Options + +- `enableMetaData`: Default `false`. Whether to use [front-matter](https://github.com/jxson/front-matter) to extract markdown meta data + +- `linkOpen`: Default `true`. Whether to add target="_blank" to all links + +- `wrapper(html, fm)`: Format the returned content using a custom function + - `html`: The result of [markdown-it](https://github.com/markdown-it/markdown-it)'s render + - `fm`: See [fm(string)](https://github.com/jxson/front-matter#fmstring). If `enableMetaData` option is `false`, the value is `undefined`. + - `attributes` + - `body` + - `frontmatter` diff --git a/packages/vant-markdown-loader/package.json b/packages/vant-markdown-loader/package.json new file mode 100644 index 000000000..5d1bb202d --- /dev/null +++ b/packages/vant-markdown-loader/package.json @@ -0,0 +1,20 @@ +{ + "name": "@vant/markdown-loader", + "version": "4.1.1", + "description": "Simple and fast vue markdown loader", + "main": "src/index.js", + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "license": "MIT", + "repository": "https://github.com/youzan/vant/tree/dev/packages/vant-markdown-loader", + "dependencies": { + "front-matter": "^4.0.2", + "highlight.js": "^10.7.1", + "loader-utils": "^2.0.0", + "markdown-it": "^12.0.4", + "markdown-it-anchor": "^7.1.0", + "transliteration": "^2.2.0" + } +} diff --git a/packages/vant-markdown-loader/src/card-wrapper.js b/packages/vant-markdown-loader/src/card-wrapper.js new file mode 100644 index 000000000..6216cdef9 --- /dev/null +++ b/packages/vant-markdown-loader/src/card-wrapper.js @@ -0,0 +1,16 @@ +module.exports = function cardWrapper(html) { + const group = html + .replace(/

{ + if (fragment.indexOf('${fragment}`; + } + + return fragment; + }) + .join(''); +}; diff --git a/packages/vant-markdown-loader/src/extract-demo.js b/packages/vant-markdown-loader/src/extract-demo.js new file mode 100644 index 000000000..384d36963 --- /dev/null +++ b/packages/vant-markdown-loader/src/extract-demo.js @@ -0,0 +1,42 @@ +const path = require('path'); +const fs = require('fs'); +const os = require('os'); +const parser = require('./md-parser'); + +function hyphenate(str) { + return str.replace(/\B([A-Z])/g, '-$1').toLowerCase(); +} + +module.exports = function extraDemo(content) { + const isWin = /^win/.test(os.platform()); + const markdownDir = path.dirname(this.resourcePath); + const demoLinks = []; + + content = content.replace( + /([\s\S]*?)<\/demo-code>/g, + function (_, attrs, link) { + link = link.trim(); // 去换行符 + const tag = 'demo-code-' + hyphenate(path.basename(link, '.vue')); + let fullLink; + if (isWin) { + fullLink = path.posix.join(...markdownDir.split(path.sep), link); + } else { + fullLink = path.join(markdownDir, link); + } + demoLinks.indexOf(fullLink) === -1 && demoLinks.push(fullLink); + const demoContent = fs.readFileSync(fullLink, { encoding: 'utf8' }); + const demoParseredContent = parser.render( + '```html\n' + demoContent + '\n```' + ); + return ` + + <${tag} /> + + `; + } + ); + + return [content, demoLinks]; +}; diff --git a/packages/vant-markdown-loader/src/highlight.js b/packages/vant-markdown-loader/src/highlight.js new file mode 100644 index 000000000..c5fed8c16 --- /dev/null +++ b/packages/vant-markdown-loader/src/highlight.js @@ -0,0 +1,10 @@ +const hljs = require('highlight.js'); + +module.exports = function highlight(str, lang) { + if (lang && hljs.getLanguage(lang)) { + // https://github.com/highlightjs/highlight.js/issues/2277 + return hljs.highlight(str, { language: lang, ignoreIllegals: true }).value; + } + + return ''; +}; diff --git a/packages/vant-markdown-loader/src/index.js b/packages/vant-markdown-loader/src/index.js new file mode 100644 index 000000000..ece432bcf --- /dev/null +++ b/packages/vant-markdown-loader/src/index.js @@ -0,0 +1,117 @@ +const path = require('path'); +const loaderUtils = require('loader-utils'); +const frontMatter = require('front-matter'); +const parser = require('./md-parser'); +const linkOpen = require('./link-open'); +const cardWrapper = require('./card-wrapper'); +const extractDemo = require('./extract-demo'); +const sideEffectTags = require('./side-effect-tags'); + +function camelize(str) { + return `-${str}`.replace(/-(\w)/g, (_, c) => (c ? c.toUpperCase() : '')); +} + +const sharedVueOptions = `mounted() { + const anchors = [].slice.call(this.$el.querySelectorAll('h2, h3, h4, h5')); + + anchors.forEach(anchor => { + anchor.addEventListener('click', this.scrollToAnchor); + }); + }, + + methods: { + scrollToAnchor(event) { + if (event.target.id) { + this.$router.push({ + name: this.$route.name, + hash: '#' + event.target.id + }) + } + } + }, +`; + +function wrapper(content) { + let demoLinks; + [content, demoLinks] = extractDemo.call(this, content); + content = cardWrapper(content); + + // 不包含 demo-code 的 md 文件,直接使绑定 HTML + if (demoLinks.length === 0) { + content = escape(content); + + return ` + +`; + } + + // 包含 demo-code 的 md 文件,需要走模版渲染 + let styles; + [content, styles] = sideEffectTags(content); + + return ` + + + + +${styles.join('\n')} +`; +} + +module.exports = function (source) { + let options = loaderUtils.getOptions(this) || {}; + this.cacheable && this.cacheable(); + + options = { + wrapper, + linkOpen: true, + ...options, + }; + + let fm; + + if (options.enableMetaData) { + fm = frontMatter(source); + source = fm.body; + } + + if (options.linkOpen) { + linkOpen(parser); + } + + return options.wrapper.call(this, parser.render(source), fm); +}; diff --git a/packages/vant-markdown-loader/src/link-open.js b/packages/vant-markdown-loader/src/link-open.js new file mode 100644 index 000000000..0e513226a --- /dev/null +++ b/packages/vant-markdown-loader/src/link-open.js @@ -0,0 +1,18 @@ +// add target="_blank" to all links +module.exports = function linkOpen(md) { + const defaultRender = + md.renderer.rules.link_open || + function(tokens, idx, options, env, self) { + return self.renderToken(tokens, idx, options); + }; + + md.renderer.rules.link_open = function(tokens, idx, options, env, self) { + const aIndex = tokens[idx].attrIndex('target'); + + if (aIndex < 0) { + tokens[idx].attrPush(['target', '_blank']); // add new attribute + } + + return defaultRender(tokens, idx, options, env, self); + }; +}; diff --git a/packages/vant-markdown-loader/src/md-parser.js b/packages/vant-markdown-loader/src/md-parser.js new file mode 100644 index 000000000..3940d886f --- /dev/null +++ b/packages/vant-markdown-loader/src/md-parser.js @@ -0,0 +1,14 @@ +const MarkdownIt = require('markdown-it'); +const markdownItAnchor = require('markdown-it-anchor'); +const highlight = require('./highlight'); +const { slugify } = require('transliteration'); + +const parser = new MarkdownIt({ + html: true, + highlight, +}).use(markdownItAnchor, { + level: 2, + slugify, +}); + +module.exports = parser; diff --git a/packages/vant-markdown-loader/src/side-effect-tags.js b/packages/vant-markdown-loader/src/side-effect-tags.js new file mode 100644 index 000000000..a7f99fd5b --- /dev/null +++ b/packages/vant-markdown-loader/src/side-effect-tags.js @@ -0,0 +1,14 @@ +module.exports = function sideEffectTags(content) { + const styles = []; + + // 从模版中移除 script 标签 + content = content.replace(/[\s\S]*?<\/script>/g, ''); + + // 从模版中移除 style 标签,并收集到 styles 数组中,以转移为 .vue 文件 的 style 标签 + content = content.replace(/([\s\S]*?)<\/style>/g, (_, css) => { + styles.push(``); + return ''; + }); + + return [content, styles]; +}; diff --git a/packages/vant-markdown-loader/yarn.lock b/packages/vant-markdown-loader/yarn.lock new file mode 100644 index 000000000..c318b4812 --- /dev/null +++ b/packages/vant-markdown-loader/yarn.lock @@ -0,0 +1,227 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.npm.taobao.org/ansi-regex/download/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha1-OIU59VF5vzkznIGvMKZU1p+Hy3U= + +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha1-7dgDYornHATIWuegkG7a00tkiTc= + dependencies: + color-convert "^2.0.1" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.npm.taobao.org/argparse/download/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE= + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.npm.taobao.org/argparse/download/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha1-JG9Q88p4oyQPbJl+ipvR6sSeSzg= + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.npm.taobao.org/big.js/download/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha1-ZfCvOC9Xi83HQr2cKB6cstd2gyg= + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.npm.taobao.org/cliui/download/cliui-7.0.4.tgz?cache=0&sync_timestamp=1604880333411&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcliui%2Fdownload%2Fcliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha1-oCZe5lVHb8gHrqnfPfjfd4OAi08= + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.npm.taobao.org/color-convert/download/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM= + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npm.taobao.org/color-name/download/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha1-wqCah6y95pVD3m9j+jmVyCbFNqI= + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.npm.taobao.org/emoji-regex/download/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha1-6Bj9ac5cz8tARZT4QpY79TFkzDc= + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.npm.taobao.org/emojis-list/download/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha1-VXBmIEatKeLpFucariYKvf9Pang= + +entities@~2.1.0: + version "2.1.0" + resolved "https://registry.npm.taobao.org/entities/download/entities-2.1.0.tgz?cache=0&sync_timestamp=1611535562200&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fentities%2Fdownload%2Fentities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" + integrity sha1-mS0xKc999ocLlsV4WMJJoSD4uLU= + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.npm.taobao.org/escalade/download/escalade-3.1.1.tgz?cache=0&sync_timestamp=1602567437752&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fescalade%2Fdownload%2Fescalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha1-2M/ccACWXFoBdLSoLqpcBVJ0LkA= + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.npm.taobao.org/esprima/download/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha1-E7BM2z5sXRnfkatph6hpVhmwqnE= + +front-matter@^4.0.2: + version "4.0.2" + resolved "https://registry.npm.taobao.org/front-matter/download/front-matter-4.0.2.tgz#b14e54dc745cfd7293484f3210d15ea4edd7f4d5" + integrity sha1-sU5U3HRc/XKTSE8yENFepO3X9NU= + dependencies: + js-yaml "^3.13.1" + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.npm.taobao.org/get-caller-file/download/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha1-T5RBKoLbMvNuOwuXQfipf+sDH34= + +highlight.js@^10.7.1: + version "10.7.1" + resolved "https://registry.npm.taobao.org/highlight.js/download/highlight.js-10.7.1.tgz#a8ec4152db24ea630c90927d6cae2a45f8ecb955" + integrity sha1-qOxBUtsk6mMMkJJ9bK4qRfjsuVU= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.npm.taobao.org/is-fullwidth-code-point/download/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha1-8Rb4Bk/pCz94RKOJl8C3UFEmnx0= + +js-yaml@^3.13.1: + version "3.13.1" + resolved "https://registry.npm.taobao.org/js-yaml/download/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha1-r/FRswv9+o5J4F2iLnQV6d+jeEc= + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +json5@^2.1.2: + version "2.1.3" + resolved "https://registry.npm.taobao.org/json5/download/json5-2.1.3.tgz?cache=0&sync_timestamp=1586045666090&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson5%2Fdownload%2Fjson5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" + integrity sha1-ybD3+pIzv+WAf+ZvzzpWF+1ZfUM= + dependencies: + minimist "^1.2.5" + +linkify-it@^3.0.1: + version "3.0.2" + resolved "https://registry.npm.taobao.org/linkify-it/download/linkify-it-3.0.2.tgz#f55eeb8bc1d3ae754049e124ab3bb56d97797fb8" + integrity sha1-9V7ri8HTrnVASeEkqzu1bZd5f7g= + dependencies: + uc.micro "^1.0.1" + +loader-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.npm.taobao.org/loader-utils/download/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" + integrity sha1-5MrOW4FtQloWa18JfhDNErNgZLA= + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + +markdown-it-anchor@^7.1.0: + version "7.1.0" + resolved "https://registry.npm.taobao.org/markdown-it-anchor/download/markdown-it-anchor-7.1.0.tgz#30fb21497bf59e83ff4d1ddc052d821962e2489e" + integrity sha1-MPshSXv1noP/TR3cBS2CGWLiSJ4= + +markdown-it@^12.0.4: + version "12.0.4" + resolved "https://registry.npm.taobao.org/markdown-it/download/markdown-it-12.0.4.tgz#eec8247d296327eac3ba9746bdeec9cfcc751e33" + integrity sha1-7sgkfSljJ+rDupdGve7Jz8x1HjM= + dependencies: + argparse "^2.0.1" + entities "~2.1.0" + linkify-it "^3.0.1" + mdurl "^1.0.1" + uc.micro "^1.0.5" + +mdurl@^1.0.1: + version "1.0.1" + resolved "https://registry.npm.taobao.org/mdurl/download/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= + +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.npm.taobao.org/minimist/download/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha1-Z9ZgFLZqaoqqDAg8X9WN9OTpdgI= + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.npm.taobao.org/require-directory/download/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.npm.taobao.org/sprintf-js/download/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.0" + resolved "https://registry.npm.taobao.org/string-width/download/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha1-lSGCxGzHssMT0VluYjmSvRY7crU= + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.npm.taobao.org/strip-ansi/download/strip-ansi-6.0.0.tgz?cache=0&sync_timestamp=1573280577145&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstrip-ansi%2Fdownload%2Fstrip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha1-CxVx3XZpzNTz4G4U7x7tJiJa5TI= + dependencies: + ansi-regex "^5.0.0" + +transliteration@^2.2.0: + version "2.2.0" + resolved "https://registry.npm.taobao.org/transliteration/download/transliteration-2.2.0.tgz#e6333cc74b25ef4465bc27086ed8465c9a19211d" + integrity sha1-5jM8x0sl70RlvCcIbthGXJoZIR0= + dependencies: + yargs "^16.1.0" + +uc.micro@^1.0.1, uc.micro@^1.0.5: + version "1.0.6" + resolved "https://registry.npm.taobao.org/uc.micro/download/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" + integrity sha1-nEEagCpAmpH8bPdAgbq6NLJEmaw= + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.npm.taobao.org/wrap-ansi/download/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha1-Z+FFz/UQpqaYS98RUpEdadLrnkM= + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +y18n@^5.0.5: + version "5.0.5" + resolved "https://registry.npm.taobao.org/y18n/download/y18n-5.0.5.tgz?cache=0&sync_timestamp=1609798602895&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fy18n%2Fdownload%2Fy18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" + integrity sha1-h2nsCNA7HqLfJQCs71YXQ7u5qxg= + +yargs-parser@^20.2.2: + version "20.2.7" + resolved "https://registry.npm.taobao.org/yargs-parser/download/yargs-parser-20.2.7.tgz?cache=0&sync_timestamp=1615404893185&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fyargs-parser%2Fdownload%2Fyargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a" + integrity sha1-Yd+FwRPt+1p6TjbriqYO9CPLyQo= + +yargs@^16.1.0: + version "16.2.0" + resolved "https://registry.npm.taobao.org/yargs/download/yargs-16.2.0.tgz?cache=0&sync_timestamp=1617506941953&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fyargs%2Fdownload%2Fyargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha1-HIK/D2tqZur85+8w43b0mhJHf2Y= + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2"