diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e291365 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 4 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..4b2744b --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,9 @@ +module.exports = { + extends: 'standard', + parser: 'babel-eslint', + rules: { + 'indent': [2, 4], + 'promise/param-names': 0, + 'comma-dangle': [2, 'always-multiline'], + }, +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b92084b --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +dist +*.log +coverage +.DS_Store +node_modules +docs/.vuepress/dist/ + +yarn.lock diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..d6e2deb --- /dev/null +++ b/.npmignore @@ -0,0 +1,5 @@ +test +*.log +coverage +.circleci +docs/.vuepress/dist diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..152b16e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) StEve Young + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e082d85 --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +

vuepress-plugin-demo-code

+ +> demo-code plugin for vuepress. + +借助这个插件,你可以通过下述的语法在展示 demo 的同时,将这段代码展示出来。 + +```md +::: demo +
Click me!
+ + +::: +``` + +## Install + +* 首先安装 [vuepress v1.x](https://github.com/vuejs/vuepress) + +* 接着安装插件 + +```bash +$ npm i -D vuepress-plugin-demo-code +# OR +$ yarn add -D vuepress-plugin-demo-code +``` + +## Usage +配置 vuepress config + +```js +module.exports = { + plugins: ['demo-code'], +} +``` + +## License + +[MIT](http://opensource.org/licenses/MIT) + +Copyright (c) StEve Young diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 0000000..12c1ce8 --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,6 @@ +module.exports = { + // https://www.npmjs.com/package/@commitlint/config-conventional + extends: ['@commitlint/config-conventional'], + "rules": { + }, +} diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js new file mode 100644 index 0000000..7f49327 --- /dev/null +++ b/docs/.vuepress/config.js @@ -0,0 +1,41 @@ +const demoCode = require('../../src/') +const { name, description } = require('../../package.json') + +module.exports = { + base: '/' + name + '/', + locales: { + '/': { title: name, description }, + }, + head: [ + ['link', { rel: 'icon', href: `/favicon.ico` }], + ], + plugins: [demoCode], + evergreen: true, + serviceWorker: true, + themeConfig: { + repo: 'BuptStEve/' + name, + docsDir: 'docs', + nav: [ + { text: 'Guide', link: '/' }, + { text: 'Example', link: '/example/' }, + ], + sidebar: { + '/example/': [{ + title: 'Example', + collapsable: false, + children: [ + '', + ], + }], + '/': [['', 'Guide']], + }, + sidebarDepth: 2, + editLinks: true, + serviceWorker: { + updatePopup: { + message: 'New content is available.', + buttonText: 'Refresh', + }, + }, + }, +} diff --git a/docs/.vuepress/public/favicon.ico b/docs/.vuepress/public/favicon.ico new file mode 100755 index 0000000..a71ad0e Binary files /dev/null and b/docs/.vuepress/public/favicon.ico differ diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..e082d85 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,46 @@ +

vuepress-plugin-demo-code

+ +> demo-code plugin for vuepress. + +借助这个插件,你可以通过下述的语法在展示 demo 的同时,将这段代码展示出来。 + +```md +::: demo +
Click me!
+ + +::: +``` + +## Install + +* 首先安装 [vuepress v1.x](https://github.com/vuejs/vuepress) + +* 接着安装插件 + +```bash +$ npm i -D vuepress-plugin-demo-code +# OR +$ yarn add -D vuepress-plugin-demo-code +``` + +## Usage +配置 vuepress config + +```js +module.exports = { + plugins: ['demo-code'], +} +``` + +## License + +[MIT](http://opensource.org/licenses/MIT) + +Copyright (c) StEve Young diff --git a/docs/example/README.md b/docs/example/README.md new file mode 100644 index 0000000..6e1689e --- /dev/null +++ b/docs/example/README.md @@ -0,0 +1,59 @@ +# Eaxmple + +## 常规操作 +直接把 demo 代码用 `::: demo` 和 `:::` 包裹。即可生成可运行的 demo 和代码。 + +### 使用示例 + +```md +::: demo + + + +::: +``` + +### 实现效果 + +::: demo + + + +::: + +## 修改语言 +你可能注意到了展示代码右上角显示了 `vue`,这里的语言可配置。(默认为 `vue`) + +### 使用示例 + +```md +::: demo html +

+ this is common html +

