create-waku 0.7.4 → 0.8.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/package.json +4 -1
  2. package/template/01_template/package.json +11 -11
  3. package/template/01_template/src/main.tsx +1 -1
  4. package/template/01_template/src/templates/root-layout.tsx +1 -1
  5. package/template/01_template/tsconfig.json +1 -4
  6. package/template/02_demo/package.json +11 -11
  7. package/template/02_demo/src/main.tsx +1 -1
  8. package/template/02_demo/src/templates/root-layout.tsx +1 -1
  9. package/template/02_demo/tsconfig.json +1 -4
  10. package/template/03_minimal/package.json +10 -10
  11. package/template/03_minimal/src/main.tsx +1 -1
  12. package/template/04_promise/package.json +10 -10
  13. package/template/04_promise/src/main.tsx +1 -1
  14. package/template/05_actions/package.json +10 -10
  15. package/template/05_actions/src/components/funcs.ts +4 -4
  16. package/template/05_actions/src/main.tsx +1 -1
  17. package/template/06_nesting/package.json +10 -10
  18. package/template/06_nesting/src/components/AppWithoutSsr.tsx +14 -0
  19. package/template/06_nesting/src/components/CounterWithoutSsr.tsx +20 -0
  20. package/template/06_nesting/src/entries.tsx +13 -3
  21. package/template/06_nesting/src/main.tsx +14 -4
  22. package/template/07_router/package.json +10 -10
  23. package/template/07_router/src/main.tsx +1 -1
  24. package/template/08_cookies/package.json +13 -13
  25. package/template/08_cookies/src/components/App.tsx +5 -10
  26. package/template/08_cookies/src/entries.tsx +6 -7
  27. package/template/08_cookies/src/main.tsx +1 -1
  28. package/template/08_cookies/src/middleware/cookie.ts +25 -0
  29. package/template/08_cookies/waku.config.ts +17 -0
  30. package/template/09_cssmodules/package.json +10 -10
  31. package/template/09_cssmodules/src/main.tsx +1 -1
  32. package/template/10_fs-router/package.json +10 -10
  33. package/template/10_fs-router/src/entries.tsx +18 -89
  34. package/template/10_fs-router/src/main.tsx +1 -1
  35. package/template/10_fs-router/src/{routes/bar/page.tsx → pages/bar.tsx} +1 -1
  36. package/template/11_form/package.json +10 -10
  37. package/template/11_form/src/components/funcs.ts +4 -4
  38. package/template/11_form/src/main.tsx +1 -1
  39. package/template/12_css/package.json +11 -11
  40. package/template/12_css/src/main.tsx +1 -1
  41. package/template/13_path-alias/package.json +11 -11
  42. package/template/13_path-alias/src/main.tsx +1 -1
  43. package/template/14_react-tweet/package.json +11 -11
  44. package/template/14_react-tweet/src/main.tsx +1 -1
  45. package/template/14_react-tweet/src/templates/root-layout.tsx +1 -1
  46. package/template/14_react-tweet/tsconfig.json +1 -4
  47. package/template/14_react-tweet/vite.config.ts +16 -14
  48. package/template/08_cookies/dev.js +0 -24
  49. package/template/08_cookies/start.js +0 -32
  50. /package/template/10_fs-router/src/{routes/layout.tsx → pages/_layout.tsx} +0 -0
  51. /package/template/10_fs-router/src/{routes/foo/page.tsx → pages/foo/index.tsx} +0 -0
  52. /package/template/10_fs-router/src/{routes/page.tsx → pages/index.tsx} +0 -0
  53. /package/template/10_fs-router/src/{routes/nested/[name]/page.tsx → pages/nested/[name].tsx} +0 -0
