1 目标
通过 PNPM 创建一个 monorepo(多个项目在一个代码仓库)项目,形成一个通用的仓库模板。
这里以在该 monorepo 项目中搭建 Web Components 类型的组件库为例,介绍从仓库搭建、组件测试到组件发布的整个流程。
这个仓库既可以用于公司存放和管理所有的项目(不限于 Web Components),也可以用于将个人班余的所有积累整合其中。
如不想一步一步搭建,可以直接下载 项目模板。
2 环境要求
核心是 PNPM 和 Node.js,没有特殊的版本要求,只要他俩能对应上即可。
当前项目使用的 PNPM 版本为 9.4.0,Node.js 为 18.20.3。
除了以上两个,项目中也使用到了以下工具或插件,可以按需添加,如不使用则不用考虑其环境要求。
vite(v5.2.0):主要用于项目运行和构建,要求 Node.js v18+ 或者 v20+。
Storybook(v8.1.7):用于组件的测试和展示,要求 Vite v4.0 +。
Changeset(v2.27.5):用于管理版本生成变更日志,无特殊要求。
3 仓库搭建
3.1 新建项目
新建一个文件夹作为项目容器。
这里起名叫 ease-life,意为轻松生活。所有的学习、工作都是为了更好地、更轻松的生活。
3.2 创建目录
3.2.1 apps
在项目根目录下创建 apps 文件夹。
在 apps 下创建 storybook 文件夹。用于测试和展示自定义的 web components。
apps 文件夹主要用来存放应用程序,如:Storybook、VitePress,还可以加上 vue-test、react-test 来对 web components 做测试。
扫描二维码关注公众号,回复: 17531652 查看本文章
3.2.2 packages
在项目根目录下创建 apps 文件夹。
在 packages 下分别创建 config(配置信息)、web-components(实现组件与框架无关) 文件夹。
- 在 config 文件下创建 eslint、stylelint 以及 typescript,用于存放对应通用的配置
- 在 web-components 创建 text 文件夹,实现一个简单的文本组件。 text 文件夹下创建 src 文件夹。
packages 底下主要包含插件、组件、命令行、类库等,除了以上的内容还可以按需加上 vue-components、react-components、cli、map-library 等等。
形成的目录结构如下:
ease-life
|-- apps
| |-- storybook
|-- packages
|-- config
| |-- eslint
| |-- stylelint
| |-- typescript
|-- web-components
|-- text
|-- src
3.3 添加文件/工具
3.3.1 PNPM 相关
- 在项目根目录下添加文件:pnpm-workspace.yaml,定义 PNPM 的工作空间:
packages:
# 匹配 packages 目录下(任意文件夹下)的所有模块
- 'packages/**'
# 匹配 apps 直接子文件夹下的所有模块
- 'apps/*'
这里的模块,说的是:包含 package.json,可以被发布到 NPM 远程仓库的项目。
- 在项目根目录下添加文件:.npmrc,定义 PNPM 的配置项:
# 允许链接工作空间中的包
link-workspace-packages = true
# 在引用工作空间中的包时,设置前缀为 *,即:使用最新版本的包
save-prefix = ''
3.3.2 Vite 相关
- 在根目录下运行以下内容:
pnpm init
从而生成 package.json,如下:
{
"name": "ease-life",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified1\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
}
- 在 web-components、web-components/text、config/eslint、config/stylelint 以及 config/typescript 下都执行 pnpm init,或直接将根目录下的 package.json 拷贝过去。
本文的目的是要每个组件都能够被单独被发布至 NPM 仓库,如:@ease-life/text。如只需要做整个组件库的统一发布,则无需在 web-components/text 下执行 pnpm init。
- 在项目最外层空间下添加 vite:
pnpm add vite -Dw
packages 里的所有模块如无特殊情况,可统一使用 vite 来运行、打包,因此只需要在项目最外层安装一次即可。
- 在项目根目录下,添加文件 vite.config.js:
import {
defineConfig } from 'vite'
import {
defineConfig } from 'vite';
export default defineConfig({
build: {
lib: {
entry: 'index.ts',
name: 'index', // UMD模块中库的全局名称,按需修改
fileName: 'index',
},
},
});
- 修改之前生成的 package.json:
{
"name": "ease-life",
"version": "1.0.0",
"description": "哥的幸福生活全靠你啦",
"scripts": {
"dev": "vite --open",
"build": "vite build",
"preview": "vite preview --open"
},
"keywords": [
"monorepo",
"web components",
"pnpm",
"storybook",
"changeset"
],
"author": "zqc",
"repository": {
"type": "git",
"url": ""
},
"license": "MIT",
"type": "module",
"devDependencies": {
"vite": "^5.2.0"
},
"engines": {
"node": ">= 18.0.0",
"pnpm": ">= 9.0.0"
}
}
3.3.3 自定义 Web Components
- 在 packages/web-components/text/src 下创建 text.ts:
import {
html, css, LitElement } from 'lit';
import {
customElement, property } from 'lit/decorators.js';
@customElement('el-text')
export class ELText extends LitElement {
static styles = css`p { color: blue }`;
@property()
name = 'Somebody';
render() {
return html`<p>Hello, ${
this.name}!</p>`;
}
}
- 在 packages/web-components/text 下创建 index.ts(导出当前组件):
export {
ELText as default } from './src/text.ts';
- 在 packages/web-components/text 下添加 tsconfig.json:
{
"compilerOptions": {
"target": "ESNext",
"experimentalDecorators": true,
"useDefineForClassFields": false,
"module": "ESNext",
"lib": [
"ES2020",
"DOM",
"DOM.Iterable"
],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": [
"src"
]
}
以上内容将会被移至 packages/config/typescript 中,待修改
- 修改 在 packages/web-components/text 下的 package.json:
{
"name": "@ease-life/text",
"version": "1.0.0",
"description": "",
"type": "module",
"files": [
"dist"
],
"main": "./dist/index.umd.cjs",
"module": "./dist/index.js",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.umd.cjs"
}
},
"scripts": {
"build": "vite build -c ../../../vite.config.js"
},
"keywords": [
"ELText"
],
"author": "",
"license": "ISC",
"dependencies": {
"lit": "^3.1.2"
}
}
3.3.4 生成 storybook
- 在 apps/storybook 文件夹的路径下运行以下内容:
pnpm dlx storybook@latest init
选择最后一个选项,回车。
此时就会在 apps/storybook 下有对应的 storybook 的内容。
- 删除 apps/storybook/src/stories 下自带的 button.css、Button.stories.ts、Button.ts、header.css、Header.stories.ts、Header.ts、page.css、Page.stories.ts、Page.ts 六个文件。
3.3.5 添加 Lint、TS 配置(可选)
通过在 config 文件夹下添加一系列通用配置来满足 monorepo 仓库的需要,同时可以将其进行发布用在其他项目中。如需使用本文已经配置好的内容,则请移步:
- ESLint:https://www.npmjs.com/package/@ease-life/eslint-config
- Stylelint:https://www.npmjs.com/package/@ease-life/stylelint-config
- TypeScript:https://www.npmjs.com/package/@ease-life/typescript-config
- ESLint
添加 index.js:
/* eslint-disable no-underscore-dangle */
import {
fixupConfigRules } from '@eslint/compat';
import {
FlatCompat } from '@eslint/eslintrc';
import eslint from '@eslint/js';
// import eslintImport from 'eslint-plugin-import';
import globals from 'globals';
import path from 'path';
import tseslint from 'typescript-eslint';
import {
fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const flatCompat = new FlatCompat({
baseDirectory: __dirname,
});
export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
...fixupConfigRules(flatCompat.extends('airbnb-base')),
...flatCompat.extends('airbnb-typescript/base'),
{
languageOptions: {
parserOptions: {
project: true,
tsconfigRootDir: import.meta.dirname,
},
},
},
{
files: ['**/*.{js,jsx,cjs,mjs}'],
...tseslint.configs.disableTypeChecked,
},
{
// 配置需要被忽略的文件,替代之前的 .eslintignore 文件
ignores: [
'.idea',
'.vscode',
'**/dist/',
],
files: ['**/*.{js,jsx,mjs,cjs,ts,ts,vue}'],
},
{
// plugins: {
// import: eslintImport, // 添加了 airbnb-base 就不用再加这个了
// },
settings: {
'import/resolver': {
typescript: {
},
},
},
languageOptions: {
globals: {
...globals.browser, // 'window' is not defined.
...globals.node, // 'process' is not defined.
},
},
},
{
files: ['eslint.config.js', 'vite.config.js', 'stylelint.config.js'],
rules: {
'import/no-extraneous-dependencies': 'off',
},
},
{
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'max-len': ['error', 200],
},
},
);
修改 package.json:
{
"name": "@ease-life/eslint-config",
"version": "1.1.0",
"description": "Custom airbnb eslint config with ESLint v9",
"main": "index.js",
"keywords": [
"eslint"
],
"author": "zqc",
"license": "MIT",
"type": "module",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@eslint/compat": "1.1.0",
"@eslint/eslintrc": "3.1.0",
"@eslint/js": "9.4.0",
"eslint": "9.4.0",
"eslint-config-airbnb-base": "15.0.0",
"eslint-config-airbnb-typescript": "18.0.0",
"eslint-import-resolver-typescript": "3.6.1",
"eslint-plugin-import": "2.29.1",
"globals": "15.4.0",
"typescript-eslint": "7.13.0"
}
}
- Stylelint
添加 index.js:
export default {
// 继承已有配置 如果规则与自己想要的冲突 可以在下面rules中修改规则
extends: [
'stylelint-config-standard',
'stylelint-config-standard-scss',
'stylelint-config-sass-guidelines',
],
// 插件是由社区创建的规则或规则集 按照规则对CSS属性进行排序
plugins: [
// 执行各种各样的 SCSS语法特性检测规则(插件包)
'stylelint-scss',
// 指定排序,比如声明的块内(插件包)属性的顺序
'stylelint-order',
],
customSyntax: 'postcss-html',
rules: {
// 允许的最大嵌套深度为 3
'max-nesting-depth': 3,
'color-named': null,
// 屏蔽一些scss等语法检查
'at-rule-no-unknown': [
true,
{
ignoreAtRules: [
'extend',
'at-root',
'debug',
'warn',
'error',
'if',
'else',
'for',
'each',
'while',
'mixin',
'include',
'content',
'return',
'function',
],
},
],
// 屏蔽没有申明通用字体
'font-family-no-missing-generic-family-keyword': null,
// ID选择器 最多使用一个
'selector-max-id': 1,
// 不允许使用的选择器的类型
'selector-no-qualifying-type': null,
// 屏蔽类选择器的检查,以确保使用字符 __
'selector-class-pattern': null,
// 允许的最大复合选择器为 5
'selector-max-compound-selectors': 5,
// 属性排序规则
'order/properties-order': [
[
'content',
'position',
'top',
'right',
'bottom',
'left',
'z-index',
'display',
'vertical-align',
'flex',
'flex-grow',
'flex-shrink',
'flex-basis',
'flex-direction',
'flex-flow',
'flex-wrap',
'grid',
'grid-area',
'grid-template',
'grid-template-areas',
'grid-template-rows',
'grid-template-columns',
'grid-row',
'grid-row-start',
'grid-row-end',
'grid-column',
'grid-column-start',
'grid-column-end',
'grid-auto-rows',
'grid-auto-columns',
'grid-auto-flow',
'grid-gap',
'grid-row-gap',
'grid-column-gap',
'gap',
'row-gap',
'column-gap',
'align-content',
'align-items',
'align-self',
'justify-content',
'justify-items',
'justify-self',
'order',
'float',
'clear',
'object-fit',
'overflow',
'overflow-x',
'overflow-y',
'overflow-scrolling',
'clip',
//
'box-sizing',
'width',
'min-width',
'max-width',
'height',
'min-height',
'max-height',
'margin',
'margin-top',
'margin-right',
'margin-bottom',
'margin-left',
'padding',
'padding-top',
'padding-right',
'padding-bottom',
'padding-left',
'border',
'border-spacing',
'border-collapse',
'border-width',
'border-style',
'border-color',
'border-top',
'border-top-width',
'border-top-style',
'border-top-color',
'border-right',
'border-right-width',
'border-right-style',
'border-right-color',
'border-bottom',
'border-bottom-width',
'border-bottom-style',
'border-bottom-color',
'border-left',
'border-left-width',
'border-left-style',
'border-left-color',
'border-radius',
'border-top-left-radius',
'border-top-right-radius',
'border-bottom-right-radius',
'border-bottom-left-radius',
'border-image',
'border-image-source',
'border-image-slice',
'border-image-width',
'border-image-outset',
'border-image-repeat',
'border-top-image',
'border-right-image',
'border-bottom-image',
'border-left-image',
'border-corner-image',
'border-top-left-image',
'border-top-right-image',
'border-bottom-right-image',
'border-bottom-left-image',
//
'background',
'background-color',
'background-image',
'background-attachment',
'background-position',
'background-position-x',
'background-position-y',
'background-clip',
'background-origin',
'background-size',
'background-repeat',
'color',
'box-decoration-break',
'box-shadow',
'outline',
'outline-width',
'outline-style',
'outline-color',
'outline-offset',
'table-layout',
'caption-side',
'empty-cells',
'list-style',
'list-style-position',
'list-style-type',
'list-style-image',
//
'font',
'font-weight',
'font-style',
'font-variant',
'font-size-adjust',
'font-stretch',
'font-size',
'font-family',
'src',
'line-height',
'letter-spacing',
'quotes',
'counter-increment',
'counter-reset',
'-ms-writing-mode',
'text-align',
'text-align-last',
'text-decoration',
'text-emphasis',
'text-emphasis-position',
'text-emphasis-style',
'text-emphasis-color',
'text-indent',
'text-justify',
'text-outline',
'text-transform',
'text-wrap',
'text-overflow',
'text-overflow-ellipsis',
'text-overflow-mode',
'text-shadow',
'white-space',
'word-spacing',
'word-wrap',
'word-break',
'overflow-wrap',
'tab-size',
'hyphens',
'interpolation-mode',
//
'opacity',
'visibility',
'filter',
'resize',
'cursor',
'pointer-events',
'user-select',
//
'unicode-bidi',
'direction',
'columns',
'column-span',
'column-width',
'column-count',
'column-fill',
'column-gap',
'column-rule',
'column-rule-width',
'column-rule-style',
'column-rule-color',
'break-before',
'break-inside',
'break-after',
'page-break-before',
'page-break-inside',
'page-break-after',
'orphans',
'widows',
'zoom',
'max-zoom',
'min-zoom',
'user-zoom',
'orientation',
'fill',
'stroke',
//
'transition',
'transition-delay',
'transition-timing-function',
'transition-duration',
'transition-property',
'transform',
'transform-origin',
'animation',
'animation-name',
'animation-duration',
'animation-play-state',
'animation-timing-function',
'animation-delay',
'animation-iteration-count',
'animation-direction',
'animation-fill-mode',
],
{
unspecified: 'bottom',
severity: 'warning',
},
],
// 屏蔽属性按字母顺序检查
'order/properties-alphabetical-order': null,
},
};
修改 package.json:
{
"name": "@ease-life/stylelint-config",
"version": "1.1.0",
"description": "Custom stylelint",
"main": "index.js",
"keywords": [
"stylelint"
],
"author": "zqc",
"license": "MIT",
"type": "module",
"publishConfig": {
"access": "public"
},
"peerDependencies": {
"postcss-html": "1.7.0",
"stylelint": "16.6.1",
"stylelint-config-sass-guidelines": "11.1.0",
"stylelint-config-standard": "36.0.0",
"stylelint-config-standard-scss": "13.1.0",
"stylelint-order": "6.0.4",
"stylelint-scss": "6.3.1"
}
}
- TypeScript
添加 base.json:
{
"compilerOptions": {
"allowJs": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"isolatedModules": true,
"lib": [
"ESNext",
"DOM"
],
"module": "ESNext",
"moduleResolution": "Bundler",
"noImplicitAny": true,
"skipLibCheck": true,
"strict": true,
"target": "ESNext"
},
"exclude": [
"**/node_modules",
"**/dist"
]
}
修改 package.json:
{
"name": "@ease-life/typescript-config",
"version": "1.1.1",
"description": "Custom tsconfig",
"keywords": [
"tsconfig"
],
"author": "zqc",
"license": "MIT",
"publishConfig": {
"access": "public"
}
}
3.3.6 添加 changeset(可选)
主要用于流程化发布和在项目中生成 CHANGELOG.md。
pnpm add @changesets/cli -Dw
3.3.7 添加其他
- 在项目跟目录下添加 .gitignore:
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
- 在项目根目录下创建 .editorconfig:
# 告诉编辑器这是最顶层的(不要再向上找了)
root = true
# All Files
[*]
charset = utf-8 # 设置编码为 utf-8
indent_style = space # 2 个空格的缩进
indent_size = 2
end_of_line = lf # Unix 风格的换行
insert_final_newline = true # 始终在文件末尾插入一个新行
trim_trailing_whitespace = true # 删除行尾空格
max_line_length = 200 # 单行最大宽度
# Markdown 文件
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false
- commitlint&husky&lintstaged
pnpm add -Dw @commitlint/cli @commitlint/config-conventional husky lint-staged
echo "export default { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js
pnpm exec husky init
打开 .husky/pre-commit 文件,删除其中的 “pnpm test” ,替换成:
pnpm exec lint-staged
继续执行:
echo "pnpm exec commitlint --edit \$1" > .husky/commit-msg
在根目录下的 package.json 中添加:
"lint-staged": {
"*.{js,vue,ts}": [
"pnpm exec eslint --fix"
],
"*.{css,scss}": [
"pnpm exec stylelint --fix"
]
}
3.3.8 安装config中的内容并配置
pnpm add -Dw @ease-life/eslint-config @ease-life/stylelint-config @ease-life/typescript-config
pnpm add -Dw eslint lint-staged postcss-html stylelint stylelint-config-sass-guidelines stylelint-config-standard stylelint-config-standard-scss stylelint-order stylelint-scss
在项目根目录下创建 commitlint.config.js:
export default {
extends: ['@commitlint/config-conventional'] };
在项目根目录下创建 eslint.config.js:
import baseConfig from '@ease-life/eslint-config';
export default [
...baseConfig,
{
// 屏蔽 web components 组件库的 eslint 规则
files: ['packages/web-components/**/*.ts', 'apps/storybook/src/my-element.ts'],
rules: {
'import/extensions': [
'error',
{
js: 'always' },
],
'import/prefer-default-export': 'off',
'no-restricted-exports': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unsafe-return': 'off',
'@typescript-eslint/indent': 'off',
},
},
];
在项目根目录下创建 stylelint.config.js:
import baseConfig from '@ease-life/stylelint-config';
export default {
...baseConfig,
};
在项目根目录下创建 tsconfig.json:
{
"extends": "@ease-life/typescript-config/base.json",
"include": [
"packages/**/*.ts",
"apps/**/*.ts"
],
}
最终项目文件目录结构如下:
ease-life
|-- github
|-- .DS_Store
|-- .editorconfig
|-- .gitignore
|-- .npmrc
|-- README.md
|-- commitlint.config.js
|-- eslint.config.js
|-- package.json
|-- pnpm-lock.yaml
|-- pnpm-workspace.yaml
|-- stylelint.config.js
|-- tsconfig.json
|-- vite.config.js
|-- .changeset
| |-- README.md
| |-- config.json
|-- .husky
| |-- commit-msg
| |-- pre-commit
| |-- _
| |-- .gitignore
| |-- applypatch-msg
| |-- commit-msg
| |-- h
| |-- husky.sh
| |-- post-applypatch
| |-- post-checkout
| |-- post-commit
| |-- post-merge
| |-- post-rewrite
| |-- pre-applypatch
| |-- pre-auto-gc
| |-- pre-commit
| |-- pre-push
| |-- pre-rebase
| |-- prepare-commit-msg
|-- apps
| |-- .DS_Store
| |-- storybook
| |-- .DS_Store
| |-- .gitignore
| |-- index.html
| |-- package.json
| |-- tsconfig.json
| |-- .storybook
| | |-- main.ts
| | |-- preview.ts
| |-- public
| | |-- vite.svg
| |-- src
| |-- index.css
| |-- my-element.ts
| |-- vite-env.d.ts
| |-- assets
| | |-- lit.svg
| |-- stories
| |-- Configure.mdx
| |-- Text.stories.ts
| |-- assets
| |-- accessibility.png
| |-- accessibility.svg
| |-- addon-library.png
| |-- assets.png
| |-- avif-test-image.avif
| |-- context.png
| |-- discord.svg
| |-- docs.png
| |-- figma-plugin.png
| |-- github.svg
| |-- share.png
| |-- styling.png
| |-- testing.png
| |-- theming.png
| |-- tutorials.svg
| |-- youtube.svg
|-- packages
|-- config
| |-- eslint
| | |-- index.js
| | |-- package.json
| |-- stylelint
| | |-- index.js
| | |-- package.json
| |-- typescript
| |-- base.json
| |-- package.json
|-- web-components
|-- text
|-- index.ts
|-- package.json
|-- dist
| |-- index.js
| |-- index.umd.cjs
|-- src
|-- main.css
|-- text.ts
4 组件测试
- 在项目根目录下运行以下内容,来对 text 进行构建:
pnpm -F @ease-life/text build
-F 是 --filter 的简写
会在 packages/web-components/text 下生成 dist 文件夹,里边有 index.js(ESM) 以及 index.umd.cjs(CommonJS)。
- 在 apps/storybook/src/stories 下添加一个 Text.stories.ts:
import type {
Meta, StoryObj } from '@storybook/web-components';
import '@ease-life/text';
const meta: Meta = {
component: 'el-text'
};
export default meta;
type Story = StoryObj;
export const Default: Story = {
args: {
name: 'world',
},
};
- 修改 apps/storybook 下的 package.json,将其中的 name 改为:
"name": "@ease-life/storybook",
- 在项目根目录下运行以下内容来安装刚才定义的 web components:
pnpm -F @ease-life/storybook add @ease-life/text
- 在项目根目录下运行以下内容,来启动 storybook:
pnpm -F @ease-life/storybook storybook
在浏览器中显示以下内容,则证明组件没有问题了。
5 组件发布
5.1 在 NPM 官网注册
如果没有注册过,则打开 NPM,点击右上角的 Sign Up,按提示填入信息。
5.2 登录账户
注册完后直接登录。
5.3 创建组织
打开创建组织的页面,在其中添加组织名称,组织名称就是 scope 的名称,也就是这里 @ 后面的内容。
@ease-life/tex,我这里创建了 ease-life 的组织。
5.3 组件发布
这里以发布至 NPM 为例。
5.3.1 用户登录
如之前没有发布过,在项目根目录下运行:
pnpm login
看到提示后,再次回车,在浏览器弹出的页面中进行登录,成功后显示以下内容:
5.3.2 组件发布
- 直接使用 PNPM,在项目根目录下运行:
pnpm publish -r
会自动发布仓库中版本发生改变的组件。
出现以上类似内容,就证明发布成功了。
- 结合 Changeset 做发布
pnpm changeset
通过键盘上下键切换到要发布的库,按空格后回车
选择版本,版本数字增加的是第一个、第二个还是第三个,通过回车跳过,按空格选中。
最后填写版本修改说明。再依次执行:
pnpm changeset version
确定版本
pnpm changeset publish
发布版本