+::: +``` + +### 实现效果 + +::: demo html +

+ this is common html +

+::: diff --git a/package.json b/package.json new file mode 100644 index 0000000..df91f38 --- /dev/null +++ b/package.json @@ -0,0 +1,79 @@ +{ + "name": "vuepress-plugin-demo-code", + "version": "0.1.0", + "description": "Demo and code plugin for vuepress", + "main": "src/index.js", + "scripts": { + "docs": "vuepress dev docs --host localhost", + "docs:build": "vuepress build docs", + "cov": "open coverage/lcov-report/index.html", + "tdd": "cross-env NODE_ENV=test jest --watch", + "test": "cross-env NODE_ENV=test jest", + "lint": "eslint --fix src/ test/", + "deploy": "yarn docs:build && gh-pages -m \"[ci skip]\" -d docs/.vuepress/dist", + "pub": "npm publish" + }, + "husky": { + "hooks": { + "pre-push": "lint-staged", + "pre-commit": "lint-staged", + "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" + } + }, + "lint-staged": { + "{src,test}/**/*.js": [ + "eslint --fix", + "git add" + ] + }, + "jest": { + "bail": true, + "clearMocks": true, + "transform": { + "^.+\\.js$": "babel-jest" + }, + "collectCoverage": true, + "collectCoverageFrom": [ + "src/**" + ] + }, + "dependencies": { + "@vuepress/shared-utils": "^1.0.0-alpha.32", + "markdown-it-container": "^2.0.0", + "prismjs": "^1.15.0" + }, + "devDependencies": { + "@commitlint/cli": "^7.3.2", + "@commitlint/config-conventional": "^7.3.1", + "babel-eslint": "^10.0.1", + "codecov": "^3.1.0", + "cross-env": "^5.2.0", + "eslint": "^5.12.1", + "eslint-config-standard": "^12.0.0", + "eslint-plugin-import": "^2.15.0", + "eslint-plugin-node": "^8.0.1", + "eslint-plugin-promise": "^4.0.1", + "eslint-plugin-standard": "^4.0.0", + "gh-pages": "^2.0.1", + "husky": "^1.3.1", + "jest": "^24.0.0", + "lint-staged": "^8.1.0", + "rimraf": "^2.6.3", + "vuepress": "^1.0.0-alpha.32" + }, + "keywords": [ + "vue", + "code", + "demo", + "vuepress", + "demo-code", + "documentation" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/BuptStEve/vuepress-plugin-demo-code.git" + }, + "homepage": "https://buptsteve.github.io/vuepress-plugin-demo-code/", + "author": "StEve Young", + "license": "MIT" +} diff --git a/src/DemoAndCode.vue b/src/DemoAndCode.vue new file mode 100644 index 0000000..f73ac9a --- /dev/null +++ b/src/DemoAndCode.vue @@ -0,0 +1,142 @@ + + + + + + + diff --git a/src/enhanceAppFile.js b/src/enhanceAppFile.js new file mode 100644 index 0000000..fc11f40 --- /dev/null +++ b/src/enhanceAppFile.js @@ -0,0 +1,5 @@ +import DemoAndCode from './DemoAndCode.vue' + +export default ({ Vue }) => { + Vue.component('DemoAndCode', DemoAndCode) +} diff --git a/src/highlight.js b/src/highlight.js new file mode 100644 index 0000000..e99f622 --- /dev/null +++ b/src/highlight.js @@ -0,0 +1,51 @@ +// copy from https://github.com/vuejs/vuepress/blob/master/packages/%40vuepress/markdown/lib/highlight.js + +const prism = require('prismjs') +const loadLanguages = require('prismjs/components/index') +const { + logger, + chalk, + escapeHtml, +} = require('@vuepress/shared-utils') + +// required to make embedded highlighting work... +loadLanguages(['markup', 'css', 'javascript']) + +function wrap (code, lang) { + if (lang === 'text') { + code = escapeHtml(code) + } + return `
${code}
` +} + +module.exports = (str, lang) => { + if (!lang) { + return wrap(str, 'text') + } + lang = lang.toLowerCase() + const rawLang = lang + if (lang === 'vue' || lang === 'html') { + lang = 'markup' + } + if (lang === 'md') { + lang = 'markdown' + } + if (lang === 'ts') { + lang = 'typescript' + } + if (lang === 'py') { + lang = 'python' + } + if (!prism.languages[lang]) { + try { + loadLanguages([lang]) + } catch (e) { + logger.warn(chalk.yellow(`[vuepress] Syntax highlight for language "${lang}" is not supported.`)) + } + } + if (prism.languages[lang]) { + const code = prism.highlight(str, prism.languages[lang], lang) + return wrap(code, rawLang) + } + return wrap(str, 'text') +} diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..ccf7114 --- /dev/null +++ b/src/index.js @@ -0,0 +1,57 @@ +const path = require('path') +const getCodeHtml = require('./highlight') +const markdownItContainer = require('markdown-it-container') + +const MARK = 'demo' +const MARKUP = ':::' +const END_TYPE = `container_${MARK}_close` + +module.exports = { + name: 'vuepress-plugin-demo-code', + enhanceAppFiles: [ + path.resolve(__dirname, 'enhanceAppFile.js'), + ], + extendMarkdown: (md) => { + md.use(markdownItContainer, MARK, { + render: (tokens, idx) => { + const { nesting, markup, info } = tokens[idx] + + if (markup !== MARKUP) return '' + if (nesting === -1) { + return '\n' + } + + let htmlStr = '' + let lastLine = 0 + const language = (info.split(MARK)[1] || 'vue').trim() + + for (let index = idx; index < tokens.length; index++) { + const { map, type, content } = tokens[index] + + if (type === END_TYPE) break + if (type === 'html_block') { + const delta = map[0] - (lastLine || map[1]) + + if (delta > 0) { + htmlStr += '\n'.repeat(delta) + } + + htmlStr += content + lastLine = map[1] + } + } + + return ` + + + +