UNPKG

wxt

Version:

⚡ Next-gen Web Extension Framework

164 lines (163 loc) 5.88 kB
import definition from "virtual:user-background-entrypoint"; import { initPlugins } from "virtual:wxt-plugins"; import { browser } from "wxt/browser"; import { MatchPattern } from "wxt/utils/match-patterns"; //#region src/utils/internal/logger.ts function print(method, ...args) { if (import.meta.env.MODE === "production") return; if (typeof args[0] === "string") method(`[wxt] ${args.shift()}`, ...args); else method("[wxt]", ...args); } /** Wrapper around `console` with a "[wxt]" prefix */ const logger = { debug: (...args) => print(console.debug, ...args), log: (...args) => print(console.log, ...args), warn: (...args) => print(console.warn, ...args), error: (...args) => print(console.error, ...args) }; //#endregion //#region src/utils/internal/dev-server-websocket.ts let ws; /** Connect to the websocket and listen for messages. */ function getDevServerWebSocket() { if (import.meta.env.COMMAND !== "serve") throw Error("Must be running WXT dev command to connect to call getDevServerWebSocket()"); if (ws == null) { const serverUrl = __DEV_SERVER_ORIGIN__; logger.debug("Connecting to dev server @", serverUrl); ws = new WebSocket(serverUrl, "vite-hmr"); ws.addWxtEventListener = ws.addEventListener.bind(ws); ws.sendCustom = (event, payload) => ws?.send(JSON.stringify({ type: "custom", event, payload })); ws.addEventListener("open", () => { logger.debug("Connected to dev server"); }); ws.addEventListener("close", () => { logger.debug("Disconnected from dev server"); }); ws.addEventListener("error", (event) => { logger.error("Failed to connect to dev server", event); }); ws.addEventListener("message", (e) => { try { const message = JSON.parse(e.data); if (message.type === "custom") ws?.dispatchEvent(new CustomEvent(message.event, { detail: message.data })); } catch (err) { logger.error("Failed to handle message", err); } }); } return ws; } //#endregion //#region src/virtual/utils/keep-service-worker-alive.ts /** https://developer.chrome.com/blog/longer-esw-lifetimes/ */ function keepServiceWorkerAlive() { setInterval(async () => { await browser.runtime.getPlatformInfo(); }, 5e3); } //#endregion //#region src/virtual/utils/reload-content-scripts.ts function reloadContentScript(payload) { if (browser.runtime.getManifest().manifest_version == 2) reloadContentScriptMv2(payload); else reloadContentScriptMv3(payload); } async function reloadContentScriptMv3({ registration, contentScript }) { if (registration === "runtime") await reloadRuntimeContentScriptMv3(contentScript); else await reloadManifestContentScriptMv3(contentScript); } async function reloadManifestContentScriptMv3(contentScript) { const id = `wxt:${contentScript.js[0]}`; logger.log("Reloading content script:", contentScript); const registered = await browser.scripting.getRegisteredContentScripts(); logger.debug("Existing scripts:", registered); const existing = registered.find((cs) => cs.id === id); if (existing) { logger.debug("Updating content script", existing); await browser.scripting.updateContentScripts([{ ...contentScript, id, css: contentScript.css ?? [] }]); } else { logger.debug("Registering new content script..."); await browser.scripting.registerContentScripts([{ ...contentScript, id, css: contentScript.css ?? [] }]); } await reloadTabsForContentScript(contentScript); } async function reloadRuntimeContentScriptMv3(contentScript) { logger.log("Reloading content script:", contentScript); const registered = await browser.scripting.getRegisteredContentScripts(); logger.debug("Existing scripts:", registered); const matches = registered.filter((cs) => { const hasJs = contentScript.js?.find((js) => cs.js?.includes(js)); const hasCss = contentScript.css?.find((css) => cs.css?.includes(css)); return hasJs || hasCss; }); if (matches.length === 0) { logger.log("Content script is not registered yet, nothing to reload", contentScript); return; } await browser.scripting.updateContentScripts(matches); await reloadTabsForContentScript(contentScript); } async function reloadTabsForContentScript(contentScript) { const allTabs = await browser.tabs.query({}); const matchPatterns = contentScript.matches.map((match) => new MatchPattern(match)); const matchingTabs = allTabs.filter((tab) => { const url = tab.url; if (!url) return false; return !!matchPatterns.find((pattern) => pattern.includes(url)); }); await Promise.all(matchingTabs.map(async (tab) => { try { await browser.tabs.reload(tab.id); } catch (err) { logger.warn("Failed to reload tab:", err); } })); } async function reloadContentScriptMv2(_payload) { throw Error("TODO: reloadContentScriptMv2"); } //#endregion //#region src/virtual/background-entrypoint.ts if (import.meta.env.COMMAND === "serve") { try { const ws = getDevServerWebSocket(); ws.addWxtEventListener("wxt:reload-extension", () => { browser.runtime.reload(); }); ws.addWxtEventListener("wxt:reload-content-script", (event) => { reloadContentScript(event.detail); }); if (import.meta.env.MANIFEST_VERSION === 3) { ws.addEventListener("open", () => ws.sendCustom("wxt:background-initialized")); keepServiceWorkerAlive(); } } catch (err) { logger.error("Failed to setup web socket connection with dev server", err); } browser.commands.onCommand.addListener((command) => { if (command === "wxt:reload-extension") browser.runtime.reload(); }); } let result; try { initPlugins(); result = definition.main(); if (result instanceof Promise) console.warn("The background's main() function return a promise, but it must be synchronous"); } catch (err) { logger.error("The background crashed on startup!"); throw err; } var background_entrypoint_default = result; //#endregion export { background_entrypoint_default as default };