使用 typescript 重写 Ramda 基于函数式编程思想的工具库(二)

1,238 阅读5分钟

Lerna 篇

回顾

在上一篇文章中我们可以使用 lerna init 来初始化我们的工程。上一篇文章忘了放 源代码

lerna 允许我们使用两种模块来管理我们的模块:Fixed 模式和 Independent 模式。

  • fixed/locked (默认)

    固定模式,也就是我们初始化时默认采用的模式。该模式为单版本号,在根目录 lerna.json 中设置,该模式你可以理解为 ‘全量发布’,即任何一个模块更新了,当你在执行 lerna publish 发布时,所有的模块都会统一更新版本号。Babel 目前就是采用该模式。

  • independent

    lerna init --independent
    

    独立模式,在这种模式下你可以单独更新某个包的版本, 你可以理解为 ‘增量发布’。在每次发布时,会针对每个包的改动的提示,配合 git 检查文件变动,只发布有变动的包。

    独立模式允许开发者更新指定 package 的版本,只需将 lerna.json 中的 version 键改成 independent 即可启用 independent 模式。

配置

在根目录的 lerna.json 中设置 lerna 相关的配置

{
  "npmClient": "yarn",
  "useWorkspaces": true,
  "version": "0.4.0",
  "packages": [
    "packages/@ramda/*"
  ],
  "changelog": {
    "repo": "essencemike/ramda-ts",
    "labels": {
      "PR: New Feature": ":rocket: New Features",
      "PR: Breaking Change": ":boom: Breaking Changes",
      "PR: Bug Fix": ":bug: Bug Fix",
      "PR: Documentation": ":memo: Documentation",
      "PR: Internal": ":house: Internal",
      "PR: Underlying Tools": ":hammer: Underlying Tools"
    },
    "cacheDir": ".changelog"
  }
}

其他配置都是字面意思,这里重点想说一下 useWorkspaces, 它可以很好的和 yarnworkspaces 很好的结合在一起,前者负责 版本管理与发布,后者负责 依赖管理

lerna 中启用 workspaces 不仅要在 lerna.json 中配置 useWorkspaces,还需要在根目录 package.json 中配置:

{
  ...
  "private": true,
  "workspaces": [
    "packages/*"
  ],
  ...
}

注意: private 是必须的,开启 workspace 工作区必须设置成 private, 保证工作区不会被发布出去。

创建模块

lerna create a
lerna create b

执行上面的命令会在 packages 中创建模块,并根据交互提示在每个模块中生成 package.json.

├── lerna.json
├── package.json
└── packages
    └── a
    		├── __tests__
    		│    └── a.test.js
    		├── lib
    		│    └── a.js
        ├── package.json
        └── README.md
    └── b
    		├── __tests__
    		│    └── b.test.js
    		├── lib
    		│    └── b.js
        ├── package.json
        └── README.md

依赖管理与 npm scripts

依赖的安装与移除

  1. 安装所有依赖

    lerna bootstrap
    

    使用 bootstrap 命令,会下载所有定义在 package.json 中的依赖包。相当于执行 npm install,并且会链接所有依赖包。

  2. 给所有 package 添加依赖

    lerna add lodash
    

    给所有的 package 包添加 lodash 依赖。

  3. 给指定的 package 添加依赖

    # 给a, b 包中加入 Lodash,会同时改变 a, b 模块中 packages.json 文件
    lerna add lodash packages/a packages/b 
    # 你也可以使用通配符, 下面这命令,会往所有 re 开头的模块包中加入依赖
    lerna add lodash packages/re-* 
    # 指定特定的范围,要使用 --scope 参数,如下:给 b 包安装 lodash 模块
    lerna add lodash --scope=b
    
  4. packages 中的包相互添加依赖

    # 指定特定的范围,要使用 --scope 参数,如下:给 b 包安装 a 模块
    lerna add a --scope=b
    
  5. 移除所有 package 的依赖

    lerna exec -- yarn remove lodash
    

    把所有 package 包中的 lodash 依赖删除。

  6. 移除指定 package 包中的依赖

    目前 lerna 中没有 remove 命名,需要在对应的 package 中的 package.json 中删除对应的依赖,然后执行 lerna bootstrap 命令即可。

