vite build on a CRXJS extension project and got:
What the error means
@crxjs/vite-plugin builds your extension in two passes. During generateBundle, the crx:manifest-post step walks every content_scripts entry in your manifest and asks the bundler for the output filename of the chunk that was emitted for each source file. When the bundler returns no chunk for that source path, the lookup comes back undefined and the plugin throws.
So the error is not “your file does not exist.” It means the plugin and the bundler disagree about how the emitted chunk for your content script is named or keyed.
Why Vite 8 triggers it
Vite 8 replaced Rollup with Rolldown as the default bundler. CRXJS was written against Rollup’s chunk emission model, and its content script resolution depends on internals that behave differently under Rolldown. The same error existed before in specific setups (it is tracked in crxjs/chrome-extension-tools#883), but the Vite 8 default made it the common case rather than the edge case.Workarounds that keep CRXJS running
Try these in order:-
Pin Vite 7. Vite 7 still uses Rollup, which CRXJS understands:
This unblocks the build today at the cost of staying behind on Vite.
-
Update
@crxjs/vite-pluginto the latest release. The project has been adding Rolldown compatibility incrementally. Check the releases page and the issue above for the current support status before pinning anything. -
Check how the manifest references the script. The lookup is keyed by path. Make the
content_scripts.jsentry match the path style of your project root (relative, no leading slash), and confirm the same file is not also imported dynamically elsewhere with a?scriptsuffix.
The structural fix
The root issue is architectural. CRXJS rebuilds extension semantics (manifest wiring, content script emission, HMR) on top of a general-purpose bundler’s plugin API, so every major bundler change can break it. Extension.js is a browser extension framework where the manifest is the source of truth and the bundler is an internal detail you never wire up:- Your
manifest.jsondeclarescontent/index.tsdirectly. No plugin resolves chunk names; the framework compiles whatever the manifest points to. - No
vite.config.ts, nomanifest.config.ts, no plugin-version matrix to keep aligned. - Chrome, Firefox, and Edge outputs from the same source, with reload behavior per surface.
chrome.* calls carry over unchanged. The full walkthrough takes about ten minutes: Migrate from CRXJS to Extension.js.

