我在兩年前曾經寫過一篇如何快速建置一個 Node.js 專案並使用 TypeScript 與 VSCode 進行開發文章,由於現在的 Node.js 大多以 ESM 為主流,TypeScript 也更新到 5.x 版,而且當時的 TSLint 也都被換成 ESLint 為主了,所以這篇文章就來更新一下內容,幫助大家更方便、更快速的建立一個支援 TypeScript, ESLint, VSCode 與 ESM 並搭配 esbuild 的專案範本。
TL;DR (Too Long; Don't Read)
依照慣例,沒空看文章的人可以直接使用我寫好的 Duotify.Templates.DotNetNew 專案範本,快速建立專案:
-
使用 .NET CLI 命令列工具,安裝我寫好的專案範本
dotnet new install Duotify.Templates.DotNetNew
-
使用 dotnet new tsnode-esm
建立專案範本並執行 npm install
安裝 npm 套件
mkdir myproj && cd myproj
dotnet new tsnode-esm -c "Will 保哥"
npm install
-
開啟 Visual Studio Code 並對 src/app.ts
檔案進行開發
code .
-
按下 Ctrl+Shift+B
即可自動執行 npm start
命令
程式會自動監視檔案變更並自動重啟程式!
如何設定 TypeScript Node.js 與原生的 ESM 模組支援
這篇文章我就不會重頭建立起專案了,完整的建立步驟依然可以參見 如何快速建置一個 Node.js 專案並使用 TypeScript 與 VSCode 進行開發 文章的說明。
要設定 TypeScript 搭配 Node.js 並使用 ESM 模組系統,最有可能先遇到的問題,就是以下錯誤:
Error [ERR_REQUIRE_ESM]: require() of ES Module not supported
而這個錯誤必須靠許多設定上的調整來解決,老實說,知識含量有點大,以下是大致的更新步驟!
-
首先,我們必須在 package.json
檔案中加入以下設定:
{
"name": "my-project",
"type": "module", // <-- 加入這一段
}
這段設定可以讓 Node 在執行程式時,主要採用 ESM 為預設模組系統。
-
所有的 import
語法載入專案內的 *.ts
檔案都一定要這樣寫:
import { qux } from './bar/baz.js'; // <-- 注意: 副檔名一定要加上 .js 結尾
-
更新你的 tsconfig.json
設定檔
這裡我們會參考到 https://github.com/tsconfig/bases 這個專案,這裡有寫好一堆常用的 tsconfig.json
設定檔,其中也包含 Node LTS 的環境,我們可以直接從自己專案的 tsconfig.json
參考並繼承這裡的設定檔,就可以大幅簡化我們的專案內的 tsconfig.json
設定。
先安裝支援 Node LTS 的 npm 套件:
npm install --save-dev @tsconfig/node-lts
然後修改你的 tsconfig.json
繼承 @tsconfig/node-lts/tsconfig.json
設定檔:
"extends": "@tsconfig/node-lts/tsconfig.json"
附帶補充一下我從 node_modules\@tsconfig\node-lts\tsconfig.json
看到的設定內容如下:
// This file was autogenerated by a script
// Equivalent to a config of: node18
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Node LTS",
"_version": "18.1.0",
"compilerOptions": {
"lib": ["es2023"],
"module": "node16",
"target": "es2022",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node16"
}
}
所以我們的 tsconfig.json
就只需要這樣設定即可:
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@tsconfig/node-lts/tsconfig.json",
"compilerOptions": {
"resolveJsonModule": true,
"outDir": "./dist"
},
"include": [
"src/**/*"
]
}
-
設定 ts-node 可以支援 ESM
加入以下設定到 tsconfig.json
之中:
{
...,
"ts-node": {
"esm": true
}
}
目前我們的 tsconfig.json
設定如下:
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@tsconfig/node-lts/tsconfig.json",
"compilerOptions": {
"resolveJsonModule": true,
"outDir": "./dist"
},
"include": [
"src/**/*"
],
"ts-node": {
"esm": true
}
}
注意: 目前 ts-node 尚未支援 TypeScript 5.0 的 moduleResolution: bundler
設定,你可以到 moduleResolution bundler support #2034 追蹤這個議題。這個 moduleResolution: bundler
設定可以讓你的程式在 import
時,在 from
的地方不用加上 .js
副檔名,例如:import { version } from "./utils";
,所以現在 import
的時候只好乖乖加上 .js
結尾。詳見 TypeScript 5.0: new mode bundler & ESM 文章。
-
加入 ESLint 設定
基本上我是參考 Getting Started | typescript-eslint 這篇文章進行設定。
不過我有稍微做一點修正,加上了 env: { node: true }
設定:
/* eslint-env node */
module.exports = {
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
root: true,
env: {
node: true,
},
};
-
設定 esbuild Bundler
我們在準備發佈應用程式時,就有可能會透過 esbuild 來做 Bundling 的動作,徹底減少部署的檔案數,避免發佈 node_modules
資料夾!
以下是我對 package.json
的 scripts
區段所做出的設定:
{
"name": "myproj",
"type": "module",
...
"main": "dist/app.js",
"scripts": {
"esbuild-base": "esbuild ./src/app.ts --bundle --outfile=dist/app.js --format=esm --platform=node",
"esbuild:prod": "npm run esbuild-base -- --minify",
"esbuild": "npm run esbuild-base -- --sourcemap",
"esbuild-watch": "npm run esbuild-base -- --sourcemap --watch",
"start": "nodemon src/app.ts",
"build": "tsc",
"node": "tsc && node dist/app.js"
},
...
}
相關連結