lerna + yarn workspace多包管理

项目背景

基于框架palife-lib-fluid的业务组件库

对于组件库有两种使用场景

  1. 通过源码输出(仅需tsc编译)给内部基于palife-lib-fluid框架的项目引用(部署在平安maven私库)
  2. 通过umd输出给外部第三方开发使用或者未使用palife-lib-fluid框架的项目引用

要求多包处理,即每个组件都可作为单独的依赖包进行发布,这里包括umd脚本及npm包。

├── lerna.json
├── package.json
└── packages
    ├── countdown
    │   ├── lib
    │	├── src
    │   └── package.json
    ├── suspendBox
    │   ├── lib
    │	├── src
    │   └── package.json
    ├── utils
    │   ├── lib
    │	├── src
    │   └── package.json
    └── demo
        ├── src
        ├── tsconfig.json
        └── package.json

技术选型

lerna + yarn workspace 前者负责版本管理与发布,后者负责依赖管理

Learn 是一个通过git和npm来优化管理多包储存的工作流的工具。

lerna有两种管理模式:

  1. 固定模式

    执行lerna publish时,如果代码有更新,会自动更新此版本号的值。即:所有的包公用一个版本号。版本号使用lerna.json文件中的version属性。

  2. 独立模式

    允许维护人员独立的增加修改每个包的版本,每次发布,所有更改的包都会提示输入指定版本号。

    lerna init --independent 或 修改lerna.json中的version值为independent

在这里我们使用独立模式,单独发包。

有关lernanpm/yarn workspace组合方案对比推荐个比较好的文章

依赖包安装

  • 为什么不用lerna bootstrap而用yarn?

    lerna bootstrap --hoist在仓库根目录安装外部依赖,这样所有包都可以使用他们。二进制文件都将链接到依赖包的node_modules/.bin/目录中,这样 npm 脚本就可以使用它们了。但是并不会主动软连接packages下的包到顶层node_modules,这样在开发环境调试就需要主动在dependencies中声明引用关系。

    lerna bootstrap不会提升依赖

    yarnworkspace会提升依赖,并创建packages软连接到顶层node_modules中。确保开发环境依赖包唯一

  • 为什么demo也要作为一个workspace使用而不隔离?

    workspaces会执行yarn会把各packages中的依赖包都提升到顶层,这样能保证各包运行时的依赖包指向同一个依赖。如果单单把demo项目隔离出去,它会有自己独立的node_modules。 那么即使在dependencies中声明了"@palife-lib-bc/suspendBox": "file:../suspendBox",让本地依赖包软连接到node_modules中,但本体依然是packages下的本地依赖包,它的引用依赖关系依然是顶层的node_modules。由于本地组件包和demo项目的依赖包不是同一个,就会出现报错的问题,在这里就是react报错的问题。

      `react.development.js:1607 Uncaught Invariant Violation: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: 
      1. You might have mismatching versions of React and the renderer (such as React DOM)
      2. You might be breaking the Rules of Hooks
      3. You might have more than one copy of React in the same app`
    

组件包调试本地src

​ 组件包都会有入口文件,通常是发布目录(在这里时lib)的index文件。lib文件更新通常只有发布的时候才会用tsc编译发生,但在本地开发环境需要的是对源文件的直接引用。所以这里借助alias对于组件包的引用重定向:

const pkgs = JSON.parse(execSync('lerna ls --json', { stdio: 'pipe' }).toString().trim())
const alias = pkgs.reduce((res, pkg) => {
    pkg.shortName = pkg.name.replace('@palife-lib-bc/', '')
    res[pkg.name] = `${pkg.name}/src`
    return res
}, {})
module.exports = { alias, pkgs }

这样可以在开发环境实时浏览组件开发情况,方便组件开发与调试。

组件包依赖处理

devDependencies、dependencies与peerDependencies的配置说明

devDependencies 开发环境依赖包。我们在开发一个 npm 包的时候,会涉及到编译和打包等等,这些操作依赖一些开发环境的包。发布出去的包在引用时该配置项会被忽略,devDependencies在npm包中不会被下载

dependencies 依赖包。无论本地开发环境还是发布出去的npm包,该依赖配置都会下载下来。主要是配置该npm包的依赖项

peerDependencies 适配依赖包。由于我们在开发的时候不知道会被哪个第三方包引用,因此这种情况一般是我们根据某个会引用我们的第三方包进行的特定开发,常见的就是插件开发。在这里我们的组件包是针对palife-lib-fluid框架开发的业务组件,所以对于这些组件来说他的适配依赖包就是palife-lib-fluidpeerDepndencies是不会被下载的

该配置对于npm版本有不同表现,v6之前如果依赖包版本与该配置不符或者node_modules不存在该配置的依赖包,npm会给出警告,提示用户自己安装。v6之后如果node_modules不存在该配置的依赖包,npm会主动安装,如果两者版本不符,则报错安装失败。

umd 文件输出

output: {
        path: path.resolve(__dir, '..', 'dist'),
        filename: (pathData) => {
            const { version } = pkgs.find(pkg => pkg.shortName === pathData.chunk.name)
            return `[name]/${version}/index.js`;
        },
        library: 'PalifeBC_[name]',
        libraryTarget: 'umd',
        libraryExport: 'default',
    }

考虑到公司cdn文件发布有​固定格式要求–包名/版本号/文件,这里要对输出文件做动态配置。

npm发布

lerna publish —no-private // 忽略掉私有包,在这里为demo

lerna publish:发布自上次发布(基于上次执行lerna publish)以来更新的包。等价于lerna version + lerna publish from-git

lerna publish不会发布private为true的包,但也是会为私有包打上标签,在这里是demo包。lerna version会为私有包打标签。需要加过滤器--no-private

你可能感兴趣的:(npm,前端,typescript)