Skip to content

Commit f2aecd4

Browse files
authored
Merge pull request #271 from brenelz/fix/tree-shaken-lazy-facade-entries
fix: reclassify lazy facades without importers
2 parents bd2ed3f + 1914c40 commit f2aecd4

6 files changed

Lines changed: 70 additions & 6 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'vite-plugin-solid': patch
3+
---
4+
5+
Reclassify emitted lazy facade chunks even when their importers are eliminated from the final bundle. Emitted chunk references are now retained so lazy facades can be identified without relying on a surviving `dynamicImports` edge.

examples/vite-8/src/App.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { onSettled } from "solid-js";
22
import { CounterProvider, useCounter } from "./CounterContext";
3-
4-
const title = 'Counter';
3+
import { title } from './UnusedLazyImporter';
54

65
function Count() {
76
const counter = useCounter();

examples/vite-8/src/UnusedLazy.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function UnusedLazy() {
2+
return <p>This lazy component should be removed from the application.</p>;
3+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { lazy } from 'solid-js';
2+
3+
export const title = 'Counter';
4+
5+
export class UnusedLazyImporter {
6+
mount() {
7+
return lazy(() => import('./UnusedLazy'));
8+
}
9+
}

examples/vite-8/vite.config.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,33 @@ import solidPlugin from 'vite-plugin-solid';
33

44
export default defineConfig({
55
plugins: [
6-
solidPlugin({ compiler: 'native' }),
6+
{
7+
name: 'simulate-eliminated-lazy-importer',
8+
enforce: 'pre',
9+
generateBundle(_options, bundle) {
10+
for (const output of Object.values(bundle)) {
11+
if (output.type === 'chunk') {
12+
output.dynamicImports = [];
13+
}
14+
}
15+
},
16+
},
17+
solidPlugin({ compiler: 'native', ssr: true }),
18+
{
19+
name: 'assert-single-entry',
20+
enforce: 'post',
21+
generateBundle(_options, bundle) {
22+
const entries = Object.values(bundle).filter(
23+
(output) => output.type === 'chunk' && output.isEntry,
24+
);
25+
if (entries.length !== 1) {
26+
throw new Error(
27+
`Expected one entry chunk, received: ${entries
28+
.map((entry) => entry.fileName)
29+
.join(', ')}`,
30+
);
31+
}
32+
},
33+
},
734
],
8-
});
35+
});

src/index.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,10 @@ export default function solidPlugin(options: Partial<Options> = {}): Plugin[] {
312312
// Driven from moduleParsed so it covers every lazy() target, including
313313
// import.meta.glob entries that never pass through the moduleUrl transform.
314314
const emittedLazyChunks = new Set<string>();
315+
// Keep the emitted references because a lazy module's importer may be
316+
// removed from the final bundle, leaving no dynamic-import edge to identify
317+
// its facade chunk during generateBundle.
318+
const emittedLazyChunkRefs: string[] = [];
315319

316320
// Whether the current hook invocation belongs to a client (browser) build.
317321
// Builder-mode builds (e.g. SolidStart's nitro plugin) run the client and
@@ -559,7 +563,9 @@ export default function solidPlugin(options: Partial<Options> = {}): Plugin[] {
559563
if (!/\.[mc]?[tj]sx?$/i.test(cleanId)) continue;
560564
if (emittedLazyChunks.has(depId)) continue;
561565
emittedLazyChunks.add(depId);
562-
this.emitFile({ type: 'chunk', id: depId, preserveSignature: 'exports-only' });
566+
emittedLazyChunkRefs.push(
567+
this.emitFile({ type: 'chunk', id: depId, preserveSignature: 'exports-only' }),
568+
);
563569
}
564570
},
565571

@@ -625,7 +631,22 @@ export default function solidPlugin(options: Partial<Options> = {}): Plugin[] {
625631
// serialized manifest read back later) so downstream plugins inspecting
626632
// the bundle don't mistake them for application entries. Must precede
627633
// the client asset map build, which keys off dynamic entries.
628-
if (options.ssr) normalizeEmittedLazyEntries(bundle);
634+
if (options.ssr) {
635+
for (const ref of emittedLazyChunkRefs) {
636+
let fileName: string;
637+
try {
638+
fileName = this.getFileName(ref);
639+
} catch {
640+
// Ignore references retained from a previous watch build.
641+
continue;
642+
}
643+
const chunk = bundle[fileName];
644+
if (!chunk || chunk.type !== 'chunk') continue;
645+
chunk.isEntry = false;
646+
chunk.isDynamicEntry = true;
647+
}
648+
normalizeEmittedLazyEntries(bundle);
649+
}
629650
substituteClientManifest(bundle, buildClientAssetMap(bundle, projectRoot, base));
630651
},
631652

0 commit comments

Comments
 (0)