diff --git a/e2e/fixtures/exports-condition/README.md b/e2e/fixtures/exports-condition/README.md
new file mode 100644
index 000000000..019239843
--- /dev/null
+++ b/e2e/fixtures/exports-condition/README.md
@@ -0,0 +1 @@
+# Exports Condition
diff --git a/e2e/fixtures/exports-condition/modules/my-module/dist/index.browser.js b/e2e/fixtures/exports-condition/modules/my-module/dist/index.browser.js
new file mode 100644
index 000000000..1664dd456
--- /dev/null
+++ b/e2e/fixtures/exports-condition/modules/my-module/dist/index.browser.js
@@ -0,0 +1,2 @@
+console.log('browser runtime called')
+export const runtime = 'browser'
\ No newline at end of file
diff --git a/e2e/fixtures/exports-condition/modules/my-module/dist/index.d.ts b/e2e/fixtures/exports-condition/modules/my-module/dist/index.d.ts
new file mode 100644
index 000000000..3d89aa203
--- /dev/null
+++ b/e2e/fixtures/exports-condition/modules/my-module/dist/index.d.ts
@@ -0,0 +1 @@
+export const runtime: string;
diff --git a/e2e/fixtures/exports-condition/modules/my-module/dist/index.js b/e2e/fixtures/exports-condition/modules/my-module/dist/index.js
new file mode 100644
index 000000000..4c849e61f
--- /dev/null
+++ b/e2e/fixtures/exports-condition/modules/my-module/dist/index.js
@@ -0,0 +1,2 @@
+console.log('server runtime called')
+export const runtime = 'server'
diff --git a/e2e/fixtures/exports-condition/modules/my-module/package.json b/e2e/fixtures/exports-condition/modules/my-module/package.json
new file mode 100644
index 000000000..b3331f78e
--- /dev/null
+++ b/e2e/fixtures/exports-condition/modules/my-module/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "my-module",
+ "private": true,
+ "type": "module",
+ "types": "./dist/index.d.ts",
+ "exports": {
+ ".": {
+ "browser": "./dist/index.browser.js",
+ "import": "./dist/index.js"
+ }
+ }
+}
diff --git a/e2e/fixtures/exports-condition/package.json b/e2e/fixtures/exports-condition/package.json
new file mode 100644
index 000000000..0d4e56983
--- /dev/null
+++ b/e2e/fixtures/exports-condition/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "ssr-basic",
+ "version": "0.1.0",
+ "type": "module",
+ "private": true,
+ "scripts": {
+ "dev": "waku dev",
+ "build": "waku build",
+ "start": "waku start"
+ },
+ "dependencies": {
+ "react": "19.0.0-rc-d6cb4e77-20240911",
+ "react-dom": "19.0.0-rc-d6cb4e77-20240911",
+ "react-server-dom-webpack": "19.0.0-rc-d6cb4e77-20240911",
+ "waku": "workspace:*",
+ "my-module": "link:./modules/my-module"
+ },
+ "devDependencies": {
+ "@types/react": "^18.3.5",
+ "@types/react-dom": "^18.3.0",
+ "typescript": "^5.6.2"
+ }
+}
diff --git a/e2e/fixtures/exports-condition/src/components/App.tsx b/e2e/fixtures/exports-condition/src/components/App.tsx
new file mode 100644
index 000000000..37289a3eb
--- /dev/null
+++ b/e2e/fixtures/exports-condition/src/components/App.tsx
@@ -0,0 +1,23 @@
+import { runtime } from 'my-module';
+import { Client } from './Client.js';
+
+const App = ({ name }: { name: string }) => {
+ return (
+
+
+ Waku example
+
+
+
+
+
+ );
+};
+
+export default App;
diff --git a/e2e/fixtures/exports-condition/src/components/Client.tsx b/e2e/fixtures/exports-condition/src/components/Client.tsx
new file mode 100644
index 000000000..ec0660eb7
--- /dev/null
+++ b/e2e/fixtures/exports-condition/src/components/Client.tsx
@@ -0,0 +1,15 @@
+'use client';
+import { runtime } from 'my-module';
+import { useEffect, useState } from 'react';
+
+export const Client = () => {
+ // avoid hydration error, since runtime is different between client/server
+ const [mount, setMount] = useState(false);
+ useEffect(() => {
+ setMount(true);
+ }, []);
+ if (mount) {
+ return {runtime}
;
+ }
+ return null;
+};
diff --git a/e2e/fixtures/exports-condition/src/entries.tsx b/e2e/fixtures/exports-condition/src/entries.tsx
new file mode 100644
index 000000000..d674a6f06
--- /dev/null
+++ b/e2e/fixtures/exports-condition/src/entries.tsx
@@ -0,0 +1,28 @@
+import { lazy } from 'react';
+import { defineEntries } from 'waku/server';
+import { Slot } from 'waku/client';
+
+const App = lazy(() => import('./components/App.js'));
+
+export default defineEntries(
+ // renderEntries
+ async (input) => {
+ return {
+ App: ,
+ };
+ },
+ // getBuildConfig
+ async () => [{ pathname: '/', entries: [{ input: '' }] }],
+ // getSsrConfig
+ async (pathname) => {
+ switch (pathname) {
+ case '/':
+ return {
+ input: '',
+ html: ,
+ };
+ default:
+ return null;
+ }
+ },
+);
diff --git a/e2e/fixtures/exports-condition/src/main.tsx b/e2e/fixtures/exports-condition/src/main.tsx
new file mode 100644
index 000000000..101c2e3fc
--- /dev/null
+++ b/e2e/fixtures/exports-condition/src/main.tsx
@@ -0,0 +1,17 @@
+import { StrictMode } from 'react';
+import { createRoot, hydrateRoot } from 'react-dom/client';
+import { Root, Slot } from 'waku/client';
+
+const rootElement = (
+
+
+
+
+
+);
+
+if ((globalThis as any).__WAKU_HYDRATE__) {
+ hydrateRoot(document, rootElement);
+} else {
+ createRoot(document as any).render(rootElement);
+}
diff --git a/e2e/fixtures/exports-condition/tsconfig.json b/e2e/fixtures/exports-condition/tsconfig.json
new file mode 100644
index 000000000..8e8eddf8c
--- /dev/null
+++ b/e2e/fixtures/exports-condition/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "compilerOptions": {
+ "composite": true,
+ "strict": true,
+ "target": "esnext",
+ "downlevelIteration": true,
+ "esModuleInterop": true,
+ "module": "nodenext",
+ "skipLibCheck": true,
+ "noUncheckedIndexedAccess": true,
+ "exactOptionalPropertyTypes": true,
+ "types": ["react/experimental"],
+ "jsx": "react-jsx",
+ "outDir": "./dist"
+ },
+ "include": ["./src", "./vite.config.ts"]
+}
diff --git a/e2e/fixtures/exports-condition/vite.config.ts b/e2e/fixtures/exports-condition/vite.config.ts
new file mode 100644
index 000000000..294ec47b0
--- /dev/null
+++ b/e2e/fixtures/exports-condition/vite.config.ts
@@ -0,0 +1,10 @@
+export default ({ mode }: { mode: string }) => {
+ if (mode === 'development') {
+ return {
+ optimizeDeps: {
+ exclude: ['ai/rsc'],
+ },
+ };
+ }
+ return {};
+};
diff --git a/packages/waku/src/lib/builder/build.ts b/packages/waku/src/lib/builder/build.ts
index 8c6ee8ac0..1add27557 100644
--- a/packages/waku/src/lib/builder/build.ts
+++ b/packages/waku/src/lib/builder/build.ts
@@ -252,6 +252,9 @@ const buildServerBundle = async (
esbuild: {
jsx: 'automatic',
},
+ resolve: {
+ conditions: ['import', 'module', 'default', 'production'],
+ },
define: {
'process.env.NODE_ENV': JSON.stringify('production'),
},
@@ -312,6 +315,9 @@ const buildSsrBundle = async (
esbuild: {
jsx: 'automatic',
},
+ resolve: {
+ conditions: ['import', 'module', 'default', 'production'],
+ },
define: {
'process.env.NODE_ENV': JSON.stringify('production'),
},
@@ -374,6 +380,9 @@ const buildClientBundle = async (
rscTransformPlugin({ isClient: true, isBuild: true, serverEntryFiles }),
...deployPlugins(config),
],
+ resolve: {
+ conditions: ['import', 'module', 'default', 'browser', 'production'],
+ },
build: {
emptyOutDir: !partial,
outDir: joinPath(rootDir, config.distDir, DIST_PUBLIC),
diff --git a/packages/waku/src/lib/middleware/dev-server-impl.ts b/packages/waku/src/lib/middleware/dev-server-impl.ts
index f0003abc5..d8d35df67 100644
--- a/packages/waku/src/lib/middleware/dev-server-impl.ts
+++ b/packages/waku/src/lib/middleware/dev-server-impl.ts
@@ -110,6 +110,9 @@ const createMainViteServer = (
rscTransformPlugin({ isClient: true, isBuild: false }),
rscHmrPlugin(),
],
+ resolve: {
+ conditions: ['import', 'module', 'default', 'production'],
+ },
optimizeDeps: {
include: ['react-server-dom-webpack/client', 'react-dom'],
exclude: ['waku', 'rsc-html-stream/server'],
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 620f38a8b..285a1a4eb 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -97,6 +97,34 @@ importers:
specifier: ^5.6.2
version: 5.6.2
+ e2e/fixtures/exports-condition:
+ dependencies:
+ my-module:
+ specifier: link:./modules/my-module
+ version: link:modules/my-module
+ react:
+ specifier: 19.0.0-rc-d6cb4e77-20240911
+ version: 19.0.0-rc-d6cb4e77-20240911
+ react-dom:
+ specifier: 19.0.0-rc-d6cb4e77-20240911
+ version: 19.0.0-rc-d6cb4e77-20240911(react@19.0.0-rc-d6cb4e77-20240911)
+ react-server-dom-webpack:
+ specifier: 19.0.0-rc-d6cb4e77-20240911
+ version: 19.0.0-rc-d6cb4e77-20240911(react-dom@19.0.0-rc-d6cb4e77-20240911(react@19.0.0-rc-d6cb4e77-20240911))(react@19.0.0-rc-d6cb4e77-20240911)(webpack@5.94.0)
+ waku:
+ specifier: workspace:*
+ version: link:../../../packages/waku
+ devDependencies:
+ '@types/react':
+ specifier: ^18.3.5
+ version: 18.3.5
+ '@types/react-dom':
+ specifier: ^18.3.0
+ version: 18.3.0
+ typescript:
+ specifier: ^5.6.2
+ version: 5.6.2
+
e2e/fixtures/partial-build:
dependencies:
react: