2026年09月28日,EmDash 的 1.0 正式發布。
EmDash 是 Cloudflare 開發的基於 Astro 的開源 CMS,管理界面直接內嵌在 Astro 網站中,由 Cloudflare Workers + D1 + R2 驅動。
可以說是 WordPress 的 Astro 版本。
之前的文章中從 microCMS 遷移過來的公司內部部落格「ゆるろぐ」一直停留在 6 月發布的 0.22 版本,所以趁著這次機會一口氣升級到最新的 1.2.0。
1.0 的亮點是穩定性和升級安全性、在沙盒中運行的插件,以及針對代理的 MCP 伺服器。
說結論的話,遷移在一天內完成了,但陷阱真不少。
如果抱著「1.0 已經穩定了吧」的輕鬆心態開始,你會在 EmDash 本體之外的周邊依賴關係上卡殼。
前提:0.22 → 1.2 只能手動升級
1.2 內包含了名叫 upgrade-emdash 的官方升級 CLI。
它會更新依賴關係並生成特定於網站的 UPGRADE.md,但只支援 0.35 之後的版本,所以從 0.22 無法使用。
0.22 到 1.2 之間有 47 個數據庫遷移,其中還有幾個破壞性的變更。
「先試試 npm install emdash@latest」這種方法根本行不通。
1. 備份後,將生產數據庫複製到本地
npx wrangler d1 export <database-name> --remote --output=backups/pre-1.2.sql
將其導入本地 D1,用與生產相同的數據進行遷移測試。
2. 更新依賴項目
套件 | 遷移前 | 遷移後 |
|---|---|---|
emdash / @emdash-cms/cloudflare / @emdash-cms/admin | 0.22.0 | 1.2.0 |
@emdash-cms/plugin-forms | 0.1.1 | 0.2.9 |
astro | 6.1.2 | 6.4.8 |
wrangler | 4.80 | 4.148 |
wrangler 必須滿足 emdash 1.2 的 peer 需求(4.99 以上),因此在這個時機點移除了 0.22 時代的應急處置。@astrojs/cloudflare滿足 peer 需求,所以在此時仍維持 13.1.6 版本。這成為了後續問題的根源。
- OAuth 路由的 patch-package:0.22 時代為迴避 bug 所套用,本體已修復
- 管理畫面的 CSS 注入:0.22 的 admin 針對 UI 函式庫 kumo 2.4 的 CSS,但 lockfile 卻引入了 2.6,導致登入和發布的強調按鈕呈現「透明背景加白字」而無法看見。1.2 版本已在本體修復
3. dev 無法啟動(陷阱:miniflare 版本不相符)
依賴更新後執行 npm run dev 無法啟動的原因是 miniflare 版本不相符。
wrangler 4.148 使用 miniflare 5,會將本機 D1 狀態(.wrangler/state/v3/下方)的 _cf_ALARM 表格更新為 3 欄。
另一方面@astrojs/cloudflare 13.1.6 內附的 miniflare 是版本 4,欄數不相符導致無法讀取本機狀態。
解決方式是同時升級據置不變的 @astrojs/cloudflare,讓 miniflare 的版本與 wrangler 保持一致。只要記住「wrangler 與適配器的 miniflare 版本要相符」,下次就能一次解決。
在 0.22 版本中,emdash dev 命令已被廢止,所以 dev 腳本改回 astro dev。遷移和型別生成會在 astro dev 啟動時執行。
4. 暫存環境的選單消失(陷阱:seed.json 中的 menus)
dev 啟動後 90 個遷移成功並推送到暫存環境,打開管理介面後發現 日文全域導覽為空。
原因是 seed/seed.json 中的 menus 和 widgetAreas。
EmDash 的種子處理若發現同名的選單或小工具區域,會 先刪除所有項目再以空狀態重新建立。
seed.json 中作為樣板保留了 "items": [] 的選單定義,因此每次執行種子時選單都被初始化。
解決方法是 menus 和 widgetAreas 整個刪除。
選單內容應在管理介面中管理,沒有理由保存在種子中。
5. 英文文章的連結變成 /en/posts/en/slug(陷阱:entry.id 的語言前綴)
這是 1.2 版本的規格變更。
在多語言網站中,getEmDashCollection() 回傳的 entry.id 現在會帶有語言前綴。英文文章會是 en/my-post。
由於 yuru.log 是從 entry.id 組建文章 URL,英文文章的連結變成了 /en/posts/en/my-post 雙重路徑。
已將清單、精選、搜尋結果、RSS 和 canonical 中所有 URL 組建的地方都改成 entry.data.slug。
由於 entry.data.slug 的型別是 string | null,接收端的型別也需要相應調整。
entry.id 是鍵值、entry.data.slug 是 URL、entry.data.id 是資料庫的 ULID(用於 API 呼叫),記住各自的角色會很有幫助。
6. 每分鐘出現錯誤(陷阱:Cron Trigger 和 Worker 進入點)
在 1.x 中,預約發佈和外掛定期處理由 Worker 的 scheduled() 處理程式驅動。
由於 Cloudflare 沒有內部計時器,如果沒有 Cron Trigger,預約發佈即使在公開時刻也不會發佈。
因此wrangler.jsonc新增了 Cron Trigger。
"triggers": { "crons": ["* * * * *"] }接著每分鐘會出現「Handler does not export a scheduled() function」的錯誤。
這是因為 Worker 入口點沒有 export scheduled()。
解決方法是將 src/worker.ts 替換為官方入點。
export { default, PluginBridge } from "@emdash-cms/cloudflare/worker";如此一來fetch和scheduled之外,還有適用於沙箱型外掛的PluginBridge。
實際嘗試使用後
升級到 1.2 的優點。
- 編輯器煥然一新,更易使用。斜線選單已分組,工具列會跟隨游標。也可在行動裝置上使用。
- 影片區塊已內置。之前用自訂外掛進行影片嵌入,打算之後逐步替換。
- 0.22 時代的臨時措施全數移除。patch-package 和 CSS 注入都不再需要。
值得注意的地方。
- 從 1.0 到 1.2 只花了 10 天,更新速度很快。今後似乎需要採取按次要版本號在預備環境確認的運營方式。
總結
- 0.22 → 1.2 是手動遷移,
upgrade-emdashCLI 僅支援 0.35 以後的版本。 - wrangler 和適配器的 miniflare 世代需要一致→不一致的話 dev 會無法啟動
- seed.json 中不要留下
menus/widgetAreas的空白模板→每次執行 seed 時都會消失 - 多語言網站的
entry.id會附加語言地區前綴→URL 應該從entry.data.slug產生 - 若要新增 Cron Trigger,Worker 進入點應設定為
@emdash-cms/cloudflare/worker
這些問題雖然一旦理解後只需要修改幾行代碼,但逐一檢查時往往比預期花費更多時間。
不過,0.22 版本時套用的補丁和 CSS 應急處理都能完全移除,這證明了朝向正式版發布時細微的錯誤已被逐漸修正。
半年前還是「試驗性地換上」的態度,但經過 1.0 版本後,只要考慮到跟進更新的工作量,EmDash 已經成為實際運用中充分值得檢討的 CMS 選項了。
從 DTP 跨足 Web 世界,轉眼間便掌握了標記語言、frontend 開發、專案指導,以及 accessibility 等各項技能——是名真正的「技術高人」。自 Liberogic 創立初期便展現多才多藝的能力,如今儼然成為公司內的活字典。最近正沉迷於利用 AI 提示詞探索「accessibility 對應能否更多依靠 AI?」的效率化研究。技術與思維都在不斷進化中。
二俣 歩
IAAP 認證網頁無障礙專家(WAS)/ 標記語言工程師 / Frontend 工程師 / 網頁總監