├── .editorconfig ├── .eslintignore ├── .eslintrc.cjs ├── .github ├── FUNDING.yml └── workflows │ └── release-tag.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.config.ts ├── docs ├── .vuepress │ ├── client.js │ ├── components │ │ └── table │ │ │ └── demo.vue │ ├── config.js │ └── public │ │ └── logo.png ├── guide │ ├── card.md │ ├── index.md │ └── other.md └── index.md ├── package.json ├── pnpm-lock.yaml ├── scripts └── verifyCommit.ts ├── src ├── client │ ├── components │ │ ├── Demo.vue │ │ ├── DemoBlock.vue │ │ ├── Message.vue │ │ └── message.js │ ├── config.ts │ ├── index.ts │ ├── styles │ │ ├── code.scss │ │ ├── index.scss │ │ └── layout.scss │ └── utils.ts ├── node │ ├── demoblock.ts │ ├── index.ts │ ├── markdown-it-container.d.ts │ ├── patterns.ts │ ├── render.ts │ └── utils.ts ├── types │ └── index.ts └── utils │ ├── getDirname.ts │ ├── index.ts │ └── util.ts ├── tsconfig.json └── v1.md /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: 'vue-eslint-parser', 3 | parserOptions: { 4 | parser: '@typescript-eslint/parser', // Specifies the ESLint parser 5 | ecmaVersion: 'latest', // Allows for the parsing of modern ECMAScript features 6 | sourceType: 'module', // Allows for the use of imports 7 | ecmaFeatures: { 8 | tsx: true, 9 | jsx: true 10 | } 11 | }, 12 | env: { 13 | node: true, 14 | browser: true 15 | }, 16 | plugins: ['@typescript-eslint'], 17 | extends: [ 18 | 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin 19 | 'plugin:vue/vue3-recommended', 20 | 'plugin:prettier/recommended' // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array. 21 | ], 22 | rules: { 23 | // js 24 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 25 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 26 | 'no-alert': process.env.NODE_ENV === 'production' ? 'error' : 'off', 27 | 'no-tabs': 2, // 禁止使用tabs 28 | 'no-useless-escape': 0, 29 | 'no-var': 2, // 使用let和const代替var 30 | 'no-mixed-spaces-and-tabs': 2, // 不允许混用tab和空格 31 | 'no-useless-return': 0, 32 | 'arrow-parens': 0, 33 | 'prefer-const': 2, // 优先使用const 34 | eqeqeq: 2, // 必须使用全等 35 | camelcase: 0, // 禁用驼峰命名检测 36 | // ts 37 | '@typescript-eslint/no-explicit-any': 0, 38 | '@typescript-eslint/ban-types': 0, 39 | '@typescript-eslint/no-empty-interface': 0, 40 | '@typescript-eslint/no-empty-function': 0, 41 | '@typescript-eslint/explicit-module-boundary-types': 0, 42 | '@typescript-eslint/ban-ts-comment': 0, 43 | '@typescript-eslint/no-non-null-assertion': 0, 44 | '@typescript-eslint/no-unused-vars': 1, 45 | '@typescript-eslint/no-var-requires': 0, 46 | '@typescript-eslint/consistent-type-imports': 1, 47 | '@typescript-eslint/no-this-alias': [ 48 | 'error', 49 | { 50 | allowedNames: [ 'self', 'context' ], // Allow `const self = this`; `[]` by default 51 | }, 52 | ], 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ['https://xinlei3166.com/about.html#sponsor'] 13 | -------------------------------------------------------------------------------- /.github/workflows/release-tag.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 5 | 6 | name: Create Release 7 | 8 | jobs: 9 | build: 10 | name: Create Release 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@master 15 | - name: Create Release for Tag 16 | id: release_tag 17 | uses: yyx990803/release-tag@master 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | with: 21 | tag_name: ${{ github.ref }} 22 | body: | 23 | Please refer to [CHANGELOG.md](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/blob/main/CHANGELOG.md) for details. 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | .cache 7 | .temp 8 | 9 | # local env files 10 | .env.local 11 | .env.*.local 12 | 13 | # Log files 14 | npm-debug.log* 15 | yarn-debug.log* 16 | yarn-error.log* 17 | pnpm-debug.log* 18 | 19 | # Editor directories and files 20 | .fleet 21 | .idea 22 | .vscode 23 | *.suo 24 | *.ntvs* 25 | *.njsproj 26 | *.sln 27 | *.sw? 28 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xinlei3166/vuepress-plugin-demoblock-plus/117ea9fd1377ce5c17326e83b374725cd85e96cd/.prettierignore -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | printWidth: 100 2 | semi: false 3 | singleQuote: true 4 | proseWrap: "never" 5 | arrowParens: "avoid" 6 | bracketSpacing: true 7 | #disableLanguages": ["custom"] 8 | endOfLine: "auto" 9 | eslintIntegration: true 10 | htmlWhitespaceSensitivity: "ignore" # 属性标签格式化符合预期 11 | ignorePath: ".prettierignore" 12 | jsxBracketSameLine: false 13 | jsxSingleQuote: false 14 | #parser: vue 15 | stylelintIntegration: true 16 | trailingComma: "none" 17 | tslintIntegration: false 18 | vueIndentScriptAndStyle: false 19 | embeddedLanguageFormatting: "auto" 20 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [2.0.4](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v2.0.3...v2.0.4) (2022-12-08) 2 | 3 | 4 | 5 | ## [2.0.3](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v2.0.2...v2.0.3) (2022-12-07) 6 | 7 | 8 | ### Bug Fixes 9 | 10 | * 临时解决 setup 语法报错,锁定 vue 版本为 3.2.44。 ([9502140](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/9502140baf1c7dfeae6a416a3ce4a5dca9ed7a8a)) 11 | 12 | 13 | 14 | ## [2.0.2](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v2.0.1...v2.0.2) (2022-11-23) 15 | 16 | 17 | ### Bug Fixes 18 | 19 | * 使用demoblock 插件不传参数报错的问题 ([391d206](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/391d206b6d14500f5ae17972a6aa1cf1e8ec642a)) 20 | 21 | 22 | 23 | ## [2.0.1](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v2.0.0...v2.0.1) (2022-11-10) 24 | 25 | 26 | 27 | # [2.0.0](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v2.0.0-beta.7...v2.0.0) (2022-11-04) 28 | 29 | 30 | 31 | # [2.0.0-beta.7](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v2.0.0-beta.6...v2.0.0-beta.7) (2022-11-04) 32 | 33 | 34 | 35 | # [2.0.0-beta.6](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v2.0.0-beta.5...v2.0.0-beta.6) (2022-09-14) 36 | 37 | 38 | 39 | # [2.0.0-beta.5](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v2.0.0-beta.4...v2.0.0-beta.5) (2022-09-06) 40 | 41 | 42 | 43 | # [2.0.0-beta.4](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v2.0.0-beta.3...v2.0.0-beta.4) (2022-09-06) 44 | 45 | 46 | 47 | # [2.0.0-beta.3](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v2.0.0-beta.2...v2.0.0-beta.3) (2022-09-06) 48 | 49 | 50 | 51 | # [2.0.0-beta.2](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v2.0.0-beta.1...v2.0.0-beta.2) (2022-09-06) 52 | 53 | 54 | ### Features 55 | 56 | * add cjs format ([7f72d85](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/7f72d85dacd995a59ad2d4f0af90be932c2e2f98)) 57 | 58 | 59 | 60 | # [2.0.0-beta.1](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v1.6.0...v2.0.0-beta.1) (2022-09-06) 61 | 62 | 63 | ### Features 64 | 65 | * support customStyleTagName ([16527c7](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/16527c797963ca44a21ba6ef4493485936e0f558)) 66 | * v2.0 开发中,使用TypeScript重写,支持vuepress-next新版 ([ed6ef3e](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/ed6ef3e756974aff3f3ba52dcaf3d6605fcf317d)) 67 | 68 | 69 | 70 | # [1.6.0](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v1.5.1...v1.6.0) (2022-05-30) 71 | 72 | 73 | ### Bug Fixes 74 | 75 | * require ID's warning ([529b1d6](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/529b1d639c88051164cce4f4a2da7ba110218b12)) 76 | 77 | 78 | ### Features 79 | 80 | * inline-component-filename ([d0bc874](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/d0bc874879014a6f52858c22b271cd4dc05a9f9f)) 81 | 82 | 83 | 84 | ## [1.5.1](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v1.5.0...v1.5.1) (2021-12-15) 85 | 86 | 87 | ### Bug Fixes 88 | 89 | * shiki css var --shiki-token-punctuation color ([699763e](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/699763e6912b363b943b9deff8eb764071633fd2)) 90 | 91 | 92 | 93 | # [1.5.0](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v1.5.0-beta.2...v1.5.0) (2021-11-30) 94 | 95 | 96 | 97 | # [1.5.0-beta.2](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v1.5.0-beta.1...v1.5.0-beta.2) (2021-11-30) 98 | 99 | 100 | 101 | # [1.5.0-beta.1](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v1.4.2...v1.5.0-beta.1) (2021-11-29) 102 | 103 | 104 | ### Features 105 | 106 | * support vuepress darkMode, support shiki css-variables and custom demoblock style ([b251cb2](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/b251cb2d29ee8c4211e3ed244779f001b7956917)) 107 | 108 | 109 | 110 | ## [1.4.2](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v1.4.1...v1.4.2) (2021-11-26) 111 | 112 | 113 | ### Bug Fixes 114 | 115 | * folded width of the demoblock ([bbd9554](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/bbd9554080409295ee0745d650948ce9dc06dd84)) 116 | 117 | 118 | 119 | ## [1.4.1](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v1.4.0...v1.4.1) (2021-11-10) 120 | 121 | 122 | ### Features 123 | 124 | * message toast style ([80152b9](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/80152b92852bab5ecef9d22174324dc486584cd5)) 125 | 126 | 127 | 128 | # [1.4.0](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v1.3.2...v1.4.0) (2021-11-10) 129 | 130 | 131 | ### Features 132 | 133 | * 复制代码后 toast 展示成功 ([9540d3b](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/9540d3bb5d8b160e3b119f9d3f5faaefd5892eb5)) 134 | 135 | 136 | 137 | ## [1.3.2](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v1.3.0...v1.3.2) (2021-10-29) 138 | 139 | 140 | ### Bug Fixes 141 | 142 | * 图标去iconfont,移动端展开后收起时controlText不消失的问题 ([924535f](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/924535fb9540a8e3a23026107c32ab5267e34bde)) 143 | 144 | 145 | ### Features 146 | 147 | * demo source overflow auto ([43d06d4](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/43d06d4c088f141fca1c7991cb3ad90017ba4575)) 148 | 149 | 150 | 151 | # [1.3.0](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v1.2.7...v1.3.0) (2021-10-27) 152 | 153 | 154 | ### Features 155 | 156 | * support custom scriptImports and custom scriptReplaces, see readme.md for details ([6c6216f](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/6c6216fb71768262c239139c5c39e284a5adfdc3)) 157 | 158 | 159 | 160 | ## [1.2.7](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v1.2.6...v1.2.7) (2021-10-25) 161 | 162 | 163 | 164 | ## [1.2.6](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v1.2.5...v1.2.6) (2021-10-21) 165 | 166 | 167 | ### Bug Fixes 168 | 169 | * **Demo:** 已经有一个展开操作区固定在下方的情况下,再展开一个会出现重叠的现象 ([d00e4a9](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/d00e4a95094ecde0a0570ed0dfab9b8f09e5713c)) 170 | 171 | 172 | 173 | ## [1.2.5](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v1.2.3...v1.2.5) (2021-10-21) 174 | 175 | 176 | ### Bug Fixes 177 | 178 | * **Demo:** 已经有一个展开操作区固定在下方的情况下,再展开一个会出现重叠的现象 ([2afc842](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/2afc842aa4b19f0e4629e9a64e67146203c93847)) 179 | 180 | 181 | 182 | ## [1.2.3](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v1.2.0...v1.2.3) (2021-10-21) 183 | 184 | 185 | ### Bug Fixes 186 | 187 | * **Demo:** demo-block-control样式问题;代码展开/收起的操作区未遮挡语言的问题 ([865af44](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/865af44fd49fd37de470b4a9dba11438bc73515f)) 188 | 189 | 190 | 191 | # [1.2.0](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v1.1.1...v1.2.0) (2021-10-20) 192 | 193 | 194 | ### Features 195 | 196 | * support css preprocessors, remove lodash-es, Optimize package volume, reduce first load time ([54c0592](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/54c059222b2993f199aa883101e700de3b61cace)) 197 | 198 | 199 | 200 | ## [1.1.1](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v1.1.0...v1.1.1) (2021-08-20) 201 | 202 | 203 | ### Features 204 | 205 | * copy success text i18n ([4811082](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/4811082a70031ac1154e86be89babdb439378171)) 206 | * path is imported from @vuepress/utils ([3f73afd](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/3f73afdd67ee795352e003784486657dd86e45fb)) 207 | 208 | 209 | 210 | # [1.1.0](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v1.0.4...v1.1.0) (2021-08-19) 211 | 212 | 213 | ### Features 214 | 215 | * vuepress-plugin-demoblock-plus@1.1.0, support script setup and typescript, support custom scriptImports and custom lang ([38d7235](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/38d7235f810f91e7c8df08cb37738f3b04070bba)) 216 | 217 | 218 | 219 | ## [1.0.4](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v1.0.3...v1.0.4) (2021-07-19) 220 | 221 | 222 | 223 | ## [1.0.3](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v1.0.2...v1.0.3) (2021-07-01) 224 | 225 | 226 | ### Features 227 | 228 | * use lodash-es ([15d2e7a](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/15d2e7a18705f28727e480dbbc56818903c6a13a)) 229 | 230 | 231 | 232 | ## [1.0.2](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/v1.0.1...v1.0.2) (2021-07-01) 233 | 234 | 235 | ### Features 236 | 237 | * clipboard-copy, update README.md ([0a4a7a5](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/0a4a7a59c016f8016b2410c75fa60aef05e3abb8)) 238 | 239 | 240 | 241 | ## [1.0.1](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/compare/a5441902890258207b3ba3f10b6533e19c7d38c3...v1.0.1) (2021-06-30) 242 | 243 | 244 | ### Features 245 | 246 | * npm package name too similar to existing packages, rename vuepress-plugin-demoblock to vuepress-plugin-demoblock-plus ([a544190](https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/commit/a5441902890258207b3ba3f10b6533e19c7d38c3)) 247 | 248 | 249 | 250 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021-present, 君惜 (xinlei3166) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vuepress-plugin-demoblock-plus 2 | 3 | > 这是2.x版本的文档,已经采用TypeScript和ESM规范重写,如果使用1.x版本请看[v1文档](v1.md)。 4 | 5 | ## 简介 6 | 7 | vuepress-plugin-demoblock-plus 是一个基于 Vuepress 2.x 的插件,它可以帮助你在编写文档的时候增加 Vue 示例,它的诞生初衷是为了降低编写组件文档时增加一些相关示例的难度。 8 | 9 | 使用 Vuepress 编写组件示例有以下不足之处: 10 | * 1.组件示例和示例代码本质上一样,却需要写两遍。 11 | * 2.Vuepress 无法渲染 Markdown 中的 script 和 style 代码块。 12 | 13 | vuepress-plugin-demoblock-plus 参考了 [Element UI](https://github.com/element-plus/element-plus) 的文档渲染,实现了和它一样的,可在 Markdown 中直接编写示例的语法。 14 | 15 | [查看Demo](https://xinlei3166.github.io/vuepress-demo/) 16 | 17 | ## 提示 18 | 由于vuepress版本不稳定,目前锁定版本为2.0.0-beta.51。 19 | 待vuepress新版本稳定文档更新后,文档和插件会同步修改。 20 | 21 | 在vue@3.2.45版本下setup语法报错,锁定vue版本为3.2.44。 22 | 23 | 24 | ## 安装 25 | 26 | ```bash 27 | npm install -D vuepress-plugin-demoblock-plus 28 | yarn add -D vuepress-plugin-demoblock-plus 29 | pnpm add -D vuepress-plugin-demoblock-plus 30 | ``` 31 | 32 | 33 | 34 | ## 快速上手 35 | 36 | .vuepress/config.js文件中使用插件 37 | 38 | import { demoblockPlugin } from 'vuepress-plugin-demoblock-plus' 39 | 40 | ```js 41 | plugins: [demoblockPlugin()] 42 | ``` 43 | 44 | 45 | ## 更多用法 46 | 47 | markdown 中的vue代码包含的style内容,会被组合成一个style统一处理,如果需要使用css预处理器,需要提前指定并且手动安装使用的css预处理器。 48 | 49 | ```js 50 | plugins: [demoblockPlugin({ cssPreprocessor: 'less' })] 51 | ``` 52 | 53 | 自定义style tag name 54 | 55 | ```js 56 | plugins: [ 57 | demoblockPlugin({ 58 | customStyleTagName: 'style lang="less"' // style标签会解析为 94 | 95 | 98 | ``` 99 | 100 | 101 | ## 多语言支持(默认是中文) 102 | 103 | ```js 104 | const locales = { 105 | '/': { 106 | 'hide-text': 'Hide', 107 | 'show-text': 'Expand', 108 | 'copy-button-text': 'Copy', 109 | 'copy-success-text': 'Copy success' 110 | }, 111 | '/zh': { 112 | 'hide-text': '隐藏代码', 113 | 'show-text': '显示代码', 114 | 'copy-button-text': '复制代码片段', 115 | 'copy-success-text': '复制成功' 116 | } 117 | } 118 | 119 | plugins: [demoblockPlugin({ locales })] 120 | ``` 121 | 122 | 123 | ## 自定义主题 124 | 支持vuepress darkMode,.vuepress/config.js 文件中配置 `darkMode: true` 125 | 126 | ``` 127 | themeConfig: { darkMode: true } 128 | ``` 129 | 130 | 使用 shiki 默认自带的主题代码高亮 131 | 132 | ```js 133 | plugins: [ 134 | demoblockPlugin({ 135 | theme: 'github-light', 136 | }) 137 | ] 138 | ``` 139 | 140 | 使用 shiki css-variables 自定义代码高亮,theme 参数设置为 `css-variables` 141 | ```css 142 | :root { 143 | --shiki-color-text: #24292f; 144 | --shiki-color-background: #ffffff; 145 | --shiki-token-constant: #0550ae; 146 | --shiki-token-string: #24292f; 147 | --shiki-token-comment: #6e7781; 148 | --shiki-token-keyword: #cf222e; 149 | --shiki-token-parameter: #24292f; 150 | --shiki-token-function: #8250df; 151 | --shiki-token-string-expression: #0a3069; // #116329 152 | --shiki-token-punctuation: #24292f; 153 | //--shiki-token-link: #000012; 154 | } 155 | 156 | html.dark { 157 | --shiki-color-text: #c9d1d9; 158 | --shiki-color-background: #0d1117; 159 | --shiki-token-constant: #79c0ff; 160 | --shiki-token-string: #a5d6ff; 161 | --shiki-token-comment: #8b949e; 162 | --shiki-token-keyword: #ff7b72; 163 | --shiki-token-parameter: #c9d1d9; 164 | --shiki-token-function: #d2a8ff; 165 | --shiki-token-string-expression: #a5d6ff; // #7ee787; 166 | --shiki-token-punctuation: #c9d1d9; 167 | //--shiki-token-link: #000012; 168 | } 169 | ``` 170 | 171 | > 如果出现类似这个错误 `Error: ENOENT: no such file or directory, node_modules/shiki/themes/css-variables.json`, 172 | > 这是因为 `shiki css-variables` 需要更高版本才能使用,删除 `node_modules`,重新安装 `@vuepress/plugin-shiki` 和 `vuepress-vite` 173 | 174 | 175 | 通过配置 customClass 类名称,自定义demoblock样式 176 | ```js 177 | plugins: [ 178 | demoblockPlugin({ 179 | customClass: 'demoblock-custom', 180 | }) 181 | ] 182 | ``` 183 | 184 | 通过配置暴露的 css-variables,自定义demoblock样式, --code-bg-color 是代码块的背景色,light和dark模式下背景色要区分 185 | ```css 186 | :root { 187 | --code-bg-color: #f9fafb; 188 | --demoblock-border: var(--c-border); 189 | --demoblock-control: #d3dce6; 190 | --demoblock-control-bg: var(--c-bg); 191 | --demoblock-control-bg-hover: #f9fafc; 192 | --demoblock-description-bg: var(--c-bg); 193 | } 194 | 195 | html.dark { 196 | --code-bg-color: #282c34; 197 | --demoblock-border: var(--c-border); 198 | --demoblock-control: #8b9eb0; 199 | --demoblock-control-bg: var(--c-bg); 200 | --demoblock-control-bg-hover: var(--c-bg); 201 | --demoblock-description-bg: var(--code-bg-color); 202 | } 203 | ``` 204 | 205 | 配置主题色 206 | ```css 207 | :root { 208 | --c-brand: #646cff; 209 | --c-brand-light: #747bff; 210 | } 211 | ``` 212 | 213 | 214 | ## 使用第三方组件库 215 | 216 | 这个插件主要是针对自己的组件库来使用的,第三方的组件库直接导入使用即可(例如element-plus)。 217 | 218 | 在 .vuepress/clientAppEnhance.js 文件中加入以下代码: 219 | ```js 220 | import { defineClientAppEnhance } from '@vuepress/client' 221 | import ElementPlus from 'element-plus' 222 | import 'element-plus/dist/index.css' 223 | 224 | export default defineClientAppEnhance(({ 225 | app, 226 | router, 227 | siteData, 228 | }) => { 229 | app.use(ElementPlus) 230 | }) 231 | ``` 232 | 233 | 使用的时候,不用导入element组件,直接使用即可: 234 | ```vue 235 | 236 | 237 | {{ title }} 238 | 点击 239 | 240 | 241 | 242 | 253 | ``` 254 | 255 | -------------------------------------------------------------------------------- /build.config.ts: -------------------------------------------------------------------------------- 1 | import { defineBuildConfig, BuildEntry } from 'unbuild' 2 | // import { path } from '@vuepress/utils' 3 | // import { findFiles, getDirname } from './src/utils' 4 | // const __dirname = getDirname(import.meta.url) 5 | // const clientFiles = findFiles(path.resolve(__dirname, 'src/client')) 6 | 7 | export default defineBuildConfig({ 8 | entries: [ 9 | { 10 | input: 'src/node/index', 11 | name: 'node/index' 12 | }, 13 | { 14 | input: 'src/client/', 15 | outDir: 'dist/client/' 16 | } 17 | ], 18 | clean: true, 19 | declaration: true, 20 | externals: [], 21 | rollup: { 22 | emitCJS: true 23 | } 24 | }) 25 | -------------------------------------------------------------------------------- /docs/.vuepress/client.js: -------------------------------------------------------------------------------- 1 | import { defineClientConfig } from '@vuepress/client' 2 | import ElementPlus from 'element-plus' 3 | import 'element-plus/dist/index.css' 4 | 5 | export default defineClientConfig({ 6 | enhance({ app, router, siteData }) { 7 | app.use(ElementPlus) 8 | }, 9 | setup() { }, 10 | rootComponents: [], 11 | }) 12 | -------------------------------------------------------------------------------- /docs/.vuepress/components/table/demo.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 17 | 18 | 30 | -------------------------------------------------------------------------------- /docs/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | import { defaultTheme, defineUserConfig } from 'vuepress' 2 | import { demoblockPlugin } from '../../dist/node/index.mjs' 3 | 4 | const locales = { 5 | '/': { 6 | 'hide-text': 'Hide', 7 | 'show-text': 'Expand', 8 | 'copy-button-text': 'Copy', 9 | 'copy-success-text': 'Copy success' 10 | }, 11 | '/zh': { 12 | 'hide-text': '隐藏代码', 13 | 'show-text': '显示代码', 14 | 'copy-button-text': '复制代码片段', 15 | 'copy-success-text': '复制成功' 16 | } 17 | } 18 | 19 | export default defineUserConfig({ 20 | title: 'VuePress', 21 | description: 'Life is short, Keep it simple.', 22 | head: [['link', { rel: 'icon', type: 'image/png', href: '/logo.png' }]], 23 | base: process.env.BASE || '/', 24 | port: 3000, 25 | theme: defaultTheme({ 26 | repo: 'xinlei3166/vuepress-plugin-demoblock', 27 | logo: '/logo.png', 28 | docsDir: 'docs', 29 | docsBranch: 'master', 30 | darkMode: true, 31 | 32 | // navbar 33 | navbar: [ 34 | { text: '文档', link: '/guide/' } 35 | ], 36 | 37 | // sidebar 38 | sidebar: { 39 | '/guide/': [ 40 | { 41 | text: '指南', 42 | link: '/guide/' 43 | }, 44 | { 45 | text: '组件', 46 | link: '/guide/card.md' 47 | }, 48 | { 49 | text: '第三方', 50 | link: '/guide/other.md' 51 | } 52 | ] 53 | }, 54 | 55 | // page meta 56 | editLinkText: '在 GitHub 上编辑此页', 57 | lastUpdatedText: '上次更新', 58 | contributorsText: '贡献者', 59 | }), 60 | markdown: { 61 | // options for markdown-it-anchor 62 | anchor: { permalink: false, level: [1, 2] }, 63 | 64 | // options for markdown-it-toc 65 | toc: { level: [1, 2] }, 66 | 67 | extractHeaders: { level: ['h2', 'h3', 'h4'] }, 68 | 69 | // disable line-numbers 70 | code: { lineNumbers: false } 71 | }, 72 | plugins: [ 73 | demoblockPlugin({ 74 | customClass: 'demoblock-custom', 75 | theme: 'css-variables', 76 | locales, 77 | cssPreprocessor: 'less', 78 | // customStyleTagName: 'style lang="less"', 79 | scriptImports: ["import * as ElementPlus from 'element-plus'"], 80 | scriptReplaces: [ 81 | { 82 | searchValue: /const ({ defineComponent as _defineComponent }) = Vue/g, 83 | replaceValue: 'const { defineComponent: _defineComponent } = Vue' 84 | }, 85 | { 86 | searchValue: /import ({.*}) from 'element-plus'/g, 87 | replaceValue: (s, s1) => `const ${s1} = ElementPlus` 88 | } 89 | ] 90 | }), 91 | ] 92 | }) 93 | -------------------------------------------------------------------------------- /docs/.vuepress/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xinlei3166/vuepress-plugin-demoblock-plus/117ea9fd1377ce5c17326e83b374725cd85e96cd/docs/.vuepress/public/logo.png -------------------------------------------------------------------------------- /docs/guide/card.md: -------------------------------------------------------------------------------- 1 | # Card 2 | 3 | 常用的卡片布局。 4 | 5 | ## 基础用法 6 | 7 | 基础的卡片用法。 8 | 9 | :::demo 使用 `size`、`style` 属性来定义 Card 的样式。 10 | 11 | ```vue 12 | 13 | 14 | {{ title }} 15 | 16 | 17 | 18 | 29 | 30 | 47 | ``` 48 | 49 | ::: 50 | 51 | 52 | ## Setup TypeScript 53 | 54 | setup typescript 用法。 55 | 56 | :::demo 使用 `size`、`style` 属性来定义 Card 的样式。 57 | 58 | ```vue 59 | 60 | 61 | {{ title }} 62 | 63 | 64 | 65 | 74 | 75 | 92 | ``` 93 | 94 | ::: 95 | 96 | 97 | -------------------------------------------------------------------------------- /docs/guide/index.md: -------------------------------------------------------------------------------- 1 | # Card 2 | 3 | 常用的卡片布局。 4 | 5 | ## 基础用法 6 | 7 | 基础的卡片用法。 8 | 9 | :::demo 10 | 11 | ```vue 12 | 13 | 14 | {{ title }} 15 | 16 | 17 | 18 | 43 | 44 | 62 | ``` 63 | 64 | ::: 65 | 66 | 67 | ## Setup TypeScript 68 | 69 | setup typescript 用法。 70 | 71 | :::demo 72 | 73 | ```vue 74 | 75 | 76 | {{ title }} 77 | 78 | 79 | 80 | 89 | 90 | 108 | ``` 109 | 110 | ::: 111 | 112 | 117 | 118 | 121 | -------------------------------------------------------------------------------- /docs/guide/other.md: -------------------------------------------------------------------------------- 1 | # Card 2 | 3 | 常用的卡片布局。 4 | 5 | ## 基础用法 6 | 7 | 基础的卡片用法。 8 | 9 | :::demo 使用 `size`、`style` 属性来定义 Card 的样式。 10 | 11 | ```vue 12 | 13 | 14 | {{ title }} 15 | 点击 16 | 17 | 18 | 19 | 35 | 36 | 53 | ``` 54 | 55 | ::: 56 | 57 | 58 | ## Setup TypeScript 59 | 60 | setup typescript 用法。 61 | 62 | :::demo 使用 `size`、`style` 属性来定义 Card 的样式。 63 | 64 | ```vue 65 | 66 | 67 | {{ title }} 68 | 点击 69 | 70 | 71 | 72 | 89 | 90 | 107 | ``` 108 | 109 | ::: 110 | 111 | 112 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | heroImage: /logo.png 4 | actions: 5 | - text: Get Started 6 | link: /guide/ 7 | type: primary 8 | - text: Learn More 9 | link: /guide/ 10 | type: secondary 11 | 12 | features: 13 | - title: 💡 Instant Server Start 14 | details: On demand file serving over native ESM, no bundling required! 15 | - title: ⚡️ Lightning Fast HMR 16 | details: Hot Module Replacement (HMR) that stays fast regardless of app size. 17 | - title: 🛠️ Rich Features 18 | details: Out-of-the-box support for TypeScript, JSX, CSS and more. 19 | - title: 📦 Optimized Build 20 | details: Pre-configured Rollup build with multi-page and library mode support. 21 | - title: 🔩 Universal Plugins 22 | details: Rollup-superset plugin interface shared between dev and build. 23 | - title: 🔑 Fully Typed APIs 24 | details: Flexible programmatic APIs with full TypeScript typing. 25 | footer: MIT Licensed | Copyright © 2021-present 君惜 26 | --- 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuepress-plugin-demoblock-plus", 3 | "version": "2.0.4", 4 | "description": "vuepress plugin for demoblock", 5 | "type": "module", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/xinlei3166/vuepress-plugin-demoblock-plus" 9 | }, 10 | "keywords": [ 11 | "vuepress2", 12 | "vuepress", 13 | "vue", 14 | "plugin" 15 | ], 16 | "bugs": { 17 | "url": "https://github.com/xinlei3166/vuepress-plugin-demoblock-plus/issues" 18 | }, 19 | "homepage": "https://github.com/xinlei3166/vuepress-plugin-demoblock-plus#readme", 20 | "author": "君惜", 21 | "license": "MIT", 22 | "files": [ 23 | "dist" 24 | ], 25 | "main": "dist/node/index.mjs", 26 | "module": "dist/node/index.cjs", 27 | "types": "dist/node/index.d.ts", 28 | "exports": { 29 | ".": { 30 | "types": "./dist/node/index.d.ts", 31 | "require": "./dist/node/index.cjs", 32 | "import": "./dist/node/index.mjs" 33 | } 34 | }, 35 | "scripts": { 36 | "preinstall": "npx only-allow pnpm", 37 | "prepare": "simple-git-hooks", 38 | "docs:dev": "vuepress dev docs", 39 | "docs:build": "vuepress build docs --clean-cache", 40 | "docs:serve": "serve docs/.vuepress/dist", 41 | "test": "vitest", 42 | "watch-build": "chokidar 'src/**/*.ts' 'src/**/*.vue' -c 'pnpm run build'", 43 | "build": "unbuild", 44 | "stub": "unbuild --stub", 45 | "lint": "eslint --fix --ext .js,.jsx,.ts,.tsx,.vue", 46 | "release": "yunque-release", 47 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0" 48 | }, 49 | "dependencies": { 50 | "@mdit-vue/plugin-sfc": "^0.11.1", 51 | "@vue/compiler-sfc": "3.2.44", 52 | "@vuepress/plugin-shiki": "2.0.0-beta.51", 53 | "@vuepress/utils": "2.0.0-beta.51", 54 | "@vuepress/client": "2.0.0-beta.51", 55 | "vuepress": "2.0.0-beta.51", 56 | "@vueuse/core": "^9.1.1", 57 | "markdown-it": "^13.0.1", 58 | "markdown-it-container": "^3.0.0", 59 | "vue": "3.2.44", 60 | "vue-router": "^4.0.0" 61 | }, 62 | "devDependencies": { 63 | "@types/markdown-it": "^12.2.3", 64 | "@types/node": "^14.14.39", 65 | "@typescript-eslint/eslint-plugin": "^4.17.0", 66 | "@typescript-eslint/parser": "^4.17.0", 67 | "@yunquejs/release": "^0.1.4", 68 | "chokidar-cli": "^3.0.0", 69 | "conventional-changelog-cli": "^2.1.1", 70 | "element-plus": "latest", 71 | "eslint": "^7.21.0", 72 | "eslint-config-prettier": "^8.1.0", 73 | "eslint-plugin-prettier": "^3.3.1", 74 | "eslint-plugin-vue": "^7.7.0", 75 | "less": "^4.1.2", 76 | "lint-staged": "^10.5.4", 77 | "npm-run-all": "^4.1.5", 78 | "picocolors": "latest", 79 | "tsx": "latest", 80 | "prettier": "^2.2.1", 81 | "simple-git-hooks": "latest", 82 | "typescript": "^4.8.2", 83 | "unbuild": "0.9.4", 84 | "vitest": "latest", 85 | "vue-eslint-parser": "^7.6.0" 86 | }, 87 | "simple-git-hooks": { 88 | "pre-commit": "pnpm exec lint-staged --concurrent false", 89 | "commit-msg": "pnpm exec tsx scripts/verifyCommit.ts $1" 90 | }, 91 | "lint-staged": { 92 | "*.{js,jsx,ts,tsx,vue}": [ 93 | "eslint --fix" 94 | ] 95 | }, 96 | "engines": { 97 | "node": ">=16.0.0" 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /scripts/verifyCommit.ts: -------------------------------------------------------------------------------- 1 | // Invoked on the commit-msg git hook by simple-git-hooks. 2 | 3 | import { readFileSync } from 'fs' 4 | import colors from 'picocolors' 5 | 6 | // get $1 from commit-msg script 7 | const msgPath = process.argv[2] 8 | const msg = readFileSync(msgPath, 'utf-8').trim() 9 | 10 | // Merge branch 11 | const mergeRE = /^Merge branch.*/ 12 | const commitRE = 13 | /^(revert: )?(feat|fix|docs|style|refactor|perf|test|chore|revert|release)(\(.+\))?: .{1,50}/ 14 | 15 | if (!mergeRE.test(msg) && !commitRE.test(msg)) { 16 | console.log() 17 | console.error( 18 | ` ${colors.bgRed(colors.white(' ERROR '))} ${colors.red( 19 | `invalid commit message format.` 20 | )}\n\n` + 21 | colors.red( 22 | ` Proper commit message format is required for automated changelog generation. Examples:\n\n` 23 | ) + 24 | ` ${colors.green(`feat: add 'comments' option`)}\n` + 25 | ` ${colors.green(`fix: handle events on blur (close #28)`)}\n` 26 | ) 27 | process.exit(1) 28 | } 29 | -------------------------------------------------------------------------------- /src/client/components/Demo.vue: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 24 | 25 | 31 | 32 | 33 | {{ controlText }} 34 | 35 | 36 | 37 | 38 | {{ locale && locale['copy-button-text'] }} 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 199 | 200 | 314 | 319 | -------------------------------------------------------------------------------- /src/client/components/DemoBlock.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 19 | 20 | 32 | -------------------------------------------------------------------------------- /src/client/components/Message.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | {{ item.content }} 9 | 10 | 11 | 12 | 13 | 52 | 53 | 100 | -------------------------------------------------------------------------------- /src/client/components/message.js: -------------------------------------------------------------------------------- 1 | import { createApp, h } from 'vue' 2 | import Message from './Message.vue' 3 | 4 | // newInstance 5 | Message.newInstance = (props = {}) => { 6 | const container = document.createElement('div') 7 | 8 | const app = createApp({ 9 | render() { 10 | return h(Message, { 11 | ...props, 12 | ref: 'messageRef' 13 | }) 14 | } 15 | }) 16 | 17 | const instance = app.mount(container) 18 | const messageRef = instance.$refs.messageRef 19 | document.body.appendChild(container.firstElementChild) 20 | 21 | return { 22 | add(messageProps) { 23 | messageRef.add(messageProps) 24 | }, 25 | remove(name) { 26 | messageRef.remove(name) 27 | } 28 | } 29 | } 30 | 31 | // message 32 | let messageInstance 33 | 34 | function getMessageInstance() { 35 | messageInstance = messageInstance || Message.newInstance() 36 | return messageInstance 37 | } 38 | 39 | function message(content, { duration = 3, type = '' }) { 40 | const instance = getMessageInstance() 41 | 42 | instance.add({ content, duration, type }) 43 | } 44 | 45 | export default { 46 | info(content, options) { 47 | return message(content, { ...options }) 48 | }, 49 | error(content, options) { 50 | return message(content, { ...options, type: 'error' }) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/client/config.ts: -------------------------------------------------------------------------------- 1 | import { h } from 'vue' 2 | import { defineClientConfig } from '@vuepress/client' 3 | import Demo from './components/Demo.vue' 4 | import DemoBlock from './components/DemoBlock.vue' 5 | import './styles/index.scss' 6 | import type { Locales } from '../types' 7 | 8 | declare const __DEMOBLOCK_LOCALES__: Locales 9 | 10 | const locales = __DEMOBLOCK_LOCALES__ 11 | 12 | export default defineClientConfig({ 13 | enhance({ app }) { 14 | app.component('Demo', (props, { slots }) => h(Demo, { locales, ...props }, slots)) 15 | app.component('DemoBlock', DemoBlock) 16 | }, 17 | setup() {}, 18 | rootComponents: [] 19 | }) 20 | -------------------------------------------------------------------------------- /src/client/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xinlei3166/vuepress-plugin-demoblock-plus/117ea9fd1377ce5c17326e83b374725cd85e96cd/src/client/index.ts -------------------------------------------------------------------------------- /src/client/styles/code.scss: -------------------------------------------------------------------------------- 1 | code { 2 | font-family: var(--code-font-family); 3 | font-size: 0.85em; 4 | border-radius: 3px; 5 | margin: 0; 6 | padding: 2px 5px; 7 | color: #d56161; 8 | background: #f6f7f9; 9 | } 10 | 11 | html.dark code { 12 | color: var(--c-text-lighter); 13 | background: var(--c-bg-light); 14 | } 15 | 16 | html.dark [class*='language-'] code { 17 | background: transparent; 18 | } 19 | 20 | [class*='language-'] code { 21 | line-height: var(--code-line-height); 22 | font-size: var(--code-font-size); 23 | } 24 | 25 | .demo-block .shiki { 26 | margin: 0; 27 | } 28 | -------------------------------------------------------------------------------- /src/client/styles/index.scss: -------------------------------------------------------------------------------- 1 | @import './layout.scss'; 2 | @import './code.scss'; 3 | 4 | :root { 5 | --font-family-mono: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; 6 | //--c-brand: #646cff; 7 | //--c-brand-light: #747bff; 8 | --code-line-height: 24px; 9 | --code-font-size: 14px; 10 | --code-font-family: var(--font-family-mono); 11 | --code-bg-color: #f9fafb; 12 | --demoblock-border: var(--c-border); 13 | --demoblock-control: #d3dce6; 14 | --demoblock-control-bg: var(--c-bg); 15 | --demoblock-control-bg-hover: #f9fafc; 16 | --demoblock-description-bg: var(--c-bg); 17 | // shiki light 18 | --shiki-color-text: #24292f; 19 | --shiki-color-background: #ffffff; 20 | --shiki-token-constant: #0550ae; 21 | --shiki-token-string: #24292f; 22 | --shiki-token-comment: #6e7781; 23 | --shiki-token-keyword: #cf222e; 24 | --shiki-token-parameter: #24292f; 25 | --shiki-token-function: #8250df; 26 | --shiki-token-string-expression: #0a3069; // #116329 27 | --shiki-token-punctuation: #24292f; 28 | //--shiki-token-link: #000012; 29 | } 30 | 31 | html.dark { 32 | --code-bg-color: #282c34; 33 | --demoblock-control: #8b9eb0; 34 | --demoblock-control-bg-hover: var(--c-bg); 35 | --demoblock-description-bg: var(--code-bg-color); 36 | // shiki dark 37 | --shiki-color-text: #c9d1d9; 38 | --shiki-color-background: #0d1117; 39 | --shiki-token-constant: #79c0ff; 40 | --shiki-token-string: #a5d6ff; 41 | --shiki-token-comment: #8b949e; 42 | --shiki-token-keyword: #ff7b72; 43 | --shiki-token-parameter: #c9d1d9; 44 | --shiki-token-function: #d2a8ff; 45 | --shiki-token-string-expression: #a5d6ff; // #7ee787; 46 | --shiki-token-punctuation: #c9d1d9; 47 | //--shiki-token-link: #000012; 48 | } 49 | 50 | // vuepress 51 | .hero img { 52 | width: 200px; 53 | //height: 200px; 54 | } 55 | 56 | .nav-bar .logo { 57 | height: 30px; 58 | margin-right: 2px; 59 | } 60 | 61 | .custom-block.tip { 62 | border-color: var(--c-brand-light); 63 | } 64 | 65 | .DocSearch { 66 | --docsearch-primary-color: var(--c-brand) !important; 67 | } 68 | 69 | // icon-caret 70 | .icon-caret-down { 71 | width: 16px; 72 | height: 100%; 73 | position: relative; 74 | } 75 | 76 | .icon-caret-down::before { 77 | content: ''; 78 | width: 0; 79 | height: 0; 80 | border: 6px solid transparent; 81 | border-top: 7.5px solid; 82 | position: absolute; 83 | top: 50%; 84 | left: 50%; 85 | margin-top: -2.5px; 86 | margin-left: -6px; 87 | } 88 | 89 | .icon-caret-up { 90 | width: 16px; 91 | height: 100%; 92 | position: relative; 93 | } 94 | 95 | .icon-caret-up::before { 96 | content: ''; 97 | width: 0; 98 | height: 0; 99 | border: 6px solid transparent; 100 | border-bottom: 7px solid; 101 | position: absolute; 102 | top: 50%; 103 | left: 50%; 104 | margin-top: -8.5px; 105 | margin-left: -6px; 106 | } 107 | -------------------------------------------------------------------------------- /src/client/styles/layout.scss: -------------------------------------------------------------------------------- 1 | // vuepress layout 2 | .theme-default-content:not(.custom), .page-meta, .page-nav { 3 | max-width: unset !important; 4 | padding-left: 3rem !important; 5 | padding-right: 12.5rem !important; 6 | } 7 | 8 | @media only screen and (max-width: 992px) { 9 | .theme-default-content:not(.custom), .page-meta, .page-nav { 10 | padding: 0 1.5rem 4rem !important; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/client/utils.ts: -------------------------------------------------------------------------------- 1 | export function stripScript(content: string) { 2 | const result = content.match(/<(script)>([\s\S]+)<\/\1>/) 3 | return result && result[2] ? result[2].trim() : '' 4 | } 5 | 6 | export function stripStyle(content: string) { 7 | const result = content.match(/<(style)\s*>([\s\S]+)<\/\1>/) 8 | return result && result[2] ? result[2].trim() : '' 9 | } 10 | 11 | // 编写例子时不一定有 template。所以采取的方案是剔除其他的内容 12 | function _stripTemplate(content: string) { 13 | content = content.trim() 14 | if (!content) { 15 | return content 16 | } 17 | return content.replace(/<(script|style)[\s\S]+<\/\1>/g, '').trim() 18 | } 19 | 20 | export function stripTemplate(content: string) { 21 | const result = _stripTemplate(content) 22 | if (result.indexOf('') === 0) { 23 | const html = result.replace(/^/, '').replace(/<\/template>$/, '') 24 | return html 25 | .replace(/^[\r?\n|\r]/, '') 26 | .replace(/[\r?\n|\r]$/, '') 27 | .trim() 28 | } 29 | return result 30 | } 31 | -------------------------------------------------------------------------------- /src/node/demoblock.ts: -------------------------------------------------------------------------------- 1 | // 参考 https://github.com/element-plus/element-plus/blob/dev/website/md-loader/index.js 2 | // 参考 https://github.com/calebman/vuepress-plugin-demo-container/blob/master/src/index.js 3 | import mdContainer from 'markdown-it-container' 4 | import renderDemoBlock from './render' 5 | import type MarkdownIt from 'markdown-it' 6 | import type Token from 'markdown-it/lib/token' 7 | import type Renderer from 'markdown-it/lib/renderer' 8 | import type { DemoblockPluginOptions } from '../types' 9 | import { createSfcRegexp, TAG_NAME_TEMPLATE } from '@mdit-vue/plugin-sfc' 10 | 11 | export const blockPlugin = (md: MarkdownIt, options: DemoblockPluginOptions) => { 12 | md.use(mdContainer, 'demo', { 13 | validate(params: string) { 14 | return params.trim().match(/^demo\s*(.*)$/) 15 | }, 16 | render(tokens: Token[], idx: number) { 17 | // const m = tokens[idx].info.trim().match(/^demo\s*(.*)$/) 18 | if (tokens[idx].nesting === 1) { 19 | // const description = m && m.length > 1 ? m[1] : '' 20 | const content = tokens[idx + 1].type === 'fence' ? tokens[idx + 1].content : '' 21 | return `${content ? `` : ''}` 24 | } 25 | return '' 26 | } 27 | }) 28 | } 29 | 30 | export const codePlugin = (md: MarkdownIt, options: DemoblockPluginOptions) => { 31 | const lang = options?.lang || 'vue' 32 | const defaultRender = md.renderer.rules.fence 33 | md.renderer.rules.fence = ( 34 | tokens: Token[], 35 | idx: number, 36 | options: MarkdownIt.Options, 37 | env: any, 38 | self: Renderer 39 | ) => { 40 | const token = tokens[idx] 41 | // 判断该 fence 是否在 :::demo 内 42 | const prevToken = tokens[idx - 1] 43 | const isInDemoContainer = 44 | prevToken && prevToken.nesting === 1 && prevToken.info.trim().match(/^demo\s*(.*)$/) 45 | if (token.info.trim() === lang && isInDemoContainer) { 46 | const m = prevToken.info.trim().match(/^demo\s*(.*)$/) 47 | const description = m && m.length > 1 ? m[1] : '' 48 | return ` 49 | ${ 50 | description 51 | ? ` 52 | ${md.renderInline(description)} 53 | ` 54 | : '' 55 | } 56 | 57 | 58 | ${md.options.highlight?.(token.content, lang, '') || ''} 59 | 60 | ` 61 | } 62 | return defaultRender?.(tokens, idx, options, env, self) as string 63 | } 64 | } 65 | 66 | const sfcRegexp = createSfcRegexp({ customBlocks: [TAG_NAME_TEMPLATE] }) 67 | export const renderPlugin = (md: MarkdownIt, options: DemoblockPluginOptions) => { 68 | const render = md.render.bind(md) 69 | md.render = (src, env) => { 70 | let rendered = render(src, env) 71 | const startTag = '' 73 | if (rendered.indexOf(startTag) !== -1 && rendered.indexOf(endTag) !== -1) { 74 | const { template, script, style } = renderDemoBlock(rendered, options) || {} 75 | const templateSfcBlock = `${template}`.match(sfcRegexp)?.groups 76 | const scriptSfcBlock = script?.match(sfcRegexp)?.groups 77 | const styleSfcBlock = style?.match(sfcRegexp)?.groups 78 | env.sfcBlocks.template = templateSfcBlock || null 79 | env.sfcBlocks.script = scriptSfcBlock || null 80 | if (styleSfcBlock) { 81 | env.sfcBlocks.styles.push(styleSfcBlock) 82 | } 83 | rendered = template! 84 | } 85 | return rendered 86 | } 87 | } 88 | 89 | export const demoblock = (md: MarkdownIt, options: DemoblockPluginOptions = {}) => { 90 | md.use(blockPlugin, options) 91 | md.use(codePlugin, options) 92 | md.use(renderPlugin, options) 93 | } 94 | -------------------------------------------------------------------------------- /src/node/index.ts: -------------------------------------------------------------------------------- 1 | import { path } from '@vuepress/utils' 2 | import { default as shiki } from '@vuepress/plugin-shiki' 3 | import type MarkdownIt from 'markdown-it' 4 | import type { DemoblockPluginOptions } from '../types' 5 | import { demoblock } from './demoblock' 6 | import { getDirname } from '../utils' 7 | 8 | const __dirname = getDirname(import.meta.url) 9 | 10 | const defaultLocales = { 11 | '/': { 12 | 'hide-text': '隐藏代码', 13 | 'show-text': '显示代码', 14 | 'copy-button-text': '复制代码片段', 15 | 'copy-success-text': '复制成功' 16 | } 17 | } 18 | 19 | /** 20 | * demoblockPlugin 21 | * @param customClass demoblock classname 22 | * @param locales i18n 23 | * @param theme shiki theme css-variables/github-light/github-dark ... 24 | * @param langs shiki langs 25 | * @param lang shiki lang 26 | * @param cssPreprocessor less/scss 27 | * @param scriptImports 28 | * @param scriptReplaces 29 | * @return {{clientConfigFile, name: string, define: {__DEMOBLOCK_LOCALES__: {'/': {'show-text': string, 'copy-success-text': string, 'hide-text': string, 'copy-button-text': string}}}, extendsMarkdown: ((function(*=): Promise)|*)}} 30 | */ 31 | export const demoblockPlugin = ({ 32 | locales = {}, 33 | customClass = '', 34 | theme = 'css-variables', 35 | langs, 36 | lang = 'vue', 37 | cssPreprocessor, 38 | scriptImports = [], 39 | scriptReplaces = [] 40 | }: DemoblockPluginOptions = {}) => { 41 | return { 42 | name: 'vuepress-plugin-demoblock-plus', 43 | clientConfigFile: path.resolve(__dirname, '../client/config.mjs'), 44 | // alias: { 45 | // 'vuepress-plugin-demoblock-plus/client': path.resolve(__dirname, '../client/index.mjs') 46 | // }, 47 | extendsMarkdown: async (md: MarkdownIt) => { 48 | await (shiki({ theme, langs }) as any).extendsMarkdown(md) 49 | md.use(demoblock, { 50 | customClass, 51 | lang, 52 | cssPreprocessor, 53 | scriptImports, 54 | scriptReplaces 55 | }) 56 | }, 57 | define: { 58 | __DEMOBLOCK_LOCALES__: { ...defaultLocales, ...locales } 59 | } 60 | } 61 | } 62 | 63 | export * from './demoblock' 64 | export * from '../types' 65 | 66 | export default demoblockPlugin 67 | -------------------------------------------------------------------------------- /src/node/markdown-it-container.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'markdown-it-container' { 2 | import type { PluginWithParams } from 'markdown-it' 3 | const container: PluginWithParams 4 | export = container 5 | } 6 | -------------------------------------------------------------------------------- /src/node/patterns.ts: -------------------------------------------------------------------------------- 1 | export const ScriptSetupPattern = /<(script)(?:.* \bsetup\b)?[^>]*>([\s\S]+)<\/\1>/ 2 | export const StylePattern = /<(style)[^>]*>([\s\S]+)<\/\1>/ 3 | export const ScriptOrStyleReplacePattern = /<(script|style)[\s\S]+<\/\1>/g 4 | export const TemplateReplacePattern = /([\s\S]+)<\/template>/g 5 | export const ScriptMatchPattern = /^.*(` 55 | } else if (content.indexOf('') + ''.length 58 | pageScript = content.slice(0, start) 59 | } 60 | // 合并 style 内容 61 | styleArr = [...new Set(styleArr)] 62 | let styleString = '' 63 | const preprocessors = ['scss', 'sass', 'less', 'stylus'] 64 | let _style = 'style' 65 | // 支持css预处理器 66 | if (preprocessors.includes(options.cssPreprocessor!)) { 67 | _style = `style lang="${options.cssPreprocessor}"` 68 | } 69 | // 支持customStyleTagName 70 | if (options.customStyleTagName) { 71 | _style = options.customStyleTagName 72 | } 73 | // 支持css预处理器 74 | if (styleArr && styleArr.length > 0) { 75 | styleString = `<${_style}>${styleArr.join('')}` 76 | } else { 77 | styleString = `` 78 | } 79 | templateArr.push(content.slice(start)) 80 | return { 81 | template: templateArr.join(''), 82 | script: pageScript, 83 | style: styleString 84 | } 85 | } 86 | 87 | export default render 88 | -------------------------------------------------------------------------------- /src/node/utils.ts: -------------------------------------------------------------------------------- 1 | import type { SFCTemplateCompileOptions } from '@vue/compiler-sfc' 2 | import { compileTemplate, TemplateCompiler, compileScript, parse } from '@vue/compiler-sfc' 3 | import type { DemoblockPluginOptions } from '../types' 4 | import { 5 | ScriptOrStyleReplacePattern, 6 | ScriptSetupPattern, 7 | StylePattern, 8 | TemplateReplacePattern 9 | } from './patterns' 10 | 11 | export function stripScript(content: string, id: any) { 12 | const result = content.match(ScriptSetupPattern) 13 | const source = result && result[0] ? result[0].trim() : '' 14 | if (source) { 15 | const { descriptor } = parse(source) 16 | const { content: scriptContent } = compileScript(descriptor, { 17 | refSugar: true, 18 | id 19 | }) 20 | return scriptContent 21 | } 22 | return source 23 | } 24 | 25 | export function stripStyle(content: string) { 26 | const result = content.match(StylePattern) 27 | return result && result[2] ? result[2].trim() : '' 28 | } 29 | 30 | // 编写例子时不一定有 template。所以采取的方案是剔除其他的内容 31 | export function stripTemplate(content: string) { 32 | content = content.trim() 33 | if (!content) { 34 | return content 35 | } 36 | return content.replace(ScriptOrStyleReplacePattern, '').trim() 37 | } 38 | 39 | export function pad(source: string) { 40 | return source 41 | .split(/\r?\n/) 42 | .map(line => ` ${line}`) 43 | .join('\n') 44 | } 45 | 46 | export function genInlineComponentText( 47 | id: any, 48 | template: string, 49 | script: string, 50 | options: DemoblockPluginOptions 51 | ) { 52 | let source = template 53 | if (TemplateReplacePattern.test(source)) { 54 | source = source.replace(TemplateReplacePattern, '$1') 55 | } 56 | const finalOptions = { 57 | id: `inline-component-${id}`, 58 | source: `${source}`, 59 | filename: `inline-component-${id}`, 60 | // compiler: TemplateCompiler, 61 | compilerOptions: { 62 | mode: 'function' 63 | } 64 | } 65 | const compiled = compileTemplate(finalOptions as SFCTemplateCompileOptions) 66 | // tips 67 | if (compiled.tips && compiled.tips.length) { 68 | compiled.tips.forEach(tip => { 69 | console.warn(tip) 70 | }) 71 | } 72 | // errors 73 | if (compiled.errors && compiled.errors.length) { 74 | console.error( 75 | `\n Error compiling template:\n${pad(compiled.source)}\n` + 76 | compiled.errors.map(e => ` - ${e}`).join('\n') + 77 | '\n' 78 | ) 79 | } 80 | let demoComponentContent = ` 81 | ${compiled.code.replace('return function render', 'function render')} 82 | ` 83 | // todo: 这里采用了硬编码有待改进 84 | script = script.trim() 85 | if (script) { 86 | script = script 87 | .replace(/export\s+default/, 'const democomponentExport =') 88 | .replace(/import ({.*}) from 'vue'/g, (s, s1) => `const ${s1} = Vue`) 89 | .replace( 90 | /const ({ defineComponent as _defineComponent }) = Vue/g, 91 | 'const { defineComponent: _defineComponent } = Vue' 92 | ) 93 | 94 | // 因为 vue 函数组件需要把 import 转换为 require,这里可附加一些其他的转换。 95 | if (options?.scriptReplaces) { 96 | for (const s of options.scriptReplaces) { 97 | script = script.replace(s.searchValue, s.replaceValue as any) 98 | } 99 | } 100 | } else { 101 | script = 'const democomponentExport = {}' 102 | } 103 | demoComponentContent = `(function() { 104 | ${demoComponentContent} 105 | ${script} 106 | return { 107 | render, 108 | ...democomponentExport 109 | } 110 | })()` 111 | return demoComponentContent 112 | } 113 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | export interface Locale { 2 | 'hide-text': string 3 | 'show-text': string 4 | 'copy-button-text': string 5 | 'copy-success-text': string 6 | } 7 | 8 | export interface Locales { 9 | [key: string]: Locale 10 | } 11 | 12 | export type ReplaceValue = () => string 13 | export interface ScriptReplace { 14 | searchValue: RegExp 15 | replaceValue: string | ReplaceValue 16 | } 17 | 18 | export interface DemoblockPluginOptions { 19 | locales?: Locales 20 | customClass?: string 21 | theme?: string 22 | langs?: any[] 23 | lang?: string 24 | cssPreprocessor?: 'sass' | 'scss' | 'less' | 'stylus' 25 | customStyleTagName?: string 26 | scriptImports?: Array 27 | scriptReplaces?: Array 28 | } 29 | -------------------------------------------------------------------------------- /src/utils/getDirname.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from 'url' 2 | import { path } from '@vuepress/utils' 3 | 4 | export const getDirname = (importMetaUrl: string) => path.dirname(fileURLToPath(importMetaUrl)) 5 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './getDirname' 2 | export * from './util' 3 | -------------------------------------------------------------------------------- /src/utils/util.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | 3 | export function findFiles(src: string, exclude: Array = []) { 4 | const files = fs.readdirSync(src) 5 | const index = files.findIndex(x => !exclude.includes(x)) 6 | if (exclude.length && index !== -1) { 7 | files.splice(index, 1) 8 | } 9 | return files.filter(f => f.endsWith('.ts') && !f.endsWith('.d.ts')) 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "strict": true, 7 | "jsx": "preserve", 8 | "sourceMap": true, 9 | "resolveJsonModule": true, 10 | "esModuleInterop": true, 11 | "allowJs": true, 12 | "noImplicitAny": true, 13 | "noImplicitThis": false, 14 | "lib": [ 15 | "ESNext", 16 | "DOM" 17 | ], 18 | "types": ["node"] 19 | // "baseUrl": ".", 20 | // "paths": { 21 | // "@/*": ["./src/*"] 22 | // } 23 | }, 24 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /v1.md: -------------------------------------------------------------------------------- 1 | # vuepress-plugin-demoblock-plus 2 | 3 | > 这是1.x版本的文档,[2.x版本](README.md)已经发布,采用TypeScript重写。 4 | 5 | ## 简介 6 | 7 | vuepress-plugin-demoblock-plus 是一个基于 Vuepress 2.x 的插件,它可以帮助你在编写文档的时候增加 Vue 示例,它的诞生初衷是为了降低编写组件文档时增加一些相关示例的难度。 8 | 9 | 使用 Vuepress 编写组件示例有以下不足之处: 10 | * 1.组件示例和示例代码本质上一样,却需要写两遍。 11 | * 2.Vuepress 无法渲染 Markdown 中的 script 和 style 代码块。 12 | 13 | vuepress-plugin-demoblock-plus 参考了 [Element UI](https://github.com/element-plus/element-plus) 的文档渲染,实现了和它一样的,可在 Markdown 中直接编写示例的语法。 14 | 15 | [查看Demo](https://xinlei3166.github.io/vuepress-demo/) 16 | 17 | ## 提示 18 | 由于vuepress最新版修改了插件的使用方式,插件v1.6.0已支持。 19 | 旧版vuepress,插件使用v1.5.1即可。 20 | 待vuepress新版本稳定文档更新后,文档会同步修改。 21 | 22 | ## 安装 23 | 24 | ```bash 25 | npm install -D vuepress-plugin-demoblock-plus 26 | yarn add -D vuepress-plugin-demoblock-plus 27 | ``` 28 | 29 | 30 | 31 | ## 用法 32 | 33 | .vuepress/config.js文件中使用插件 34 | 35 | ```js 36 | plugins: [ 37 | ['vuepress-plugin-demoblock-plus'] 38 | ] 39 | ``` 40 | 41 | 42 | 43 | markdown 中的vue代码包含的style内容,会被组合成一个style统一处理,如果需要使用css预处理器,需要提前指定并且手动安装使用的css预处理器。 44 | 45 | ```js 46 | plugins: [ 47 | ['vuepress-plugin-demoblock-plus', { 48 | cssPreprocessor: 'less' 49 | }] 50 | ] 51 | ``` 52 | 53 | 54 | 55 | markdown 中的vue代码被编译为了 vue 函数组件,需要把 import 转换为 require,这里可附加一些其他的转换。 56 | vue已经内置做了转换,例如 `import { ref } from 'vue'` 会被转换为 `const { ref } = Vue`。 57 | 这里编码风格使用的是单引号,如果你使用的是双引号,需自行处理(详见[#21](https://github.com/xinlei3166/vitepress-theme-demoblock/issues/21))。 58 | ```js 59 | plugins: [ 60 | ['vuepress-plugin-demoblock-plus', { 61 | scriptImports: ["import * as ElementPlus from 'element-plus'"], 62 | scriptReplaces: [ 63 | { searchValue: /const ({ defineComponent as _defineComponent }) = Vue/g, 64 | replaceValue: 'const { defineComponent: _defineComponent } = Vue' 65 | }, 66 | { searchValue: /import ({.*}) from 'element-plus'/g, 67 | replaceValue: (s, s1) => `const ${s1} = ElementPlus` 68 | } 69 | ] 70 | }] 71 | ] 72 | ``` 73 | 74 | 75 | 76 | 多语言支持(默认是中文) 77 | 78 | ```js 79 | const locales = { 80 | '/': { 81 | 'hide-text': 'Hide', 82 | 'show-text': 'Expand', 83 | 'copy-button-text': 'Copy', 84 | 'copy-success-text': 'Copy success' 85 | }, 86 | '/zh': { 87 | 'hide-text': '隐藏代码', 88 | 'show-text': '显示代码', 89 | 'copy-button-text': '复制代码片段', 90 | 'copy-success-text': '复制成功' 91 | } 92 | } 93 | 94 | plugins: [ 95 | ['vuepress-plugin-demoblock-plus', { locales }] 96 | ] 97 | ``` 98 | 99 | 100 | ## 自定义主题 101 | 支持vuepress darkMode,.vuepress/config.js 文件中配置 `darkMode: true` 102 | 103 | ``` 104 | themeConfig: { darkMode: true } 105 | ``` 106 | 107 | 使用 shiki 默认自带的主题代码高亮 108 | 109 | ```js 110 | plugins: [ 111 | ['vuepress-plugin-demoblock-plus', { 112 | theme: 'github-light', 113 | }] 114 | ] 115 | ``` 116 | 117 | 使用 shiki css-variables 自定义代码高亮,theme 参数设置为 `css-variables` 118 | ```css 119 | :root { 120 | --shiki-color-text: #24292f; 121 | --shiki-color-background: #ffffff; 122 | --shiki-token-constant: #0550ae; 123 | --shiki-token-string: #24292f; 124 | --shiki-token-comment: #6e7781; 125 | --shiki-token-keyword: #cf222e; 126 | --shiki-token-parameter: #24292f; 127 | --shiki-token-function: #8250df; 128 | --shiki-token-string-expression: #0a3069; // #116329 129 | --shiki-token-punctuation: #24292f; 130 | //--shiki-token-link: #000012; 131 | } 132 | 133 | html.dark { 134 | --shiki-color-text: #c9d1d9; 135 | --shiki-color-background: #0d1117; 136 | --shiki-token-constant: #79c0ff; 137 | --shiki-token-string: #a5d6ff; 138 | --shiki-token-comment: #8b949e; 139 | --shiki-token-keyword: #ff7b72; 140 | --shiki-token-parameter: #c9d1d9; 141 | --shiki-token-function: #d2a8ff; 142 | --shiki-token-string-expression: #a5d6ff; // #7ee787; 143 | --shiki-token-punctuation: #c9d1d9; 144 | //--shiki-token-link: #000012; 145 | } 146 | ``` 147 | 148 | > 如果出现类似这个错误 `Error: ENOENT: no such file or directory, node_modules/shiki/themes/css-variables.json`, 149 | > 这是因为 `shiki css-variables` 需要更高版本才能使用,删除 `node_modules`,重新安装 `@vuepress/plugin-shiki` 和 `vuepress-vite` 150 | 151 | 152 | 通过配置 customClass 类名称,自定义demoblock样式 153 | ```js 154 | plugins: [ 155 | ['vuepress-plugin-demoblock-plus', { 156 | customClass: 'demoblock-custom', 157 | }] 158 | ] 159 | ``` 160 | 161 | 通过配置暴露的 css-variables,自定义demoblock样式, --code-bg-color 是代码块的背景色,light和dark模式下背景色要区分 162 | ```css 163 | :root { 164 | --code-bg-color: #f9fafb; 165 | --demoblock-border: var(--c-border); 166 | --demoblock-control: #d3dce6; 167 | --demoblock-control-bg: var(--c-bg); 168 | --demoblock-control-bg-hover: #f9fafc; 169 | --demoblock-description-bg: var(--c-bg); 170 | } 171 | 172 | html.dark { 173 | --code-bg-color: #282c34; 174 | --demoblock-border: var(--c-border); 175 | --demoblock-control: #8b9eb0; 176 | --demoblock-control-bg: var(--c-bg); 177 | --demoblock-control-bg-hover: var(--c-bg); 178 | --demoblock-description-bg: var(--code-bg-color); 179 | } 180 | ``` 181 | 182 | 配置主题色 183 | ```css 184 | :root { 185 | --c-brand: #646cff; 186 | --c-brand-light: #747bff; 187 | } 188 | ``` 189 | 190 | 191 | ## 使用第三方组件库 192 | 193 | 这个插件主要是针对自己的组件库来使用的,第三方的组件库直接导入使用即可(例如element-plus)。 194 | 195 | 在 .vuepress/clientAppEnhance.js 文件中加入以下代码: 196 | ```js 197 | import { defineClientAppEnhance } from '@vuepress/client' 198 | import ElementPlus from 'element-plus' 199 | import 'element-plus/dist/index.css' 200 | 201 | export default defineClientAppEnhance(({ 202 | app, 203 | router, 204 | siteData, 205 | }) => { 206 | app.use(ElementPlus) 207 | }) 208 | ``` 209 | 210 | 使用的时候,不用导入element组件,直接使用即可: 211 | ```vue 212 | 213 | 214 | {{ title }} 215 | 点击 216 | 217 | 218 | 219 | 230 | ``` 231 | 232 | --------------------------------------------------------------------------------