From 3c7a2bd6d5db09bad2919aeec2cdf94499a2ec08 Mon Sep 17 00:00:00 2001 From: Tyler <26290074+thegitduck@users.noreply.github.com> Date: Thu, 23 Jan 2025 22:36:08 -0800 Subject: [PATCH 01/11] docs: Form example for demo --- examples/06_form-demo/.gitignore | 7 ++ examples/06_form-demo/package.json | 24 ++++++ examples/06_form-demo/postcss.config.js | 7 ++ .../06_form-demo/public/images/favicon.png | Bin 0 -> 5713 bytes examples/06_form-demo/src/components/App.tsx | 27 +++++++ .../06_form-demo/src/components/Counter.tsx | 27 +++++++ examples/06_form-demo/src/components/Form.tsx | 53 +++++++++++++ .../src/components/ServerForm.tsx | 67 ++++++++++++++++ .../06_form-demo/src/components/footer.tsx | 18 +++++ examples/06_form-demo/src/components/funcs.ts | 17 ++++ .../06_form-demo/src/components/header.tsx | 19 +++++ examples/06_form-demo/src/pages/_layout.tsx | 39 +++++++++ examples/06_form-demo/src/pages/index.tsx | 27 +++++++ examples/06_form-demo/src/styles.css | 5 ++ examples/06_form-demo/tailwind.config.js | 10 +++ examples/06_form-demo/tsconfig.json | 15 ++++ pnpm-lock.yaml | 75 +++++++++--------- 17 files changed, 398 insertions(+), 39 deletions(-) create mode 100644 examples/06_form-demo/.gitignore create mode 100644 examples/06_form-demo/package.json create mode 100644 examples/06_form-demo/postcss.config.js create mode 100644 examples/06_form-demo/public/images/favicon.png create mode 100644 examples/06_form-demo/src/components/App.tsx create mode 100644 examples/06_form-demo/src/components/Counter.tsx create mode 100644 examples/06_form-demo/src/components/Form.tsx create mode 100644 examples/06_form-demo/src/components/ServerForm.tsx create mode 100644 examples/06_form-demo/src/components/footer.tsx create mode 100644 examples/06_form-demo/src/components/funcs.ts create mode 100644 examples/06_form-demo/src/components/header.tsx create mode 100644 examples/06_form-demo/src/pages/_layout.tsx create mode 100644 examples/06_form-demo/src/pages/index.tsx create mode 100644 examples/06_form-demo/src/styles.css create mode 100644 examples/06_form-demo/tailwind.config.js create mode 100644 examples/06_form-demo/tsconfig.json diff --git a/examples/06_form-demo/.gitignore b/examples/06_form-demo/.gitignore new file mode 100644 index 000000000..ad583432d --- /dev/null +++ b/examples/06_form-demo/.gitignore @@ -0,0 +1,7 @@ +node_modules +dist +.env* +*.tsbuildinfo +.cache +.DS_Store +*.pem diff --git a/examples/06_form-demo/package.json b/examples/06_form-demo/package.json new file mode 100644 index 000000000..8d21174d6 --- /dev/null +++ b/examples/06_form-demo/package.json @@ -0,0 +1,24 @@ +{ + "name": "06_form-demo", + "version": "0.1.0", + "type": "module", + "private": true, + "scripts": { + "dev": "waku dev", + "build": "waku build", + "start": "waku start" + }, + "dependencies": { + "react": "19.0.0", + "react-dom": "19.0.0", + "react-server-dom-webpack": "19.0.0", + "waku": "0.21.17" + }, + "devDependencies": { + "@types/react": "19.0.8", + "@types/react-dom": "19.0.3", + "autoprefixer": "10.4.20", + "tailwindcss": "3.4.17", + "typescript": "5.7.3" + } +} diff --git a/examples/06_form-demo/postcss.config.js b/examples/06_form-demo/postcss.config.js new file mode 100644 index 000000000..709af5d83 --- /dev/null +++ b/examples/06_form-demo/postcss.config.js @@ -0,0 +1,7 @@ +/** @type {import('postcss-load-config').Config} */ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/examples/06_form-demo/public/images/favicon.png b/examples/06_form-demo/public/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..cd90d79081c0c40519bb3c583f5b43357b3fcd3b GIT binary patch literal 5713 zcmV-X7Ov@uP)Tr@Rcq~iUiaBO-93-CXKarrPH@JF#<4@} zjKMgPLx_y90!WA;Bt&8)zBm#R@&}Y4K7o)3LJ0VTz%l_TL;=YnawHzkL@1H+@I#5m z9(!=+)zj16x9`2@?8jPFd{}#*du~sU`&{zDNGaW;^ICWBs#^8l>Q*bNY}U2RZW*N^T~vh<&sG~A?JjdQPmY~+cBL? zXuFP_GdnvwG)+U>bxbBTF;=uqyIwC28k7(ttJR9WPXM?XqDmDbv+0zS6RA(^?Co-T zx+HZSv)PoCjg&JnD3iLT?OIfn7$d7@#e6bl*{ld6OzMiZ>sd^voVE>B4D@Zsw65q0 zxQ3@fke|GJ^Yy*hcKzQy*xx@C(NCtFe_!BtRQP>FzNTWYyE{2&+m5O-#Aj=SBetNQD} zR~L)LSEGincKGum@`5|b%y9>E$IQMeL}K~;$%UJ{iLsOSgkD&ja3t-*Q8++GNa z**8@5>$O+k$RT`7L|#JV21x6G4~&^vu^N~2%nz1SzAMVy{2q}(xuci@B8bRDM6Xm; z^{9xvBZTnI#bW+$Re9G=wRorF*G&WsLX%RtMs`xpWXq(~lT#+=MAtT_vFqBjJUu-p zyow;hGa{&fsGy>V$YZMdSP1%~387LYhJfd^s$;lOhuN#B{E>*~0-Tie9;f9+Z ziOQ*{HX+27ISmdjhNwyiT8rvTOlL91h2VRWy1EvHD?#Oy|4&r44D>tGg4#~9U9B0FGCZ)t1 zcaA?0bI!kUa@u@$Hk_z>hJW-(q^ct~EO0D=QiNd6+)N%mb=7#IO-w@Z z(s4I>7g~pv3oXjhg;N)nC@WD;OgMF=5utHmX+rBl66hV$sHDN(MFm_?DZ&Mu+KW|9 z+?XrO1g37x)L01224UyoOx&5PGsUUIh=bA{C%MyR=h~#=@4q74TBc?`k=IXqf3EBL ze-KrWDn{HPD5!@0RcBY;wg`BOC`*{319&CI!1j~`Ra{{lQ zeJ%LLMecbmxG0?qlZo(4&)@#iU;WC_)3L4z!jKJ$Eb+X4o$FQPR(3x7TK*>=d1~=5 zpK^h~gft6qw zL_i%3#DxjqUCY0Ft^bEngiGQajd5nJsu0RuRKT2TI8{(?_E~=JTK$I~zC3yN5+%k! zQ03CB=K5kia_b&6C#V?~8x*5jib!&=L^$gD%8H^@UU zG!fi|0%?Vid)$m{j*9Zys^!(=C7z9xjfH1^^^NWe|LR8jWBXC*%vp`H4vLm}6)}fJ z6?n21zHztV=AfcK74_k#r}DAReP^cZym)G}XRl44`T6%h`ia~d*{nqSgG+q=>u=on zZ-4OZuRk`Ec@UjcDiJ|_jRpuA=XUSR3GdtCPCDxD& zBGhSQZ#wzX-lG16?9Q#$=p2^r)GB;vf5Ni|HEnNvb(K&F2tr(FL1m3OH+tiH78QT_ zkp=HRobcjF&qAP9PQI~p2JGpPrYL=|I%x#{Bq+QXy70S2eE*&DSE>2-*{|?+gXT1JVP82VJ9euP2xY_z0IvQ(`2LW604ymm`UjYR5)+nn{iM_ zIy2ObCK<1`Mi^pHa_9y(k7!N~?o(Q`>DP@r>XMY%`4q$albnO{r9;7jsM=RyMmF0CkpulPN+qX@A zCp*F2B?EcL5~<|;`b&57=U#067v}@Kja|wB9QDrSMfkvv?8pz_$zDL)W572a74xZcfGfN1=+G<=oqDCKfX`|M(;oq}EZ!lB%t+cFOUbS2fMe8w4 zro$#=s_Ei@gMC7m3%?r9o3KL4#!+ttOs%84FsqeGD8X;EsyD&eQTWaHaK0IMUJ2uq zk9~j=5FMfEd-|?J%s~ULfk}+)Ou|}nMKCus=i<&$2{&)|BvJTr(f&rx#_S;@zr)1P$2wXpFe-pq$hWjx|AEG5n%$IA!+1DQ7=aeAeE`u45 z9+u5%(e_lOLWUa!^Yc5iUT0>Nxw!T@|Z> z(xr04xEOpP0~Nxe(so*U9yle^cUB&+1_~@el?b|(_ z8@oZ)7ACNAbxaE88ADR!9tu((BR|=Tdwk@u;s^EueU}!^sv+Zq5QwoRn&CpcwmU-? z72mvdgqu-mQLc}!zY!ldKl6KdSSOB+$Er_U-<|WW%ljzI`jlu>OKuvHE6W6T8>`=5 z3)imHeEw$3GCO;*G)%@!b}zlzu8g0!9H~?|O8y-u z!1w)bQ}}200`J>XUgGXHyod6c+0Vj)>? zoGi^)nbWxQa+^7f@bSxmTH*LUN{DxcbdV@8cSnqm9aOx3FL0boSsRCt8H>i%H(~8S z+1%S?bkPQtK%0%asyLX>Ia)4uN(~*`XH1hhk?9 zdqptE#sXxMnv|(y#qMm*$!a;U`_@WW_K9md3*K|(kk+gSKgvmQ&-50@6a-`zg?TvN z`0O06PlZojtN6yssPv43_~}O`=Px3#%Hs^+JfkM15IP@H*0|d@CqkRlnNd|03+60W z%d;EZNBFCY8Baa5Pro7jrZDE^arRlU##$jc`fpz85fLW$Ts{MfAS`qPZ3FSO{@kU= zY{K>PYCcZRz9B(MZe6c)aNNpl?*6`RITK=JI-Sw9t2epOvQIp+Gv~e6E|JaX&6wXO zaR*?c!;p}($?%}4@XgjFAwJtO^~B}^!Kw4J&PuWTGps*MpLwgxIbhicv1S5vZ8NYh ztop>mJH_X_Z1fvGe=n73@A;a_DOM4L@K~*ykv!e<_*dL^AwX8B6&cISO$#x^jZ(0D zCo)S&j6|k%?TV()TwTn0_d^FXHVA+I(&wl%M*?=*`&LO3se`V)oKJqLg{3m?Y=v#%={=PsvI7C zp-q9dfX3(67lq~ZsnFqbb4LU{2%`N(6fnJCm*6dE7K9+8Wn_DXs7h{j`e8ROLfc26 zVSTXxqYO^Uj1s{LD%W`s>FokBU|UT=Ija-JLmslQ4@XbU2Pizez6hmlBH!lvqGa>o zNX?jy%mo3RiD(q4w~YbQ3*3CB5D_&mvo&)k3_G5EIIiXwGike^0tO|Q>UzAFuj7F3 z1_?>e7jfT-F6II%L_tUYAM5<=LR9r>R3>q|JC7;9?H`04&r>qUc_;=;6?|w|wuZt77l>Io2optmFQ%`96_|jkdaZ#?LdO0HFB4qI1q2DqHstC!Qo35|opEd~QDhBX91vBKne~=PwjB^s8PK2#p`@^_$Q>$?VfSUt z!?2G!SJ9bL>i#XR7k4j^1rB3DSZO#OGj4&Iky7%m?`d^AP-B$Z{%x}wWjkh})B;h} z8RmXtHZX($@<=8A%^sFkT6hnl_`6%~Snon-i9O-uAQbbf#fjcicPs9$+MCav^c=RS zoY#8*jrDvL(u0=u6;(9$*IjGbxq#db3N&>Ou`53_g?s6}|Rg64Sn zPfxn^bKwH1wcj#ptjYPl-Np1Pw}577J`_Oa1Qi|$&g(bt{#euJFX$G0#8tnY31%}w z6<^sEn8uCs4qCWsJaFWCvvW}2KCs9Ie{bdPZE{bSqs9@Eqhnj5jLI_Fu1Crbpm_IW zwbhNYIkQPE3hTzFND-i%i84x$Xp64KAB%=_aT^CI#@`05XYb1%h$ocTQ0Uri7L#Jt{oMYU=0=9)rA&jj z7bkVy_KN{`zZpb+t*i8!8Qo}c>o&0yvU3AuEBE@pG;IDn-NKh&00000NkvXXu0mjf DW2Q3= literal 0 HcmV?d00001 diff --git a/examples/06_form-demo/src/components/App.tsx b/examples/06_form-demo/src/components/App.tsx new file mode 100644 index 000000000..afb38d4ff --- /dev/null +++ b/examples/06_form-demo/src/components/App.tsx @@ -0,0 +1,27 @@ +import { Counter } from './Counter'; +import { Form } from './Form'; +import { ServerForm } from './ServerForm'; +import { getMessage, greet, increment } from './funcs'; + +const App = ({ name }: { name: string }) => { + return ( + + + Waku + + +
+

