@@ -628,6 +628,10 @@ export default defineConfig({
628628// module are resolved by the Deno plugin and emitted unhashed. The browser
629629// keys modules on the url it fetched, so one file under two urls becomes two
630630// instances. This is a resolve-time problem, not a load-time one.
631+ //
632+ // `@preact/signals` is deliberately left out of `include` here: pre-bundling
633+ // it would serve it from Vite's cache instead, so the clash this covers could
634+ // no longer happen. The test below pre-bundles it on purpose.
631635integrationTest (
632636 "vite dev - optimizeDeps does not duplicate dependency instances" ,
633637 async ( ) => {
@@ -647,7 +651,6 @@ export default defineConfig({
647651 await withBrowser ( async ( page ) => {
648652 await page . goto ( address , { waitUntil : "networkidle2" } ) ;
649653
650- // A path fetched under more than one query string is loaded twice.
651654 const urls : string [ ] = await page . evaluate ( ( ) =>
652655 performance . getEntriesByType ( "resource" ) . map ( ( entry ) => entry . name )
653656 ) ;
@@ -665,6 +668,7 @@ export default defineConfig({
665668 queries . add ( parsed . search ) ;
666669 }
667670
671+ // A path fetched under more than one query string is loaded twice.
668672 const duplicated = Array . from ( queriesByPath )
669673 . filter ( ( [ , queries ] ) => queries . size > 1 )
670674 . map ( ( [ pathname ] ) => pathname ) ;
@@ -681,6 +685,55 @@ export default defineConfig({
681685 } ,
682686) ;
683687
688+ // A pre-bundled dependency is served from Vite's own cache, so anything that
689+ // still resolves to the package on disk loads it alongside the bundle. Deno
690+ // rewrites imports inside jsr modules to `npm:` specifiers, which do not match
691+ // the optimizer's keys, so the lookup has to fall back to the pre-bundled
692+ // source to recognise them.
693+ integrationTest (
694+ "vite dev - optimizeDeps serves pre-bundled dependencies once" ,
695+ async ( ) => {
696+ const fixture = path . join ( FIXTURE_DIR , "remote_island" ) ;
697+ await using tmp = await prepareDevServer ( fixture , {
698+ config : `import { defineConfig } from "vite";
699+ import { fresh } from "@fresh/plugin-vite";
700+
701+ export default defineConfig({
702+ plugins: [fresh({ islandSpecifiers: ["@marvinh-test/fresh-island"] })],
703+ optimizeDeps: { include: ["preact", "@preact/signals"] },
704+ });
705+ ` ,
706+ } ) ;
707+
708+ await launchDevServer ( tmp . dir , async ( address ) => {
709+ await withBrowser ( async ( page ) => {
710+ await page . goto ( address , { waitUntil : "networkidle2" } ) ;
711+
712+ const paths : string [ ] = await page . evaluate ( ( ) =>
713+ performance . getEntriesByType ( "resource" ) . map ( ( entry ) =>
714+ new URL ( entry . name ) . pathname
715+ )
716+ ) ;
717+
718+ // Sanity check that the optimizer actually ran.
719+ expect ( paths . some ( ( p ) => p . includes ( "/.vite/deps/" ) ) ) . toEqual ( true ) ;
720+
721+ // The remote island imports both as `npm:` specifiers. They are
722+ // pre-bundled, so neither may also be fetched from node_modules.
723+ const rawCopies = paths . filter ( ( p ) =>
724+ / n o d e _ m o d u l e s \/ .* \/ ( p r e a c t \/ d i s t \/ | @ p r e a c t \/ s i g n a l s \/ d i s t \/ ) / . test ( p )
725+ ) ;
726+
727+ expect ( rawCopies ) . toEqual ( [ ] ) ;
728+
729+ await page . locator ( ".remote-island" ) . wait ( ) ;
730+ await page . locator ( ".increment" ) . click ( ) ;
731+ await waitForText ( page , ".result" , "Count: 1" ) ;
732+ } ) ;
733+ } ) ;
734+ } ,
735+ ) ;
736+
684737// issue: https://github.com/denoland/fresh/issues/3666
685738integrationTest (
686739 "vite dev - basePath does not intercept Vite URLs" ,
0 commit comments