Skip to content

Commit c067304

Browse files
Fix next/dynamic mock to properly handle ssr: false with named exports
- Remove early deletion of options.ssr that was ineffective - Add proper handling for ssr: false option by deleting ssr, webpack, and modules - Always use convertModule wrapper for consistent module structure - Add test cases for dynamic imports with named exports and ssr: false Co-authored-by: valentinpalkovic <[email protected]>
1 parent 9336747 commit c067304

File tree

3 files changed

+59
-4
lines changed

3 files changed

+59
-4
lines changed

example/src/app/components/DynamicImport/DynamicImport.stories.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,17 @@ const DynamicComponentNoSSR = dynamic(() => import("./DynamicImport"), {
77
ssr: false,
88
});
99

10+
// Test case that matches the user's issue pattern: using .then((mod) => mod.Component) with ssr: false
11+
const DynamicNamedComponentNoSSR = dynamic(
12+
() => import("./NamedExport").then((mod) => mod.NamedComponent),
13+
{ ssr: false },
14+
);
15+
16+
// Test without ssr: false to check if issue is SSR-specific
17+
const DynamicNamedComponent = dynamic(() =>
18+
import("./NamedExport").then((mod) => mod.NamedComponent),
19+
);
20+
1021
function Component() {
1122
return <DynamicComponent />;
1223
}
@@ -31,4 +42,35 @@ export const Default: Story = {
3142

3243
export const NoSSR: Story = {
3344
render: () => <DynamicComponentNoSSR />,
45+
play: async ({ canvas }) => {
46+
await waitFor(() =>
47+
expect(
48+
canvas.getByText("I am a dynamically loaded component"),
49+
).toBeDefined(),
50+
);
51+
},
52+
};
53+
54+
// Test without ssr: false
55+
export const NamedExport: Story = {
56+
render: () => <DynamicNamedComponent />,
57+
play: async ({ canvas }) => {
58+
await waitFor(() =>
59+
expect(
60+
canvas.getByText("I am a named export dynamically loaded component"),
61+
).toBeDefined(),
62+
);
63+
},
64+
};
65+
66+
// This test case matches the user's issue pattern
67+
export const NamedExportNoSSR: Story = {
68+
render: () => <DynamicNamedComponentNoSSR />,
69+
play: async ({ canvas }) => {
70+
await waitFor(() =>
71+
expect(
72+
canvas.getByText("I am a named export dynamically loaded component"),
73+
).toBeDefined(),
74+
);
75+
},
3476
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import React from "react";
2+
3+
export function NamedComponent() {
4+
return <div>I am a named export dynamically loaded component</div>;
5+
}

src/plugins/next-mocks/alias/dynamic/index.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,6 @@ export default function dynamic<P = Record<string, unknown>>(
7676
options?: DynamicOptions<P>,
7777
): React.ComponentType<P> {
7878
const loadableFn = Loadable as LoadableFn<P>;
79-
if (options?.ssr === false) {
80-
// biome-ignore lint/performance/noDelete: <explanation>
81-
delete options.ssr;
82-
}
8379

8480
let loadableOptions: LoadableOptions<P> = {
8581
// A loading component is not required, so we default it
@@ -136,5 +132,17 @@ export default function dynamic<P = Record<string, unknown>>(
136132
delete loadableOptions.loadableGenerated;
137133
}
138134

135+
// support for disabling server side rendering, eg: dynamic(() => import('../hello-world'), {ssr: false}).
136+
// In browser environments (Storybook/Vitest), we simply remove webpack and modules options
137+
// and proceed with the loadable component since we're always on the client-side.
138+
if (typeof loadableOptions.ssr === "boolean" && !loadableOptions.ssr) {
139+
// biome-ignore lint/performance/noDelete: <explanation>
140+
delete loadableOptions.ssr;
141+
// biome-ignore lint/performance/noDelete: <explanation>
142+
delete loadableOptions.webpack;
143+
// biome-ignore lint/performance/noDelete: <explanation>
144+
delete loadableOptions.modules;
145+
}
146+
139147
return loadableFn({ ...loadableOptions, loader: loader as Loader<P> });
140148
}

0 commit comments

Comments
 (0)