Hello {name}!!

+

This is a server component.

+ +
+ +
+ + + ); +}; + +export default App; diff --git a/examples/06_form-demo/src/components/Counter.tsx b/examples/06_form-demo/src/components/Counter.tsx new file mode 100644 index 000000000..0102207de --- /dev/null +++ b/examples/06_form-demo/src/components/Counter.tsx @@ -0,0 +1,27 @@ +'use client'; + +import { useActionState } from 'react'; +import { useFormStatus } from 'react-dom'; + +const FormStatus = () => { + const { pending } = useFormStatus(); + return pending ? 'Pending...' : null; +}; + +export const Counter = ({ + increment, +}: { + increment: (count: number) => Promise; +}) => { + const [count, dispatch] = useActionState(increment, 0); + return ( +
+ +

Count: {count}

+ + + +

This is a client component.

+
+ ); +}; diff --git a/examples/06_form-demo/src/components/Form.tsx b/examples/06_form-demo/src/components/Form.tsx new file mode 100644 index 000000000..5bae7a169 --- /dev/null +++ b/examples/06_form-demo/src/components/Form.tsx @@ -0,0 +1,53 @@ +'use client'; + +import { useFormStatus } from 'react-dom'; + +const SubmitButton = () => { + const { pending } = useFormStatus(); + return ( + <> + + + ); +}; + +export const Form = ({ + message, + greet, +}: { + message: Promise; + greet: (formData: FormData) => Promise; +}) => ( +
+

{message ?? ' '}

+
+
+
+ Name:{' '} + +
+
+ Email:{' '} + +
+ +
+
+

This is a client component.

+
+); diff --git a/examples/06_form-demo/src/components/ServerForm.tsx b/examples/06_form-demo/src/components/ServerForm.tsx new file mode 100644 index 000000000..05b87d1d1 --- /dev/null +++ b/examples/06_form-demo/src/components/ServerForm.tsx @@ -0,0 +1,67 @@ +async function submitUserProfile(formData: FormData) { + 'use server'; + const name = formData.get('name'); + const age = formData.get('age'); + const favoriteColor = formData.get('favoriteColor'); + const hobby = formData.get('hobby'); + const isSubscribed = formData.get('newsletter') === 'on'; + + console.log({ + name, + age, + favoriteColor, + hobby, + isSubscribed, + }); +} + +export const ServerForm = () => { + return ( +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + +
+ ); +}; diff --git a/examples/06_form-demo/src/components/footer.tsx b/examples/06_form-demo/src/components/footer.tsx new file mode 100644 index 000000000..8cfd9c897 --- /dev/null +++ b/examples/06_form-demo/src/components/footer.tsx @@ -0,0 +1,18 @@ +export const Footer = () => { + return ( + + ); +}; diff --git a/examples/06_form-demo/src/components/funcs.ts b/examples/06_form-demo/src/components/funcs.ts new file mode 100644 index 000000000..138ac8144 --- /dev/null +++ b/examples/06_form-demo/src/components/funcs.ts @@ -0,0 +1,17 @@ +'use server'; + +import { unstable_rerenderRoute } from 'waku/router/server'; + +// module state on server +let message = ''; + +export const getMessage = async () => message; + +export const greet = async (formData: FormData) => { + // simulate a slow server response + await new Promise((resolve) => setTimeout(resolve, 1000)); + message = `Hello ${formData.get('name') || 'Anonymous'} from server!`; + unstable_rerenderRoute('/'); +}; + +export const increment = async (count: number) => count + 1; diff --git a/examples/06_form-demo/src/components/header.tsx b/examples/06_form-demo/src/components/header.tsx new file mode 100644 index 000000000..13ddf7b5e --- /dev/null +++ b/examples/06_form-demo/src/components/header.tsx @@ -0,0 +1,19 @@ +import { Link } from 'waku'; + +export const Header = () => { + return ( +
+

