最近在研究多包的管理,参阅了很多大牛项目,比如create-react-app, vue-cli, storybook, material-ui。它们都是采用了lerna做的分包管理。
MateriaUI的一套流程是非常棒的,值得我们学习,他实现了自动修改版本号,发包,自动通过commit的记录生成changelog,因此想着能模仿它来创建一个咱们自己的多包组件库。
全局环境安装
node环境:V16
全局三方库:
npm i yarn verdaccio -g
项目结构搭建
项目结构如下:三个子组件包,一个storybook包
子包的包名分别是
分别给三个子包追加依赖:
比如base/theme包:
{
"name": "@joseph/base",
"version": "1.0.0",
"description": "base component",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/esm/index.d.ts",
"author": "joseph",
"license": "MIT",
"private": false,
"scripts": {
"build": "rimraf dist/* && rollup -c"
},
"files": [
"dist"
],
"dependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2",
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@mui/icons-material": "^5.6.0",
"@mui/lab": "^5.0.0-alpha.76",
"@mui/material": "^5.6.0",
"@mui/x-data-grid": "^5.8.0",
"@joseph/theme": "^1.0.0"
},
"devDependencies": {
"@babel/core": "^7.17.8",
"@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.16.7",
"@babel/preset-typescript": "^7.16.7",
"@rollup/plugin-commonjs": "^21.0.3",
"@rollup/plugin-image": "^2.1.1",
"@rollup/plugin-node-resolve": "^13.1.3",
"rollup": "^2.70.1",
"rollup-plugin-babel": "^4.4.0",
"rollup-plugin-dts": "^4.2.0",
"rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.31.2",
"style-loader": "^3.3.1",
"styled-components": "^5.3.5",
"ts-loader": "^9.2.8",
"typescript": "^4.6.3",
"rimraf": "^3.0.2"
}
}
composite包
可以看到,它依赖了theme和base包
{
"name": "@joseph/composite",
"version": "1.0.0",
"description": "base component",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/esm/index.d.ts",
"author": "joseph",
"license": "MIT",
"private": false,
"scripts": {
"build": "rimraf dist/* && rollup -c"
},
"files": [
"dist"
],
"dependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2",
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@mui/icons-material": "^5.6.0",
"@mui/lab": "^5.0.0-alpha.76",
"@mui/material": "^5.6.0",
"@mui/x-data-grid": "^5.8.0",
"@joseph/theme": "^1.0.0",
"@joseph/base": "^1.0.0"
},
"devDependencies": {
"@babel/core": "^7.17.8",
"@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.16.7",
"@babel/preset-typescript": "^7.16.7",
"@rollup/plugin-commonjs": "^21.0.3",
"@rollup/plugin-image": "^2.1.1",
"@rollup/plugin-node-resolve": "^13.1.3",
"rollup": "^2.70.1",
"rollup-plugin-babel": "^4.4.0",
"rollup-plugin-dts": "^4.2.0",
"rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.31.2",
"style-loader": "^3.3.1",
"styled-components": "^5.3.5",
"ts-loader": "^9.2.8",
"typescript": "^4.6.3",
"rimraf": "^3.0.2"
}
}
storybook包,storybook相关参考官网,此处不全。
{
"name": "@joseph/storybook",
"version": "1.0.0",
"description": "base component",
"main": "index.js",
"author": "joseph",
"license": "MIT",
"private": false,
"scripts": {
"storybook": "start-storybook -p 6009",
"build-storybook": "build-storybook"
},
"dependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2",
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@mui/icons-material": "^5.6.0",
"@mui/lab": "^5.0.0-alpha.76",
"@mui/material": "^5.6.0",
"@mui/x-data-grid": "^5.8.0",
"@joseph/theme": "^1.0.0",
"@joseph/base": "^1.0.0",
"@joseph/composite": "^1.0.0"
},
"devDependencies": {
"@storybook/addon-a11y": "^6.4.22",
"@storybook/addon-actions": "^6.4.19",
"@storybook/addon-docs": "^6.4.20",
"@storybook/addon-essentials": "^6.4.19",
"@storybook/addon-interactions": "^6.4.19",
"@storybook/addon-links": "^6.4.19",
"@storybook/addons": "^6.4.22",
"@storybook/react": "^6.4.19",
"@storybook/testing-library": "0.0.9",
"@storybook/theming": "^6.4.22"
}
}
还要配置根目录的package.json, 我们使用yarn管理,添加workspace。
{
"name": "lerna-tpl",
"version": "1.0.0",
"description": "for testing",
"private": true,
"main": "index.js",
"scripts": {
"build": "lerna run build",
"start": "lerna run storybook --scope @joseph/storybook",
"publish:local": "lerna publish from-package --registry=\"http://localhost:4873/\""
},
"workspaces": ["packages/*", "storybook", "demo/*"],
"repository": {
"type": "git",
"url": "https://gitee.com/cary123/lerna-tpl"
},
"keywords": [
"lerna"
],
"author": "joseph",
"license": "ISC",
"devDependencies": {
"lerna": "^4.0.0",
"babel-loader": "^8.2.4",
"babel-preset-react-app": "^10.0.1",
"eslint": "^8.12.0",
"eslint-config-google": "^0.14.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.29.4",
"husky": "^7.0.4",
"less": "^4.1.2",
"less-loader": "^10.2.0",
"lint-staged": "^12.3.7",
"postcss": "^8.4.12",
"prettier": "^2.5.1"
}
}
然后配置lerna.json
{
"npmClient": "yarn",
"useWorkspaces": true,
"version": "independent"
}
rollup.config.js,给每个子文件夹拷贝一份,这里图省事,当然你可以在根目录只配置一个,通过在script里面传参去调用打包也可以。
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from 'rollup-plugin-typescript2';
import { terser } from 'rollup-plugin-terser';
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
import postcss from 'rollup-plugin-postcss';
import dts from 'rollup-plugin-dts';
import image from '@rollup/plugin-image';
const isProd = process.env.NODE_ENV === 'production';
const packageJson = require('./package.json');
export default [
{
input: 'src/components/index.ts',
output: [
{
file: packageJson.main,
format: 'cjs',
sourcemap: true
},
{
file: packageJson.module,
format: 'esm',
sourcemap: true
}
],
plugins: [
peerDepsExternal({ includeDependencies: !isProd }),
resolve(),
commonjs(),
typescript({
useTsconfigDeclarationDir: true
}),
postcss(),
// terser(),
image()
]
},
{
input: 'dist/esm/types/index.d.ts',
output: [{ file: 'dist/esm/index.d.ts', format: 'esm' }],
external: [/\.css$/],
plugins: [dts(), image()]
}
];
ts的配置,也是每个子文件一份,当然只在根目录放一份,子目录使用extend的方式也行,但是路径上可能会出错。
{
"compilerOptions": {
"strict": true,
"strictPropertyInitialization": false,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": false,
"outDir": "dist",
"module": "esnext",
"target": "es5",
"lib": [
"es2019",
"DOM"
],
"declaration": true,
"declarationDir": "dist/esm/types",
"emitDeclarationOnly": false,
"jsx": "react",
"allowJs": false,
"checkJs": false,
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
}
}
{
"presets": [
"@babel/preset-env",
"@babel/preset-typescript",
["@babel/preset-react", { "runtime": "automatic" }]
]
}
创建私有仓库
cmd去跑一下:
verdaccio
它会在http://localhost:4873开个服务,浏览器打开如下:
接着根据它说的:
npm adduser --registry http://localhost:4873/
然后输入用户名和密码,它就会记住你的身份
此服务不能停,一直开着。
安装包
跑如下命令,因为我们使用的是yarn的workspace的功能,所以不要用npm
yarn
此时它会把所有的包和依赖都给你建好,统一放在了根节点的node_modules里面,而子节点地下,只是放了一些bin里面的命令。
看,软链都给我们生成好了。
打包
跑下面命令
yarn build
它会使用lerna去扫描所有workspace,去寻找build命令,因为我们给每个子包都写了build的命令,所以会独立打包,打完包之后你会看到每个子包都有dist目录。
发版
yarn publish:local
因为我们没有配置scope,导致lerna把storybook也给我们发到仓库去了,现在刷新一下仓库看看:
四个包都在,先不管storybook那个包,我们随便下载一个其它包,解压看看是不是我们想要的效果。
跑storybook
yarn start
正常工作
而且你可以看到,storybook可以通过宝明直接找到我们workspace里面的package,不用写'../../package/...'这样的引用,大大方便了开发。
至此告一段落,后面会继续研究version和changelog的管理。。。更新待续
项目地址:
发表评论
所有评论(0)