package/package.json CHANGED
@@ -1,6 +1,9 @@
1
1
  {
2
2
  "name": "create-waku",
3
- "version": "0.7.4",
3
+ "version": "0.8.0-alpha.0",
4
+ "publishConfig": {
5
+ "tag": "next"
6
+ },
4
7
  "author": "Daishi Kato",
5
8
  "type": "module",
6
9
  "contributors": [
@@ -4,21 +4,21 @@
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "scripts": {
7
- "dev": "waku dev --with-ssr",
8
- "build": "waku build --with-ssr",
9
- "start": "waku start --with-ssr"
7
+ "dev": "waku dev",
8
+ "build": "waku build",
9
+ "start": "waku start"
10
10
  },
11
11
  "dependencies": {
12
- "react": "18.3.0-canary-6c3b8dbfe-20240226",
13
- "react-dom": "18.3.0-canary-6c3b8dbfe-20240226",
14
- "react-server-dom-webpack": "18.3.0-canary-6c3b8dbfe-20240226",
15
- "waku": "0.19.4"
12
+ "react": "18.3.0-canary-0066e0b68-20240306",
13
+ "react-dom": "18.3.0-canary-0066e0b68-20240306",
14
+ "react-server-dom-webpack": "18.3.0-canary-0066e0b68-20240306",
15
+ "waku": "0.20.0-alpha.0"
16
16
  },
17
17
  "devDependencies": {
18
- "@types/react": "18.2.59",
19
- "@types/react-dom": "18.2.19",
20
- "autoprefixer": "10.4.17",
18
+ "@types/react": "18.2.64",
19
+ "@types/react-dom": "18.2.21",
20
+ "autoprefixer": "10.4.18",
21
21
  "tailwindcss": "3.4.1",
22
- "typescript": "5.3.3"
22
+ "typescript": "5.4.2"
23
23
  }
24
24
  }
@@ -12,7 +12,7 @@ const rootElement = (
12
12
  </StrictMode>
13
13
  );
14
14
 
15
- if (import.meta.env.WAKU_HYDRATE) {
15
+ if (document.body.dataset.hydrate) {
16
16
  hydrateRoot(document.body, rootElement);
17
17
  } else {
18
18
  createRoot(document.body).render(rootElement);
@@ -11,7 +11,7 @@ export const RootLayout = async ({ children }: RootLayoutProps) => {
11
11
  const data = await getData();
12
12
 
13
13
  return (
14
- <div id="__waku" className="font-['Nunito']">
14
+ <div className="font-['Nunito']">
15
15
  <meta property="description" content={data.description} />
16
16
  <link rel="icon" type="image/png" href={data.icon} />
17
17
  <Header />
@@ -9,9 +9,6 @@
9
9
  "noUncheckedIndexedAccess": true,
10
10
  "exactOptionalPropertyTypes": true,
11
11
  "types": ["react/experimental"],
12
- "jsx": "react-jsx",
13
- "paths": {
14
- "~/*": ["./src/*"]
15
- }
12
+ "jsx": "react-jsx"
16
13
  }
17
14
  }
@@ -4,21 +4,21 @@
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "scripts": {
7
- "dev": "waku dev --with-ssr",
8
- "build": "waku build --with-ssr",
9
- "start": "waku start --with-ssr"
7
+ "dev": "waku dev",
8
+ "build": "waku build",
9
+ "start": "waku start"
10
10
  },
11
11
  "dependencies": {
12
- "react": "18.3.0-canary-6c3b8dbfe-20240226",
13
- "react-dom": "18.3.0-canary-6c3b8dbfe-20240226",
14
- "react-server-dom-webpack": "18.3.0-canary-6c3b8dbfe-20240226",
15
- "waku": "0.19.4"
12
+ "react": "18.3.0-canary-0066e0b68-20240306",
13
+ "react-dom": "18.3.0-canary-0066e0b68-20240306",
14
+ "react-server-dom-webpack": "18.3.0-canary-0066e0b68-20240306",
15
+ "waku": "0.20.0-alpha.0"
16
16
  },
17
17
  "devDependencies": {
18
- "@types/react": "18.2.59",
19
- "@types/react-dom": "18.2.19",
20
- "autoprefixer": "10.4.17",
18
+ "@types/react": "18.2.64",
19
+ "@types/react-dom": "18.2.21",
20
+ "autoprefixer": "10.4.18",
21
21
  "tailwindcss": "3.4.1",
22
- "typescript": "5.3.3"
22
+ "typescript": "5.4.2"
23
23
  }
24
24
  }
@@ -12,7 +12,7 @@ const rootElement = (
12
12
  </StrictMode>
13
13
  );
14
14
 
15
- if (import.meta.env.WAKU_HYDRATE) {
15
+ if (document.body.dataset.hydrate) {
16
16
  hydrateRoot(document.body, rootElement);
17
17
  } else {
18
18
  createRoot(document.body).render(rootElement);
@@ -11,7 +11,7 @@ export const RootLayout = async ({ children }: RootLayoutProps) => {
11
11
  const data = await getData();
12
12
 
13
13
  return (
14
- <div id="__waku" className="font-nunito">
14
+ <div className="font-nunito">
15
15
  <meta property="description" content={data.description} />
16
16
  <link rel="icon" type="image/png" href={data.icon} />
17
17
  <Header />
@@ -9,9 +9,6 @@
9
9
  "noUncheckedIndexedAccess": true,
10
10
  "exactOptionalPropertyTypes": true,
11
11
  "types": ["react/experimental"],
12
- "jsx": "react-jsx",
13
- "paths": {
14
- "~/*": ["./src/*"]
15
- }
12
+ "jsx": "react-jsx"
16
13
  }
17
14
  }
@@ -4,19 +4,19 @@
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "scripts": {
7
- "dev": "waku dev --with-ssr",
8
- "build": "waku build --with-ssr",
9
- "start": "waku start --with-ssr"
7
+ "dev": "waku dev",
8
+ "build": "waku build",
9
+ "start": "waku start"
10
10
  },
11
11
  "dependencies": {
12
- "react": "18.3.0-canary-6c3b8dbfe-20240226",
13
- "react-dom": "18.3.0-canary-6c3b8dbfe-20240226",
14
- "react-server-dom-webpack": "18.3.0-canary-6c3b8dbfe-20240226",
15
- "waku": "0.19.4"
12
+ "react": "18.3.0-canary-0066e0b68-20240306",
13
+ "react-dom": "18.3.0-canary-0066e0b68-20240306",
14
+ "react-server-dom-webpack": "18.3.0-canary-0066e0b68-20240306",
15
+ "waku": "0.20.0-alpha.0"
16
16
  },
17
17
  "devDependencies": {
18
- "@types/react": "18.2.59",
19
- "@types/react-dom": "18.2.19",
20
- "typescript": "5.3.3"
18
+ "@types/react": "18.2.64",
19
+ "@types/react-dom": "18.2.21",
20
+ "typescript": "5.4.2"
21
21
  }
22
22
  }
@@ -10,7 +10,7 @@ const rootElement = (
10
10
  </StrictMode>
11
11
  );
12
12
 
13
- if (import.meta.env.WAKU_HYDRATE) {
13
+ if (document.body.dataset.hydrate) {
14
14
  hydrateRoot(document.body, rootElement);
15
15
  } else {
16
16
  createRoot(document.body).render(rootElement);
@@ -4,19 +4,19 @@
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "scripts": {
7
- "dev": "waku dev --with-ssr",
8
- "build": "waku build --with-ssr",
9
- "start": "waku start --with-ssr"
7
+ "dev": "waku dev",
8
+ "build": "waku build",
9
+ "start": "waku start"
10
10
  },
11
11
  "dependencies": {
12
- "react": "18.3.0-canary-6c3b8dbfe-20240226",
13
- "react-dom": "18.3.0-canary-6c3b8dbfe-20240226",
14
- "react-server-dom-webpack": "18.3.0-canary-6c3b8dbfe-20240226",
15
- "waku": "0.19.4"
12
+ "react": "18.3.0-canary-0066e0b68-20240306",
13
+ "react-dom": "18.3.0-canary-0066e0b68-20240306",
14
+ "react-server-dom-webpack": "18.3.0-canary-0066e0b68-20240306",
15
+ "waku": "0.20.0-alpha.0"
16
16
  },
17
17
  "devDependencies": {
18
- "@types/react": "18.2.59",
19
- "@types/react-dom": "18.2.19",
20
- "typescript": "5.3.3"
18
+ "@types/react": "18.2.64",
19
+ "@types/react-dom": "18.2.21",
20
+ "typescript": "5.4.2"
21
21
  }
22
22
  }
@@ -12,7 +12,7 @@ const rootElement = (
12
12
  </StrictMode>
13
13
  );
14
14
 
15
- if (import.meta.env.WAKU_HYDRATE) {
15
+ if (document.body.dataset.hydrate) {
16
16
  hydrateRoot(document.body, rootElement);
17
17
  } else {
18
18
  createRoot(document.body).render(rootElement);
@@ -4,20 +4,20 @@
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "scripts": {
7
- "dev": "waku dev --with-ssr",
8
- "build": "waku build --with-ssr",
9
- "start": "waku start --with-ssr"
7
+ "dev": "waku dev",
8
+ "build": "waku build",
9
+ "start": "waku start"
10
10
  },
11
11
  "dependencies": {
12
- "react": "18.3.0-canary-6c3b8dbfe-20240226",
13
- "react-dom": "18.3.0-canary-6c3b8dbfe-20240226",
14
- "react-server-dom-webpack": "18.3.0-canary-6c3b8dbfe-20240226",
12
+ "react": "18.3.0-canary-0066e0b68-20240306",
13
+ "react-dom": "18.3.0-canary-0066e0b68-20240306",
14
+ "react-server-dom-webpack": "18.3.0-canary-0066e0b68-20240306",
15
15
  "react-wrap-balancer": "1.1.0",
16
- "waku": "0.19.4"
16
+ "waku": "0.20.0-alpha.0"
17
17
  },
18
18
  "devDependencies": {
19
- "@types/react": "18.2.59",
20
- "@types/react-dom": "18.2.19",
21
- "typescript": "5.3.3"
19
+ "@types/react": "18.2.64",
20
+ "@types/react-dom": "18.2.21",
21
+ "typescript": "5.4.2"
22
22
  }
23
23
  }
@@ -1,6 +1,6 @@
1
1
  'use server';
2
2
 
3
- import type { RenderContext } from 'waku/server';
3
+ import { rerender } from 'waku/server';
4
4
 
5
5
  export const greet = (name: string) => `Hello ${name} from server!`;
6
6
 
@@ -9,7 +9,7 @@ let counter = 0;
9
9
 
10
10
  export const getCounter = () => counter;
11
11
 
12
- export function increment(this: RenderContext) {
12
+ export const increment = () => {
13
13
  counter += 1;
14
- this.rerender('Waku');
15
- }
14
+ rerender('Waku');
15
+ };
@@ -10,7 +10,7 @@ const rootElement = (
10
10
  </StrictMode>
11
11
  );
12
12
 
13
- if (import.meta.env.WAKU_HYDRATE) {
13
+ if (document.body.dataset.hydrate) {
14
14
  hydrateRoot(document.body, rootElement);
15
15
  } else {
16
16
  createRoot(document.body).render(rootElement);
@@ -4,19 +4,19 @@
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "scripts": {
7
- "dev": "waku dev --with-ssr",
8
- "build": "waku build --with-ssr",
9
- "start": "waku start --with-ssr"
7
+ "dev": "waku dev",
8
+ "build": "waku build",
9
+ "start": "waku start"
10
10
  },
11
11
  "dependencies": {
12
- "react": "18.3.0-canary-6c3b8dbfe-20240226",
13
- "react-dom": "18.3.0-canary-6c3b8dbfe-20240226",
14
- "react-server-dom-webpack": "18.3.0-canary-6c3b8dbfe-20240226",
15
- "waku": "0.19.4"
12
+ "react": "18.3.0-canary-0066e0b68-20240306",
13
+ "react-dom": "18.3.0-canary-0066e0b68-20240306",
14
+ "react-server-dom-webpack": "18.3.0-canary-0066e0b68-20240306",
15
+ "waku": "0.20.0-alpha.0"
16
16
  },
17
17
  "devDependencies": {
18
- "@types/react": "18.2.59",
19
- "@types/react-dom": "18.2.19",
20
- "typescript": "5.3.3"
18
+ "@types/react": "18.2.64",
19
+ "@types/react-dom": "18.2.21",
20
+ "typescript": "5.4.2"
21
21
  }
22
22
  }
@@ -0,0 +1,14 @@
1
+ import { CounterWithoutSsr } from './CounterWithoutSsr.js';
2
+
3
+ const AppWithoutSsr = () => {
4
+ return (
5
+ <div style={{ border: '3px red dashed', margin: '1em', padding: '1em' }}>
6
+ <title>Waku</title>
7
+ <h1>Hello!!</h1>
8
+ <h3>This is a server component without SSR.</h3>
9
+ <CounterWithoutSsr />
10
+ </div>
11
+ );
12
+ };
13
+
14
+ export default AppWithoutSsr;
@@ -0,0 +1,20 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+
5
+ export const CounterWithoutSsr = () => {
6
+ if (typeof window === 'undefined') {
7
+ throw new Error('This component is for client only.');
8
+ }
9
+ const [count, setCount] = useState(0);
10
+ const handleClick = () => {
11
+ setCount((c) => c + 1);
12
+ };
13
+ return (
14
+ <div style={{ border: '3px blue dashed', margin: '1em', padding: '1em' }}>
15
+ <p>Count: {count}</p>
16
+ <button onClick={handleClick}>Increment</button>
17
+ <h3>This is a client component.</h3>
18
+ </div>
19
+ );
20
+ };
@@ -1,10 +1,10 @@
1
- import { lazy } from 'react';
2
1
  import type { ReactNode } from 'react';
3
2
  import { defineEntries } from 'waku/server';
4
3
  import { Slot } from 'waku/client';
5
4
 
6
- const App = lazy(() => import('./components/App.js'));
7
- const InnerApp = lazy(() => import('./components/InnerApp.js'));
5
+ import App from './components/App.js';
6
+ import InnerApp from './components/InnerApp.js';
7
+ import AppWithoutSsr from './components/AppWithoutSsr.js';
8
8
 
9
9
  export default defineEntries(
10
10
  // renderEntries
@@ -17,6 +17,9 @@ export default defineEntries(
17
17
  if (params.has('InnerApp')) {
18
18
  result.InnerApp = <InnerApp count={Number(params.get('InnerApp'))} />;
19
19
  }
20
+ if (params.has('AppWithoutSsr')) {
21
+ result.AppWithoutSsr = <AppWithoutSsr />;
22
+ }
20
23
  return result;
21
24
  },
22
25
  // getBuildConfig
@@ -32,6 +35,11 @@ export default defineEntries(
32
35
  { input: 'InnerApp=5', skipPrefetch: true },
33
36
  ],
34
37
  },
38
+ {
39
+ pathname: '/no-ssr',
40
+ entries: [{ input: 'AppWithoutSsr' }],
41
+ isStatic: true,
42
+ },
35
43
  ],
36
44
  // getSsrConfig
37
45
  async (pathname) => {
@@ -41,6 +49,8 @@ export default defineEntries(
41
49
  input: '',
42
50
  body: <Slot id="App" />,
43
51
  };
52
+ case '/no-ssr':
53
+ return null;
44
54
  default:
45
55
  return null;
46
56
  }
@@ -2,15 +2,25 @@ import { StrictMode } from 'react';
2
2
  import { createRoot, hydrateRoot } from 'react-dom/client';
3
3
  import { Root, Slot } from 'waku/client';
4
4
 
5
+ const pathname = window.location.pathname;
6
+
5
7
  const rootElement = (
6
8
  <StrictMode>
7
- <Root>
8
- <Slot id="App" />
9
- </Root>
9
+ {pathname === '/' ? (
10
+ <Root>
11
+ <Slot id="App" />
12
+ </Root>
13
+ ) : pathname === '/no-ssr' ? (
14
+ <Root initialInput="AppWithoutSsr">
15
+ <Slot id="AppWithoutSsr" />
16
+ </Root>
17
+ ) : (
18
+ <h1>Not Found</h1>
19
+ )}
10
20
  </StrictMode>
11
21
  );
12
22
 
13
- if (import.meta.env.WAKU_HYDRATE) {
23
+ if (document.body.dataset.hydrate) {
14
24
  hydrateRoot(document.body, rootElement);
15
25
  } else {
16
26
  createRoot(document.body).render(rootElement);
@@ -4,20 +4,20 @@
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "scripts": {
7
- "dev": "waku dev --with-ssr",
8
- "build": "waku build --with-ssr",
9
- "start": "waku start --with-ssr"
7
+ "dev": "waku dev",
8
+ "build": "waku build",
9
+ "start": "waku start"
10
10
  },
11
11
  "dependencies": {
12
- "react": "18.3.0-canary-6c3b8dbfe-20240226",
13
- "react-dom": "18.3.0-canary-6c3b8dbfe-20240226",
14
- "react-server-dom-webpack": "18.3.0-canary-6c3b8dbfe-20240226",
15
- "waku": "0.19.4"
12
+ "react": "18.3.0-canary-0066e0b68-20240306",
13
+ "react-dom": "18.3.0-canary-0066e0b68-20240306",
14
+ "react-server-dom-webpack": "18.3.0-canary-0066e0b68-20240306",
15
+ "waku": "0.20.0-alpha.0"
16
16
  },
17
17
  "devDependencies": {
18
- "@types/react": "18.2.59",
19
- "@types/react-dom": "18.2.19",
18
+ "@types/react": "18.2.64",
19
+ "@types/react-dom": "18.2.21",
20
20
  "server-only": "0.0.1",
21
- "typescript": "5.3.3"
21
+ "typescript": "5.4.2"
22
22
  }
23
23
  }
@@ -12,7 +12,7 @@ const rootElement = (
12
12
  </StrictMode>
13
13
  );
14
14
 
15
- if (import.meta.env.WAKU_HYDRATE) {
15
+ if (document.body.dataset.hydrate) {
16
16
  hydrateRoot(document.body, rootElement);
17
17
  } else {
18
18
  createRoot(document.body).render(rootElement);
@@ -4,22 +4,22 @@
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "scripts": {
7
- "dev": "node dev.js --with-ssr",
8
- "build": "waku build --with-ssr",
9
- "start": "node start.js --with-ssr"
7
+ "dev": "waku dev",
8
+ "build": "waku build",
9
+ "start": "waku start"
10
10
  },
11
11
  "dependencies": {
12
- "cookie-parser": "1.4.6",
13
- "express": "4.18.2",
14
- "react": "18.3.0-canary-6c3b8dbfe-20240226",
15
- "react-dom": "18.3.0-canary-6c3b8dbfe-20240226",
16
- "react-server-dom-webpack": "18.3.0-canary-6c3b8dbfe-20240226",
17
- "waku": "0.19.4"
12
+ "cookie": "0.6.0",
13
+ "react": "18.3.0-canary-0066e0b68-20240306",
14
+ "react-dom": "18.3.0-canary-0066e0b68-20240306",
15
+ "react-server-dom-webpack": "18.3.0-canary-0066e0b68-20240306",
16
+ "waku": "0.20.0-alpha.0"
18
17
  },
19
18
  "devDependencies": {
20
- "@types/node": "20.11.20",
21
- "@types/react": "18.2.59",
22
- "@types/react-dom": "18.2.19",
23
- "typescript": "5.3.3"
19
+ "@types/cookie": "0.6.0",
20
+ "@types/node": "20.11.25",
21
+ "@types/react": "18.2.64",
22
+ "@types/react-dom": "18.2.21",
23
+ "typescript": "5.4.2"
24
24
  }
25
25
  }
@@ -1,20 +1,15 @@
1
+ import { getContext } from 'waku/server';
2
+
1
3
  import { Counter } from './Counter.js';
2
4
 
3
- const App = ({
4
- name,
5
- count,
6
- items,
7
- }: {
8
- name: string;
9
- count: number;
10
- items: unknown[];
11
- }) => {
5
+ const App = ({ name, items }: { name: string; items: unknown[] }) => {
6
+ const context = getContext<{ count: number }>();
12
7
  return (
13
8
  <div style={{ border: '3px red dashed', margin: '1em', padding: '1em' }}>
14
9
  <title>Waku</title>
15
10
  <h1>Hello {name}!!</h1>
16
11
  <h3>This is a server component.</h3>
17
- <p>Cookie count: {count}</p>
12
+ <p>Cookie count: {context.count}</p>
18
13
  <Counter />
19
14
  <p>Item count: {items.length}</p>
20
15
  </div>
@@ -1,17 +1,16 @@
1
1
  import path from 'node:path';
2
2
  import { fileURLToPath } from 'node:url';
3
3
  import fsPromises from 'node:fs/promises';
4
- import { lazy } from 'react';
5
- import { defineEntries } from 'waku/server';
4
+ import { defineEntries, getContext } from 'waku/server';
6
5
  import { Slot } from 'waku/client';
7
6
 
8
- const App = lazy(() => import('./components/App.js'));
7
+ import App from './components/App.js';
9
8
 
10
9
  export default defineEntries(
11
10
  // renderEntries
12
- async function (input) {
13
- const ctx = this.context as { count: number };
14
- ++ctx.count;
11
+ async (input) => {
12
+ const context = getContext<{ count: number }>();
13
+ ++context.count;
15
14
  const items = JSON.parse(
16
15
  await fsPromises.readFile(
17
16
  path.join(
@@ -22,7 +21,7 @@ export default defineEntries(
22
21
  ),
23
22
  );
24
23
  return {
25
- App: <App name={input || 'Waku'} count={ctx.count} items={items} />,
24
+ App: <App name={input || 'Waku'} items={items} />,
26
25
  };
27
26
  },
28
27
  // getBuildConfig
@@ -10,7 +10,7 @@ const rootElement = (
10
10
  </StrictMode>
11
11
  );
12
12
 
13
- if (import.meta.env.WAKU_HYDRATE) {
13
+ if (document.body.dataset.hydrate) {
14
14
  hydrateRoot(document.body, rootElement);
15
15
  } else {
16
16
  createRoot(document.body).render(rootElement);
@@ -0,0 +1,25 @@
1
+ import * as cookie from 'cookie';
2
+
3
+ import type { Middleware } from 'waku/config';
4
+
5
+ // XXX we would probably like to extend config.
6
+ const COOKIE_OPTS = {};
7
+
8
+ const cookieMiddleware: Middleware = () => {
9
+ return async (ctx, next) => {
10
+ const cookies = cookie.parse(ctx.req.headers.cookie || '');
11
+ ctx.context.count = Number(cookies.count) || 0;
12
+ await next();
13
+ ctx.res.headers ||= {};
14
+ let origSetCookie = ctx.res.headers['set-cookie'] || ([] as string[]);
15
+ if (typeof origSetCookie === 'string') {
16
+ origSetCookie = [origSetCookie];
17
+ }
18
+ ctx.res.headers['set-cookie'] = [
19
+ ...origSetCookie,
20
+ cookie.serialize('count', String(ctx.context.count), COOKIE_OPTS),
21
+ ];
22
+ };
23
+ };
24
+
25
+ export default cookieMiddleware;
@@ -0,0 +1,17 @@
1
+ const DO_NOT_BUNDLE = '';
2
+
3
+ /** @type {import('waku/config').Config} */
4
+ export default {
5
+ middleware: (cmd: 'dev' | 'start') => [
6
+ import('./src/middleware/cookie.js'),
7
+ ...(cmd === 'dev'
8
+ ? [
9
+ import(
10
+ /* @vite-ignore */ DO_NOT_BUNDLE + 'waku/middleware/dev-server'
11
+ ),
12
+ ]
13
+ : []),
14
+ import('waku/middleware/rsc'),
15
+ import('waku/middleware/fallback'),
16
+ ],
17
+ };
@@ -4,19 +4,19 @@
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "scripts": {
7
- "dev": "waku dev --with-ssr",
8
- "build": "waku build --with-ssr",
9
- "start": "waku start --with-ssr"
7
+ "dev": "waku dev",
8
+ "build": "waku build",
9
+ "start": "waku start"
10
10
  },
11
11
  "dependencies": {
12
- "react": "18.3.0-canary-6c3b8dbfe-20240226",
13
- "react-dom": "18.3.0-canary-6c3b8dbfe-20240226",
14
- "react-server-dom-webpack": "18.3.0-canary-6c3b8dbfe-20240226",
15
- "waku": "0.19.4"
12
+ "react": "18.3.0-canary-0066e0b68-20240306",
13
+ "react-dom": "18.3.0-canary-0066e0b68-20240306",
14
+ "react-server-dom-webpack": "18.3.0-canary-0066e0b68-20240306",
15
+ "waku": "0.20.0-alpha.0"
16
16
  },
17
17
  "devDependencies": {
18
- "@types/react": "18.2.59",
19
- "@types/react-dom": "18.2.19",
20
- "typescript": "5.3.3"
18
+ "@types/react": "18.2.64",
19
+ "@types/react-dom": "18.2.21",
20
+ "typescript": "5.4.2"
21
21
  }
22
22
  }
@@ -10,7 +10,7 @@ const rootElement = (
10
10
  </StrictMode>
11
11
  );
12
12
 
13
- if (import.meta.env.WAKU_HYDRATE) {
13
+ if (document.body.dataset.hydrate) {
14
14
  hydrateRoot(document.body, rootElement);
15
15
  } else {
16
16
  createRoot(document.body).render(rootElement);
@@ -4,20 +4,20 @@
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "scripts": {
7
- "dev": "waku dev --with-ssr",
8
- "build": "waku build --with-ssr",
9
- "start": "waku start --with-ssr"
7
+ "dev": "waku dev",
8
+ "build": "waku build",
9
+ "start": "waku start"
10
10
  },
11
11
  "dependencies": {
12
12
  "glob": "10.3.10",
13
- "react": "18.3.0-canary-6c3b8dbfe-20240226",
14
- "react-dom": "18.3.0-canary-6c3b8dbfe-20240226",
15
- "react-server-dom-webpack": "18.3.0-canary-6c3b8dbfe-20240226",
16
- "waku": "0.19.4"
13
+ "react": "18.3.0-canary-0066e0b68-20240306",
14
+ "react-dom": "18.3.0-canary-0066e0b68-20240306",
15
+ "react-server-dom-webpack": "18.3.0-canary-0066e0b68-20240306",
16
+ "waku": "0.20.0-alpha.0"
17
17
  },
18
18
  "devDependencies": {
19
- "@types/react": "18.2.59",
20
- "@types/react-dom": "18.2.19",
21
- "typescript": "5.3.3"
19
+ "@types/react": "18.2.64",
20
+ "@types/react-dom": "18.2.21",
21
+ "typescript": "5.4.2"
22
22
  }
23
23
  }
@@ -1,92 +1,21 @@
1
- import { fileURLToPath } from 'node:url';
2
- import path from 'node:path';
3
- import { existsSync } from 'node:fs';
4
- import fsPromises from 'node:fs/promises';
5
- import { lazy } from 'react';
6
- import { glob } from 'glob';
7
- import { unstable_defineRouter as defineRouter } from 'waku/router/server';
1
+ import { fsRouter } from 'waku/router/server';
8
2
 
9
- const routesDir = path.join(
10
- path.dirname(fileURLToPath(import.meta.url)),
11
- 'routes',
12
- );
3
+ export default fsRouter(import.meta.url, loader);
13
4
 
14
- const getRoute = (items: string[]) =>
15
- lazy(() => {
16
- // HACK: replace "_slug_" to "[slug]"
17
- items = items.map((item) => item.replace(/^_(\w+)_$/, '[$1]'));
18
- switch (items.length) {
19
- case 1:
20
- return import(`./routes/${items[0]}.tsx`);
21
- case 2:
22
- return import(`./routes/${items[0]}/${items[1]}.tsx`);
23
- case 3:
24
- return import(`./routes/${items[0]}/${items[1]}/${items[2]}.tsx`);
25
- default:
26
- throw new Error('too deep route');
27
- }
28
- });
29
-
30
- const getMappingAndItems = async (id: string) => {
31
- const mapping: Record<string, string> = {};
32
- const items = id.split('/');
33
- for (let i = 0; i < items.length - 1; ++i) {
34
- const dir = path.join(routesDir, ...items.slice(0, i));
35
- if (!existsSync(dir)) {
36
- return null;
37
- }
38
- const files = await fsPromises.readdir(dir);
39
- if (!files.includes(items[i]!)) {
40
- const slug = files.find((file) => file.match(/^(\[\w+\]|_\w+_)$/));
41
- if (slug) {
42
- mapping[slug.slice(1, -1)] = items[i]!;
43
- items[i] = slug;
44
- }
45
- }
46
- }
47
- if (
48
- !existsSync(path.join(routesDir, ...items) + '.js') &&
49
- !existsSync(path.join(routesDir, ...items) + '.tsx')
50
- ) {
51
- return null;
5
+ function loader(dir: string, file: string) {
6
+ const p = file.replace(/\.\w+$/, '').split('/');
7
+ switch (p.length) {
8
+ case 1:
9
+ return import(`./${dir}/${p[0]}.tsx`);
10
+ case 2:
11
+ return import(`./${dir}/${p[0]}/${p[1]}.tsx`);
12
+ case 3:
13
+ return import(`./${dir}/${p[0]}/${p[1]}/${p[2]}.tsx`);
14
+ case 4:
15
+ return import(`./${dir}/${p[0]}/${p[1]}/${p[2]}/${p[3]}.tsx`);
16
+ case 5:
17
+ return import(`./${dir}/${p[0]}/${p[1]}/${p[2]}/${p[3]}/${p[5]}.tsx`);
18
+ default:
19
+ throw new Error('too deep');
52
20
  }
53
-
54
- return { mapping, items };
55
- };
56
-
57
- const getPathConfig = async () => {
58
- const files = await glob('**/page.{tsx,js}', { cwd: routesDir });
59
- return files.map((file) => {
60
- const names = file.split('/').filter(Boolean).slice(0, -1);
61
- const pathSpec = names.map((name) => {
62
- const match = name.match(/^(\[\w+\]|_\w+_)$/);
63
- if (match) {
64
- return { type: 'group', name: match[1]!.slice(1, -1) } as const;
65
- }
66
- return { type: 'literal', name } as const;
67
- });
68
- return {
69
- path: pathSpec,
70
- isStatic: pathSpec.every(({ type }) => type === 'literal'),
71
- };
72
- });
73
- };
74
-
75
- export default defineRouter(
76
- // getPathConfig
77
- () => getPathConfig(),
78
- // getComponent (id is "**/layout" or "**/page")
79
- async (id, unstable_setShouldSkip) => {
80
- unstable_setShouldSkip({}); // always skip if possible
81
- const result = await getMappingAndItems(id);
82
- if (result === null) {
83
- return null;
84
- }
85
- const { mapping, items } = result;
86
- const Route = getRoute(items);
87
- const Component = (props: Record<string, unknown>) => (
88
- <Route {...props} {...mapping} />
89
- );
90
- return Component;
91
- },
92
- );
21
+ }
@@ -12,7 +12,7 @@ const rootElement = (
12
12
  </StrictMode>
13
13
  );
14
14
 
15
- if (import.meta.env.WAKU_HYDRATE) {
15
+ if (document.body.dataset.hydrate) {
16
16
  hydrateRoot(document.body, rootElement);
17
17
  } else {
18
18
  createRoot(document.body).render(rootElement);
@@ -1,4 +1,4 @@
1
- import { Counter } from '../../components/Counter.js';
1
+ import { Counter } from '../components/Counter.js';
2
2
 
3
3
  const Bar = () => (
4
4
  <div>
@@ -4,19 +4,19 @@
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "scripts": {
7
- "dev": "waku dev --with-ssr",
8
- "build": "waku build --with-ssr",
9
- "start": "waku start --with-ssr"
7
+ "dev": "waku dev",
8
+ "build": "waku build",
9
+ "start": "waku start"
10
10
  },
11
11
  "dependencies": {
12
- "react": "18.3.0-canary-6c3b8dbfe-20240226",
13
- "react-dom": "18.3.0-canary-6c3b8dbfe-20240226",
14
- "react-server-dom-webpack": "18.3.0-canary-6c3b8dbfe-20240226",
15
- "waku": "0.19.4"
12
+ "react": "18.3.0-canary-0066e0b68-20240306",
13
+ "react-dom": "18.3.0-canary-0066e0b68-20240306",
14
+ "react-server-dom-webpack": "18.3.0-canary-0066e0b68-20240306",
15
+ "waku": "0.20.0-alpha.0"
16
16
  },
17
17
  "devDependencies": {
18
- "@types/react": "18.2.59",
19
- "@types/react-dom": "18.2.19",
20
- "typescript": "5.3.3"
18
+ "@types/react": "18.2.64",
19
+ "@types/react-dom": "18.2.21",
20
+ "typescript": "5.4.2"
21
21
  }
22
22
  }
@@ -1,15 +1,15 @@
1
1
  'use server';
2
2
 
3
- import type { RenderContext } from 'waku/server';
3
+ import { rerender } from 'waku/server';
4
4
 
5
5
  // module state on server
6
6
  let message = '';
7
7
 
8
8
  export const getMessage = () => message;
9
9
 
10
- export function greet(this: RenderContext, formData: FormData) {
10
+ export const greet = (formData: FormData) => {
11
11
  message = `Hello ${formData.get('name') || 'Anonymous'} from server!`;
12
- this.rerender('');
13
- }
12
+ rerender('');
13
+ };
14
14
 
15
15
  export const increment = (count: number) => count + 1;
@@ -10,7 +10,7 @@ const rootElement = (
10
10
  </StrictMode>
11
11
  );
12
12
 
13
- if (import.meta.env.WAKU_HYDRATE) {
13
+ if (document.body.dataset.hydrate) {
14
14
  hydrateRoot(document.body, rootElement);
15
15
  } else {
16
16
  createRoot(document.body).render(rootElement);
@@ -4,25 +4,25 @@
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "scripts": {
7
- "dev": "waku dev --with-ssr",
8
- "build": "waku build --with-ssr",
9
- "start": "waku start --with-ssr"
7
+ "dev": "waku dev",
8
+ "build": "waku build",
9
+ "start": "waku start"
10
10
  },
11
11
  "dependencies": {
12
12
  "@stylexjs/stylex": "0.5.1",
13
13
  "@vanilla-extract/css": "1.14.1",
14
14
  "classnames": "2.3.2",
15
- "react": "18.3.0-canary-6c3b8dbfe-20240226",
16
- "react-dom": "18.3.0-canary-6c3b8dbfe-20240226",
17
- "react-server-dom-webpack": "18.3.0-canary-6c3b8dbfe-20240226",
18
- "waku": "0.19.4"
15
+ "react": "18.3.0-canary-0066e0b68-20240306",
16
+ "react-dom": "18.3.0-canary-0066e0b68-20240306",
17
+ "react-server-dom-webpack": "18.3.0-canary-0066e0b68-20240306",
18
+ "waku": "0.20.0-alpha.0"
19
19
  },
20
20
  "devDependencies": {
21
- "@types/react": "18.2.59",
22
- "@types/react-dom": "18.2.19",
21
+ "@types/react": "18.2.64",
22
+ "@types/react-dom": "18.2.21",
23
23
  "@vanilla-extract/vite-plugin": "3.9.5",
24
- "typescript": "5.3.3",
25
- "vite": "5.1.4",
24
+ "typescript": "5.4.2",
25
+ "vite": "5.1.5",
26
26
  "vite-plugin-stylex-dev": "0.3.0"
27
27
  }
28
28
  }
@@ -10,7 +10,7 @@ const rootElement = (
10
10
  </StrictMode>
11
11
  );
12
12
 
13
- if (import.meta.env.WAKU_HYDRATE) {
13
+ if (document.body.dataset.hydrate) {
14
14
  hydrateRoot(document.body, rootElement);
15
15
  } else {
16
16
  createRoot(document.body).render(rootElement);
@@ -4,21 +4,21 @@
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "scripts": {
7
- "dev": "waku dev --with-ssr",
8
- "build": "waku build --with-ssr",
9
- "start": "waku start --with-ssr"
7
+ "dev": "waku dev",
8
+ "build": "waku build",
9
+ "start": "waku start"
10
10
  },
11
11
  "dependencies": {
12
- "react": "18.3.0-canary-6c3b8dbfe-20240226",
13
- "react-dom": "18.3.0-canary-6c3b8dbfe-20240226",
14
- "react-server-dom-webpack": "18.3.0-canary-6c3b8dbfe-20240226",
15
- "waku": "0.19.4"
12
+ "react": "18.3.0-canary-0066e0b68-20240306",
13
+ "react-dom": "18.3.0-canary-0066e0b68-20240306",
14
+ "react-server-dom-webpack": "18.3.0-canary-0066e0b68-20240306",
15
+ "waku": "0.20.0-alpha.0"
16
16
  },
17
17
  "devDependencies": {
18
- "@types/react": "18.2.59",
19
- "@types/react-dom": "18.2.19",
20
- "typescript": "5.3.3",
21
- "vite": "5.1.4",
18
+ "@types/react": "18.2.64",
19
+ "@types/react-dom": "18.2.21",
20
+ "typescript": "5.4.2",
21
+ "vite": "5.1.5",
22
22
  "vite-tsconfig-paths": "4.3.1"
23
23
  }
24
24
  }
@@ -10,7 +10,7 @@ const rootElement = (
10
10
  </StrictMode>
11
11
  );
12
12
 
13
- if (import.meta.env.WAKU_HYDRATE) {
13
+ if (document.body.dataset.hydrate) {
14
14
  hydrateRoot(document.body, rootElement);
15
15
  } else {
16
16
  createRoot(document.body).render(rootElement);
@@ -4,22 +4,22 @@
4
4
  "type": "module",
5
5
  "private": true,
6
6
  "scripts": {
7
- "dev": "waku dev --with-ssr",
8
- "build": "waku build --with-ssr",
9
- "start": "waku start --with-ssr"
7
+ "dev": "waku dev",
8
+ "build": "waku build",
9
+ "start": "waku start"
10
10
  },
11
11
  "dependencies": {
12
- "react": "18.3.0-canary-6c3b8dbfe-20240226",
13
- "react-dom": "18.3.0-canary-6c3b8dbfe-20240226",
14
- "react-server-dom-webpack": "18.3.0-canary-6c3b8dbfe-20240226",
12
+ "react": "18.3.0-canary-0066e0b68-20240306",
13
+ "react-dom": "18.3.0-canary-0066e0b68-20240306",
14
+ "react-server-dom-webpack": "18.3.0-canary-0066e0b68-20240306",
15
15
  "react-tweet": "^3.2.0",
16
- "waku": "0.19.4"
16
+ "waku": "0.20.0-alpha.0"
17
17
  },
18
18
  "devDependencies": {
19
- "@types/react": "18.2.59",
20
- "@types/react-dom": "18.2.19",
21
- "autoprefixer": "10.4.17",
19
+ "@types/react": "18.2.64",
20
+ "@types/react-dom": "18.2.21",
21
+ "autoprefixer": "10.4.18",
22
22
  "tailwindcss": "3.4.1",
23
- "typescript": "5.3.3"
23
+ "typescript": "5.4.2"
24
24
  }
25
25
  }
@@ -12,7 +12,7 @@ const rootElement = (
12
12
  </StrictMode>
13
13
  );
14
14
 
15
- if (import.meta.env.WAKU_HYDRATE) {
15
+ if (document.body.dataset.hydrate) {
16
16
  hydrateRoot(document.body, rootElement);
17
17
  } else {
18
18
  createRoot(document.body).render(rootElement);
@@ -11,7 +11,7 @@ export const RootLayout = async ({ children }: RootLayoutProps) => {
11
11
  const data = await getData();
12
12
 
13
13
  return (
14
- <div id="__waku" className="font-['Nunito']">
14
+ <div className="font-['Nunito']">
15
15
  <meta property="description" content={data.description} />
16
16
  <link rel="icon" type="image/png" href={data.icon} />
17
17
  <Header />
@@ -9,9 +9,6 @@
9
9
  "noUncheckedIndexedAccess": true,
10
10
  "exactOptionalPropertyTypes": true,
11
11
  "types": ["react/experimental"],
12
- "jsx": "react-jsx",
13
- "paths": {
14
- "~/*": ["./src/*"]
15
- }
12
+ "jsx": "react-jsx"
16
13
  }
17
14
  }
@@ -1,15 +1,17 @@
1
1
  /** @type {import('vite').UserConfig} */
2
- export default ({ mode }: { mode: string }) => ({
3
- ...(mode === 'development' && {
4
- optimizeDeps: {
5
- include: [
6
- 'react-dom/client',
7
- 'react-tweet > use-sync-external-store/shim/index.js',
8
- 'react-tweet > date-fns/format/index.js',
9
- ],
10
- },
11
- ssr: {
12
- noExternal: ['react-tweet'],
13
- },
14
- }),
15
- });
2
+ export default ({ mode }: { mode: string }) => {
3
+ if (mode === 'development') {
4
+ return {
5
+ optimizeDeps: {
6
+ include: [
7
+ 'react-tweet > use-sync-external-store/shim/index.js',
8
+ 'react-tweet > date-fns/format/index.js',
9
+ ],
10
+ },
11
+ ssr: {
12
+ noExternal: ['react-tweet'],
13
+ },
14
+ };
15
+ }
16
+ return {};
17
+ };
@@ -1,24 +0,0 @@
1
- import express from 'express';
2
- import cookieParser from 'cookie-parser';
3
- import { unstable_connectMiddleware as connectMiddleware } from 'waku/dev';
4
-
5
- const withSsr = process.argv[2] === '--with-ssr';
6
-
7
- const app = express();
8
- app.use(cookieParser());
9
- app.use(
10
- connectMiddleware({
11
- unstable_prehook: (req) => {
12
- return { count: Number(req.orig.cookies.count) || 0 };
13
- },
14
- unstable_posthook: (req, res, ctx) => {
15
- res.orig.cookie('count', String(ctx.count));
16
- },
17
- ssr: withSsr,
18
- }),
19
- );
20
-
21
- const port = process.env.PORT || 3000;
22
- app.listen(port, () => {
23
- console.info('Listening on', port);
24
- });
@@ -1,32 +0,0 @@
1
- import { fileURLToPath, pathToFileURL } from 'node:url';
2
- import path from 'node:path';
3
- import express from 'express';
4
- import cookieParser from 'cookie-parser';
5
- import { unstable_connectMiddleware as connectMiddleware } from 'waku/prd';
6
-
7
- const withSsr = process.argv[2] === '--with-ssr';
8
-
9
- const root = path.dirname(fileURLToPath(import.meta.url));
10
-
11
- const app = express();
12
- app.use(cookieParser());
13
- app.use(
14
- connectMiddleware({
15
- loadEntries: () =>
16
- import(pathToFileURL(path.join(root, 'dist', 'entries.js')).toString()),
17
- unstable_prehook: (req) => {
18
- return { count: Number(req.orig.cookies.count) || 0 };
19
- },
20
- unstable_posthook: (req, res, ctx) => {
21
- res.orig.cookie('count', String(ctx.count));
22
- },
23
- ssr: withSsr,
24
- }),
25
- );
26
- app.use(express.static(path.join(root, 'dist', 'public')));
27
- express.static.mime.default_type = '';
28
-
29
- const port = process.env.PORT || 8080;
30
- app.listen(port, () => {
31
- console.info('Listening on', port);
32
- });