+ Waku form demo +

+ + (source) + +
+ ); +}; diff --git a/examples/06_form-demo/src/pages/_layout.tsx b/examples/06_form-demo/src/pages/_layout.tsx new file mode 100644 index 000000000..02e79cfe5 --- /dev/null +++ b/examples/06_form-demo/src/pages/_layout.tsx @@ -0,0 +1,39 @@ +import '../styles.css'; + +import type { ReactNode } from 'react'; + +import { Header } from '../components/header'; +import { Footer } from '../components/footer'; + +type RootLayoutProps = { children: ReactNode }; + +export default async function RootLayout({ children }: RootLayoutProps) { + const data = await getData(); + + return ( +
+ + +
+
+ {children} +
+
+
+ ); +} + +const getData = async () => { + const data = { + description: 'An internet website!', + icon: '/images/favicon.png', + }; + + return data; +}; + +export const getConfig = async () => { + return { + render: 'static', + } as const; +}; diff --git a/examples/06_form-demo/src/pages/index.tsx b/examples/06_form-demo/src/pages/index.tsx new file mode 100644 index 000000000..280668c19 --- /dev/null +++ b/examples/06_form-demo/src/pages/index.tsx @@ -0,0 +1,27 @@ +import { Form } from '../components/Form'; +import { getMessage, greet } from '../components/funcs'; +import { ServerForm } from '../components/ServerForm'; + +export default async function HomePage() { + return ( + <> + Waku pokemon +
+
+

Server Form

+ +
+
+

Client Form

+
+
+
+ + ); +} + +export const getConfig = async () => { + return { + render: 'dynamic', + } as const; +}; diff --git a/examples/06_form-demo/src/styles.css b/examples/06_form-demo/src/styles.css new file mode 100644 index 000000000..06dc50147 --- /dev/null +++ b/examples/06_form-demo/src/styles.css @@ -0,0 +1,5 @@ +@import url('https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,400;0,700;1,400;1,700&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Zen+Maru+Gothic:wght@400;700&display=swap'); +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/examples/06_form-demo/tailwind.config.js b/examples/06_form-demo/tailwind.config.js new file mode 100644 index 000000000..b15775b26 --- /dev/null +++ b/examples/06_form-demo/tailwind.config.js @@ -0,0 +1,10 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ['./src/**/*.{js,jsx,ts,tsx}'], + theme: { + fontFamily: { + nunito: ['"Nunito"', 'sans-serif'], + 'zen-maru-gothic': ['"Zen Maru Gothic"', 'serif'], + }, + }, +}; diff --git a/examples/06_form-demo/tsconfig.json b/examples/06_form-demo/tsconfig.json new file mode 100644 index 000000000..33d25f480 --- /dev/null +++ b/examples/06_form-demo/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "strict": true, + "target": "esnext", + "downlevelIteration": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "skipLibCheck": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "types": ["node", "react/experimental"], + "jsx": "react-jsx" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3f37e4de7..9dd2cfbcd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -669,6 +669,37 @@ importers: specifier: 5.7.3 version: 5.7.3 + examples/06_form-demo: + dependencies: + react: + specifier: 19.0.0 + version: 19.0.0 + react-dom: + specifier: 19.0.0 + version: 19.0.0(react@19.0.0) + react-server-dom-webpack: + specifier: 19.0.0 + version: 19.0.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(webpack@5.97.1) + waku: + specifier: 0.21.17 + version: link:../../packages/waku + devDependencies: + '@types/react': + specifier: 19.0.8 + version: 19.0.8 + '@types/react-dom': + specifier: 19.0.3 + version: 19.0.3(@types/react@19.0.8) + autoprefixer: + specifier: 10.4.20 + version: 10.4.20(postcss@8.5.1) + tailwindcss: + specifier: 3.4.17 + version: 3.4.17 + typescript: + specifier: 5.7.3 + version: 5.7.3 + examples/11_fs-router: dependencies: react: @@ -1265,7 +1296,7 @@ importers: version: 7.4.3 tsup: specifier: ^8.3.5 - version: 8.3.5(@swc/core@1.10.9)(jiti@2.4.2)(postcss@8.5.1)(tsx@4.19.2)(typescript@5.7.3)(yaml@2.7.0) + version: 8.3.5(@swc/core@1.10.9(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.5.1)(tsx@4.19.2)(typescript@5.7.3)(yaml@2.7.0) update-check: specifier: ^1.5.4 version: 1.5.4 @@ -3168,11 +3199,6 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.24.3: - resolution: {integrity: sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - browserslist@4.24.4: resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -3233,9 +3259,6 @@ packages: resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} engines: {node: '>=14.16'} - caniuse-lite@1.0.30001690: - resolution: {integrity: sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==} - caniuse-lite@1.0.30001695: resolution: {integrity: sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==} @@ -3526,9 +3549,6 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.5.77: - resolution: {integrity: sha512-AnJSrt5JpRVgY6dgd5yccguLc5A7oMSF0Kt3fcW+Hp5WTuFbl5upeSFZbMZYy2o7jhmIhU8Ekrd82GhyXUqUUg==} - electron-to-chromium@1.5.86: resolution: {integrity: sha512-/D7GAAaCRBQFBBcop6SfAAGH37djtpWkOuYhyAajw0l5vsfeSsUQYxaFPwr1c/mC/flARCDdKFo5gpFqNI+18w==} @@ -5710,12 +5730,6 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} - update-browserslist-db@1.1.1: - resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - update-browserslist-db@1.1.2: resolution: {integrity: sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==} hasBin: true @@ -6124,7 +6138,7 @@ snapshots: dependencies: '@babel/compat-data': 7.26.3 '@babel/helper-validator-option': 7.25.9 - browserslist: 4.24.3 + browserslist: 4.24.4 lru-cache: 5.1.1 semver: 6.3.1 @@ -7720,13 +7734,6 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.24.3: - dependencies: - caniuse-lite: 1.0.30001690 - electron-to-chromium: 1.5.77 - node-releases: 2.0.19 - update-browserslist-db: 1.1.1(browserslist@4.24.3) - browserslist@4.24.4: dependencies: caniuse-lite: 1.0.30001695 @@ -7787,8 +7794,6 @@ snapshots: camelcase@7.0.1: {} - caniuse-lite@1.0.30001690: {} - caniuse-lite@1.0.30001695: {} capnp-ts@0.7.0: @@ -8029,8 +8034,6 @@ snapshots: eastasianwidth@0.2.0: {} - electron-to-chromium@1.5.77: {} - electron-to-chromium@1.5.86: {} emoji-regex-xs@1.0.0: {} @@ -8294,7 +8297,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@9.18.0(jiti@2.4.2)))(eslint@9.18.0(jiti@2.4.2)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.18.0(jiti@2.4.2)): dependencies: debug: 3.2.7 optionalDependencies: @@ -8316,7 +8319,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.18.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@9.18.0(jiti@2.4.2)))(eslint@9.18.0(jiti@2.4.2)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.18.0(jiti@2.4.2)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -10615,7 +10618,7 @@ snapshots: tslib@2.8.1: {} - tsup@8.3.5(@swc/core@1.10.9)(jiti@2.4.2)(postcss@8.5.1)(tsx@4.19.2)(typescript@5.7.3)(yaml@2.7.0): + tsup@8.3.5(@swc/core@1.10.9(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.5.1)(tsx@4.19.2)(typescript@5.7.3)(yaml@2.7.0): dependencies: bundle-require: 5.1.0(esbuild@0.24.2) cac: 6.7.14 @@ -10788,12 +10791,6 @@ snapshots: universalify@2.0.1: {} - update-browserslist-db@1.1.1(browserslist@4.24.3): - dependencies: - browserslist: 4.24.3 - escalade: 3.2.0 - picocolors: 1.1.1 - update-browserslist-db@1.1.2(browserslist@4.24.4): dependencies: browserslist: 4.24.4 From 656d7adf5c63b19b9014d00081ea4c3465da6dac Mon Sep 17 00:00:00 2001 From: Tyler <26290074+thegitduck@users.noreply.github.com> Date: Thu, 23 Jan 2025 23:26:09 -0800 Subject: [PATCH 02/11] fix tests --- examples/06_form-demo/src/pages/_layout.tsx | 1 + examples/06_form-demo/src/pages/index.tsx | 21 +++++++++------------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/examples/06_form-demo/src/pages/_layout.tsx b/examples/06_form-demo/src/pages/_layout.tsx index 02e79cfe5..78f1b0452 100644 --- a/examples/06_form-demo/src/pages/_layout.tsx +++ b/examples/06_form-demo/src/pages/_layout.tsx @@ -12,6 +12,7 @@ export default async function RootLayout({ children }: RootLayoutProps) { return (
+ Waku
diff --git a/examples/06_form-demo/src/pages/index.tsx b/examples/06_form-demo/src/pages/index.tsx index 280668c19..481fc51ef 100644 --- a/examples/06_form-demo/src/pages/index.tsx +++ b/examples/06_form-demo/src/pages/index.tsx @@ -4,19 +4,16 @@ import { ServerForm } from '../components/ServerForm'; export default async function HomePage() { return ( - <> - Waku pokemon -
-
-

Server Form

- -
-
-

Client Form

- -
+
+
+

Server Form

+
- +
+

Client Form

+ +
+
); } From e87a98d2179cef74d7f1553a502cbf24d78a7291 Mon Sep 17 00:00:00 2001 From: Tyler <26290074+thegitduck@users.noreply.github.com> Date: Thu, 23 Jan 2025 23:54:23 -0800 Subject: [PATCH 03/11] await message result before rendering --- examples/06_form-demo/src/pages/index.tsx | 2 +- examples/36_form/src/components/App.tsx | 4 ++-- examples/36_form/src/components/Form.tsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/06_form-demo/src/pages/index.tsx b/examples/06_form-demo/src/pages/index.tsx index 481fc51ef..6a84efb3a 100644 --- a/examples/06_form-demo/src/pages/index.tsx +++ b/examples/06_form-demo/src/pages/index.tsx @@ -11,7 +11,7 @@ export default async function HomePage() {

Client Form

- +
); diff --git a/examples/36_form/src/components/App.tsx b/examples/36_form/src/components/App.tsx index afb38d4ff..5a39b33d2 100644 --- a/examples/36_form/src/components/App.tsx +++ b/examples/36_form/src/components/App.tsx @@ -3,7 +3,7 @@ import { Form } from './Form'; import { ServerForm } from './ServerForm'; import { getMessage, greet, increment } from './funcs'; -const App = ({ name }: { name: string }) => { +const App = async ({ name }: { name: string }) => { return ( @@ -16,7 +16,7 @@ const App = ({ name }: { name: string }) => {

Hello {name}!!

This is a server component.

- + diff --git a/examples/36_form/src/components/Form.tsx b/examples/36_form/src/components/Form.tsx index 4c2f348e2..4abd4773a 100644 --- a/examples/36_form/src/components/Form.tsx +++ b/examples/36_form/src/components/Form.tsx @@ -18,7 +18,7 @@ export const Form = ({ message, greet, }: { - message: Promise; + message: string; greet: (formData: FormData) => Promise; }) => (
From 8d73a793a12050feb7e2277c6d7314758a8b2f59 Mon Sep 17 00:00:00 2001 From: Tyler <26290074+thegitduck@users.noreply.github.com> Date: Thu, 23 Jan 2025 23:56:00 -0800 Subject: [PATCH 04/11] fix type --- examples/06_form-demo/src/components/Form.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/06_form-demo/src/components/Form.tsx b/examples/06_form-demo/src/components/Form.tsx index 5bae7a169..0bd5e99aa 100644 --- a/examples/06_form-demo/src/components/Form.tsx +++ b/examples/06_form-demo/src/components/Form.tsx @@ -21,7 +21,7 @@ export const Form = ({ message, greet, }: { - message: Promise; + message: string; greet: (formData: FormData) => Promise; }) => (
From 5e8a2983aa2aa327c16dee55de207c2cf9606a18 Mon Sep 17 00:00:00 2001 From: Tyler <26290074+thegitduck@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:34:06 -0800 Subject: [PATCH 05/11] missed one --- examples/06_form-demo/src/components/App.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/06_form-demo/src/components/App.tsx b/examples/06_form-demo/src/components/App.tsx index afb38d4ff..5a39b33d2 100644 --- a/examples/06_form-demo/src/components/App.tsx +++ b/examples/06_form-demo/src/components/App.tsx @@ -3,7 +3,7 @@ import { Form } from './Form'; import { ServerForm } from './ServerForm'; import { getMessage, greet, increment } from './funcs'; -const App = ({ name }: { name: string }) => { +const App = async ({ name }: { name: string }) => { return ( @@ -16,7 +16,7 @@ const App = ({ name }: { name: string }) => {

Hello {name}!!

This is a server component.

- +
From 720bb3ae8903da2611d0c9f017a65f2b812e7f2c Mon Sep 17 00:00:00 2001 From: Tyler <26290074+thegitduck@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:50:40 -0800 Subject: [PATCH 06/11] remove unused components --- examples/06_form-demo/src/components/App.tsx | 27 ------------------- .../06_form-demo/src/components/Counter.tsx | 27 ------------------- 2 files changed, 54 deletions(-) delete mode 100644 examples/06_form-demo/src/components/App.tsx delete mode 100644 examples/06_form-demo/src/components/Counter.tsx diff --git a/examples/06_form-demo/src/components/App.tsx b/examples/06_form-demo/src/components/App.tsx deleted file mode 100644 index 5a39b33d2..000000000 --- a/examples/06_form-demo/src/components/App.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { Counter } from './Counter'; -import { Form } from './Form'; -import { ServerForm } from './ServerForm'; -import { getMessage, greet, increment } from './funcs'; - -const App = async ({ name }: { name: string }) => { - return ( - - - Waku - - -
-

Hello {name}!!

-

This is a server component.

- - - -
- - - ); -}; - -export default App; diff --git a/examples/06_form-demo/src/components/Counter.tsx b/examples/06_form-demo/src/components/Counter.tsx deleted file mode 100644 index 0102207de..000000000 --- a/examples/06_form-demo/src/components/Counter.tsx +++ /dev/null @@ -1,27 +0,0 @@ -'use client'; - -import { useActionState } from 'react'; -import { useFormStatus } from 'react-dom'; - -const FormStatus = () => { - const { pending } = useFormStatus(); - return pending ? 'Pending...' : null; -}; - -export const Counter = ({ - increment, -}: { - increment: (count: number) => Promise; -}) => { - const [count, dispatch] = useActionState(increment, 0); - return ( -
- -

Count: {count}

- - - -

This is a client component.

-
- ); -}; From 0af968b572bbdb2905a3cd86efd0dfff39574ba5 Mon Sep 17 00:00:00 2001 From: Tyler <26290074+thegitduck@users.noreply.github.com> Date: Fri, 24 Jan 2025 19:22:19 -0800 Subject: [PATCH 07/11] fixes from review --- examples/06_form-demo/private/message.txt | 1 + examples/06_form-demo/src/components/Form.tsx | 2 +- examples/06_form-demo/src/components/funcs.ts | 15 ++++++++++----- 3 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 examples/06_form-demo/private/message.txt diff --git a/examples/06_form-demo/private/message.txt b/examples/06_form-demo/private/message.txt new file mode 100644 index 000000000..f85ff7304 --- /dev/null +++ b/examples/06_form-demo/private/message.txt @@ -0,0 +1 @@ +Hello from server! \ No newline at end of file diff --git a/examples/06_form-demo/src/components/Form.tsx b/examples/06_form-demo/src/components/Form.tsx index 0bd5e99aa..9a83bd312 100644 --- a/examples/06_form-demo/src/components/Form.tsx +++ b/examples/06_form-demo/src/components/Form.tsx @@ -25,7 +25,7 @@ export const Form = ({ greet: (formData: FormData) => Promise; }) => (
-

{message ?? ' '}

+

{message}

diff --git a/examples/06_form-demo/src/components/funcs.ts b/examples/06_form-demo/src/components/funcs.ts index 138ac8144..4c5e097ab 100644 --- a/examples/06_form-demo/src/components/funcs.ts +++ b/examples/06_form-demo/src/components/funcs.ts @@ -1,16 +1,21 @@ 'use server'; +import { readFile, writeFile } from 'node:fs/promises'; import { unstable_rerenderRoute } from 'waku/router/server'; -// module state on server -let message = ''; - -export const getMessage = async () => message; +export const getMessage = async () => { + const data = await readFile('./private/message.txt', 'utf8'); + return data; +}; export const greet = async (formData: FormData) => { // simulate a slow server response await new Promise((resolve) => setTimeout(resolve, 1000)); - message = `Hello ${formData.get('name') || 'Anonymous'} from server!`; + const currentData = await getMessage(); + await writeFile( + './private/message.txt', + currentData + '\n' + formData.get('name') + ' from server!', + ); unstable_rerenderRoute('/'); }; From 420c304d2ddbfdc66ed9b98d56c9f0c7c3ea2d93 Mon Sep 17 00:00:00 2001 From: daishi Date: Sat, 25 Jan 2025 13:45:39 +0900 Subject: [PATCH 08/11] revert 36_form --- examples/36_form/src/components/App.tsx | 4 ++-- examples/36_form/src/components/Form.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/36_form/src/components/App.tsx b/examples/36_form/src/components/App.tsx index 5a39b33d2..afb38d4ff 100644 --- a/examples/36_form/src/components/App.tsx +++ b/examples/36_form/src/components/App.tsx @@ -3,7 +3,7 @@ import { Form } from './Form'; import { ServerForm } from './ServerForm'; import { getMessage, greet, increment } from './funcs'; -const App = async ({ name }: { name: string }) => { +const App = ({ name }: { name: string }) => { return ( @@ -16,7 +16,7 @@ const App = async ({ name }: { name: string }) => {

Hello {name}!!

This is a server component.

- +
diff --git a/examples/36_form/src/components/Form.tsx b/examples/36_form/src/components/Form.tsx index 4abd4773a..4c2f348e2 100644 --- a/examples/36_form/src/components/Form.tsx +++ b/examples/36_form/src/components/Form.tsx @@ -18,7 +18,7 @@ export const Form = ({ message, greet, }: { - message: string; + message: Promise; greet: (formData: FormData) => Promise; }) => (
From 184d8cbc5813014c0763ce43974b3bf902c908ab Mon Sep 17 00:00:00 2001 From: daishi Date: Sat, 25 Jan 2025 23:50:31 +0900 Subject: [PATCH 09/11] hack renderHtml for now --- examples/36_form/src/components/App.tsx | 2 +- packages/waku/src/lib/renderers/html.ts | 78 ++++++++++++++++--------- 2 files changed, 51 insertions(+), 29 deletions(-) diff --git a/examples/36_form/src/components/App.tsx b/examples/36_form/src/components/App.tsx index afb38d4ff..879340765 100644 --- a/examples/36_form/src/components/App.tsx +++ b/examples/36_form/src/components/App.tsx @@ -15,8 +15,8 @@ const App = ({ name }: { name: string }) => { >

Hello {name}!!

This is a server component.

- +
diff --git a/packages/waku/src/lib/renderers/html.ts b/packages/waku/src/lib/renderers/html.ts index 5f81bd76d..383053e88 100644 --- a/packages/waku/src/lib/renderers/html.ts +++ b/packages/waku/src/lib/renderers/html.ts @@ -158,6 +158,9 @@ const rectifyHtml = () => { }); }; +// FIXME Why does it error on the rist time? +let hackToIgnoreTheVeryFirstError = true; + export async function renderHtml( config: PureConfig, ctx: Pick, @@ -220,34 +223,53 @@ export async function renderHtml( const htmlNode: Promise = createFromReadableStream(htmlStream, { serverConsumerManifest: { moduleMap, moduleLoading: null }, }); - const readable = await renderToReadableStream( - createElement( - ServerRoot as FunctionComponent< - Omit, 'children'> - >, - { elements: elementsPromise }, - htmlNode as any, - ), - { - formState: - actionResult === undefined - ? null - : await getExtractFormState(ctx)(actionResult), - onError(err: unknown) { - console.error(err); + try { + const readable = await renderToReadableStream( + createElement( + ServerRoot as FunctionComponent< + Omit, 'children'> + >, + { elements: elementsPromise }, + htmlNode as any, + ), + { + formState: + actionResult === undefined + ? null + : await getExtractFormState(ctx)(actionResult), + onError(err: unknown) { + if (hackToIgnoreTheVeryFirstError) { + return; + } + console.error(err); + }, }, - }, - ); - const injected: ReadableStream & { allReady?: Promise } = readable - .pipeThrough(rectifyHtml()) - .pipeThrough( - injectHtmlHead( - config.basePath + config.rscBase + '/' + encodeRscPath(rscPath), + ); + const injected: ReadableStream & { allReady?: Promise } = readable + .pipeThrough(rectifyHtml()) + .pipeThrough( + injectHtmlHead( + config.basePath + config.rscBase + '/' + encodeRscPath(rscPath), + htmlHead, + isDev ? `${config.basePath}${config.srcDir}/${SRC_MAIN}` : '', + ), + ) + .pipeThrough(injectRSCPayload(stream2)); + injected.allReady = readable.allReady; + return injected as never; + } catch (e) { + if (hackToIgnoreTheVeryFirstError) { + hackToIgnoreTheVeryFirstError = false; + return renderHtml( + config, + ctx, htmlHead, - isDev ? `${config.basePath}${config.srcDir}/${SRC_MAIN}` : '', - ), - ) - .pipeThrough(injectRSCPayload(stream2)); - injected.allReady = readable.allReady; - return injected as never; + elements, + html, + rscPath, + actionResult, + ); + } + throw e; + } } From c3e7ed0b1705b012dfecf53d41707c1607583f7d Mon Sep 17 00:00:00 2001 From: daishi Date: Sat, 25 Jan 2025 23:54:21 +0900 Subject: [PATCH 10/11] make Form in 06 like that in 36 --- examples/06_form-demo/src/components/Form.tsx | 2 +- examples/06_form-demo/src/pages/index.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/06_form-demo/src/components/Form.tsx b/examples/06_form-demo/src/components/Form.tsx index 9a83bd312..ea1e97ee0 100644 --- a/examples/06_form-demo/src/components/Form.tsx +++ b/examples/06_form-demo/src/components/Form.tsx @@ -21,7 +21,7 @@ export const Form = ({ message, greet, }: { - message: string; + message: Promise; greet: (formData: FormData) => Promise; }) => (
diff --git a/examples/06_form-demo/src/pages/index.tsx b/examples/06_form-demo/src/pages/index.tsx index 6a84efb3a..a049ca53d 100644 --- a/examples/06_form-demo/src/pages/index.tsx +++ b/examples/06_form-demo/src/pages/index.tsx @@ -2,7 +2,7 @@ import { Form } from '../components/Form'; import { getMessage, greet } from '../components/funcs'; import { ServerForm } from '../components/ServerForm'; -export default async function HomePage() { +export default function HomePage() { return (
@@ -11,7 +11,7 @@ export default async function HomePage() {

Client Form

- +
); From c078def558d219489ecc9d954922879c488a4e3a Mon Sep 17 00:00:00 2001 From: daishi Date: Sun, 26 Jan 2025 00:02:59 +0900 Subject: [PATCH 11/11] prefer inline directive --- examples/06_form-demo/src/components/funcs.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/06_form-demo/src/components/funcs.ts b/examples/06_form-demo/src/components/funcs.ts index 4c5e097ab..822cf91d3 100644 --- a/examples/06_form-demo/src/components/funcs.ts +++ b/examples/06_form-demo/src/components/funcs.ts @@ -1,5 +1,3 @@ -'use server'; - import { readFile, writeFile } from 'node:fs/promises'; import { unstable_rerenderRoute } from 'waku/router/server'; @@ -9,6 +7,7 @@ export const getMessage = async () => { }; export const greet = async (formData: FormData) => { + 'use server'; // simulate a slow server response await new Promise((resolve) => setTimeout(resolve, 1000)); const currentData = await getMessage(); @@ -19,4 +18,7 @@ export const greet = async (formData: FormData) => { unstable_rerenderRoute('/'); }; -export const increment = async (count: number) => count + 1; +export const increment = async (count: number) => { + 'use server'; + return count + 1; +};