实现 Rollup 插件alias 并使用vitest提高开发效率

本篇文章是对 实现 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.ts
import { Plugin } from 'rollup'

export function alias(): Plugin {
  return {
    name: 'alias',
    resolveId (source: string, importer: string | undefined) {
      console.log('resolveId', source, importer)
      return source
    }
  }
}

由于 .ts 文件无法直接被运行,接下来需要将 index.ts 编译成可执行的 js 文件,新增一个配置文件rollup.config.js,并指定输入以及输出:

// rollup.config.js
import { defineConfig } from 'rollup'
import typescript from "@rollup/plugin-typescript"

export default defineConfig({
  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"
  },

一般执行到这里会有一个 CommonJSES module 的类型冲突,如图所示:

在这里插入图片描述
我们只需要在 package.json 指定类型即可:

// package.json
{
    // 省略部分代码
	"type": "module",
}

再次运行 pnpm build 可以发现在 dist 目录下会生成打包完成之后的 index.js 文件。

作为一个 npm 依赖包,当别人去安装的时候需要指定执行的文件在哪,即修改 package.json 里面的 main 字段:

"main": "index.js" ——> "main": "./dist/index.js"

测试开发好的 alias 插件

在根目录下新增一个 example 文件夹并且新增测试文件 index.js、add.js 写入相关的测试代码:

// example/index.js
import { add } from './utils/add.js'
console.log(add(1, 2))

// example/utils/add.js
export function add (a, b) {
  return a + b;
}

example 文件夹下新增配置文件 rollup.config.js,并且补充 build 命令,由于这里使用的是 .js 为测试文件所以不需要使用 typescript 具体操作和上文类似:

// example/rollup.config.js
import { defineConfig } from 'rollup'

export default defineConfig({
  input: 'index.js', // 入口文件
  output: {
    file: './dist/index.js', // 输出文件
    format: 'es'
  }
})

如何在 example 里面引入我们开发的插件呢?在 example 目录下执行如下命令就可以使用我们开发的插件。

// ../上层目录
pnpm i ../ -D

解释下上述代码的含义:

pnpm i ../ -D 是一个命令,它可以将当前项目(即插件开发项目)中的依赖包链接到上层或其他目录的项目中。

假设我们在插件开发时,需要引用某些库,这些库又已经在上层项目中安装过,在不需要重复安装依赖的情况下,可以使用 pnpm i ../ -D 命令将上层项目中的依赖包快速链接到当前开发目录中。

在执行该命令时,会寻找 ../ 目录下的 package.json 文件,然后安装该文件中所包含的依赖包,并在当前项目中创建依赖的符号链接。

  • …/:表示上层目录,也可以是其他目录
  • -D:表示安装依赖包,并将其添加到当前项目的 devDependencies 中

这个命令一般用于本地开发、测试和调试,请注意在发布插件时,依赖的包需要在 package.json 中定义并通过 npm 或相关的包管理工具进行安装和打包。

执行完之后在会 example/package.json 新增如下代码:

"scripts": {
  "build": "rollup -c rollup.config.js"
},
"devDependencies": {
  "rollup": "^3.26.2",
  "rollup-alias": "link:.." //  新增的依赖
}

在这里插入图片描述
example/rollup.config.js 里面引入我们编写的 alias 插件,完整的代码如下:

import { defineConfig } from 'rollup'
import { alias} from 'rollup-alias'

export default defineConfig({
  input: 'index.js',
  output: {
    file: './dist/index.js',
    format: 'es'
  },
  plugins: [
    alias()
  ]
})

在此执行 pnpm build 可以发现已经成功的打印出了 log

在这里插入图片描述

为插件添加TS类型提示

首先补充插件的参数类型提示并且完善一下插件逻辑:

// rollup.config.js
import { Plugin } from 'rollup'

interface AliasOptions {
  entries: { [key: string]: string }
}

export function alias(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 插件在传参的时候并没有对应的参数类型提示:

在这里插入图片描述
可以进行如下修改以完善 ts 类型提示:

  • 需要在根目录下的 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.ts
import { 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.js
import { Plugin } from 'rollup'

interface AliasOptions {
  entries: { [key: string]: string } | { find: string, replacement: string }[]
}

export function alias(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)
    }
  }
}

function normalizeEntries(entries: AliasOptions["entries"]) {
  if (Array.isArray(entries)) {
    return entries.map(({ find, replacement }) => {
      return new Entry(find, replacement)
    })
  } else {
    return Object.keys(entries).map((key) => {
      return new Entry(key, entries[key])
    })
  }
}

class Entry {
  constructor(private find: string, private replacement: string) { }
  match(filePath: string) {
    return filePath.startsWith(this.find)
  }
  replace(filePath: string) {
    return filePath.replace(this.find, this.replacement)
  }
}

以上就简单的实现了一个 rollup 插件开发的大致流程。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值