mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
feat(cli): vant-cli 支持预览PC端组件
This commit is contained in:
parent
bf1652e938
commit
a6aa9e322b
@ -68,8 +68,5 @@ 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/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/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/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)
|
- [更新日志](https://github.com/youzan/vant/tree/dev/packages/vant-cli/changelog.md)
|
||||||
|
|
||||||
## 关于桌面端组件
|
|
||||||
|
|
||||||
目前 Vant Cli 仅支持移动端组件的预览,桌面端组件暂不支持预览(欢迎 PR)。
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
- [site.versions](#siteversions)
|
- [site.versions](#siteversions)
|
||||||
- [site.baiduAnalytics](#sitebaiduanalytics)
|
- [site.baiduAnalytics](#sitebaiduanalytics)
|
||||||
- [site.searchConfig](#sitesearchconfig)
|
- [site.searchConfig](#sitesearchconfig)
|
||||||
|
- [site.hideSimulator](#sitehidesimulator)
|
||||||
- [Webpack](#webpack)
|
- [Webpack](#webpack)
|
||||||
- [Babel](#babel)
|
- [Babel](#babel)
|
||||||
- [默认配置](#-1)
|
- [默认配置](#-1)
|
||||||
@ -165,6 +166,8 @@ module.exports = {
|
|||||||
path: 'home',
|
path: 'home',
|
||||||
// 导航项文案
|
// 导航项文案
|
||||||
title: '介绍',
|
title: '介绍',
|
||||||
|
// 是否隐藏当前页右侧的手机模拟器(默认不隐藏)
|
||||||
|
hideSimulator: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -222,6 +225,13 @@ module.exports = {
|
|||||||
|
|
||||||
配置内容参见 [docsearch](https://docsearch.algolia.com/docs/behavior)。
|
配置内容参见 [docsearch](https://docsearch.algolia.com/docs/behavior)。
|
||||||
|
|
||||||
|
### site.hideSimulator
|
||||||
|
|
||||||
|
- Type: `boolean`
|
||||||
|
- Default: `false`
|
||||||
|
|
||||||
|
是否隐藏所有页面右侧的手机模拟器,默认不隐藏
|
||||||
|
|
||||||
### site.htmlPluginOptions
|
### site.htmlPluginOptions
|
||||||
|
|
||||||
- Type: `object`
|
- Type: `object`
|
||||||
|
84
packages/vant-cli/docs/desktop.md
Normal file
84
packages/vant-cli/docs/desktop.md
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
## 关于桌面端组件
|
||||||
|
|
||||||
|
Vant Cli 也支持预览桌面端组件,你可以在组件的 `demo` 目录下新建一个 `.vue` 文件,并在组件的 `README` 中按如下格式声明要预览的组件:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<demo-code>./demo/MyDemo.vue</demo-code>
|
||||||
|
```
|
||||||
|
|
||||||
|
`demo-code` 标签中间的文本为 `README` 到 `demo` 文件的相对路径。
|
||||||
|
|
||||||
|
```
|
||||||
|
button
|
||||||
|
├─ demo # 组件示例
|
||||||
|
│ └─ MyDemo.vue # 要预览的 demo 文件
|
||||||
|
├─ index.js # 组件入口
|
||||||
|
├─ index.less # 组件样式
|
||||||
|
└─ README.md # 组件文档
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
`demo-code` 标签支持以下属性:
|
||||||
|
|
||||||
|
| 名称 | 类型 | 描述 |
|
||||||
|
| --------- | ------- | --------------------------------------- |
|
||||||
|
| compact | boolean | 紧凑模式 |
|
||||||
|
| transform | boolean | 防止预览区内 fixed 定位的元素飞出预览区 |
|
||||||
|
| inline | boolean | 只显示组件本身,不显示预览区边框和代码 |
|
||||||
|
|
||||||
|
### `compact`
|
||||||
|
|
||||||
|
```html
|
||||||
|
<demo-code compact>./demo/MyDemo.vue</demo-code>
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### `transform`
|
||||||
|
|
||||||
|
```html
|
||||||
|
<demo-code transform>./demo/MyDemo.vue</demo-code>
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### `inline`
|
||||||
|
|
||||||
|
```html
|
||||||
|
<demo-code inline>./demo/MyDemo.vue</demo-code>
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 去除手机模拟器
|
||||||
|
|
||||||
|
对于 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, // 只针对某个页面不显示
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
@ -73,6 +73,7 @@
|
|||||||
"commander": "^5.1.0",
|
"commander": "^5.1.0",
|
||||||
"consola": "^2.12.2",
|
"consola": "^2.12.2",
|
||||||
"conventional-changelog": "^3.1.21",
|
"conventional-changelog": "^3.1.21",
|
||||||
|
"copy-text-to-clipboard": "^3.0.1",
|
||||||
"cross-env": "^7.0.2",
|
"cross-env": "^7.0.2",
|
||||||
"css-loader": "^3.5.3",
|
"css-loader": "^3.5.3",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
:config="config"
|
:config="config"
|
||||||
:versions="versions"
|
:versions="versions"
|
||||||
:simulator="simulator"
|
:simulator="simulator"
|
||||||
|
:has-simulator="hasSimulator"
|
||||||
:lang-configs="langConfigs"
|
:lang-configs="langConfigs"
|
||||||
>
|
>
|
||||||
<router-view />
|
<router-view />
|
||||||
@ -28,6 +29,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
packageVersion,
|
packageVersion,
|
||||||
simulator: `${path}mobile.html${location.hash}`,
|
simulator: `${path}mobile.html${location.hash}`,
|
||||||
|
hasSimulator: true,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -67,16 +69,16 @@ export default {
|
|||||||
watch: {
|
watch: {
|
||||||
lang(val) {
|
lang(val) {
|
||||||
setLang(val);
|
setLang(val);
|
||||||
this.setTitle();
|
this.setTitleAndToogleSimulator();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
this.setTitle();
|
this.setTitleAndToogleSimulator();
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
setTitle() {
|
setTitleAndToogleSimulator() {
|
||||||
let { title } = this.config;
|
let { title } = this.config;
|
||||||
|
|
||||||
if (this.config.description) {
|
if (this.config.description) {
|
||||||
@ -84,6 +86,20 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
document.title = title;
|
document.title = title;
|
||||||
|
|
||||||
|
const navItems = this.config.nav.reduce(
|
||||||
|
(result, nav) => [...result, ...nav.items],
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const current = navItems.find((item) => {
|
||||||
|
return item.path === this.$route.meta.name;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.hasSimulator = !(
|
||||||
|
config.site.hideSimulator ||
|
||||||
|
this.config.hideSimulator ||
|
||||||
|
(current && current.hideSimulator)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
188
packages/vant-cli/site/desktop/components/DemoPlayground.vue
Normal file
188
packages/vant-cli/site/desktop/components/DemoPlayground.vue
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
<template>
|
||||||
|
<div :class="{ 'demo-playground': !inline, transform }">
|
||||||
|
<slot v-if="inline" />
|
||||||
|
<template v-else>
|
||||||
|
<div class="demo-playground--previewer" :class="{ compact }">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
<div class="demo-playground--code">
|
||||||
|
<div class="demo-playground--code--actions">
|
||||||
|
<span></span>
|
||||||
|
<button
|
||||||
|
title="Copy source code"
|
||||||
|
class="action-icon"
|
||||||
|
role="copy"
|
||||||
|
:data-status="copyStatus"
|
||||||
|
@click="copySourceCode"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
title="Toggle source code panel"
|
||||||
|
class="action-icon"
|
||||||
|
role="source"
|
||||||
|
@click="toogleSource"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-show="showSource"
|
||||||
|
v-html="unescape(codeSnippet)"
|
||||||
|
class="demo-playground--code--content"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import copy from 'copy-text-to-clipboard';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'demo-playground',
|
||||||
|
props: {
|
||||||
|
originCode: String, // 源文件内容
|
||||||
|
codeSnippet: String, // 源文件转 html 后的内容
|
||||||
|
transform: Boolean, // 防止 position fixed 内容飞出预览区域
|
||||||
|
compact: Boolean, // 紧凑模式
|
||||||
|
inline: Boolean, // 不需要容器
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showSource: false,
|
||||||
|
copyStatus: 'ready',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
unescape,
|
||||||
|
toogleSource() {
|
||||||
|
this.showSource = !this.showSource;
|
||||||
|
},
|
||||||
|
copySourceCode() {
|
||||||
|
copy(unescape(this.originCode));
|
||||||
|
this.copyStatus = 'copied';
|
||||||
|
setTimeout(() => {
|
||||||
|
this.copyStatus = 'ready';
|
||||||
|
}, 2000);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.demo-playground {
|
||||||
|
margin: 24px 0;
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #ebedf1;
|
||||||
|
border-radius: 1px;
|
||||||
|
&.transform {
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
&--previewer {
|
||||||
|
padding: 40px 24px;
|
||||||
|
border-bottom: 1px solid #ebedf1;
|
||||||
|
&.compact {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&--code {
|
||||||
|
&--actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 40px;
|
||||||
|
padding: 0 1em;
|
||||||
|
> a:not(:last-child),
|
||||||
|
> button:not(:last-child) {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
> a {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.6;
|
||||||
|
transition: opacity 0.2s, background 0.2s;
|
||||||
|
// expand click area
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
top: -8px;
|
||||||
|
right: -8px;
|
||||||
|
bottom: -8px;
|
||||||
|
left: -8px;
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
&:active {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
&:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.2;
|
||||||
|
}
|
||||||
|
&[role='codesandbox'] {
|
||||||
|
background-position: -18px 0;
|
||||||
|
}
|
||||||
|
&[role='codepen'] {
|
||||||
|
background-position: -36px 0;
|
||||||
|
}
|
||||||
|
&[role='source'] {
|
||||||
|
background-position: -72px 0;
|
||||||
|
}
|
||||||
|
&[role='change-jsx'] {
|
||||||
|
background-position: -90px 0;
|
||||||
|
}
|
||||||
|
&[role='change-tsx'] {
|
||||||
|
background-position: -108px 0;
|
||||||
|
}
|
||||||
|
&[role='open-demo'] {
|
||||||
|
background-position: -126px 0;
|
||||||
|
}
|
||||||
|
&[role='motions'] {
|
||||||
|
background-position: -162px 0;
|
||||||
|
}
|
||||||
|
&[role='sketch-component'] {
|
||||||
|
background-position: -182px 0;
|
||||||
|
}
|
||||||
|
&[role='sketch-group'] {
|
||||||
|
background-position: -200px 0;
|
||||||
|
}
|
||||||
|
&[role='copy'][data-status='ready'] {
|
||||||
|
background-position: -54px 0;
|
||||||
|
}
|
||||||
|
&[role='copy'][data-status='copied'] {
|
||||||
|
background-position: -54px -16px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
&[role='refresh'] {
|
||||||
|
background-position-x: -144px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// split action buttons by a blank node
|
||||||
|
> span {
|
||||||
|
display: inline-block;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&--content {
|
||||||
|
border-top: 1px dashed #ebedf1;
|
||||||
|
/deep/ pre {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
/deep/ .language-html {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.action-icon {
|
||||||
|
background: url('data:image/png;base64,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')
|
||||||
|
no-repeat 0 0/230px auto;
|
||||||
|
}
|
||||||
|
</style>
|
@ -8,12 +8,12 @@
|
|||||||
@switch-version="$emit('switch-version', $event)"
|
@switch-version="$emit('switch-version', $event)"
|
||||||
/>
|
/>
|
||||||
<doc-nav :lang="lang" :nav-config="config.nav" />
|
<doc-nav :lang="lang" :nav-config="config.nav" />
|
||||||
<doc-container :has-simulator="!!simulator">
|
<doc-container :has-simulator="hasSimulator">
|
||||||
<doc-content>
|
<doc-content>
|
||||||
<slot />
|
<slot />
|
||||||
</doc-content>
|
</doc-content>
|
||||||
</doc-container>
|
</doc-container>
|
||||||
<doc-simulator v-if="simulator" :src="simulator" />
|
<doc-simulator v-if="hasSimulator" :src="simulator" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -39,6 +39,7 @@ export default {
|
|||||||
lang: String,
|
lang: String,
|
||||||
versions: Array,
|
versions: Array,
|
||||||
simulator: String,
|
simulator: String,
|
||||||
|
hasSimulator: Boolean,
|
||||||
langConfigs: Array,
|
langConfigs: Array,
|
||||||
config: {
|
config: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -2,18 +2,23 @@ import Vue from 'vue';
|
|||||||
import App from './App';
|
import App from './App';
|
||||||
import { router } from './router';
|
import { router } from './router';
|
||||||
import { scrollToAnchor } from './utils';
|
import { scrollToAnchor } from './utils';
|
||||||
|
import DemoPlayground from './components/DemoPlayground';
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
Vue.config.productionTip = false;
|
Vue.config.productionTip = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
new Vue({
|
Vue.component(DemoPlayground.name, DemoPlayground);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.$route.hash) {
|
if (this.$route.hash) {
|
||||||
scrollToAnchor(this.$route.hash);
|
scrollToAnchor(this.$route.hash);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render: h => h(App),
|
render: (h) => h(App),
|
||||||
router,
|
router,
|
||||||
});
|
});
|
||||||
|
}, 0);
|
||||||
|
@ -47,9 +47,9 @@ function resolveDocuments(components: string[]): DocumentItem[] {
|
|||||||
|
|
||||||
if (locales) {
|
if (locales) {
|
||||||
const langs = Object.keys(locales);
|
const langs = Object.keys(locales);
|
||||||
langs.forEach(lang => {
|
langs.forEach((lang) => {
|
||||||
const fileName = lang === defaultLang ? 'README.md' : `README.${lang}.md`;
|
const fileName = lang === defaultLang ? 'README.md' : `README.${lang}.md`;
|
||||||
components.forEach(component => {
|
components.forEach((component) => {
|
||||||
docs.push({
|
docs.push({
|
||||||
name: formatName(component, lang),
|
name: formatName(component, lang),
|
||||||
path: join(SRC_DIR, component, fileName),
|
path: join(SRC_DIR, component, fileName),
|
||||||
@ -57,7 +57,7 @@ function resolveDocuments(components: string[]): DocumentItem[] {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
components.forEach(component => {
|
components.forEach((component) => {
|
||||||
docs.push({
|
docs.push({
|
||||||
name: formatName(component),
|
name: formatName(component),
|
||||||
path: join(SRC_DIR, component, 'README.md'),
|
path: join(SRC_DIR, component, 'README.md'),
|
||||||
@ -65,7 +65,9 @@ function resolveDocuments(components: string[]): DocumentItem[] {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const staticDocs = glob.sync(normalizePath(join(DOCS_DIR, '**/*.md'))).map(path => {
|
const staticDocs = glob
|
||||||
|
.sync(normalizePath(join(DOCS_DIR, '**/*.md')))
|
||||||
|
.map((path) => {
|
||||||
const pairs = parse(path).name.split('.');
|
const pairs = parse(path).name.split('.');
|
||||||
return {
|
return {
|
||||||
name: formatName(pairs[0], pairs[1] || defaultLang),
|
name: formatName(pairs[0], pairs[1] || defaultLang),
|
||||||
@ -73,18 +75,25 @@ function resolveDocuments(components: string[]): DocumentItem[] {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
return [...staticDocs, ...docs.filter(item => existsSync(item.path))];
|
return [...staticDocs, ...docs.filter((item) => existsSync(item.path))];
|
||||||
|
}
|
||||||
|
|
||||||
|
function genInstall() {
|
||||||
|
return `import Vue from 'vue';
|
||||||
|
import PackageEntry from './package-entry';
|
||||||
|
import './package-style';
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function genImportDocuments(items: DocumentItem[]) {
|
function genImportDocuments(items: DocumentItem[]) {
|
||||||
return items
|
return items
|
||||||
.map(item => `import ${item.name} from '${normalizePath(item.path)}';`)
|
.map((item) => `import ${item.name} from '${normalizePath(item.path)}';`)
|
||||||
.join('\n');
|
.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
function genExportDocuments(items: DocumentItem[]) {
|
function genExportDocuments(items: DocumentItem[]) {
|
||||||
return `export const documents = {
|
return `export const documents = {
|
||||||
${items.map(item => item.name).join(',\n ')}
|
${items.map((item) => item.name).join(',\n ')}
|
||||||
};`;
|
};`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,9 +113,12 @@ export function genSiteDesktopShared() {
|
|||||||
const dirs = readdirSync(SRC_DIR);
|
const dirs = readdirSync(SRC_DIR);
|
||||||
const documents = resolveDocuments(dirs);
|
const documents = resolveDocuments(dirs);
|
||||||
|
|
||||||
const code = `${genImportConfig()}
|
const code = `${genInstall()}
|
||||||
|
${genImportConfig()}
|
||||||
${genImportDocuments(documents)}
|
${genImportDocuments(documents)}
|
||||||
|
|
||||||
|
Vue.use(PackageEntry);
|
||||||
|
|
||||||
${genExportConfig()}
|
${genExportConfig()}
|
||||||
${genExportDocuments(documents)}
|
${genExportDocuments(documents)}
|
||||||
${genExportVersion()}
|
${genExportVersion()}
|
||||||
|
@ -62,6 +62,15 @@ if (existsSync(tsconfigPath)) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const VUE_LOADER = {
|
||||||
|
loader: 'vue-loader',
|
||||||
|
options: {
|
||||||
|
compilerOptions: {
|
||||||
|
preserveWhitespace: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const baseConfig: WebpackConfig = {
|
export const baseConfig: WebpackConfig = {
|
||||||
mode: 'development',
|
mode: 'development',
|
||||||
resolve: {
|
resolve: {
|
||||||
@ -71,17 +80,7 @@ export const baseConfig: WebpackConfig = {
|
|||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.vue$/,
|
test: /\.vue$/,
|
||||||
use: [
|
use: [CACHE_LOADER, VUE_LOADER],
|
||||||
CACHE_LOADER,
|
|
||||||
{
|
|
||||||
loader: 'vue-loader',
|
|
||||||
options: {
|
|
||||||
compilerOptions: {
|
|
||||||
preserveWhitespace: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(js|ts|jsx|tsx)$/,
|
test: /\.(js|ts|jsx|tsx)$/,
|
||||||
@ -113,7 +112,7 @@ export const baseConfig: WebpackConfig = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.md$/,
|
test: /\.md$/,
|
||||||
use: [CACHE_LOADER, 'vue-loader', '@vant/markdown-loader'],
|
use: [CACHE_LOADER, VUE_LOADER, '@vant/markdown-loader'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -3945,6 +3945,11 @@ copy-descriptor@^0.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
|
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
|
||||||
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
|
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.6.2:
|
core-js-compat@^3.6.2:
|
||||||
version "3.6.4"
|
version "3.6.4"
|
||||||
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.4.tgz#938476569ebb6cda80d339bcf199fae4f16fff17"
|
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.4.tgz#938476569ebb6cda80d339bcf199fae4f16fff17"
|
||||||
|
35
packages/vant-markdown-loader/src/extract-demo.js
Normal file
35
packages/vant-markdown-loader/src/extract-demo.js
Normal file
@ -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(
|
||||||
|
/<demo-code([\s\S]*?)>([\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 `
|
||||||
|
<demo-playground${attrs}
|
||||||
|
origin-code="${escape(demoContent)}"
|
||||||
|
code-snippet="${escape(demoParseredContent)}">
|
||||||
|
<${tag} />
|
||||||
|
</demo-playground>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return [content, demoLinks];
|
||||||
|
};
|
@ -1,59 +1,61 @@
|
|||||||
|
const path = require('path');
|
||||||
const loaderUtils = require('loader-utils');
|
const loaderUtils = require('loader-utils');
|
||||||
const MarkdownIt = require('markdown-it');
|
|
||||||
const markdownItAnchor = require('markdown-it-anchor');
|
|
||||||
const frontMatter = require('front-matter');
|
const frontMatter = require('front-matter');
|
||||||
const highlight = require('./highlight');
|
const parser = require('./md-parser');
|
||||||
const linkOpen = require('./link-open');
|
const linkOpen = require('./link-open');
|
||||||
const cardWrapper = require('./card-wrapper');
|
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) {
|
function wrapper(content) {
|
||||||
|
let demoLinks;
|
||||||
|
let styles;
|
||||||
|
[content, demoLinks] = extractDemo.call(this, content);
|
||||||
|
[content, styles] = sideEffectTags(content);
|
||||||
content = cardWrapper(content);
|
content = cardWrapper(content);
|
||||||
content = escape(content);
|
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<template>
|
<template>
|
||||||
<section v-html="content" v-once />
|
<section v-once>
|
||||||
|
${content}
|
||||||
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
${demoLinks
|
||||||
|
.map((link) => {
|
||||||
|
return `import ${camelize(path.basename(link, '.vue'))} from '${link}';`;
|
||||||
|
})
|
||||||
|
.join('\n')}
|
||||||
export default {
|
export default {
|
||||||
created() {
|
components: {
|
||||||
this.content = unescape(\`${content}\`);
|
${demoLinks.map((link) => camelize(path.basename(link, '.vue'))).join(',')}
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
const anchors = [].slice.call(this.$el.querySelectorAll('h2, h3, h4, h5'));
|
const anchors = [].slice.call(this.$el.querySelectorAll('h2, h3, h4, h5'));
|
||||||
|
|
||||||
anchors.forEach(anchor => {
|
anchors.forEach(anchor => {
|
||||||
anchor.addEventListener('click', this.scrollToAnchor);
|
anchor.addEventListener('click', this.scrollToAnchor);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
scrollToAnchor(event) {
|
scrollToAnchor(event) {
|
||||||
if (event.target.id) {
|
if (event.target.id) {
|
||||||
this.$router.push({
|
this.$router.push({
|
||||||
path: this.$route.path,
|
name: this.$route.name,
|
||||||
hash: event.target.id
|
hash: '#' + event.target.id
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
${styles.join('\n')}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parser = new MarkdownIt({
|
module.exports = function (source) {
|
||||||
html: true,
|
|
||||||
highlight,
|
|
||||||
}).use(markdownItAnchor, {
|
|
||||||
level: 2,
|
|
||||||
slugify,
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = function(source) {
|
|
||||||
let options = loaderUtils.getOptions(this) || {};
|
let options = loaderUtils.getOptions(this) || {};
|
||||||
this.cacheable && this.cacheable();
|
this.cacheable && this.cacheable();
|
||||||
|
|
||||||
@ -74,5 +76,5 @@ module.exports = function(source) {
|
|||||||
linkOpen(parser);
|
linkOpen(parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
return options.wrapper(parser.render(source), fm);
|
return options.wrapper.call(this, parser.render(source), fm);
|
||||||
};
|
};
|
||||||
|
14
packages/vant-markdown-loader/src/md-parser.js
Normal file
14
packages/vant-markdown-loader/src/md-parser.js
Normal file
@ -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;
|
14
packages/vant-markdown-loader/src/side-effect-tags.js
Normal file
14
packages/vant-markdown-loader/src/side-effect-tags.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
module.exports = function sideEffectTags(content) {
|
||||||
|
const styles = [];
|
||||||
|
|
||||||
|
// 从模版中移除 script 标签
|
||||||
|
content = content.replace(/<script[\s\S]*?>[\s\S]*?<\/script>/g, '');
|
||||||
|
|
||||||
|
// 从模版中移除 style 标签,并收集到 styles 数组中,以转移为 .vue 文件 的 style 标签
|
||||||
|
content = content.replace(/<style[\s\S]*?>([\s\S]*?)<\/style>/g, (_, css) => {
|
||||||
|
styles.push(`<style scoped>${css}</style>`);
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
|
return [content, styles];
|
||||||
|
};
|
@ -1,5 +1,5 @@
|
|||||||
import Vue, { CreateElement } from 'vue';
|
import Vue, { CreateElement } from 'vue';
|
||||||
import '../docs/site/mobile';
|
import '../docs/site/entry';
|
||||||
import Locale from '../src/locale';
|
import Locale from '../src/locale';
|
||||||
import { mount, later } from '.';
|
import { mount, later } from '.';
|
||||||
|
|
||||||
|
@ -5,7 +5,8 @@ module.exports = function () {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
entry: {
|
entry: {
|
||||||
'site-mobile': ['./docs/site/mobile'],
|
'site-mobile': ['./docs/site/entry'],
|
||||||
|
'site-desktop': ['./docs/site/entry'],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user