diff --git a/packages/vant-cli/README.md b/packages/vant-cli/README.md index 7532fcdb2..5f16e2bd5 100644 --- a/packages/vant-cli/README.md +++ b/packages/vant-cli/README.md @@ -68,8 +68,6 @@ yarn add @vant/cli --dev - [命令](https://github.com/youzan/vant/tree/dev/packages/vant-cli/docs/commands.md) - [配置指南](https://github.com/youzan/vant/tree/dev/packages/vant-cli/docs/config.md) - [目录结构](https://github.com/youzan/vant/tree/dev/packages/vant-cli/docs/directory.md) +- [桌面端组件](https://github.com/youzan/vant/tree/dev/packages/vant-cli/docs/desktop.md) - [更新日志](https://github.com/youzan/vant/tree/dev/packages/vant-cli/changelog.md) -## 关于桌面端组件 - -目前 Vant Cli 仅支持移动端组件的预览,桌面端组件暂不支持预览(欢迎 PR)。 diff --git a/packages/vant-cli/docs/config.md b/packages/vant-cli/docs/config.md index 73a79c32f..6900ec0a2 100644 --- a/packages/vant-cli/docs/config.md +++ b/packages/vant-cli/docs/config.md @@ -12,6 +12,7 @@ - [site.versions](#siteversions) - [site.baiduAnalytics](#sitebaiduanalytics) - [site.searchConfig](#sitesearchconfig) + - [site.hideSimulator](#sitehidesimulator) - [Webpack](#webpack) - [Babel](#babel) - [默认配置](#-1) @@ -165,6 +166,8 @@ module.exports = { path: 'home', // 导航项文案 title: '介绍', + // 是否隐藏当前页右侧的手机模拟器(默认不隐藏) + hideSimulator: true, }, ], }, @@ -222,6 +225,16 @@ module.exports = { 配置内容参见 [docsearch](https://docsearch.algolia.com/docs/behavior)。 +- [site.hideSimulator](#sitehidesimulator) + +### site.hideSimulator + +- Type: `boolean` +- Default: `false` + +是否隐藏所有页面右侧的手机模拟器,默认不隐藏 + + ### site.htmlPluginOptions - Type: `object` diff --git a/packages/vant-cli/docs/desktop.md b/packages/vant-cli/docs/desktop.md new file mode 100644 index 000000000..d4a27ca10 --- /dev/null +++ b/packages/vant-cli/docs/desktop.md @@ -0,0 +1,83 @@ +## 关于桌面端组件 + +Vant Cli 也支持预览桌面端组件,你可以在组件的 `demo` 目录下新建一个 `.vue` 文件,并在组件的 `README` 中按如下格式声明要预览的组件: + +```html +./demo/MyDemo.vue +``` + +`demo-code` 标签中间的文本为 `README` 到 `demo` 文件的相对路径。 + +``` +button +├─ demo # 组件示例 +│ └─ MyDemo.vue # 要预览的 demo 文件 +├─ index.js # 组件入口 +├─ index.less # 组件样式 +└─ README.md # 组件文档 +``` + +![image](https://user-images.githubusercontent.com/5093611/111076378-0e981a00-8527-11eb-8e3f-31f0be7e4021.png) + +`demo-code` 标签支持以下属性: + +| 名称 | 类型 | 描述 | +| --------- | ------- | --------------------------------------- | +| compact | boolean | 紧凑模式 | +| transform | boolean | 防止预览区内 fixed 定位的元素飞出预览区 | +| inline | boolean | 只显示组件本身,不显示预览区边框和代码 | + + +### `compact` + +```html +./demo/MyDemo.vue +``` +![image](https://user-images.githubusercontent.com/5093611/111076728-77cc5d00-8528-11eb-85f1-e7217344ab14.png) + +### `transform` + +```html +./demo/MyDemo.vue +``` +![image](https://user-images.githubusercontent.com/5093611/111076799-d5f94000-8528-11eb-973f-c9d69f91d2a7.png) + +### `inline` + +```html +./demo/MyDemo.vue +``` +![image](https://user-images.githubusercontent.com/5093611/111076845-15c02780-8529-11eb-9cfb-76c9b25dc2a2.png) + +### 去除手机模拟器 + +对于 PC 端的组件,如果不需要右侧的手机模拟器,可以在 `vant.config.js` 文件中设置 `site.hideSimulator` 为 `true`,这样在所有页面都会隐藏手机模拟器,也可以只针对具体页面设置。 + +```js +module.exports = { + site: { + defaultLang: 'zh-CN', + hideSimulator: true, // 所有页面都不显示 + locales: { + 'zh-CN': { + title: 'Vant', + description: '轻量、可靠的移动端 Vue 组件库', + hideSimulator: true, // 中文下所有页面都不显示 + nav: [ + { + title: '基础组件', + items: [ + { + path: 'button', + title: 'Button 按钮', + hideSimulator: true, // 只针对某个页面不显示 + }, + ], + }, + ], + }, + }, + }, +}; + +``` \ No newline at end of file diff --git a/packages/vant-cli/package.json b/packages/vant-cli/package.json index 36419c29b..ce771563e 100644 --- a/packages/vant-cli/package.json +++ b/packages/vant-cli/package.json @@ -65,6 +65,7 @@ "commander": "^6.2.1", "consola": "^2.15.0", "conventional-changelog": "^3.1.24", + "copy-text-to-clipboard": "^3.0.1", "css-loader": "^4.0.0", "eslint": "^7.17.0", "fast-glob": "^3.2.4", diff --git a/packages/vant-cli/site/desktop/App.vue b/packages/vant-cli/site/desktop/App.vue index 64a1b6bd2..e18e94467 100644 --- a/packages/vant-cli/site/desktop/App.vue +++ b/packages/vant-cli/site/desktop/App.vue @@ -6,6 +6,7 @@ :config="config" :versions="versions" :simulator="simulator" + :has-simulator="hasSimulator" :lang-configs="langConfigs" > @@ -28,6 +29,7 @@ export default { return { simulator: `${path}mobile.html${location.hash}`, + hasSimulator: true, }; }, @@ -63,18 +65,18 @@ export default { watch: { // eslint-disable-next-line '$route.path'() { - this.setTitle(); + this.setTitleAndToogleSimulator(); }, lang(val) { setLang(val); - this.setTitle(); + this.setTitleAndToogleSimulator(); }, config: { handler(val) { if (val) { - this.setTitle(); + this.setTitleAndToogleSimulator(); } }, immediate: true, @@ -82,7 +84,7 @@ export default { }, methods: { - setTitle() { + setTitleAndToogleSimulator() { let { title } = this.config; const navItems = this.config.nav.reduce( @@ -101,6 +103,8 @@ export default { } document.title = title; + + this.hasSimulator = !(config.site.hideSimulator || this.config.hideSimulator || (current && current.hideSimulator)); }, }, }; diff --git a/packages/vant-cli/site/desktop/components/DemoPlayground.vue b/packages/vant-cli/site/desktop/components/DemoPlayground.vue new file mode 100644 index 000000000..8174a8416 --- /dev/null +++ b/packages/vant-cli/site/desktop/components/DemoPlayground.vue @@ -0,0 +1,208 @@ + + + + + diff --git a/packages/vant-cli/site/desktop/components/index.vue b/packages/vant-cli/site/desktop/components/index.vue index 4be02f785..1160c1351 100644 --- a/packages/vant-cli/site/desktop/components/index.vue +++ b/packages/vant-cli/site/desktop/components/index.vue @@ -8,12 +8,12 @@ @switch-version="$emit('switch-version', $event)" /> - + - + @@ -39,6 +39,7 @@ export default { lang: String, versions: Array, simulator: String, + hasSimulator: Boolean, langConfigs: Array, config: { type: Object, diff --git a/packages/vant-cli/site/desktop/main.js b/packages/vant-cli/site/desktop/main.js index 0e6974d07..3f4c4d7b0 100644 --- a/packages/vant-cli/site/desktop/main.js +++ b/packages/vant-cli/site/desktop/main.js @@ -1,5 +1,11 @@ import { createApp } from 'vue'; +import { packageEntry } from 'site-desktop-shared'; import App from './App'; +import DemoPlayground from './components/DemoPlayground'; import { router } from './router'; -window.app = createApp(App).use(router).mount('#app'); +window.app = createApp(App) + .use(router) + .use(packageEntry) + .component(DemoPlayground.name, DemoPlayground) + .mount('#app'); diff --git a/packages/vant-cli/src/compiler/gen-site-desktop-shared.ts b/packages/vant-cli/src/compiler/gen-site-desktop-shared.ts index 0ce42825a..0daab5355 100644 --- a/packages/vant-cli/src/compiler/gen-site-desktop-shared.ts +++ b/packages/vant-cli/src/compiler/gen-site-desktop-shared.ts @@ -47,9 +47,9 @@ function resolveDocuments(components: string[]): DocumentItem[] { if (locales) { const langs = Object.keys(locales); - langs.forEach(lang => { + langs.forEach((lang) => { const fileName = lang === defaultLang ? 'README.md' : `README.${lang}.md`; - components.forEach(component => { + components.forEach((component) => { docs.push({ name: formatName(component, lang), path: join(SRC_DIR, component, fileName), @@ -57,7 +57,7 @@ function resolveDocuments(components: string[]): DocumentItem[] { }); }); } else { - components.forEach(component => { + components.forEach((component) => { docs.push({ name: formatName(component), path: join(SRC_DIR, component, 'README.md'), @@ -65,26 +65,28 @@ function resolveDocuments(components: string[]): DocumentItem[] { }); } - const staticDocs = glob.sync(normalizePath(join(DOCS_DIR, '**/*.md'))).map(path => { - const pairs = parse(path).name.split('.'); - return { - name: formatName(pairs[0], pairs[1] || defaultLang), - path, - }; - }); + const staticDocs = glob + .sync(normalizePath(join(DOCS_DIR, '**/*.md'))) + .map((path) => { + const pairs = parse(path).name.split('.'); + return { + name: formatName(pairs[0], pairs[1] || defaultLang), + path, + }; + }); - return [...staticDocs, ...docs.filter(item => existsSync(item.path))]; + return [...staticDocs, ...docs.filter((item) => existsSync(item.path))]; } function genImportDocuments(items: DocumentItem[]) { return items - .map(item => `import ${item.name} from '${normalizePath(item.path)}';`) + .map((item) => `import ${item.name} from '${normalizePath(item.path)}';`) .join('\n'); } function genExportDocuments(items: DocumentItem[]) { return `export const documents = { - ${items.map(item => item.name).join(',\n ')} + ${items.map((item) => item.name).join(',\n ')} };`; } @@ -100,13 +102,23 @@ function genExportVersion() { return `export const packageVersion = '${getPackageJson().version}';`; } +function genInstall() { + return `import './package-style';`; +} + +function genExportPackageEntry() { + return `export { default as packageEntry } from './package-entry';`; +} + export function genSiteDesktopShared() { const dirs = readdirSync(SRC_DIR); const documents = resolveDocuments(dirs); const code = `${genImportConfig()} +${genInstall()} ${genImportDocuments(documents)} +${genExportPackageEntry()} ${genExportConfig()} ${genExportDocuments(documents)} ${genExportVersion()} diff --git a/packages/vant-cli/src/config/webpack.base.ts b/packages/vant-cli/src/config/webpack.base.ts index a6a73c26c..2cde4d6ec 100644 --- a/packages/vant-cli/src/config/webpack.base.ts +++ b/packages/vant-cli/src/config/webpack.base.ts @@ -24,6 +24,15 @@ const CSS_LOADERS = [ }, ]; +const VUE_LOADER = { + loader: 'vue-loader', + options: { + compilerOptions: { + preserveWhitespace: false, + }, + }, +}; + const plugins = [ new webpack.DefinePlugin({ __VUE_OPTIONS_API__: 'true', @@ -74,16 +83,7 @@ export const baseConfig: WebpackConfig = { rules: [ { test: /\.vue$/, - use: [ - { - loader: 'vue-loader', - options: { - compilerOptions: { - preserveWhitespace: false, - }, - }, - }, - ], + use: [VUE_LOADER], }, { test: /\.(js|ts|jsx|tsx)$/, @@ -115,7 +115,7 @@ export const baseConfig: WebpackConfig = { }, { test: /\.md$/, - use: ['@vant/markdown-loader'], + use: [VUE_LOADER, '@vant/markdown-loader'], }, ], }, diff --git a/packages/vant-cli/yarn.lock b/packages/vant-cli/yarn.lock index 4fa41985d..811854aeb 100644 --- a/packages/vant-cli/yarn.lock +++ b/packages/vant-cli/yarn.lock @@ -3415,6 +3415,11 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= +copy-text-to-clipboard@^3.0.1: + version "3.0.1" + resolved "https://registry.npm.taobao.org/copy-text-to-clipboard/download/copy-text-to-clipboard-3.0.1.tgz?cache=0&sync_timestamp=1613626493019&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcopy-text-to-clipboard%2Fdownload%2Fcopy-text-to-clipboard-3.0.1.tgz#8cbf8f90e0a47f12e4a24743736265d157bce69c" + integrity sha1-jL+PkOCkfxLkokdDc2Jl0Ve85pw= + core-js-compat@^3.8.0: version "3.8.2" resolved "https://registry.npm.taobao.org/core-js-compat/download/core-js-compat-3.8.2.tgz?cache=0&sync_timestamp=1609682123020&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcore-js-compat%2Fdownload%2Fcore-js-compat-3.8.2.tgz#3717f51f6c3d2ebba8cbf27619b57160029d1d4c" 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..b142fce78 --- /dev/null +++ b/packages/vant-markdown-loader/src/extract-demo.js @@ -0,0 +1,35 @@ +const path = require('path'); +const fs = require('fs'); +const parser = require('./md-parser'); + +function hyphenate(str) { + return str.replace(/\B([A-Z])/g, '-$1').toLowerCase(); +} + +module.exports = function extraDemo(content) { + const markdownDir = path.dirname(this.resourcePath); + const demoLinks = []; + + content = content.replace( + /([\s\S]*?)<\/demo-code>/g, + function (_, attrs, link) { + link = link.trim(); // 去换行符 + const tag = hyphenate(path.basename(link, '.vue')); + const 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/index.js b/packages/vant-markdown-loader/src/index.js index 824d70658..401c72207 100644 --- a/packages/vant-markdown-loader/src/index.js +++ b/packages/vant-markdown-loader/src/index.js @@ -1,22 +1,41 @@ +const path = require('path'); const loaderUtils = require('loader-utils'); -const MarkdownIt = require('markdown-it'); -const markdownItAnchor = require('markdown-it-anchor'); const frontMatter = require('front-matter'); -const highlight = require('./highlight'); +const parser = require('./md-parser'); const linkOpen = require('./link-open'); const cardWrapper = require('./card-wrapper'); -const { slugify } = require('transliteration'); +const extractDemo = require('./extract-demo'); +const sideEffectTags = require('./side-effect-tags'); + +function camelize(str) { + return str.replace(/-(\w)/g, (_, c) => (c ? c.toUpperCase() : '')); +} function wrapper(content) { + let demoLinks; + let styles; + [content, demoLinks] = extractDemo.call(this, content); + [content, styles] = sideEffectTags(content); content = cardWrapper(content); - content = escape(content); - return ` -import { h } from 'vue'; + -const content = unescape(\`${content}\`); + + +${styles.join('\n')} `; } -const parser = new MarkdownIt({ - html: true, - highlight, -}).use(markdownItAnchor, { - level: 2, - slugify, -}); - module.exports = function (source) { let options = loaderUtils.getOptions(this) || {}; this.cacheable && this.cacheable(); @@ -72,5 +82,5 @@ module.exports = function (source) { linkOpen(parser); } - return options.wrapper(parser.render(source), fm); + return options.wrapper.call(this, parser.render(source), fm); }; 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]; +};