打包TypeScript库文件并发布到npm

本文讲解了如何使用rollup打包TypeScript代码以及发布打包文件到npm,并最终自动化发布过程。涉及到的技术栈如下:
rollupTypeScriptnpmNode.js
由于整个项目搭建过程过于繁琐,笔者创建了一个模板项目 ,来帮助开发者快速创建项目,本文记录了实现的具体过程。如果你也有类似需求的话,可以参考文章内容了解具体步骤,也可以直接使用模板项目,跳过繁琐的配置环节。
使用rollup打包TypeScript代码
首先我们需要先安装相关依赖
npm i typescript rollup rollup-plugin-typescript2 tslib -D
这里需要使用rollup-plugin-typescript2来让rollup能够打包TypeScript代码,rollup的具体配置如下:
import typescript from 'rollup-plugin-typescript2';
const mode = process.env.MODE;
const isProd = mode === 'prod';
const pkg = require('./package.json');
export default {
input: `lib/index.ts`,
output: [
{
file: pkg.main,
exports: 'named',
format: 'cjs',
sourcemap: !isProd
},
{
file: pkg.module,
format: 'es',
sourcemap: !isProd
},
{
file: 'build/my-lib.global.js',
name: 'MyLib',
format: 'iife',
sourcemap: !isProd
},
],
plugins: [typescript({
useTsconfigDeclarationDir: true,
tsconfigOverride: { compilerOptions: { sourceMap: !isProd } }
})],
};
在rollup的配置文件中,我们通过output 中的format 来分别生成commonjs、esModule以及支持直接通过script在html中引入 的iife( 立即调用函数表达式)。即用户可以通过以下方式来使用打包后的代码:
// esModule
import MyLib from 'my-lib'
// commonjs
const MyLib = require('my-lib')
<!-- iife -->
<script src="node_modules/my-lib/build/my-lib.global.js"></script>
<script>
console.log('MyLib', MyLib)
</script>
当我们通过script进行引入时,提供的全局变量是在output数组中format:iife对应的name属性,之后用户便可以通过这个全局变量来实现相应的需求。
除了提供相应的功能,由于我们使用了TypeScript,也应该让同样使用TypeScript的用户能够进行类型提示,相应配置如下:
export default {
plugins: [typescript({
useTsconfigDeclarationDir: true,
tsconfigOverride: { compilerOptions: { sourceMap: !isProd } }
})]
}
这里会使用tsconfig.json中的配置来打包代码,并且会覆盖掉tsconfig.json中的sourceMap,保证只在开发环境生成sourceMap文件,方便代码调试。
相应的tsconfig.json中也需要提供如下配置:
{
// ... some other config
// 打包生成ES5代码
"target": "ES5",
// 生成类型声明文件
"declaration": true,
// 类型声明文件目录
"declarationDir": "build/types"
}
这里target: "ES5"可以帮我们将代码打包为ES5的语法,所以这里不用再使用babel来进行语法转换了。
最后要在package.json中指定js入口文件以及类型入口文件:
{
"main": "build/my-lib.cjs.js",
"module": "build/my-lib.es.js",
"types": "build/my-lib/types/index.d.ts"
}
module字段并不是package.json中预先指定的字段,而是自定义字段。不过webpack,rollup等打包器都可以识别该字段,从而优先使用esModule格式。- 有些开源库也将
types字段定义为typings,俩者含义完全相同
接下来在npm中添加如下scripts:
{
"dev": "rollup -w -c rollup.config.ts --environment MODE:dev",
"build": "rollup -c rollup.config.ts --environment MODE:prod"
}
执行npm run dev便可以进入开发模式,书写代码并实时编译。
修改npm相关配置
在发布之前,要修改一些package.json中的相关配置配置。
name: my-lib
package.json中的name用来表示我们包的名字,发布到npm之后,其它开发者便可以通过npm install my-lib来进行安装。
由于命名冲突问题,有很多包会放到一个组织下,此时name属性会以@组织名为前缀。一般公司内部也会有自己的组织,包文件都会放到该组织下。以vue为例,其中reactivity的name 字段为:@vue/reactivity。
private:false: 要将private设置为false,否则发布会出错
发布到npm中的代码便是用户真正使用的代码,而有很多文件只是在开发时使用。为了排除生产环境中不会用到的代码文件,npm为我们提供了以下俩种方法:
.npmignore文件package.json中的files字段
.npmignore文件可以忽略发布到npm中不需要的文件,而files恰好相反,可以通过这个字段来指定发布到npm时要包含哪些文件。
通常我们只需要配置files字段,把打包后的文件用来发布即可:
{
"files": [
"build"
]
}
发布代码到npm
为了能让更多的人使用我们写好的代码,我们可以将代码发布到npm,这样所有的开发者都可以通过npm install来进行安装。
要发布代码,首先需要将最先的代码进行打包。由于打包后生成的类型文件比较分散,这里我们使用@microsoft/api-extractor 将打包后生成的类型声明文件整合为一个单独的文件,之后将原有类型声明文件删除。
发布之前,首先需要通过执行npm version <newversion>来增加package.json以及package-lock.json中的版本号。需要注意的是,该命令会帮我们创建一个git commit, 并且使用新的版本号创建git tag
在version升级后,可以通过Conventional Changelog 生成更改日志
版本提升以后,便可以执行npm publish来进行发布。如果提示未登录,需要执行npm login登录后再执行npm publish。由于日常开发时,大多数人都会使用淘宝源来安装依赖,在发布时要记得将淘宝源切换为npm 官方源。
发布npm完成之后,将changelog.md的最新改动提交到git,然后将所有更改以及创建的版本号tag提交到GitHub远程仓库。
发布完成后,我们可能想看一下到底上传了哪些代码到npm,这里推荐以下俩种方式:
- 在项目根目录执行
npm pack,会生成最终用户安装到node_modules下的代码 - 通过
npmview来在线搜索查看自己发布到npm的包文件
通过Node.js来自动化发布
在日常的开发中,发布功能会被不停的使用,那么我们就得每次重复上述的工作。在重复的过程中,很有可能因为某次的不小心,而导致某个步骤出错或者漏掉。如忘记在发布之前将npm源设置为官方源、发布之前忘记打包等等问题。
借助Node.js,我们可以将发布流程进行自动化,只需在命令行执行npm run release便可以让程序自动处理发布时要做的一些工作。
在自动化之前,将整个发布流程梳理一下:
npm run build生成打包文件,并利用@microsoft/api-extractor将类型声明文件合并到一个文件npm version newversion提升版本号(同时也会创建一个commit以及tag)npm run genlog生成changlog.md文件来记录更改- 提交
changelog.md的最新改动到git npm publish --reg=https://registry.npmjs.org使用npm官方源来发布代码- 将最新的改动以及新创建的
tag提交到githubgit push: 推送代码到远程仓库git push origin tagName:tag需要单独提交
整个过程中我们会用到以下工具库:
execa: 通过Node.js子进程来执行命令行代码enquirer: 可以让Node.js与命令行进行交互,如输入或者选择内容chalk: 设置终端字符串样式minimist: 解析命令行参数semver: 处理语义化版本相关逻辑
核心逻辑便是通过execa 执行命令行代码,一步步完成我们的发布过程,期间会通过enquirer 来让用户选择或输入新的版本号。整个过程提供了dry 模式,在命令行中执行npm run release -- --dryRun 便可以通过日志来查看发布的整个流程,而不会触发真正对应的操作,方便调试

想要查看源代码的小伙伴可以点击这里 。
最终我们在命令行执行npm run release,根据提示输入或选择新的版本号,等到命令行提示发布成功,便可以在npm中看到我们发布的包了。
结语
之前笔者在开发公用的代码库时,对整个打包和发布过程一知半解,最终决定学习其中涉及到的相关知识点, 并参考Vue3 的源码 实现了满足需求的发布脚本。如果你也想了解发布过程中的一些知识,希望本文可以帮到你。
TypeScript Library Template