昨天的延伸設計有提到可以建立一個 訊息路由器 (message router),集中管理不同 action。
這個做法有點類似前端框架中 store 的感覺,以下的方法如果專案成長得更大時,可以試試看~
// messageHandler.js
/**
 * 註冊訊息處理器
 * @param {Object} handlers - key 為 action,value 為處理函式
 */
export function registerMessageHandlers(handlers) {
  chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    const { action, data } = message;
    if (handlers[action]) {
      // 支援同步或非同步處理
      const result = handlers[action](data, sender);
      if (result instanceof Promise) {
        result.then(sendResponse).catch((err) => {
          console.error("Message Handler Error:", err);
          sendResponse({ error: err.message });
        });
        return true; // 啟用 async sendResponse
      } else {
        sendResponse(result);
      }
    }
  });
}
/**
 * 傳送訊息給 background
 */
export function sendToBackground(action, data) {
  return new Promise((resolve, reject) => {
    chrome.runtime.sendMessage({ action, data }, (response) => {
      if (chrome.runtime.lastError) {
        reject(chrome.runtime.lastError);
      } else {
        resolve(response);
      }
    });
  });
}
/**
 * 傳送訊息給指定 tab (通常用於 content_script)
 */
export function sendToTab(tabId, action, data) {
  return new Promise((resolve, reject) => {
    chrome.tabs.sendMessage(tabId, { action, data }, (response) => {
      if (chrome.runtime.lastError) {
        reject(chrome.runtime.lastError);
      } else {
        resolve(response);
      }
    });
  });
}
// background.js
import { registerMessageHandlers, sendToTab } from "./messageHandler.js";
registerMessageHandlers({
  FETCH_KEY: async (_, sender) => {
    const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
    return sendToTab(tab.id, "GET_PROJECT_KEY");
  },
});
// content_script.js
import { registerMessageHandlers } from "./messageHandler.js";
registerMessageHandlers({
  GET_PROJECT_KEY: () => {
    const url = window.location.href;
    const match = url.match(/\/([^/]+)\/-\/merge_requests/);
    return { projectKey: match ? match[1] : "未知專案" };
  },
});
// popup.html
import { sendToBackground } from "./messageHandler.js";
document.getElementById("getKeyBtn").addEventListener("click", async () => {
  try {
    const resp = await sendToBackground("FETCH_KEY");
    document.getElementById("output").innerText = resp.projectKey;
  } catch (err) {
    document.getElementById("output").innerText = "錯誤:" + err.message;
  }
});