[Pkg] Env Aligner / npm 套件開發
先簡介一下這個套件好了,Env Aligner 主要是用來檢查 .env
跟 .env.example
這兩個檔案的環境變數是否有對齊,這樣可以避免在開發或是部署時因為環墋變數對不齊而造成問題。
做這個套件的起因,就像某業界前輩說的「軟體的開發都源自生活中的問題」,Env Aligner 的出現其實是來自公司需求一個工具在前端自己開發、build 或是 CI/CD pipeline 時可以檢查環境變數是否有齊全,沒有齊全的話就噴個錯誤並把流程終止,來做到環境變數的簡易把控。
在我 survey 的過程裡,其實有發現不少套件有類似的功能,但總在一些細枝末節上跟我的需求稍有差異,沒有辦法達到 100% 吻合,這其實也正常,一般開源性套件通常都是開發者為了因應自身某些需求而開發,然後想說做都做了就放到 npm 上讓大家用用看,而我的需求可能跟他的需求不一樣也是合情合理。
那為了達到 100% 的需求,我一開始寫了個腳本,確確實實地 work 了,但如果要應用在各專案上,不太可能一直 copy / paste 這個腳本,那是否應該做一個新的套件呢?於是 Env Aligner 因應而出。
這篇會先簡介一下 Env Aligner 的功能,但其實重點是後方章節怎麼簡易地製作套件並發布到 npm 上。
Env Aligner 功能介紹
Features
Env Aligner 主要有五個功能:
- 參照
.env.example
檢查.env
檔案中是否有缺少的變數。 - 參照
.env.example
檢查.env
檔案中是否有空值的變數。 - 參照
.env.example
檢查.env
檔案中是否有多餘的變數。(strict 模式限定) - 參照
.env.example
自動對齊.env
檔案中的變數。此項會移除使用者在.env
檔案中多出的註解以及變數,但會保留使用者已經填入變數的值。(strict 模式限定) - 參照
.env.example
自動產生一份.env
檔案,就不用手動 copy / paste。
Installation
npm install env-aligner
CLI Options
Option | Description | Default |
---|---|---|
--dir | Root directory to scan. | process.cwd() |
--schema | Schema file name (usually .env.example ). | .env.example |
--env | Env file name to validate or align. | .env |
--strict | Enable strict mode: warns about extra keys in .env not in the schema. | Not enabled (unless flag is present) |
--align | Enable align mode: auto-fix .env format based on schema. ⚠️ Only works with --strict . | Not enabled (unless flag is present) |
--clone | Clone schema to env file if env does not exist in the folder. | Not enabled (unless flag is present) |
Examples:
npx env-aligner
npx env-aligner --dir .devcontainer --schema config.example --env config.env
npx env-aligner --clone
npx env-aligner --strict --align
Programmatic Usage (Optional)
雖然 Env Aligner 本意上是做成主要使用 CLI,但其實還是有支援再 JavaScript 中使用。當然,我預想會這樣用的人不多。
因為 Env Aligner 開發之初希望它能支援大部分專案,不論新舊,所以它是用 CommonJS 開發的,這樣可以確保在大部分 Node.js 環境中都能運作。
如果專案上是使用 ESM (現今大部分專案都是),那建議在使用時是放在 cjs
腳本中。
但我一概推薦使用 CLI。
const envAligner = require('env-aligner')
envAligner({
rootDir: string, // Root directory to scan (default: process.cwd())
fileNames: {
schemaName: string, // Schema file name (e.g., '.env.example')
envName: string // Env file name to validate (e.g., '.env')
},
mode: {
isStrict: boolean, // Enable strict mode (extra key detection)
isAlign: boolean // Enable align mode (auto format fix)
},
isClone: boolean // Whether to generate the env file if missing
})
發布套件
其實發布套件比想像中更簡單,我是看著 PJ 老師的部落格一步一步發布的,氮這裡沒做到自動化啦,因為這玩意兒應該不會有很多更新~
然後在開始前,可以先去 npm 註冊一個帳號,然後在終端機中登入:
npm login
Step 1: 當然是寫好程式碼啦
這沒什麼好說的,就是把你的程式碼寫好,然後確保它可以正常運作。
但這裡我踩到一個坑,就是 CommonJS 跟 ES module 系統的問題。雖說 esm 是現在的趨勢,但不得不說 cjs 還是很多系統的標準,比如 node.js。
我一開始用 esm 開發,然後後來遇到 node.js 無法跑 esm 的問題,所以又花了一晚上把程式碼改回 cjs,所以開發時要想好套件目標是要在哪些系統上運作。
cjs 跟 esm 最大的差異在 cjs 使用 require
跟 module.exports
,而 esm 使用 import
跟 export
。
這式開發時最容易碰到的問題。
Step 2: 準備好 package.json
{
"name": "env-aligner", // 這裡是套件名稱
"version": "1.0.0-beta.2", // 這裡是套件版本號
"description": "A developer-friendly tool.", // 這裡可以寫套件的描述
"keywords": [ // 這裡可以指定關鍵字
".env",
".env.example",
"dotenv",
"environment variables",
"check",
"align"
],
"homepage": "https://github.com/ChungYingHo/env-aligner", // 這裡可以指定套件的首頁 (如果你有單獨做介紹的網頁不然就放 github 也行)
"bugs": { // 這裡可以指定 bug 回報的位置
"url": "https://github.com/ChungYingHo/env-aligner/issues"
},
"repository": { // 這裡可以指定套件的 git 位置
"type": "git",
"url": "https://github.com/ChungYingHo/env-aligner.git"
},
"contributors": [ // 這裡可以列出貢獻者
"Jeremy Ho <ag.cyho@gmail.com>(https://codefictionist.com/)",
"MJC <*****@gmail.com>(https://github.com/*****)"
],
"license": "MIT", // 這裡可以指定授權方式
"main": "dist/index.min.js", // 這裡是指定主要的程式碼路徑
"bin": {
"env-aligner": "dist/cli.min.js" // 這裡是指定 CLI 指令的路徑
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "rollup -c -w",
"build": "rmdir /s /q dist && rollup -c"
},
"dependencies": {
// 這個不用解釋吧
},
"devDependencies": {
// 這個也不用解釋吧
},
"files": [
"dist" // 這裡可以指定要發布的檔案路徑
],
"engines": {
"node": ">=18.0.0" // 這裡可以指定要安裝這個套件需滿足的 node.js 版本
}
}
Step 3: 套件打包 (Optional)
其實如果像是我只輸出一個 js 檔案的話,這步驟可以省略,但如果套件很大的話,通常還是會打包一下,這樣可以減少檔案大小。
套件打包通常用 rollup,我去了解了一下後,會較少用 webpack 是因為 rollup 較適合打包 library。
- 安裝 rollup
npm install rollup -D
-
安裝一些 rollup 的 plugin
- rollup-plugin-terser:壓縮 js 檔案
- @rollup/plugin-json:讀取 json 檔案
- @rollup/plugin-commonjs:讀取 commonjs 檔案
-
建立一個
rollup.config.js
檔案
const { terser } = require('rollup-plugin-terser')
const commonjs = require('@rollup/plugin-commonjs')
const json = require('@rollup/plugin-json')
module.exports = [
{
input: 'src/lib/index.js', // 引用的檔案
output: {
file: 'dist/index.min.js', // 輸出的檔案路徑與名稱
format: 'cjs', // 輸出的格式
sourcemap: true, // 是否產生 sourcemap
exports: 'default' // 輸出的方式,這個跟 cjs 以及 esm 有關
},
plugins: [ // 使用的 plugin
terser(),
commonjs()
],
external: ['dotenv', 'chalk', 'fs', 'path'] // 這裡是指定外部套件,這樣 rollup 就不會把這些套件打包進去
},
{ // 以下同理,這只是打包 CLI 指令的部分
input: 'src/bin/cli.js',
output: {
file: 'dist/cli.min.js',
format: 'cjs',
sourcemap: true,
exports: 'default',
banner: '#!/usr/bin/env node'
},
plugins: [
terser(),
commonjs(),
json()
],
external: ['dotenv', 'chalk', 'fs', 'path', 'commander']
}
]
- 最後執行打包指令:
npm run build
其實 rollup 對 esm 稍微友善一些,但是對 cjs 也是可以的,只是要注意一些額外設定,如 exports
。
但其實如果真的有錯誤之類的,rollup 還是很貼心地會提示你該怎麼改的。
Step 4: 發布套件
發布套件有分 beta 或正式版,指令也會略有不同:
- 發布 beta 版
npm publish --tag beta
- 發布正式版
npm publish
假設已經發布了兩個 beta 版,這時會發現怎麼第一個 beta 版標籤變 latest 了?
這是因為 npm 規定有複數版本時,至少要有一個 latest 版本。
移除 npm 上已發布的套件版本
這用在有時為了測試套件穩定性會優先發布 beta 版本,但為了 debug,beta 版可能會源源不絕地增生,這時就需要適時移除 npm 上那些已發布的版本。
npm unpublish env-aligner@1.0.0-beta.1 --force
幫自己的 commit 上 tags
這項不是必做,但如果套件是打算持續更新的,那難免會需要寫 Changelog。
一般寫 Changelog 都會放個 github 上兩個版本之間的 diff,那麼就需要在 commit 上加上 tags,這樣才能在 github 上看到兩個版本之間的差異。
git tag -a v1.0.0 <commit-hash> -m "Release v1.0.0"
git push origin v1.0.0
如果需要 tag 的就是剛剛最新的 commit ,可以直接寫:
git tag -a v1.0.0 -m "Release v1.0.0"
之後有 v2.0.0 時,同樣上完 tag 就可以依下面方式看版本間的 diff:
https://github.com/你的帳號/你的專案/compare/v1.0.0...v2.0.0