npm、yarn、pnpm实现monorepo并使用changset管理版本

在前端开发领域中有很多库和框架他都是使用的monorepo架构实现的,它的好处有很多,可以很方便的管理自己的目录结构,还可以实现分包的功能等。比如在vue3中就使用到了这个架构,它会将runtime阶段和complier阶段和响应式阶段进行更细致的划分。当我们需要什么功能就可以引入单独的包来实现。

像这种多包的概念,我们每次更新都要去手动更改package.json中的版本,这样会很麻烦。可以使用changeset来管理我们每个包的版本。接下来我们就来实现一下。

bash 复制代码
mkdir monorepo-npm-yarn-pnpm
cd monorepo-npm-yarn-pnpm

我有这样一个场景,我有一个logger方法就是用来记录函数调用日志或者接口日志等等的一些日志信息的。我们用monorepo架构的方式来实现一下,这种场景确实不需要monorepo架构,但是为了掩饰使用我们就用这一个工具吧。

npm monorepo

先来看npm如何实现monorepo。

bash 复制代码
mkdir monorepo-npm
cd monorepo-npm

npm init -y

npm install typescript @types/node --save-dev // 安装typescript和node类型提示

npx tsc --init // 初始化tsconfig

修改tsconfig.json修改成如下内容

json 复制代码
{
  "compilerOptions": {
    "outDir": "dist",
    "types": [ "node" ],
    "target": "es2016", 
    "module": "NodeNext", 
    "moduleResolution": "NodeNext",
    "declaration": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true,
  }
}

然后创建两个包

bash 复制代码
npm init -w packages/core -y
npm init -w packages/logger -y

可以看到两个包已经创建完成,package.json也自动加了workspaces配置项。

我们需要在core中添加logger的依赖

bash 复制代码
npm install logger --workspace core          

可以看到core的依赖中引入了logger的包并且在node_modules中创建了looger的软链接。

改下包的名称,然后install一下。

接下来我们初始化子包的tsconfig文件。这里我们需要使用npm exec --workspace 子包名称 -- 命令这样的命令来找到子包并运行命令。

bash 复制代码
npm exec --workspace @spring-npm/core -- npx tsc --init
npm exec --workspace @spring-npm/logger -- npx tsc --init

然后修改tsconfig.json文件和上面的一样就行。

接下来我们来写logger日志的方法。

这里需要使用chalk第三方工具库来为我们的日志信息添加颜色。当我们需要为子包安装包的时候需要使用命令npm install --workspace 包名 库名

bash 复制代码
npm install --workspace @spring-npm/logger chalk

首先在package.json中添加type: 'module'的配置,让nodejs支持esmodule的模块规范。

这个loggerFun方法就根据日志类型打印不同的颜色的日志信息,非常简单。信息包含了函数调用的时间、函数名称以及函数的返回值。

ts 复制代码
import chalk from "chalk";

export enum LogLevel {
  DEBUG,
  INFO,
  WARN,
  ERROR,
}

export function loggerFun<T>(type: LogLevel, funName: string, message: string, reutrnValue: T = {} as T): void {
  const time = new Date().toLocaleTimeString();

  let resultValue: string = "";
  if (Object.prototype.toString.call(reutrnValue) == "[object Object]") {
    resultValue = JSON.stringify(reutrnValue);
  } else if (Object.prototype.toString.call(reutrnValue) == "[object Array]") {
    resultValue = "[" + (reutrnValue as Array<any>).join(", ") + "]";
  } else {
    resultValue = reutrnValue + "";
  }

  if (type === LogLevel.DEBUG) {
    debugLogger(funName, message, resultValue, time);
  } else if (type === LogLevel.INFO) {
    infoLogger(funName, message, resultValue, time);
  } else if (type === LogLevel.WARN) {
    warnLogger(funName, message, resultValue, time);
  } else if (type === LogLevel.ERROR) {
    errorLogger(funName, message, resultValue, time);
  } else {
    console.log(chalk.whiteBright(`[] ${time} - ${funName} - returned: ${reutrnValue}`));
  }
}

function debugLogger(funName: string, message: string, reutrnValue: string, time: string) {
  console.log(chalk.blueBright(`[DEBUG] ${time} - ${funName} - ${message} - returned: ${reutrnValue}`));
}

function infoLogger(funName: string, message: string, reutrnValue: string, time: string) {
  console.log(chalk.greenBright(`[INFO] ${time} - ${funName} - ${message} - returned: ${reutrnValue}`));
}

function warnLogger(funName: string, message: string, reutrnValue: string, time: string) {
  console.log(chalk.yellowBright(`[WARN] ${time} - ${funName} - ${message} - returned: ${reutrnValue}`));
}

