Fix crxjs dev service worker crashes after Vite upgrade.

Downgrade to Vite 6 with crxjs 2.6, add dev-only CSP for HMR, and stop the background script from importing Svelte plugin UI into the service worker.
This commit is contained in:
SethBurkart123
2026-06-16 12:45:56 +10:00
parent 5c195f1148
commit feaf4dced5
5 changed files with 1129 additions and 202 deletions
+1041 -178
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -42,7 +42,7 @@
"@babel/plugin-transform-runtime": "^7.26.9", "@babel/plugin-transform-runtime": "^7.26.9",
"@babel/runtime": "^7.26.9", "@babel/runtime": "^7.26.9",
"@bedframe/cli": "^0.1.2", "@bedframe/cli": "^0.1.2",
"@crxjs/vite-plugin": "^2.4.0", "@crxjs/vite-plugin": "^2.6.1",
"@types/d3-scale": "^4.0.9", "@types/d3-scale": "^4.0.9",
"@types/d3-shape": "^3.1.8", "@types/d3-shape": "^3.1.8",
"@types/jest": "^30.0.0", "@types/jest": "^30.0.0",
@@ -77,7 +77,7 @@
"@codemirror/search": "^6.5.10", "@codemirror/search": "^6.5.10",
"@codemirror/state": "^6.5.2", "@codemirror/state": "^6.5.2",
"@codemirror/view": "^6.36.4", "@codemirror/view": "^6.36.4",
"@sveltejs/vite-plugin-svelte": "^7.0.0", "@sveltejs/vite-plugin-svelte": "^5.0.3",
"@tailwindcss/forms": "^0.5.10", "@tailwindcss/forms": "^0.5.10",
"@tsconfig/svelte": "^5.0.4", "@tsconfig/svelte": "^5.0.4",
"@types/chrome": "^0.1.4", "@types/chrome": "^0.1.4",
@@ -122,7 +122,7 @@
"svelte": "^5.46.4", "svelte": "^5.46.4",
"typescript": "^5.8.2", "typescript": "^5.8.2",
"uuid": "^11.1.0", "uuid": "^11.1.0",
"vite": "^8.0.5", "vite": "^6.2.1",
"webextension-polyfill": "^0.12.0" "webextension-polyfill": "^0.12.0"
} }
} }
+41
View File
@@ -0,0 +1,41 @@
/**
* Serializable plugin setting defaults for cloud sync backfill.
*
* Kept separate from `@/plugins` so the service worker never imports Svelte UI or
* Vite HMR clients. Values must match each plugin's non-component settings.
*/
function defaultSearchHotkey(): string {
if (typeof navigator !== "undefined") {
return navigator.platform.toUpperCase().includes("MAC") ? "cmd+k" : "ctrl+k";
}
return "ctrl+k";
}
/** `plugin.<id>.settings` defaults (component/button keys omitted). */
export const SYNCABLE_PLUGIN_SETTING_DEFAULTS: Record<
string,
Record<string, unknown>
> = {
themes: {},
"animated-background": { speed: 1 },
"assessments-average": { lettergrade: false },
notificationCollector: {},
timetable: {},
timetableEdit: {},
"profile-picture": { useCloudPfp: false },
"assessments-overview": {},
"background-music": { volume: 0.5, pauseOnHidden: true },
messageFolders: {
showTagsInAllMessages: true,
hideFolderedMessagesInAll: true,
},
"enhanced-navigation": { autoScrollOnClick: false },
"global-search": {
searchHotkey: defaultSearchHotkey(),
showRecentFirst: true,
transparencyEffects: true,
runIndexingOnLoad: true,
passiveIndexing: true,
},
"grade-analytics": { cacheTtlHours: 24 },
};
@@ -1,5 +1,5 @@
import browser from "webextension-polyfill"; import browser from "webextension-polyfill";
import { getAllPluginSettings } from "@/plugins"; import { SYNCABLE_PLUGIN_SETTING_DEFAULTS } from "@/plugins/syncablePluginDefaults";
import { getDefaultSettingsState } from "@/seqta/utils/defaultSettings"; import { getDefaultSettingsState } from "@/seqta/utils/defaultSettings";
import { import {
isKeyIncludedInCloudUploadPayload, isKeyIncludedInCloudUploadPayload,
@@ -38,18 +38,6 @@ const OPTIONAL_UNSET_MEANS_DEFAULT_KEYS = [
"profile_picture_revision", "profile_picture_revision",
] as const; ] as const;
function buildDefaultPluginSettings(
plugin: ReturnType<typeof getAllPluginSettings>[number],
): Record<string, unknown> {
const out: Record<string, unknown> = {};
for (const [key, setting] of Object.entries(plugin.settings)) {
const meta = setting as { type?: string; default?: unknown };
if (meta.type === "component" || meta.type === "button") continue;
out[key] = meta.default;
}
return out;
}
/** /**
* Flat default map in upload shape (plugin-format only; no legacy keys). * Flat default map in upload shape (plugin-format only; no legacy keys).
*/ */
@@ -65,9 +53,10 @@ export function getSyncableStorageDefaults(): Record<string, unknown> {
delete flat[key]; delete flat[key];
} }
for (const plugin of getAllPluginSettings()) { for (const [pluginId, settings] of Object.entries(
flat[`plugin.${plugin.pluginId}.settings`] = SYNCABLE_PLUGIN_SETTING_DEFAULTS,
buildDefaultPluginSettings(plugin); )) {
flat[`plugin.${pluginId}.settings`] = settings;
} }
return flat; return flat;
+38 -4
View File
@@ -4,7 +4,7 @@ import { join, resolve } from "path";
import touchGlobalCSSPlugin from "./lib/touchGlobalCSS"; import touchGlobalCSSPlugin from "./lib/touchGlobalCSS";
import InlineWorkerPlugin from "./lib/inlineWorker"; import InlineWorkerPlugin from "./lib/inlineWorker";
import { base64Loader } from "./lib/base64loader"; import { base64Loader } from "./lib/base64loader";
import type { BuildTarget } from "./lib/types"; import type { BuildTarget, Manifest } from "./lib/types";
import ClosePlugin from "./lib/closePlugin"; import ClosePlugin from "./lib/closePlugin";
import { firefoxStripFunctionProbe } from "./lib/firefoxStripFunctionProbe"; import { firefoxStripFunctionProbe } from "./lib/firefoxStripFunctionProbe";
@@ -22,6 +22,37 @@ import { crx } from "@crxjs/vite-plugin";
const targets: BuildTarget[] = [chrome, brave, edge, firefox, opera, safari]; const targets: BuildTarget[] = [chrome, brave, edge, firefox, opera, safari];
const DEV_SERVER_PORT = 5173;
/** Vite HMR needs localhost script + ws origins; only applied during `vite dev`. */
function withDevManifestCsp(manifest: Manifest, command: string): Manifest {
if (command !== "serve") return manifest;
const extensionPages = manifest.content_security_policy?.extension_pages;
if (!extensionPages) return manifest;
const localhost = `http://localhost:${DEV_SERVER_PORT}`;
const localhostWs = `ws://localhost:${DEV_SERVER_PORT}`;
const loopback = `http://127.0.0.1:${DEV_SERVER_PORT}`;
const loopbackWs = `ws://127.0.0.1:${DEV_SERVER_PORT}`;
return {
...manifest,
content_security_policy: {
...manifest.content_security_policy,
extension_pages: extensionPages
.replace(
"script-src 'self'",
`script-src 'self' ${localhost} ${loopback}`,
)
.replace(
/connect-src ([^;]+)/,
`connect-src $1 ${localhost} ${localhostWs} ${loopback} ${loopbackWs}`,
),
},
};
}
const mode = process.env.MODE || "chrome"; // Check the environment variable to determine which build type to use. const mode = process.env.MODE || "chrome"; // Check the environment variable to determine which build type to use.
//const sourcemap = (process.env.SOURCEMAP === "true") || false; // Check whether we want sourcemaps. //const sourcemap = (process.env.SOURCEMAP === "true") || false; // Check whether we want sourcemaps.
/** Million's compiler can emit `new Function()`, which Firefox extension pages block (strict CSP, no unsafe-eval). */ /** Million's compiler can emit `new Function()`, which Firefox extension pages block (strict CSP, no unsafe-eval). */
@@ -46,9 +77,11 @@ export default defineConfig(({ command }) => ({
}), }),
...(useMillion ? [million.vite({ auto: true })] : []), ...(useMillion ? [million.vite({ auto: true })] : []),
crx({ crx({
manifest: manifest: withDevManifestCsp(
targets.find((t) => t.browser === mode.toLowerCase())?.manifest ?? targets.find((t) => t.browser === mode.toLowerCase())?.manifest ??
chrome.manifest, chrome.manifest,
command,
),
browser: mode.toLowerCase() === "firefox" ? "firefox" : "chrome", browser: mode.toLowerCase() === "firefox" ? "firefox" : "chrome",
}), }),
touchGlobalCSSPlugin(), touchGlobalCSSPlugin(),
@@ -61,11 +94,12 @@ export default defineConfig(({ command }) => ({
}, },
}, },
server: { server: {
port: 5173, port: DEV_SERVER_PORT,
strictPort: true,
hmr: { hmr: {
host: "localhost", host: "localhost",
protocol: "ws", protocol: "ws",
port: 5173, port: DEV_SERVER_PORT,
}, },
}, },
css: { css: {