打包TypeScript
库文件并发布到npm
本文讲解了如何使用rollup
打包TypeScript
代码以及发布打包文件到npm
,并最终自动化发布过程。涉及到的技术栈如下:
rollup
TypeScript
npm
Node.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
提交到github
git 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
的源码 实现了满足需求的发布脚本。如果你也想了解发布过程中的一些知识,希望本文可以帮到你。