function errorLogger(funName: string, message: string, reutrnValue: string, time: string) {
  console.log(chalk.redBright(`[ERROR] ${time} - ${funName} - ${message} - returned: ${reutrnValue}`));
}

接下来我们对这个方法进行tsc编译成js文件,然后修改成package.json将入口指向变异后的文件。

bash 复制代码
npm exec --workspace @spring-npm/logger -- npx tsc

修改package.json文

这里logger包就完成了。

接下来写core的代码。

ts 复制代码
import { loggerFun, LogLevel } from "@spring-npm/logger";

function add(a: number | string, b: number | string): number {
  loggerFun(LogLevel.DEBUG, add.name, "进入函数");
  if (typeof a !== "number" || typeof b !== "number") {
    loggerFun(LogLevel.ERROR, add.name, "参数错误");
    throw new Error("参数错误");
  }
  loggerFun(LogLevel.INFO, add.name, "完成", a + b);
  return a + b;
}

add(1, 2);
add(1, '2');

changeset 版本管理

到这里monorepo的架构就完成了,功能也实现了。接下来我们结合changeset来进行版本管理。

登陆npm,然后添加一个组织

因为我们是@spring-npm开头的包,所以我们就叫spring-npm。

接下来将core和logger包在package.json添加属性,都改成公有的

json 复制代码
  "publishConfig": {
    "access": "public"
  }

执行npm adduser登陆npm。

bash 复制代码
npm adduser

如果你npm弹出来的是中文页面,那么就需要将淘宝镜像改成npm的镜像。我是使用nrm来改的。

bash 复制代码
nrm use npm

这样就登陆成功了。

然后安装changeset

bash 复制代码
npm install --save-dev @changesets/cli

初始化changeset,会生产一个.changeset目录,里面就是记录你的版本信息的。

bash 复制代码
npx changeset init

因为changeset是基于git代码提交变动的,所以我们要初始化git仓库。

bash 复制代码
git init

git add . 

git commit -m 'first commit'

然后执行npx changeset add,创建一次changeset记录

bash 复制代码
npx changeset add

这里我们选择更改小版本minor。

然后npx changset version 来修改版本

bash 复制代码
npx changeset version

可以看到版本全部修改,修改的是小版本。

然后提交git,并发布到npm上

bash 复制代码
git add .
git commit -m 'version 1.1.0'

npx changeset publish

这里就发布完成。

然后将core和logger的index.ts都加个回车,我们修改大版本试一下。

然后上面的步骤都是一样的。

这里升级大版本也完成了。这里就是npm + monorepo + changeset就完成了。

yarn

yarn 和 pnpm 其实都是差不多的,只有命令不同。这里就不带着大家一一去写了。只有执行子包的命令和安装子包的第三方库有些命令上的不同。其它都是一样的,使用changeset也是一样的。

yarn 初始化两个目录

bash 复制代码
npm init -w packages/logger -y 
npm init -w packages/core -y

yarn core引用logger包

bash 复制代码
yarn workspace core add logger@1.0.0

yarn 为子包安装依赖

bash 复制代码
yarn workspace cli add chalk commander

yarn 执行子包命令

bash 复制代码
cd packages/core
npx tsc --init

pnpm

pnpm添加pnpm-workspace.yaml文件,指定子包目录。

ymal 复制代码
packages: - 'packages/*'

创建两个包

bash 复制代码
mkdir packages packages/core packages/logger 
cd packages/core 
npm init -y 
cd ../logger 
npm init -y

为core包添加logger的依赖

bash 复制代码
pnpm --filter logger add core --workspace

执行子包的命令

bash 复制代码
pnpm --filter logger exec npx tsc --init

子包添加依赖

bash 复制代码
pnpm --filter logger add chalk

这里pnpm为主包添加依赖需要使用-w

bash 复制代码
pnpm add --save-dev -w @changesets/cli

总结

pnpm monorepo会更好用一点,npm和yarn都是存在幽灵依赖的问题,同时npm的monorepo受版本的影响。pnpm安装包更快没有幽灵依赖的问题。

无论是npm、yarn还是pnpm它们的monorepo架构都是相同的,只有执行子包的命令不同。只需要知道执行子包的命令就行了。写法上都是通用了。大家有时间可以将yarn和pnpm都实现一遍。如果遇到什么问题可以评论区讨论一下。

相关推荐
卸任1 天前
Vibe coding工具使用心得
前端·ai编程
brien91 天前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance1 天前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M1 天前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写1 天前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李1 天前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易1 天前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
清晨Coding1 天前
删了 Cookie 还能自动登录?Google系网站登录态自动恢复的原理分析
javascript
风骏时光牛马1 天前
前沿视界
前端
kyriewen1 天前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员