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/index.browser.js b/e2e/fixtures/exports-condition/modules/my-module/index.browser.js
new file mode 100644
index 000000000..fa3d07eec
--- /dev/null
+++ b/e2e/fixtures/exports-condition/modules/my-module/index.browser.js
@@ -0,0 +1 @@
+export const runtime = 'browser'
diff --git a/e2e/fixtures/exports-condition/modules/my-module/index.d.ts b/e2e/fixtures/exports-condition/modules/my-module/index.d.ts
new file mode 100644
index 000000000..3d89aa203
--- /dev/null
+++ b/e2e/fixtures/exports-condition/modules/my-module/index.d.ts
@@ -0,0 +1 @@
+export const runtime: string;
diff --git a/e2e/fixtures/exports-condition/modules/my-module/index.js b/e2e/fixtures/exports-condition/modules/my-module/index.js
new file mode 100644
index 000000000..a57f3df9f
--- /dev/null
+++ b/e2e/fixtures/exports-condition/modules/my-module/index.js
@@ -0,0 +1 @@
+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..ddb684747
--- /dev/null
+++ b/e2e/fixtures/exports-condition/modules/my-module/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "my-module",
+ "private": true,
+ "type": "module",
+ "types": "./index.d.ts",
+ "exports": {
+ ".": {
+ "browser": "./index.browser.js",
+ "import": "./index.js"
+ }
+ }
+}
diff --git a/e2e/fixtures/exports-condition/package.json b/e2e/fixtures/exports-condition/package.json
new file mode 100644
index 000000000..20e16e221
--- /dev/null
+++ b/e2e/fixtures/exports-condition/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "exports-condition",
+ "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/packages/waku/src/lib/builder/build.ts b/packages/waku/src/lib/builder/build.ts
index dda65be6d..bb5098c1f 100644
--- a/packages/waku/src/lib/builder/build.ts
+++ b/packages/waku/src/lib/builder/build.ts
@@ -251,6 +251,9 @@ const buildServerBundle = async (
esbuild: {
jsx: 'automatic',
},
+ resolve: {
+ conditions: ['import', 'module', 'default'],
+ },
define: {
'process.env.NODE_ENV': JSON.stringify('production'),
},
@@ -311,6 +314,9 @@ const buildSsrBundle = async (
esbuild: {
jsx: 'automatic',
},
+ resolve: {
+ conditions: ['import', 'module', 'default'],
+ },
define: {
'process.env.NODE_ENV': JSON.stringify('production'),
},
diff --git a/packages/waku/src/lib/middleware/dev-server-impl.ts b/packages/waku/src/lib/middleware/dev-server-impl.ts
index 9c21e2e9a..5e3090e10 100644
--- a/packages/waku/src/lib/middleware/dev-server-impl.ts
+++ b/packages/waku/src/lib/middleware/dev-server-impl.ts
@@ -112,6 +112,9 @@ const createMainViteServer = (
rscHmrPlugin(),
fsRouterTypegenPlugin(config),
],
+ resolve: {
+ conditions: ['import', 'module', 'default'],
+ },
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 e421eab02..acbf4cc53 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:
diff --git a/tsconfig.e2e.json b/tsconfig.e2e.json
index 9b2e8c2ea..fc159ffc2 100644
--- a/tsconfig.e2e.json
+++ b/tsconfig.e2e.json
@@ -47,6 +47,9 @@
},
{
"path": "./e2e/fixtures/broken-links/tsconfig.json"
+ },
+ {
+ "path": "./e2e/fixtures/exports-condition/tsconfig.json"
}
]
}