本文的主题是一步一步建立 Rollup TypeScript 代码模板。
前言
首先看看,我们需要做什么。通常一个库,在发布前他的目录树是这样的。
代码语言:javascript复制1.
2├── dist
3├── esm
4├── lib
5├── node_modules
6├── package.json
7├── pnpm-lock.yaml
8├── rollup.config.js
9├── src
10├── tsconfig.json
11├── vite.config.js
COPY
其中,dist 目录一般是通过 Rollup 等打包器打包后的入口文件,一般具有多种格式,以不同后缀命令,如: index.cjs.js
index.esm.js
。lib 和 esm 目录可以是 TypeScript 编译后生成的文件,目录下的结构基本和原项目结构相同,只是后缀变为 js,lib 一般为 CommonJS 格式,esm 为 ESModule 格式。而这些是一个库最基本的需要发布的文件。
初始化项目
通过如下命令快速开始:
bash
代码语言:javascript复制1npm init -y
2npm i -D typescript rollup @rollup/plugin-typescript @rollup/plugin-commonjs @rollup/plugin-node-resolve rollup-plugin-terse rollup-plugin-peer-deps-external
3npx run tsc --init
4mkdir src
5touch src/index.ts
6echo 'export {}' >> src/index.ts
COPY
注:基本配置不再过多赘述,@rollup/plugin-commonjs
为 ES6 转换插件,@rollup/plugin-node-resolve
为 Node 模块解析插件,rollup-plugin-terse
为代码压缩插件,rollup-plugin-peer-deps-external
为打包时使用外部库插件(就是说,打包的时候不把依赖库打包进去,node_modules 依赖链你也知道)。
建立 rollup.config.js,编写基本配置以支持 TypeScript。
js
代码语言:javascript复制1//@ts-check
2import commonjs from '@rollup/plugin-commonjs'
3import { nodeResolve } from '@rollup/plugin-node-resolve'
4import typescript from '@rollup/plugin-typescript'
5import peerDepsExternal from 'rollup-plugin-peer-deps-external'
6import { terser } from 'rollup-plugin-terser'
7
8const packageJson = require('./package.json')
9
10const umdName = packageJson.name
11
12const globals = {
13 ...packageJson.devDependencies,
14}
15
16const dir = 'dist'
17
18/**
19 * @type {import('rollup').RollupOptions[]}
20 */
21const config = [
22 {
23 input: 'src/index.ts',
24 // ignore lib
25 external: ['lodash', 'lodash-es', ...Object.keys(globals)],
26
27 output: [
28 {
29 file: dir '/index.umd.js',
30 format: 'umd',
31 sourcemap: true,
32 name: umdName,
33 },
34 {
35 file: dir '/index.umd.min.js',
36 format: 'umd',
37 sourcemap: true,
38 name: umdName,
39 plugins: [terser()],
40 },
41 {
42 file: dir '/index.cjs.js',
43 format: 'cjs',
44 sourcemap: true,
45 },
46 {
47 file: dir '/index.cjs.min.js',
48 format: 'cjs',
49 sourcemap: true,
50 plugins: [terser()],
51 },
52 {
53 file: dir '/index.esm.js',
54 format: 'es',
55 sourcemap: true,
56 },
57 {
58 file: dir '/index.esm.min.js',
59 format: 'es',
60 sourcemap: true,
61 plugins: [terser()],
62 },
63 ],
64 plugins: [
65 nodeResolve(),
66 commonjs({ include: 'node_modules/**' }),
67 typescript({ tsconfig: './src/tsconfig.json', declaration: false }),
68
69 // @ts-ignore
70 peerDepsExternal(),
71 ],
72
73 treeshake: true,
74 },
75]
76
77export default config
COPY
配置之后,使用 rollup -c,就可以编译打包 ts 文件到 dist 目录了。
但是这才刚刚开始。
Path Alias
一般的也会用 Path Alias 方便方法的引入。
在 tsconfig.json 配置 paths
,如
json
代码语言:javascript复制1{
2 "compilerOptions": {
3 "baseUrl": "./src",
4 "paths": {
5 "~/*": [
6 "*"
7 ]
8 },
9 }
10}
COPY
就可以用 import foo from '~/'
的形式了。为什么要讲这个。因为这个是巨坑。请看下节。
TSC 编译与 Path Alias
上面说了 Rollup 的打包,再来说说 TSC,其实也比较简单。一般的,我会在根目录下新建一个 tsconfig.json 作为基本 tsconfig,然后在建立 build 用 tsconfig,开发用 tsconfig,都是从根目录的 extends 出来。这样做的好处就是不同环境用不同配置比较灵活。
大概这就像这样:
代码语言:javascript复制1.
2├── package.json
3├── pnpm-lock.yaml
4├── readme.md
5├── renovate.json
6├── rollup.config.js
7├── src
8│ ├── tsconfig.build.json # build
9│ ├── tsconfig.cjs.json # cjs build
10│ ├── tsconfig.json # dev
11├── tsconfig.json # base
12├── vite.config.js
13└── yarn-error.log
COPY
前面说了 tsc 编译也要两种格式一个是 ESM,另一个是 CJS。就需要编写两个配置了,唯一的不同其实就是 outDir
和 module
。然后编译跑这行就行了。
json
代码语言:javascript复制1{
2 "scripts": {
3 "build": "tsc --build src/tsconfig.build.json && tsc --build src/tsconfig.cjs.json"
4 }
5}
COPY
好像没有什么不对,但是仔细一看人傻了,tsc 编译之后的产物没有把 Path Alias 转换过来。但是这个库被调包侠调过来之后,它的环境咋知道 ~
alias 是个啥,况且 js 也不读 tsconfig 的配置。
这可咋整。
一查发现别的 CLI 都用了一个工具叫 tsconfigs-paths,但是这玩意好像只是个库,用起来比较麻烦。在这之后有大佬写一个 TypeScript 的插件叫 @zerollup/ts-transform-paths
。用于解决这个问题。由于目前 TypeScript 还不支持自定义 transformer 所以得用 ttypescript 替换 TypeScript。
bash
代码语言:javascript复制1npm i -g @zerollup/ts-transform-paths ttypescript
COPY
json
代码语言:javascript复制1// tsconfig.json
2{
3 "compilerOptions": {
4 "outDir": "./dist",
5 "baseUrl": "./src",
6 "paths": {
7 "~/*": [
8 "*"
9 ]
10 },
11 "plugins": [
12 {
13 "transform": "@zerollup/ts-transform-paths",
14 }
15 ]
16 }
17}
COPY
把 tsc
全换成 ttsc
,
bash
代码语言:javascript复制1ttsc --build src/tsconfig.build.json && ttsc --build src/tsconfig.cjs.json"
COPY
之后。
打包 DTS
DTS 就是 tsc 生成 d.ts,我们要做的就是把 dts 也打包一份,全部扔到一个 index.d.ts。这样的话如果引用的是任何一个 dist 下的 index.js (比如dist/index.esm.js
)都会识别到 type definition。
但是,@rollup/plugin-typescript
和 rollup-plugins-typescript2
都没有这一功能。
之后就发现了一个神器 dts-bundle-generator。可以做到这个需求,同时它也支持 Path Alias 的转换。
使用也非常的简单。
bash
代码语言:javascript复制1dts-bundle-generator -o build/index.d.ts src/index.ts --project tsconfig.json --no-check
COPY
一些不能工作的点
- ttsc 在 typescript 4.5.2 的环境可能会报错。
TypeError: Cannot read properties of undefined (reading 'impliedNodeFormat')
。 解决方式:降级到 4.4.4 - dts-bundle-generator 不能支持没有提前引入的泛型的值的解析(也可能是 目前 TS 的 bug)参考:https://github.com/timocov/dts-bundle-generator/issues/178
Package.json
注明需要发布的文件,以及入口文件、类型文件。
json
代码语言:javascript复制1{
2 "main": "build/index.cjs.js",
3 "module": "build/index.esm.js",
4 "types": "build/index.d.ts",
5 "unpkg": "build/index.umd.min.js"
6}
COPY
后
完整模板:
rollup-typescript-lib