引言
Monorepo是一种管理代码库的方式,它将多个相关代码仓库合并成一个大型的代码仓库,并使用工具集来管理和协调它们之间的依赖关系。Monorepo可以提高代码共享和复用的效率,减少代码冗余和过时,简化代码构建和部署流程,促进跨团队和跨项目的协作和协调。
monorepo适用场景
-
长期维护需要考虑:Monorepo往往包含大量代码和依赖关系。因此,在管理和维护Monorepo时必须谨慎分析和仔细考虑,包括代码提交流程、版本管理、构建和测试等方面的细节,以确保代码的稳定性和可靠性。
-
依赖管理和版本控制:Monorepo中的模块使用相同的代码仓库,并相互依赖。因此,必须确保良好的依赖管理和版本控制,以避免潜在的问题和错误。同时,还应该要考虑到各种依赖关系之间的冲突和版本问题,确保其互相之间能够兼容并且正常运行。
-
构建和测试:构建和测试是保证代码质量的重要步骤,对于Monorepo也是如此。需要以高质量的方式编写测试用例,并且确保构建工具链能够正常工作。使用适当的持续集成(CI)工具来管理和自动化构建和测试流程,以一致而可靠的方式构建和运行代码。
-
团队合作和协调:使用Monorepo需要各个团队之间的密切协作和协调,避免不同的修改之间产生冲突、互相干扰或损害项目的整体稳定性。团队需要严格遵守规范化的提交流程以及代码审查机制,确保代码的及时更新和维护。
-
开放沟通和信息共享:使用Monorepo建议开放沟通和信息共享,增强透明度,确保各个团队之间的信息可以互相共享,这样可以促进协同工作,更有效的协作和讨论。
总的来说,monorepo适用场景
- 中大型复杂项目中多个模块既可以独立发布适用,又可以内部相互引用作为整体发布,比如:babel、 vue3等;
- 开发许多轻量的项目(页面不会很多),各个项目又互相独立,需要单独部署,但是各个项目之间又有许多相同的功能,比如合并、登录等。
multirepo 与 monorepo 优缺点对比
monorepo开发管理
scripts脚本命令管理
通过bin命令式交互读取执行
"scripts": {
"task": "node ./bin/index.js task",
"commit": "git add . && cz-customizable",
"postinstall": "husky install",
"preinstall": "node ./bin/preinstall.js",
"format": "prettier --write \"**/*.{js,ts,vue}\""
}
// pnpm task
代码目录:/bin/index.js
#!/usr/bin/env node// 引用commander 处理命令行参数和选项 --helpconst program = require('commander')const pkg = require('../package.json')const cli = require('../cli')// 设置版本号program.version(pkg.version, '-v --version').usage('<command> [options]')// 初始化命令program .command('task') .description('运行项目') // .option('-t, --template [template]', 'JSON数据 http地址或者是文件相对路径') .action((options) => { cli.exec('task', options) })program.parse(process.argv)
/cli/index.js
class CliScript {
async exec(name, options) {
await require(`./${name}`).setup(options)
}
}
module.exports = new CliScript()
/cli/task.js
const path = require('path')
const fs = require('fs')
const shell = require('shelljs')
// const fs = require('fs-extra')
const PACKAGES_DIR = path.join(__dirname, '..', 'packages')
class Task {
subject = '' //当前选中的子目录,
selectScript = '' //当前选中的命令
async setup(options) {
const dirList = fs.readdirSync(PACKAGES_DIR)
this.selectSubject(dirList)
}
async selectSubject(subjects) {
// console.log(20000, await require('inquirer'))
const inquirer = await require('inquirer')
const answers = await inquirer.prompt([
{
type: 'list',
name: 'subject',
message: '请选择子项目',
choices: subjects
}
])
this.subject = answers.subject
this.getPackageCommander(
path.join(__dirname, '..', `packages/${answers.subject}/package.json`)
)
}
async getPackageCommander(subjectPackage) {
const subjectPkg = await require(subjectPackage)
const pkgCommander = subjectPkg.scripts || {}
const inquirer = await require('inquirer')
const answers = await inquirer.prompt([
{
type: 'list',
name: 'script',
message: '请选择当前目录命令',
choices: Object.keys(pkgCommander)
}
])
this.selectScript = answers.script
this.runCommander(this.subject, this.selectScript)
}
async runCommander(subjectPath, selectScript) {
shell.exec(`pnpm -C ./packages/${subjectPath} ${selectScript}`)
}
}
module.exports = new Task()
执行效果如下:
统一配置:增加commit提交规范 以及 统一配置执行 - Eslint,Typescript 与 Babel规则
1、.cz-config.js、commitlint.config.js等配置对commit必要格式和内容进行规范。
{
"name": "monorepo-demo",
"version": "1.0.0",
"description": "",
"scripts": {
"task": "node ./bin/index.js task",
"commit": "git add . && cz-customizable",
"postinstall": "husky install",
"preinstall": "node ./bin/preinstall.js",
"format": "prettier --write \"**/*.{js,ts,vue}\""
},
"author": "",
"license": "ISC",
"devDependencies": {
"@commitlint/cli": "^17.6.1",
"@commitlint/config-conventional": "^17.6.1",
"@rollup/plugin-commonjs": "^21.0.1",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-node-resolve": "^13.0.6",
"@rollup/plugin-terser": "^0.4.1",
"@vue/compiler-sfc": "^3.2.21",
"chalk": "4.0.0",
"commitizen": "^4.3.0",
"commitlint-config-cz": "^0.13.3",
"commitlint-config-git-commit-emoji": "^1.0.0",
"cz-conventional-changelog": "^3.3.0",
"cz-customizable": "^7.0.0",
"fast-glob": "^3.2.7",
"husky": "^8.0.3",
"inquirer": "8.2.0",
"lint-staged": "^13.2.1",
"minimist": "^1.2.8",
"nanospinner": "^1.1.0",
"npm-run-all": "^4.1.5",
"rollup": "^2.79.1",
"rollup-plugin-typescript2": "^0.30.0",
"sass": "^1.43.2",
"sucrase": "^3.20.2",
"ts-morph": "^12.2.0",
"typescript": "^4.9.5"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"**/*.{js,ts,vue}": ["prettier --write", "eslint --fix", "git add"]
},
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
},
"volta": {
"node": "16.14.0"
}
}
增加commit格式校验.cz-config.js
module.exports = {
types: [
{
value: ':sparkles: feat',
name: '✨ feat: 新功能'
},
{
value: ':bug: fix',
name: '🐛 fix: 修复bug'
},
{
value: ':package: build',
name: '📦️ build: 打包'
},
{
value: ':zap: perf',
name: '⚡️ perf: 性能优化'
},
{
value: ':tada: release',
name: '🎉 release: 发布正式版'
},
{
value: ':lipstick: style',
name: '💄 style: 代码的样式美化'
},
{
value: ':recycle: refactor',
name: '♻️ refactor: 重构'
},
{
value: ':pencil2: docs',
name: '✏️ docs: 文档变更'
},
{
value: ':white_check_mark: test',
name: '✅ test: 测试'
},
{
value: ':rewind: revert',
name: '⏪️ revert: 回退'
},
{
value: ':rocket: chore',
name: '🚀 chore: 构建/工程依赖/工具'
},
{
value: ':construction_worker: ci',
name: '👷 ci: CI related changes'
}
],
messages: {
type: '请选择提交类型(必填)',
customScope: '请输入文件修改范围(必填)',
subject: '请简要描述提交(必填)',
body: '请输入详细描述(可选)',
breaking: '列出任何BREAKING CHANGES(可选)',
footer: '请输入要关闭的issue(可选)',
confirmCommit: '确定提交此说明吗?'
},
allowCustomScopes: true,
// 跳过问题
skipQuestions: ['body', 'footer'],
subjectLimit: 72
}
辅助提交拦截commitlint.config.js
module.exports = { extends: ['git-commit-emoji', 'cz']}
锁定环境配置:Volta
Volta 是一个 JavaScript 工具管理器,它可以让我们轻松地在项目中锁定 node,npm 和 yarn 的版本。你只需在安装完 Volta 后,在项目的根目录中执行
volta pin命令,那么无论您当前使用的 node 或 npm(yarn)版本是什么,volta 都会自动切换为您指定的版本。
执行如下命令
volta pin yarn@1.22.4
volta pin node@16.14.0
会在当前根目录的package.json中追加如下内容,这样后面从其他目录切换回当前目录后,会自动切换对应的绑定工具版本
pnpm安装依赖校验
preinstall.js内容如下:
#!/usr/bin/env nodeif (!/pnpm/.test(process.env.npm_execpath || '')) { console.warn(`请使用pnpm install 安装依赖`) process.exit(1)}
在package.json中增加如下代码:在使用非pnpm安装依赖时会提示错误,禁止安装,强制使用pnpm安装
"scripts": { "preinstall": "node ./bin/preinstall.js", },