本篇文章是对 实现 Rollup 插件 alias | 使用 TypeScript 实现库的基本流程 | 使用单元测试提高开发效率 的总结。其中涉及到开发一个组件库的诸多知识点。
实现一个经常用的 alias 插件
首先执行
npm init
命令初始化一个
package.json
文件,由于插件使用了
typescript
作为类型校验,需要执行
tsc --init
命令去生成一个ts的配置文件
tsconfig.json
,执行完上述的命令之后安装项目依赖。
pnpm i rollup typescript @rollup/plugin-typescript tslib -D
先简单实现一下这个插件,插件要求导出一个方法并且返回一个对象:
// src/index.tsimport{ Plugin }from'rollup'exportfunctionalias(): Plugin {return{
name:'alias',resolveId(source:string, importer:string|undefined){console.log('resolveId', source, importer)return source
}}}
接下来需要将
index.ts
编译成可执行的
js
文件,新增一个配置文件
rollup.config.js
,指定输入以及输出:
// rollup.config.jsimport{ defineConfig }from'rollup'import typescript from"@rollup/plugin-typescript"exportdefaultdefineConfig({
input:'./src/index.ts',// 入口文件
output:{
file:'./dist/index.js',// 输出文件
format:'es'},
plugins:[typescript({
module:'esnext'})]})
在
package.json
里面新增一条命令,并执行
pnpm build
:
"scripts":{"build":"rollup -c rollup.config.js"},
一般执行到这里会有一个
CommonJS
和
ES module
的类型冲突,如图所示:
我们只需要在
package.json
指定类型即可:
// package.json{// 省略部分代码"type":"module",}
再次运行
pnpm build
可以发现在
dist
目录下会生成打包完成之后的
index.js
文件。
同时,当别人去安装你的包的时候需要指定执行的文件在哪,即修改
package.json
里面的
main
字段:
"main":"index.js" ——>"main":"./dist/index.js"
使用开发好的 alias 插件
在根目录下新增一个
example
文件夹新增
index.js、add.js
写入相关的测试代码:
// example/index.jsimport{ add }from'./utils/add.js'
console.log(add(1,2))// example/utils/add.jsexportfunctionadd(a, b){return a + b;}
在
example
里面新增配置文件
rollup.config.js
,并且补充
build
命令,具体操作和上文类似:
// example/rollup.config.jsimport{ defineConfig }from'rollup'exportdefaultdefineConfig({input:'index.js',// 入口文件output:{file:'./dist/index.js',// 输出文件format:'es'}})
通过在
example
目录下执行如下命令就可以使用我们开发的插件。
// ../上层目录
pnpm i ../-D
执行完之后会新增如下代码:
// example/package.json"scripts":{"build":"rollup -c rollup.config.js"},"devDependencies":{"rollup":"^3.26.2","rollup-alias":"link:.."// 新增的依赖}
在
example/rollup.config.js
里面引入我们编写的
alias
插件,完整的代码如下:
// example/rollup.config.jsimport{ defineConfig }from'rollup'import{ alias}from'rollup-alias'exportdefaultdefineConfig({input:'index.js',output:{file:'./dist/index.js',format:'es'},plugins:[alias()]})
在此执行
pnpm build
可以发现已经成功的打印出了
log
。
为插件添加TS类型提示
首先补充插件的参数类型提示并且完善一下插件逻辑:
// rollup.config.jsimport{ Plugin }from'rollup'interfaceAliasOptions{entries:{[key: string]: string }}exportfunctionalias(options: AliasOptions): Plugin {const{ entries }= options
return{name:'alias',resolveId(source: string,importer: string |undefined){
console.log('resolveId', source, importer)const key = Object.keys(entries).find((e)=>{return source.startsWith(e)})if(!key)return source
return source.replace(key, entries[key])+'.js'}}}
执行
build
之后在我们会发现
alias
插件在传参的时候并没有对应的参数类型提示:
- 需要在根目录下的
tsconfig.json
文件中开启"declaration": true
功能 - 设置
"outDir": "./dist"
- 在
package.json
里面添加"types": "./dist/index.d.ts"
- 执行完上述操作之后再次执行
pnpm build
补充单元测试
安装
pnpm i vitest -D
,补充单元测试文件
index.spec.ts
,添加测试命令:
// src/index.spec.tsimport{ describe, it, expect }from'vitest'import{ alias }from'.'describe('alias',()=>{it('should replace when match successful',()=>{const aliasObj:any=alias({
entries:{'@':'./utils'}})expect(aliasObj.resolveId('@/add')).toBe('./utils/add.js')})it('should not replace when match fail',()=>{const aliasObj:any=alias({
entries:{'@':'./utils'}})expect(aliasObj.resolveId('!/add')).toBe('!/add')})})
补充测试命令:
"scripts":{+"test":"vitest"},
同时,我们需要在
build
的时候排除掉我们的测试文件在根目录下的
tsconfig.json
补充如下代码
"exclude": ["./src/*.spec.ts"]
。
然后执行
pnpm test
可以看到这里的测试用例是通过的也可以证明我们写的代码是没问题的。
entries 支持数组格式
这里直接贴完成之后的代码:
// rollup.config.jsimport{ Plugin }from'rollup'interfaceAliasOptions{entries:{[key: string]: string }|{find: string,replacement: string }[]}exportfunctionalias(options: AliasOptions): Plugin {const entries =normalizeEntries(options.entries)return{name:'alias',resolveId(source: string,importer: string |undefined){
console.log('resolveId', source, importer)const entry = entries.find((e)=> e.match(source))if(!entry)return source
return entry.replace(source)}}}functionnormalizeEntries(entries: AliasOptions["entries"]){if(Array.isArray(entries)){return entries.map(({ find, replacement })=>{returnnewEntry(find, replacement)})}else{return Object.keys(entries).map((key)=>{returnnewEntry(key, entries[key])})}}classEntry{constructor(privatefind: string,privatereplacement: string){}match(filePath: string){return filePath.startsWith(this.find)}replace(filePath: string){return filePath.replace(this.find,this.replacement)}}
以上就简单的实现了一个
rollup
插件开发的大致流程。
版权归原作者 Avicli 所有, 如有侵权,请联系我们删除。