import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { viteStaticCopy } from "vite-plugin-static-copy";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
// One build pass per entry (LTT_ENTRY), driven by scripts/build.mjs:
// Vite/Rollup cannot emit IIFE for a code-splitting (multi-entry) build, and
// content scripts / MV3 service workers must be single self-contained files.
const entryNames = ["background", "content", "options"] as const;
type EntryName = (typeof entryNames)[number];
const entryPaths: Record<EntryName, string> = {
background: resolve(__dirname, "src/background/index.ts"),
content: resolve(__dirname, "src/content/index.ts"),
options: resolve(__dirname, "src/options/options.html"),
};
// Build targets: --mode chrome | --mode firefox
export default defineConfig(({ mode }) => {
const isFirefox = mode === "firefox";
const outDir = resolve(__dirname, "dist", isFirefox ? "firefox" : "chrome");
const only = (process.env.LTT_ENTRY ?? "") as EntryName;
const entry: EntryName | null = entryNames.includes(only) ? only : null;
const input = entry
? { [entry]: entryPaths[entry] }
: Object.fromEntries(entryNames.map((name) => [name, entryPaths[name]]));
return {
plugins: [
vue(),
viteStaticCopy({
targets: [
// kit.css references /assets/fonts/... absolutely; the overlay rewrites
// those to runtime.getURL('') + '/assets/...' at runtime
{ src: "../../node_modules/gnexus-ui-kit/dist/assets/*", dest: "assets" },
],
}),
],
build: {
outDir,
// empty once per target, on the first pass
emptyOutDir: entry === "background",
target: "esnext",
// keep all CSS in one extracted file (assets/content.css): IIFE builds
// otherwise inline CSS into a <style> appended to the host page's head,
// which leaks styles there and trips strict style-src CSP
cssCodeSplit: false,
sourcemap: mode.endsWith("-dev"),
rollupOptions: {
input,
output: {
// content scripts and MV3 backgrounds must be single self-contained
// files (no ES module imports at runtime)
format: "iife",
inlineDynamicImports: Boolean(entry),
entryFileNames: (chunk) =>
chunk.name === "background" || chunk.name === "content" ? `${chunk.name}.js` : "assets/[name].js",
chunkFileNames: "assets/[name].js",
assetFileNames: () => {
// cssCodeSplit:false funnels each pass's CSS into one file; give the
// options page its own name so the content pass (which runs last and
// owns assets/style.css for the overlay) cannot overwrite it
return only === "options" ? "assets/options.css" : "assets/style.css";
},
},
},
},
};
});