monodic
v1.0.0
Published
Monorepo management tools
Downloads
14
Readme
monodic
多项目单仓库工程管理方案
monodic 是一个低侵入性的多项目单仓库(Monorepo)工程化管理工具。
通过这种文件夹之间的切换处理,实现对项目所依赖的框架、工程化设施解耦,不管是什么框架的项目,开发方式都跟原来的一样。
仅仅改变了启动命令,从项目文件夹里 npm run xxx
转变成 monodic start
.
使用
第一步:配置 package.json 的 scripts 命令
支持的 node.js 版本为 10.15.00 以上,如低于该版本,请先升级一下 node.js
在项目根目录下新建 package.json 或者执行 npm init -y
自动生成。
执行一下命令安装 monodic
npm install --save-dev monodic
添加 monodic
对应的命令
monodic start
:运行monodic
项目任务管理,选择项目,选择命令即可。- 这个命令实际上是帮开发者去运行:
npm run start
,npm run build
,npm run test
- 这个命令实际上是帮开发者去运行:
monodic release
:运行monodic
项目发布,选择要发布的项目即可- 这个命令实际上是帮开发者将指定的文件夹,发布到 git 的指定分支
monodic reset
:将所有 links 的软链接关系重置成初始状态- 这个命令是在文件夹关联关系凌乱时用以恢复,它会将真实文件夹放到 src 字段的位置。
monodic release-all
:将config.release
里的全部项目发布到它们指定的分支- 相比
monodic release
需要选择一个项目,该命令发布所有项目
- 相比
monodic command
:在链接关系重置状态下输入指令,方便进行 git add/commit 等操作
{
"scripts": {
"start": "monodic start --mode=copy",
"release": "monodic release --mode=copy",
"release-all": "monodic release-all --mode=copy",
"reset": "monodic reset",
"command": "monodic command"
}
}
然后
npm run start
运行某个项目,monodic 启动时,会注入IS_MONODIC=YES
环境变量,可以根据该变量,调整配置属性等npm run release
发布某个项目npm run reset
重置文件夹的关联关系npm run release-all
发布所有项目npm run command
在重置状态下运行指令
快速启动
{
"scripts": {
"start:h5": "monodic start --project=projects/h5 --script=start",
"release:h5": "monodic release --project=h5"
}
}
从 monodic v1.3.0
开始,支持命令行参数,可以跳过选择,直接启动目标项目和脚本任务。
这个功能只对 monodic start
和 monodic release
生效。
monodic start --mode={copy/exchange} --project={your project path} --script={your script name}
mode
选择启动模式,默认是exchange
模式,即交换真实文件夹和软链接的位置。 设置copy
模式时,将在项目目录下新建.monodic
,将源码文件拷贝进该目录,后续所有命令都将在.monodic
目录里执行project
参数选择项目,参数值跟 cli 里列出的路径保持一致script
参数选择package.json
里的npm scripts
的 key,比如start
、test
、build
等(不需要写全npm run start
)
monodic release --mode={copy/exchange} --project={your project key}
mode
选择启动模式,默认是exchange
模式,即交换真实文件夹和软链接的位置。 设置copy
模式时,将在项目目录下新建.monodic
,将源码文件拷贝进该目录,后续所有命令都将在.monodic
目录里执行project
参数选择项目,它跟monodic start
里不同,它不是路径,而是monodic.config.js
的release
配置里的 key 值,即跟 cli 里列出来的保持一致。
第二步:新建 monodic.config.js
config.links
配置共享文件夹,数组结构,每个 item 包含 { src, dest } 两个字段src
字段为字符串类型,表示源文件夹位置dest
为数组类型,表示需要跟 src 进行软链接关联起来的文件夹地址
config.release
配置项目的发布目录和分支,对象结构,对象的 key 为项目名,对象的 value 包含 { src, branch } 两个必选字段src
必选:项目的发布内容所在的目录地址dest
可选:项目在当前分支的发布目录branch
可选:项目发布的目标分支message
可选:提交达到发布分支时的 commit message,支持函数和异步函数,async ({ name: string, version: string }) => string
返回 message 字符串。include
可选字段,匹配要发布的文件,匹配规则见gh-pages 的 options.src 文档prerelease
可选:项目发布前需要执行的命令(cwd 会切换到 src 目录最近的 pakcage.json 所在的目录),可以在这里填写npm run build
等构建命令postrelease
可选:项目发布后需要执行的命令(cwd 会切换到 src 目录最近的 package.json 所在的目录),可以在这里填写git add -A && git commit -m "PKG:xxx"
提交 git 的命令ignoreSrcPackage
可选:release.src
发布目录也可能出现package.json
,设置ignoreSrcPackage
为true
可以跳过release.src
目录的package.json
,在release.src
父级目录里寻找package.json
,去执行prerelease
和postrelease
config.ignorePackages
配置需要忽略扫描的package.json
目录,数组类型
const { createConfig } = require("monodic");
module.exports = createConfig({
// 忽略扫描 package.json 的目录
ignorePackages: [
"publish",
"packages/pure-model/dist",
"projects/react-imvc/publish",
],
// 构建共享目录的分配方式
links: [
{
// 源代码目录
src: "./projects/isomorphic/src",
// 需要软链接过去的目录
dest: [
"./projects/react-imvc/src/isomorphic",
"./projects/react-app/src/isomorphic",
],
},
{
src: "./packages/pure-model/src",
dest: ["./projects/isomorphic/src/pure-model"],
},
],
// 发布配置
release: {
"react-imvc": {
src: "./projects/react-imvc",
dest: "./publish/react-imvc",
prerelease: "npm run build:imvc",
postrelease: `git add -A && git commit -m "PKG:react-imvc"`,
branch: "react-imvc-release",
},
"react-app": {
src: "./projects/react-app/build",
branch: "react-app-release",
message: "测试自定义发布的 git commit message",
},
monodic: {
src: "./packages/monodic",
branch: "monodic-release",
},
"pure-model": {
src: "./packages/pure-model/dist",
branch: "pure-model-release",
},
},
});
异步配置
自 v1.6.0
版本开始。monodic
支持异步配置,使用方式如下:
const { createConfig } = require("monodic");
module.exports = async () => {
// 使用异步函数获取动态配置
return createConfig({
})
}
Copy VS Exchange
自 v1.5.1
版本开始,monodic
支持两种启动模式:Copy
和 Exchange
。默认采取 Exchange
模式。
Exchange Mode
它的工作原理是:
- 配置文件夹之间的关联关系,保留唯一的真实文件夹,其余地址都设置为软链接,指向该真实文件夹。实现文件内容的唯一性和位置的多样性。
- 通过 cli 接管多个项目的运行脚本,间接启动不同的
projects/{name}
的项目:monodic start
- 在命令行里选择项目文件夹,然后选择所要执行的
npm scripts
命令 monodic
会在执行命令前,进行文件夹切换- step1: 将真实的文件夹移动到待启动的项目所在的目录,将原来的位置设置为软链接
- step2: 运行开发者选中的命令
- step3: 运行结束后,重置文件夹之间的软链接关系
- 在命令行里选择项目文件夹,然后选择所要执行的
- 通过 cli 将不同的项目的 build 产物,发布到不同的分支,实现根目录下只有一个项目的功能,可适配对仓库目录结构有要求的发布系统。
Copy Mode
Copy
模式下,monodic
会将项目里除 node_modules|.git
以外的文件,拷贝进 .monodic
目录,并在 .monodic
目录里启动应用。
monodic
会监听项目里的文件变动,实时同步到 .monodic
目录,对开发者保持透明。开发者无须编辑.monodic
目录里的代码。
Copy 模式启动时有 copy 文件的动作,如果项目文件里存在大文件,可能拖慢启动速度。如果这些大文件跟运行无关,可以在 monodic.config.js
中设置 ignoreFiles
将它们忽略。
注:建议在根目录的 .gitignore
里添加 .monodic
,让 git
忽略它们。
Copy Or Exchange
monodic
默认采用 Exchange
模式,但只是处于历史原因,Exchange
比 Copy
更早开发。推荐使用 Copy
模式。
Copy
模式解决了 Exchange
的以下问题:
Exchange
模式一次只能运行一个项目,Copy
模式可以同时运行多个项目Exchange
模式运行时,对 git 来说,有文件删除和移动的变化,Copy
模式对 git commit 更友好。不必运行monodic command
,直接使用git
命令提交代码。Exchange
模式包含一定失败风险,失败后,可能删除了部分代码,需要通过 git checkout 等方式恢复。Copy
模式几乎不会改动源文件,更加安全可靠。
从 Exchange
模式迁移到 Copy
模式步骤
- 在根目录的
.gitignore
文件中添加.monodic
- 在根目录的
package.json
添加命令行参数monodic start --mode=copy
monodic release --mode=copy
monodic release-all --mode=copy
React-Native 项目 Copy 模式配置方式
React-Native 项目使用 Metro 配置的,除以上配置以外,还需按照一下方式配置 metro.config.js
,以避免启动时报 node_modules 里的模块引用错误。
注意:如果使用 crn-cli
启动项目,需要再新增 metro.bu.config.js
配置文件,内容跟 metro.config.js
一致。
/**
* Metro configuration for React Native
* https://github.com/facebook/react-native
*/
const path = require("path");
const blacklist = require("metro-config/src/defaults/blacklist");
const escapeRegexString = require("escape-regex-string");
let monodicWatchFolders = [];
let monodicBlackList = [];
if (process.env.IS_MONODIC === "YES") {
// monodic copy 模式启动时,代码被复制到 .monodic 目录,真实的 node_module 文件夹在父级目录
monodicWatchFolders = [
path.resolve(__dirname, "../node_modules"),
];
// monodic copy 模式启动时,代码被复制到 .monodic 目录,忽略 .monodic 目录下的 node_modules 软链
monodicBlackList = [
new RegExp(
`^${escapeRegexString(path.resolve(__dirname, "node_modules"))}\\/.*$`
),
];
} else {
// 非 monodic copy 模式启动时,忽略 .monodic 目录,避免检索出多个 package.json
monodicBlackList = [
new RegExp(
`^${escapeRegexString(path.resolve(__dirname, ".monodic"))}\\/.*$`
),
];
}
module.exports = {
watchFolders: [...monodicWatchFolders],
resolver: {
blacklistRE: blacklist([...monodicBlackList]),
},
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: true,
inlineRequires: true,
},
}),
},
};
FAQ
问题排查
为什么运行时报错 Error: EPERM: operation not permitted ?
可能原因是 VSCode 编辑器跟命令行对于同一个文件的操作权限产生了冲突。可以先关闭 VSCode,再运行命令,然后打开 VSCode 即可。
报此错误时,文件夹可能已经被 monodic 操作,但未完成正确的衔接,可能产生冗余文件夹等。
可运行 npm run reset
启动重置命令,恢复文件夹。如继续报错提示,请按照提示进行操作。
注:在执行有风险的命令之前,可通过 git add -A
先将文件保存在 git 的工作区。而后在运行命令不达预期后,可通过 git checkout .
撤销修改。git 会将文件恢复到跟工作区的版本保持一致的状态。
为什么运行项目时报错 xxx is not install
,模块依赖没有安装 ?
这是由于每个项目都是独立的,需要各自安装 npm install --save {name}
依赖。
即,如果一个共享模块依赖的 redux
,所有使用该模块的项目,都要自行 npm install --save redux
。
monodic
只是一个文件夹切换和项目命令管理工具,并不知晓模块之间的依赖关系。
为什么 windows 里软链接创建失败?
在 windows 里通过脚本创建软链接需要管理员权限,可以设置 windows 的 本地策略/Local Policies
开启。
可以点击 Ember 的文档:Enabling symlinks 按步骤设置
为什么运行 monodic release
时报错 “branch already exists”?
这是 monodic
依赖的发布工具 gh-pages
的已知问题,可以通过删除 node_modules/gh-pages/.cache
缓存文件来解决。
见 gh-pages
文档查看更多
为什么报错“配置中的路径所对应的本地文件夹都不存在,请添加一个文件夹”?
这是因为 monodic.config.js
里的 links 配置里,src
和 dest
都不存在,无法进行软链接关联。
请先添加一个源文件夹,之后再重新启动。
为什么报错“预期只有一个真实的文件夹,其余为软链接。目前找到的文件夹数量为 n”?
这是因为 monodic
的工作原理是,保留唯一的真实文件夹,其余为软链接。
有可能因为一些不可预料的原因,软链接变成了真实文件夹。
解决方案是,将多余的文件夹删除,只保留一份真实文件夹。再重新启动。
为什么报错“预期只有一个真实的文件夹,其余为软链接。目前找到 n 个真实文件。请删除多余的”?
原因同上。
git 对软链接的处理方式是,将它变成一个文件,内部是它软链接到的真实地址。
git 有可能在 git pull 时没有正确的将上述包含真实地址的文件,转换成软链接。它成了真实的文件。
将这些冗余文件删除,只保留一份真实文件夹。再启动即可。
另外,windows 里安装 git 时,如果没有勾选 Enable symbolic links
,git 不会在 git pull 后转换成软链接。
可以通过卸载 git,重新下载/安装时勾选该选项。
为什么运行 monodic release
发布到 git 分支时,总是提示输入 username 和密码?
git 需要设置保存用户信息后,才可以不输入用户名和密码。
或参考下面的命令:
$ git config credential.helper store
$ git push http://example.com/repo.git
Username: <type your username>
Password: <type your password>
[several days later]
$ git push http://example.com/repo.git
[your credentials are used automatically]