tailwind config 使用 ESM
給自己筆記一下。有用過 tailwind + vite 的話,應該會注意到 tailwind 使用的是 CommonJS 的 module 格式,而不是 ES6 Module。
所以配合預設採用 ESM 的 vite 使用時,需要將 postcss 與 tailwind 的設定檔改為 .cjs
才能運作。
// postcss.config.cjs
/** @type {ProcessOptions & { plugins?: AcceptedPlugin[] }} */
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
}
註: postcss 的設定檔 type 可以參考 Vite 文件,tailwind 跟 postcss 官網都沒寫。
// tailwind.config.cjs
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{html,js}"],
theme: {
extend: {},
},
plugins: [],
}
這設定是 tailwind 官網給的 example。可以看出,採用的是 CommonJS 規格。
大部分使用情境這樣就夠了,但這次碰到一個需求,希望把 theme 設定寫在外面跟其他 UI 庫共用,讓各自 import 進來,這下就碰到問題了。
如果外部設定檔用 ESM 寫,就無法 import 到 CJS 的 tailwind config。但如果用 CJS 來寫,就無法 import 到其他使用 ESM 的檔案……
後來研究老半天,發現網上其實有解。
這篇樓主遇上跟我一樣的問題,他直接跑去 Vite 官方 Discord 詢問,結果真問到了一種寫法。雖然他沒說原理,但乍看之下,重點在於最後把 postcss config 給引入到了 vite 集成打包。
由於改成了 ESM 格式,所以也能直接換成 typescript。
// postcss.config.ts
import tailwind from 'tailwindcss'
import autoprefixer from 'autoprefixer'
import tailwindConfig from './tailwind.config'
import type { ProcessOptions, AcceptedPlugin } from 'postcss'
const config: ProcessOptions & { plugins?: AcceptedPlugin[] } = {
plugins: [
tailwind(tailwindConfig),
autoprefixer,
],
}
export default config
postcss 這邊要注意,原本 plugins 是用 object,這邊改成了 array。這個注意點其實 vite 的文件也有寫。
// tailwind.config.ts
import type { Config } from 'tailwindcss'
const config: Config = {
content: ["./src/**/*.{html,js}"],
theme: {
extend: {},
},
plugins: [],
}
export default config
tailwind 基本上一樣,只是改成了 ESM 跟 typescript 上型別而已。
然後最重要的動作,把 postcss config 引入給 vite config 使用。
import postcss from './postcss.config'
export default defineConfig({
// ...
css: { postcss },
})
這樣修改一波,就可以讓 tailwind config 順利使用 ESM,並且 import 其他基於 ESM 的 js / ts 檔案。
原理上,猜測是原本 tailwind 編譯主導的是 postcss,但它是用 CJS 寫的,所以無法改成 ESM。這邊將其用 vite 規定的 plugins 格式改寫 postcss config,導入到 vite 之後,編譯的主導者就變成了 vite,然後內部運作就稀哩呼嚕解決了。
注意點
使用這種作法雖然可以 work,可這其實不是完美解…… 終究 tailwind 現況仍舊是用 CJS 寫的,這問題會反映在 VScode 的 tailwind 插件上面。
如果我們改用了 ESM 來寫 config,那 tailwind 的語法提示插件就會直接陣亡,因為它看不懂基於 ESM 的 tailwind config。
万策尽きた!!
不依賴這個插件的人或許不影響,但我個人覺得少了這插件寫起來的爽度差異非常巨大。
所以最後還是默默地退回 .cjs
,然後把共用的 theme 設定檔改用 json
來寫,這樣管你是 ESM 還是 CJS 都能 import 了。
tailwind 的 ESM 支援問題,官方有在 issues 回說這是有計畫要處理的事,只是時間還沒定下,只能等官方更新了。