Vue3+TS+Vite develops component library and publishes it to npm
Reference documents:
1. Installation dependencies and configuration
1. Install vitepress
It is recommended to use pnpm, install pnpm:
npm install -g pnpm
Install vitepress:
pnpm add vitepress -D
or yarn add vitepress -D
2. Add instructions to package.json
Add the document website startup and packaging instructions in the script, specify port 8000 at startup, and open it automatically
"scripts": {
"docs:dev": "vitepress dev docs --port 8000 --open",
"docs:build": "vitepress build docs"
}
The complete package.json file is as follows:
{
"name": "vue-amazing-ui",
"version": "0.0.30",
"private": false,
"type": "module",
"files": [
"dist"
],
"main": "./dist/vue-amazing-ui.umd.cjs",
"module": "./dist/vue-amazing-ui.js",
"exports": {
"./dist/style.css": "./dist/style.css",
"./css": "./dist/style.css",
".": {
"import": "./dist/vue-amazing-ui.js",
"require": "./dist/vue-amazing-ui.umd.cjs"
}
},
"scripts": {
"dev": "vite --port 9000 --open --force",
"build": "run-p type-check build-only",
"docs:dev": "vitepress dev docs --port 8000 --open",
"docs:build": "vitepress build docs",
"docs:deploy": "sh script/deploy.sh",
"pub": "sh script/publish.sh",
"preview": "vite preview",
"build-only": "vite build",
"type-check": "vue-tsc --noEmit",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
},
"dependencies": {
"@vuepic/vue-datepicker": "^4.5.1",
"@vueuse/core": "^10.1.2",
"@vueuse/integrations": "^10.1.2",
"ant-design-vue": "^3.2.20",
"core-js": "^3.30.2",
"date-fns": "^2.30.0",
"qrcode": "^1.5.3",
"swiper": "^9.3.2",
"vue": "^3.3.4",
"vue-amazing-ui": "^0.0.30",
"vue-router": "^4.2.1"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.3.0",
"@types/node": "^18.16.14",
"@vitejs/plugin-vue": "^4.2.3",
"@vue/eslint-config-typescript": "^11.0.3",
"@vue/tsconfig": "^0.1.3",
"eslint": "^8.41.0",
"eslint-plugin-vue": "^9.14.0",
"less": "^4.1.3",
"npm-run-all": "^4.1.5",
"prettier": "^2.8.8",
"rollup-plugin-visualizer": "^5.9.0",
"terser": "^5.17.6",
"typescript": "~4.7.4",
"unplugin-vue-components": "^0.25.0",
"vite": "^4.3.8",
"vitepress": "1.0.0-beta.1",
"vue-tsc": "^1.6.5"
},
"description": "This template should help get you started developing with Vue Amazing UI in Vue 3.",
"repository": {
"type": "git",
"url": "git+https://github.com/themusecatcher/vue-amazing-ui.git"
},
"keywords": [
"Vue3",
"TS",
"Vite",
"Amazing",
"UI",
"Components"
],
"author": "theMuseCatcher",
"license": "ISC",
"bugs": {
"url": "https://github.com/themusecatcher/vue-amazing-ui/issues"
},
"homepage": "https://github.com/themusecatcher/vue-amazing-ui#readme"
}
3. Create a document website directory and files
- create
docs/
directory - Create a new homepage file
docs/
underindex.md
- Create a new folder
docs/
under ( used to store various static resources, such as: website logo, homepage picture, etc. )public/
4. Start the project
After startup, the project will automatically generate a .vitepress/ directory under /docs
pnpm docs:dev
2. Write component library documentation
1. Create relevant files and directories
in docs/.vitepress
- Create
config.ts
configuration file - create
theme/
folder theme/
Createindex.ts
a file intheme/
Create in toglobal.less
store global styles (using less files requires installation of corresponding dependencies)- Create
utils/
a folder (used to store custom ts methods, which will be used in the custom display version label on the home page later) - (less is used in the document) Install the less preprocessor: related documents
pnpm add less -D
2. Write the homepage of the website
Home page configuration reference document
Write homepage related content in docs/index.md, where
fetchVersion()
the custom method is used to add a custom version label after the homepage tagline
---
layout: home
title: Vue Amazing UI
titleTemplate: Amazing UI Components Library
hero:
name: Vue Amazing UI
text: Amazing UI 组件库
tagline: 基于 Vue3 + TS + Vite 开发
image:
src: /logo-with-shadow.png
alt: Vue Amazing UI
actions:
- theme: brand
text: Get Started
link: /guide/features
- theme: alt
text: View on GitHub
link: https://github.com/themusecatcher/vue-amazing-ui
- theme: alt
text: View on NPM
link: https://www.npmjs.com/package/vue-amazing-ui
---
import { onMounted } from 'vue'
import { fetchVersion } from './.vitepress/utils/fetchVersion'
onMounted(() => {
fetchVersion()
})
docs/.vitepress/utils/
CreatefetchVersion.ts
a file in
// 远程读取 github 仓库中 package.json 文件中的 version 版本号
// 方式一:
// 读取规则:https://api.github.com/repos///contents/?ref=
// return fetch('https://api.github.com/repos/themusecatcher/vue-amazing-ui/contents/package.json?ref=master', {
// headers: {
// // See https://docs.github.com/en/rest/overview/media-types
// Accept: 'application/vnd.github.v3.raw',
// // See https://docs.github.com/en/rest/guides/getting-started-with-the-rest-api#authentication
// // Authorization: 'token ${GITHUB_TOKEN}',
// }
// })
// 方式二:
// 读取规则:https://raw.githubusercontent.com
export function fetchVersion() {
return fetch('https://raw.githubusercontent.com/themusecatcher/vue-amazing-ui/master/package.json')
.then(res => res.json())
.then(json => json.version ?? '')
.then(version => {
if (!version) return
const tagLineParagragh = document.querySelector('div.VPHero.has-image.VPHomeHero > div > div.main > p.tagline')
const docsVersionSpan = document.createElement('samp')
docsVersionSpan.classList.add('version-tag')
docsVersionSpan.innerText = version
tagLineParagragh?.appendChild(docsVersionSpan)
})
}
docs/.vitepress/theme/global.less
Write label styles in
.version-tag {
font-size: 14px;
line-height: 1.571;
font-weight: bold;
padding: 4px 6px;
margin-left: 6px;
background: #bd34fe;
color: #FFF;
border-radius: 10px;
display: inline-block;
vertical-align: top;
margin-top: 4px;
}
The effect is as shown in the figure below, version label:
0.0.30
3. Write the global style of the document
Write styles in
theme/global.less
, the following styles are all derived from the global styles used in the vite official website project, and slightly modified:
/**
* Colors
* -------------------------------------------------------------------------- */
:root {
--vp-c-brand: #646cff;
--vp-c-brand-light: #747bff;
--vp-c-brand-lighter: #9499ff;
--vp-c-brand-lightest: #bcc0ff;
--vp-c-brand-dark: #535bf2;
--vp-c-brand-darker: #454ce1;
--vp-c-brand-dimm: rgba(100, 108, 255, 0.08);
--c-brand: #646cff;
--c-brand-light: #747bff;
}
/**
* Component: Button
* -------------------------------------------------------------------------- */
:root {
--vp-button-brand-border: var(--vp-c-brand-light);
--vp-button-brand-text: var(--vp-c-white);
--vp-button-brand-bg: var(--vp-c-brand);
--vp-button-brand-hover-border: var(--vp-c-brand-light);
--vp-button-brand-hover-text: var(--vp-c-white);
--vp-button-brand-hover-bg: var(--vp-c-brand-light);
--vp-button-brand-active-border: var(--vp-c-brand-light);
--vp-button-brand-active-text: var(--vp-c-white);
--vp-button-brand-active-bg: var(--vp-button-brand-bg);
}
/**
* Component: Home
* -------------------------------------------------------------------------- */
:root {
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: -webkit-linear-gradient(
120deg,
#bd34fe 30%,
#41d1ff
);
--vp-home-hero-image-background-image: linear-gradient(
-45deg,
#bd34fe 50%,
#47caff 50%
);
--vp-home-hero-image-filter: blur(40px);
}
@media (min-width: 640px) {
:root {
--vp-home-hero-image-filter: blur(56px);
}
}
@media (min-width: 960px) {
:root {
--vp-home-hero-image-filter: blur(72px);
}
}
/**
* Component: Custom Block
* -------------------------------------------------------------------------- */
:root {
--vp-custom-block-tip-border: var(--vp-c-brand);
--vp-custom-block-tip-text: var(--vp-c-brand-darker);
--vp-custom-block-tip-bg: var(--vp-c-brand-dimm);
}
.dark {
--vp-custom-block-tip-border: var(--vp-c-brand);
--vp-custom-block-tip-text: var(--vp-c-brand-lightest);
--vp-custom-block-tip-bg: var(--vp-c-brand-dimm);
}
/**
* Component: Algolia
* -------------------------------------------------------------------------- */
.DocSearch {
--docsearch-primary-color: var(--vp-c-brand) !important;
}
/**
* VitePress: Custom fix
* -------------------------------------------------------------------------- */
/*
Use lighter colors for links in dark mode for a11y.
Also specify some classes twice to have higher specificity
over scoped class data attribute.
*/
.dark .vp-doc a,
.dark .vp-doc a > code,
.dark .VPNavBarMenuLink.VPNavBarMenuLink:hover,
.dark .VPNavBarMenuLink.VPNavBarMenuLink.active,
.dark .link.link:hover,
.dark .link.link.active,
.dark .edit-link-button.edit-link-button,
.dark .pager-link .title {
color: var(--vp-c-brand-lighter);
}
.dark .vp-doc a:hover,
.dark .vp-doc a > code:hover {
color: var(--vp-c-brand-lightest);
opacity: 1;
}
.vp-doc a {
font-weight: normal;
}
.vp-doc p {
margin: 0;
}
/* Transition by color instead of opacity */
.dark .vp-doc .custom-block a {
transition: color 0.25s;
}
a:hover {
text-decoration: none !important;
}
summary {
font-weight: 600;
&:hover {
cursor: pointer;
color: var(--vp-c-brand-lighter);
}
}
svg {
fill: var(--vp-c-text-1);
}
.VPNavBarTitle .title {
transition: all 0.25s;
&:hover {
color: var(--vp-c-brand);
}
}
.version-tag {
font-size: 14px;
line-height: 1.571;
font-weight: bold;
padding: 4px 6px;
margin-left: 6px;
background: #bd34fe;
color: #FFF;
border-radius: 10px;
display: inline-block;
vertical-align: top;
margin-top: 4px;
}
4. Introduce the default theme, global style and component library
Install component library
pnpm add vue-amazing-ui
theme/index.ts
Introduce default themes, global styles and globally register component libraries in
import DefaultTheme from 'vitepress/theme'
import './global.less' // global less
import VueAmazingUI from 'vue-amazing-ui'
import 'vue-amazing-ui/css'
// import VueAmazingUI from '../../../dist/vue-amazing-ui'
// import '../../../dist/style.css'
export default {
extends: DefaultTheme, // or ...DefaultTheme
enhanceApp ({
app }) {
app.use(VueAmazingUI)
}
}
5. Configure the website
docs/.vitepress/config.ts
Configure your site in
import {
defineConfig } from 'vitepress'
export default defineConfig({
title: `Vue Amazing UI`,
description: 'Amazing UI 组件库',
base: '/vue-amazing-ui/',
head: [ // 网站图标
['link', {
rel: 'icon', type: 'image/svg+xml', href: 'logo.svg' }],
// ['link', { rel: 'icon', type: 'image/x-icon', href: 'favicon.ico' }],
],
appearance: true, // 默认 true,设为 false 则无法切换dark/light主题,可选 'dark' true false
markdown: {
lineNumbers: false // 是否显示行数,默认false
},
themeConfig: {
logo: '/logo.svg',
editLink: {
pattern: 'https://github.com/themusecatcher/vue-amazing-ui/tree/master/docs/:path',
text: 'Suggest changes to this page',
},
// 默认支持icon包括:'discord'|'facebook'|'github'|'instagram'|'linkedin'|'mastodon'|'slack'|'twitter'|'youtube'
socialLinks: [
{
icon: 'github', link: 'https://github.com/themusecatcher/vue-amazing-ui' },
// 自定义icon
// {
// icon: {
// svg: 'Dribbble'
// },
// link: 'https://www.npmjs.com/package/vue-amazing-ui'
// }
],
// search: { // vitepress 内置 search
// provider: 'local'
// },
algolia: {
// algolia 搜索服务 与 内置 search 可二选一
appId: 'SHDNEYGA8Z',
apiKey: '91419401b0b0efd31b610e54e5b97249',
indexName: 'vue-amazing-ui'
},
footer: {
message: 'Released under the MIT License.',
copyright: 'Copyright © 2023-present The Muse Catcher',
},
nav: [
{
text: '组件', link: '/guide/features', activeMatch: '/guide/' },
{
text: '工具', link: '/utils/started', activeMatch: '/utils/' },
{
text: '链接',
items: [
{
text: 'My Github', link: 'https://github.com/themusecatcher' },
{
text: 'My CSDN', link: 'https://blog.csdn.net/Dandrose?type=blog' },
{
items: [
{
text: 'vue',
link: 'https://cn.vuejs.org/',
},
{
text: 'vitepress',
link: 'https://vitepress.dev/',
}
]
}
]
}
],
sidebar: {
'/guide/': [
{
text: '指引',
items: [
{
text: '特性',
link: '/guide/features'
},
{
text: '快速上手',
link: '/guide/started'
}
]
},
{
text: '组件',
items: [
{
text: '面包屑 Breadcrumb',
link: '/guide/components/breadcrumb'
},
{
text: '按钮 Button',
link: '/guide/components/button'
},
{
text: '走马灯 Carousel',
link: '/guide/components/carousel'
},
{
text: '级联选择 Cascader',
link: '/guide/components/cascader'
},
{
text: '多选框 Checkbox',
link: '/guide/components/checkbox'
},
{
text: '折叠面板 Collapse',
link: '/guide/components/collapse'
},
{
text: '倒计时 Countdown',
link: '/guide/components/countdown'
},
{
text: '日期选择 DatePicker',
link: '/guide/components/datepicker'
},
{
text: '对话框 Dialog',
link: '/guide/components/dialog'
},
{
text: '分割线 Divider',
link: '/guide/components/divider'
},
{
text: '空状态 Empty',
link: '/guide/components/empty'
},
{
text: '图片 Image',
link: '/guide/components/image'
},
{
text: '数字输入框 InputNumber',
link: '/guide/components/inputnumber'
},
{
text: '全局提示 Message',
link: '/guide/components/message'
},
{
text: '信息提示 Modal',
link: '/guide/components/modal'
},
{
text: '通知提醒框 Notification',
link: '/guide/components/notification'
},
{
text: '分页器 Pagination',
link: '/guide/components/pagination'
},
{
text: '进度条 Progress',
link: '/guide/components/progress'
},
{
text: '二维码 QRCode',
link: '/guide/components/qrcode'
},
{
text: '单选框 Radio',
link: '/guide/components/radio'
},
{
text: '评分 Rate',
link: '/guide/components/rate'
},
{
text: '选择器 Select',
link: '/guide/components/select'
},
{
text: '滑动输入条 Slider',
link: '/guide/components/slider'
},
{
text: '加载中 Spin',
link: '/guide/components/spin'
},
{
text: '步骤条 Steps',
link: '/guide/components/steps'
},
{
text: '触摸滑动插件 Swiper',
link: '/guide/components/swiper'
},
{
text: '开关 Switch',
link: '/guide/components/switch'
},
{
text: '表格 Table',
link: '/guide/components/table'
},
{
text: '标签页 Tabs',
link: '/guide/components/tabs'
},
{
text: '文字滚动 TextScroll',
link: '/guide/components/textscroll'
},
{
text: '时间轴 Timeline',
link: '/guide/components/timeline'
},
{
text: '文字提示 Tooltip',
link: '/guide/components/tooltip'
},
{
text: '上传 Upload',
link: '/guide/components/upload'
},
{
text: '播放器 Video',
link: '/guide/components/video'
},
{
text: '瀑布流 Waterfall',
link: '/guide/components/waterfall'
}
]
}
],
'/utils/': [
{
text: '指引',
items: [
{
text: '快速上手',
link: '/utils/started'
}
]
},
{
text: '工具',
items: [
{
text: 'date 日期格式化',
link: '/utils/date-format'
},
{
text: 'raf 动画帧',
link: '/utils/animation-frame'
},
{
text: 'raf 定时器',
link: '/utils/raf-timeout'
},
{
text: 'throttle 节流',
link: '/utils/throttle'
},
{
text: 'debounce 防抖',
link: '/utils/debounce'
},
{
text: 'add 加法',
link: '/utils/add'
},
{
text: 'downloadFile 下载文件',
link: '/utils/download-file'
}
]
}
]
}
}
})
6. Configure Algolia search
Apply for search service
Fill in the website address, email address, and code warehouse address deployed to the public network, check all of them, and submit!
Waiting for the mail of the application
Reply Mail
I am the maintainer of the website, I can modify the code ~
waiting for reply email
get
appId
,apiKey
,indexName
Configure and import algolia search service
docs/.vitepress/config.ts
Write the following configuration in
import {
defineConfig } from 'vitepress'
export default defineConfig({
themeConfig: {
algolia: {
appId: 'SHDNEYGA8Z',
apiKey: '91419401b0b0efd31b610e54e5b97249',
indexName: 'vue-amazing-ui'
}
}
})
The algolia search effect is as follows:
The overall directory structure of the website is as follows:
The effect of the homepage of the website is as follows:
3. Website content directory
Create a content directory file
Create a new folder
docs/
under/guide
Note: The directory structure needs to correspond to the properties
docs/.vitepress/config.ts
in the configuration filesidebar
Create
components/
a directory to store the documentation pages corresponding to each component
Create
features.md
andstarted.md
use and write features and quickstart pages in site guides
features.md
document:
# 特性
## 简要介绍
该组件库采用 Vue3 + TS + Vite3 + Less 实现!
开箱即用!
## 三种使用方式
- 全局引入所有组件
- 按需引入部分组件
- git clone [vue-amazing-ui](https://github.com/themusecatcher/vue-amazing-ui) 到本地后,从 packages 下单独拷贝单文件组件 (SFC) 到项目内使用
started.md
document:
# 快速上手
## 安装
**With PNPM**
```bash
$ pnpm i vue-amazing-ui
# or
$ pnpm add vue-amazing-ui
```
**With Yarn**
```bash
$ yarn add vue-amazing-ui
```
**With NPM**
```bash
$ npm i vue-amazing-ui
```
## 使用
**Global**
```ts
import { createApp } from 'vue'
import App from './App.vue'
import VueAmazingUI from 'vue-amazing-ui'
import 'vue-amazing-ui/css'
const app = createApp(App)
app.use(VueAmazingUI)
app.mount('#app')
```
**Local**
```vue
import { Button } from 'vue-amazing-ui'
import 'vue-amazing-ui/css'
```
Create individual component documents
components/
Create component correspondingmd
files in the directory
Example: To write
按钮 button
component documentation:
# 按钮
## 何时使用
- 当需要添加一个操作按钮时
<script setup lang="ts">
import { ref } from 'vue'
const loading = ref(true)
function onClick (e: Event) {
console.log('click')
}
</script>
## 基本使用
<div :class="$style['m-flex']">
<Button @click="onClick">Default</Button>
<Button effect="reverse" @click="onClick">Reverse</Button>
<Button type="primary" @click="onClick">Primary</Button>
<Button type="danger" @click="onClick">Danger</Button>
<Button disabled @click="onClick">Disabled</Button>
</div>
::: details Show Code
```vue
<script setup lang="ts">
function onClick (e: Event) {
console.log('click')
}
</script>
<template>
<Button @click="onClick">Default</Button>
<Button effect="reverse" @click="onClick">Reverse</Button>
<Button type="primary" @click="onClick">Primary</Button>
<Button type="danger" @click="onClick">Danger</Button>
<Button disabled @click="onClick">Disabled</Button>
</template>
```
:::
## 大、中、小三种尺寸
<div :class="$style['m-flex']">
<Button size="small" @click="onClick">Small</Button>
<Button @click="onClick">Default</Button>
<Button size="large" @click="onClick">Large</Button>
</div>
::: details Show Code
```vue
<script setup lang="ts">
function onClick (e: Event) {
console.log('click')
}
</script>
<template>
<Button size="small" @click="onClick">Small</Button>
<Button @click="onClick">Default</Button>
<Button size="large" @click="onClick">Large</Button>
</template>
```
:::
## 自定义样式
<Button :width="120" :height="40" :border-radius="8" size="large" @click="onClick">
<p style="font-size: 18px;">自定义样式</p>
</Button>
::: details Show Code
```vue
<script setup lang="ts">
function onClick (e: Event) {
console.log('click')
}
</script>
<template>
<Button :width="120" :height="40" :border-radius="8" size="large" @click="onClick">
<p style="font-size: 18px;">自定义样式</p>
</Button>
</template>
```
:::
## 加载中状态
<div :class="$style['m-flex']">
<Button :loading="loading" @click="onClick">Default</Button>
<Button :loading="loading" type="primary" @click="onClick">Primary</Button>
<Button :loading="loading" type="danger" @click="onClick">Danger</Button>
</div>
<div :class="$style['m-flex']" style="margin-top: 30px;">
<h3 :class="$style['u-h3']">Loading state: </h3>
<Switch v-model:checked="loading" />
</div>
::: details Show Code
```vue
<script setup lang="ts">
import { ref } from 'vue'
const loading = ref(true)
function onClick (e: Event) {
console.log('click')
}
</script>
<template>
<Button :loading="loading" @click="onClick">Default</Button>
<Button :loading="loading" type="primary" @click="onClick">Primary</Button>
<Button :loading="loading" type="danger" @click="onClick">Danger</Button>
<h3>Loading state: <Switch v-model:checked="loading" /></h3>
</template>
```
:::
<style module>
.m-flex {
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: center;
}
.u-h3 {
margin-top: 0 !important;
}
</style>
## APIs
参数 | 说明 | 类型 | 默认值 | 必传
-- | -- | -- | -- | --
name | 默认文本 | string | slot | '按钮' | false
type | 类型 | 'default' | 'primary' | 'danger' | 'default' | false
effect | 悬浮变化效果,只有 type 为 default 时,effect 才生效 | 'fade' | 'reverse' | ''
size | 尺寸 | 'small' | 'middle' | 'large' | '_self' | false
width | 宽度,优先级高于size属性,为0时自适应内容的宽度 | number | 0 | false
height | 高度,优先级高于size属性 | number | 0 | false
borderRadius | 圆角 | number | 5 | false
route | 跳转目标URL地址 | {path?: string, query?: object} | {} | false
target | 如何打开目标URL,设置 route 时生效 | '_self' | '_blank' | '_self' | false
disabled | 是否禁用 | boolean | false | false
loading | 是否加载中 | boolean | false | false
center | 是否将按钮设置为块级元素并居中展示 | boolean | false | false
## Events
事件名称 | 说明 | 参数
-- | -- | --
click | 点击按钮时的回调,未设置 route 时生效 | (e: Event) => void
4. Package the static website and deploy it to GitHub
Create script directory
script/
Create a directory in the root directory of the project , which is mainly used to store various script files, so as to facilitate the automated development process in the future!
Created
deploy.sh
for packaging static websites and deploying to Github
Create
publish.sh
to package and upgrade the component library, and publish it to npm, and automatically upgrade the dependency version of the component library in the project to the latest
deploy.sh
document:
# /bin/bash
# 确保脚本抛出遇到的错误
set -e
# 重新打包组件库
pnpm build
# 打包生成静态文件
pnpm docs:build
# 进入待发布的 dist/ 目录
cd docs/.vitepress/dist
git init
git add .
git commit -m 'deploy'
# 部署到 https://.github.io/
git push -f [email protected]:themusecatcher/vue-amazing-ui.git master:github-pages
# 提交所有代码到github
cd ../../../
git add .
git cm -m 'update'
git push
publish.sh
File (need to log in to the npm account in advance, otherwise it cannot be published directly):
# /bin/bash
# 确保脚本抛出遇到的错误
set -e
# 读取package.json中的version
version=`jq -r .version package.json`
# 打包构建
pnpm build
# 发布到npm,pnpm(高性能的npm)
pnpm publish
# 升级 vue-amazing-ui 依赖版本
pnpm up vue-amazing-ui@$version
# 提交版本更新代码到github
git add .
git cm -m "update $version"
git push
Add directives to package.json
"scripts": {
"docs:deploy": "sh script/deploy.sh",
"pub": "sh script/publish.sh"
}
package deployment
pnpm docs:deploy
configure github
view website
After the save is successful, click Visit site to open and view the website:
view content