vitest支持cjs的workaround(TypeScript產物commonjs場景)

咲奈 2022-05-14 14:35:31 阅读数:620

vitest支持cjsworkaroundtypescript

前言

開門見山,首先 vitest 是一個 esm first 的測試工具,所以他的定比特應該是 純 esm / 純 cjs 的場景,不像 jest 可以通過一些 transform 插件支持混雜場景。

什麼是混雜場景,比如我們使用 typescript 編寫 esm 格式的 source ,但是把產物編譯為 commonjs 格式,最後給 node 調用,這在 Native ESM 普及之前是 node 庫的基本操作。

當然現在有 type: "module" 帶來了 typescript 編譯的 esm 格式包和真正的 Native ESM 包。

我們這裏不就 esm 話題展開聊,關鍵在於這種源碼 esm 產物 cjs 是一種 混雜 場景。

這就違背了 vitest 的設計,不在 vitest 的概念內,所以自然無法使用 vitest 。

在混雜場景下,我不建議使用 vitest ,而使用 jest ,但如果非要使用 vitest 怎麼辦呢?我們有一種 workaround 的方法。

該方法的靈感和現在社區熱導入 ts 文件的做法一致,也是 esno / swno / esbuild-jest 等庫的底層原理。

底層邏輯剖析

在了解解法前,我們先簡單剖析一下 vitest 不支持的底層邏輯。

由於 vitest 在底層使用 vite 的 esbuild 能力 transform ts 文件,所以關鍵在 esbuild 的配置。

當使用 cjs 作為產物的場景時
// vitest.config.ts
import {
 defineConfig } from 'vitest/config'
export default defineConfig({

esbuild: {

target: 'node14',
format: 'cjs'
},
})

當我們把所有 transform 的 format 都定為 cjs 時,這將導致 *.test.ts 轉換為 cjs 格式,這就意味著最終 *.test.ts 內會出現 ts 文件的導入:

// before: test file
// example.test.ts
import {
 method } from './some-file'
// after: transformed result
// example.test.js
const {
 method } = require('./some-file')

注:以上轉換為偽代碼描述,並非真實結果。

其中 methodsome-file.ts 文件的一個變量,關鍵在於 require() 是無法正常導入一個 .ts 文件的,由此將引發:

Error: Cannot find module ‘./some-file’
Require stack:
- …

當使用 esm 作為產物的場景時
// vitest.config.ts
import {
 defineConfig } from 'vitest/config'
export default defineConfig({

esbuild: {

target: 'node14',
},
})

默認情况 vitest 是 esm 優先,所以我們的 test file *.test.ts 和相關導入的 .ts 文件會原模原樣使用 esm 格式來處理,但別忘了我們使用 typescript 的產物是 cjs 格式,最終運行時是 cjs 而不是 esm ,這就導致了兩者的概念發生沖突,將導致測試不符合預期,最終失敗。

關於此處的錯誤可能是千人千面的,大多可能和 cjs 的導入報錯相關,比如 TypeError: default is not a function 等。

解法

剖析完底層邏輯,我們來看解法。

在上文的剖析中,既然 require() 不能導入 .ts 文件,那麼我們讓他支持就好了。

在每次啟動測試前人為 hook require.extensions 來支持 .ts 文件導入:

// hook.ts
import {
 EsbuildPhoenix } from '@xn-sakina/phoenix'
// 這個類實現了 hook 對 .ts 文件的 require 導入並進行熱轉換
new EsbuildPhoenix({

target: 'es2019'
})

vitest 配置:

// vitest.config.ts
import {
 defineConfig } from https://github.com/vitest-dev/vitest
export default defineConfig({

test: {

setupFiles: ['./hook']
},
esbuild: {

target: 'node14',
format: 'cjs'
},
})

由此,我們便可以 workaround 掉這個問題。

進一步思考,還有什麼沒考慮到的?

  • 性能和 ts-jest / esbuild-jest 等 jest transform 插件實現思路一致,那麼速度和 vitest 誰快?

  • hook require 的一個邊緣情况是存在 require.cache ,如何應對?

  • 如何應對 .tsx 的 jsx element 場景?

  • 其他 edge case 是否都能無傷應對?

在此解法下,我順利跑通了兩大組非 jsx 測試,還是比較滿意的。

但綜合來看,仍然存在一些謎點待驗證,我們還不能認為他是萬能的解法。

總結

Migrating from jest

雖然 vitest 在混合場景可圈可點,但在這一切之前,我們還需要注意 migrating from jest 的方法,關於這一點,官方文檔 有所描述,可以看到需要應對的點還是有不少的。

React strategy

值得注意的是,我還是建議在業務 React Project 場景使用 RTL ,因為更成熟,但簡單的 libs jsx 場景可以使用 react-test-renderer ,這也是 vitest 推薦的做法。

Use native esm

同時,當你准備好擁抱 native esm 時,vitest 將是你不二的選擇,你也可以在官方倉庫 examples 找到更多的用例和打開姿勢。

版权声明:本文为[咲奈]所创,转载请带上原文链接,感谢。 https://gsmany.com/2022/134/202205141432588203.html