網站搜尋

用於瀏覽器和 Node.js 的庫的 TypeScript 模組設置有哪些


關於這個有很多資源,但它很容易令人困惑,而且我找不到任何最近的資源:

在2024年的今天,我想創建一個用 TypeScript 編寫的程式庫。它將用於使用 webpack 打包器的 React 專案中。該應用將在伺服器(node.js)和瀏覽器中呈現。

我該如何配置 tsconfig.json?我應該為 module 選擇哪個設定?

背景:

這個函式庫已經存在於 CJS 中,但在目前正在進行的大規模重寫中,我正在考慮升級到 ESM,因為我們在使用這個函式庫的應用程式(目前也是 CJS)中遇到了僅 ESM 模組的導入問題。此外,使用 CJS 時,我們無法在 webpack 中利用動態導入來自動拆分程式包。

文件

TypeScript 對 module 的參考指出,es2015、es2020、es2022、esnext 不應用於 node.js

而且它指出

node16 和 nodenext 是針對所有打算在 Node.js v12 或更高版本運行的應用程式和程式庫的唯一正確模組選項

但沒有提到 nodenext 或 node16 是否也可以用於瀏覽器/打包工具如 webpack。

這是什麼?我應該選哪一個?

我想知道我是否應該採用分裂路線來發布一個 /lib/cjs 和 /lib/esm 版本,並在 package.json 的 export 設定中使用 require 和 module 欄位?

這仍然讓我有個問題,即在 tsconfig 中應該使用哪個 module 輸出設置,以便瀏覽器(通過 webpack)和 node.js 都能使用 ESM。或者我應該為了其中一個環境而選擇繼續使用 CJS?

答案#1

由於歷史原因,組合方式實在太多,但大多數已經不再需要。總而言之,如果你想發布僅限 ESM 的套件,並讓 Node.js 和瀏覽器都能使用,以下四條規則已經足夠了:

  • 你的套件應該使用 "type": "module'。
  • 如果您正在編寫僅適用於 Node.js 的函式庫,則:

    • "module": "NodeNext"
    • "moduleResolution": "NodeNext"
  • 如果你正在僅透過 打包工具 為前端撰寫庫,那麼:

    • "module": "ESNext"
    • "moduleResolution": "Bundler"
  • 最後,如果你正在為 Node.js 和前端編寫函式庫,那麼:

    • "module": "NodeNext"
    • "moduleResolution": "NodeNext"

更多說明

NodeNext + NodeNext 的組合是 ESNext + Bundler 的一個子集。換句話說,NodeNext + NodeNext 是受 Node.js 模組解析演算法設置限制的 ESM。最常見的例子是在導入語句中必須包含檔案副檔名,例如 NodeNext + NodeNext 中的 import {} from 'other.js'。這種限制在 ESNext + Bundler 模式下則不適用。

所以,如果你打算同時為 Node.js(預期不使用打包)和前端(預期使用 import-maps 或打包器)發佈,那麼你應該使用 module: NodeNext + moduleResolution: NodeNext。

這確保了 TypeScript(以及其他現代打包工具)會遵守 package.json 規範中指定的 main、exports 聲明。

另外,請記住 "type": "module" 是很重要的。如果缺少它,即使有 module: NodeNext,TypeScript 編譯器也會將 *.ts 文件編譯為使用 CJS require 語句的 *.js 文件,並將 *.mts 文件編譯為使用 ESM import 語句的 *.mjs 文件。

最後,也有另一種思考方式!如果你使用 TypeScript 作為編譯器來編譯你的函式庫,那麼你應該使用 NodeNext + NodeNext。而如果你使用打包工具,你可以選擇任意組合(儘管我仍然建議使用 NodeNext + NodeNext,因為它帶來了嚴格的語義)。

關於 module 和 moduleResolution 的更詳細深入說明,這是一篇不錯的閱讀資料。