执行 npm scripts

  1. 执行所有 package 中的 scripts 命令 使用 lerna run 命令可以执行 package 中的 scripts 命令,前提是需要先在 package 中写好 scripts 命令。比如每个 package 中都有如下命令:
    ...
    "scripts": {
        "test:coverage": "jest --coverage",
        "test:dev": "jest --watch --coverage",
        "test:unit": "jest",
        "clean": "rm -rf lib/*",
        "build": "npm run clean && rollup -c ../../../rollup.config.js --name F"
    },
    ...
    
    即可使用如下命令在每个 package 中执行测试、构建了:
    lerna run test:unit
    lerna run build
    
  2. 执行指定 package 中的 scripts 命令
    # 执行 a 包中的 build 命令
    lerna exec --scope a -- yarn run build
    

发布模块

使用 lerna publish 命令可以将我们的包发布到 npm 仓库中。

这里可能会失败, 如下图

lerna info execute Skipping releases
lerna info git Pushing tags...
lerna info publish Publishing packages to npm...
lerna info Verifying npm credentials
lerna http fetch GET 200 https://registry.npmjs.org/-/npm/v1/user 6266ms
lerna http fetch GET 200 https://registry.npmjs.org/-/org/emike/package?format=cli 1671ms
lerna info Checking two-factor auth mode
lerna http fetch GET 200 https://registry.npmjs.org/-/npm/v1/user 1197ms
lerna http fetch PUT 404 https://registry.npmjs.org/@ramda%2ff 1962ms
lerna ERR! E404 Not found
    { Error: Command failed with exit code 1 (EPERM): /Users/imike/code/github/ramda-ts/node_modules/lerna/cli.js publish 0.4.1 --dist-tag latest
        at makeError (/Users/imike/code/github/ramda-ts/node_modules/execa/lib/error.js:58:11)
        at handlePromise (/Users/imike/code/github/ramda-ts/node_modules/execa/index.js:112:26)
        at process._tickCallback (internal/process/next_tick.js:68:7)
      command:
       '/Users/imike/code/github/ramda-ts/node_modules/lerna/cli.js publish 0.4.1 --dist-tag latest',
      exitCode: 1,
      exitCodeName: 'EPERM',
      stdout: undefined,
      stderr: undefined,
      failed: true,
      timedOut: false,
      isCanceled: false,
      killed: false,
      signal: undefined }
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! @ release: `npm run build && node scripts/release.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the @ release script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/imike/.npm/_logs/2019-09-18T14_16_39_196Z-debug.log

可以看到我们的包名是 @ramda/f, 当我们以这种方式命令包名时,就可能会出现问题,那是什么原因呢?

其实当我们执行 lerna publish 的时候,本质上还是执行 npm publish。那么我们首先就需要现在 npm 仓库上注册用户, 并登录。

$ npm adduser
username:
password:
email:
... 等等信息

注意点:

  • npm publish 默认发布私有包,想要发布公共包必须使用

    npm publish --acsess=public
    

    也可以在 package.json 中配置

    ...
    "publishConfig": {
        "access": "public"
    },
    ...
    
  • 组织包(scope packages)需要先创建团队(团队删除了,包就没有了~)

其实上面我们所说的 @ramda/f 就是组织包的形式,这里的 scope 就是 @ramda

如何你想一直在这个 scope 中发布 npm 包, 可以运行全局的 npm 配置命令进行设置

npm config set scope @ramda

在这里有两种方式效果看起来是一样的。

  1. 使用非组织包方式,那么你的包名必须以 npm 账户名为前缀。

    $ npm adduser
    Username: @ramda
    Password:
    

    如何不是的话就会报我们上面提到的错误。

  2. 使用组织包方式发布,直接 publish 也是不行的,需要

    • npm 仓库中创建团队
    • 将自己的账号添加到该团队中

npm
这样就可以愉快的发布了。

更多命令

lerna 的更多命令这里不再详细罗列,详情请参考 lerna

总结

本文学习了如何使用 lerna 管理并发布我们的模块,下一篇文章我们将学习如何使用 rollup 结合 babeltypescript 来编译我们的代码,以及如何使用 jest 来进行单元测试。

参考文章