waku 0.16.0 → 0.17.1
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.
- package/README.md +299 -0
- package/dist/cjs/cli.js +75 -331
- package/dist/cjs/client.js +67 -348
- package/dist/cjs/config.js +0 -9
- package/dist/cjs/lib/builder.js +339 -1013
- package/dist/cjs/lib/config.js +76 -243
- package/dist/cjs/lib/middleware/rsc/ssr.js +346 -0
- package/dist/cjs/lib/middleware/rsc/utils.js +40 -74
- package/dist/cjs/lib/middleware/rsc/worker-api.js +90 -375
- package/dist/cjs/lib/middleware/rsc/worker-impl.js +362 -1109
- package/dist/cjs/lib/middleware/rsc.js +198 -252
- package/dist/cjs/lib/vite-plugin/nonjs-resolve-plugin.js +19 -230
- package/dist/cjs/lib/vite-plugin/patch-react-refresh.js +33 -0
- package/dist/cjs/lib/vite-plugin/rsc-analyze-plugin.js +81 -35
- package/dist/cjs/lib/vite-plugin/rsc-delegate-plugin.js +31 -46
- package/dist/cjs/lib/vite-plugin/rsc-hmr-plugin.js +29 -176
- package/dist/cjs/lib/vite-plugin/rsc-index-plugin.js +22 -178
- package/dist/cjs/lib/vite-plugin/rsc-reload-plugin.js +30 -204
- package/dist/cjs/lib/vite-plugin/rsc-transform-plugin.js +57 -337
- package/dist/cjs/main.js +6 -180
- package/dist/cjs/node-loader.js +12 -210
- package/dist/cjs/router/client.js +89 -181
- package/dist/cjs/router/common.js +37 -121
- package/dist/cjs/router/server.js +81 -436
- package/dist/cjs/server.js +14 -30
- package/dist/cli.js +70 -310
- package/dist/client.d.ts +6 -2
- package/dist/client.js +65 -349
- package/dist/config.d.ts +13 -18
- package/dist/config.js +1 -3
- package/dist/lib/builder.d.ts +3 -1
- package/dist/lib/builder.js +339 -1013
- package/dist/lib/config.d.ts +14 -57
- package/dist/lib/config.js +41 -249
- package/dist/lib/middleware/rsc/ssr.d.ts +6 -0
- package/dist/lib/middleware/rsc/ssr.js +280 -0
- package/dist/lib/middleware/rsc/utils.d.ts +2 -3
- package/dist/lib/middleware/rsc/utils.js +34 -72
- package/dist/lib/middleware/rsc/worker-api.d.ts +29 -30
- package/dist/lib/middleware/rsc/worker-api.js +89 -371
- package/dist/lib/middleware/rsc/worker-impl.js +322 -1110
- package/dist/lib/middleware/rsc.d.ts +3 -2
- package/dist/lib/middleware/rsc.js +156 -256
- package/dist/lib/vite-plugin/nonjs-resolve-plugin.d.ts +1 -1
- package/dist/lib/vite-plugin/nonjs-resolve-plugin.js +19 -230
- package/dist/lib/vite-plugin/patch-react-refresh.d.ts +2 -0
- package/dist/lib/vite-plugin/patch-react-refresh.js +23 -0
- package/dist/lib/vite-plugin/rsc-analyze-plugin.d.ts +2 -2
- package/dist/lib/vite-plugin/rsc-analyze-plugin.js +78 -34
- package/dist/lib/vite-plugin/rsc-delegate-plugin.d.ts +1 -1
- package/dist/lib/vite-plugin/rsc-delegate-plugin.js +28 -45
- package/dist/lib/vite-plugin/rsc-hmr-plugin.d.ts +1 -1
- package/dist/lib/vite-plugin/rsc-hmr-plugin.js +29 -176
- package/dist/lib/vite-plugin/rsc-index-plugin.d.ts +1 -1
- package/dist/lib/vite-plugin/rsc-index-plugin.js +22 -178
- package/dist/lib/vite-plugin/rsc-reload-plugin.d.ts +2 -2
- package/dist/lib/vite-plugin/rsc-reload-plugin.js +27 -203
- package/dist/lib/vite-plugin/rsc-transform-plugin.d.ts +1 -1
- package/dist/lib/vite-plugin/rsc-transform-plugin.js +53 -335
- package/dist/main.d.ts +4 -5
- package/dist/main.js +3 -169
- package/dist/node-loader.d.ts +3 -1
- package/dist/node-loader.js +12 -210
- package/dist/router/client.d.ts +10 -5
- package/dist/router/client.js +89 -179
- package/dist/router/common.d.ts +0 -8
- package/dist/router/common.js +37 -121
- package/dist/router/server.d.ts +2 -4
- package/dist/router/server.js +75 -427
- package/dist/server.d.ts +6 -22
- package/dist/server.js +14 -26
- package/package.json +22 -10
- package/src/cli.ts +39 -68
- package/src/client.ts +30 -23
- package/src/config.ts +13 -29
- package/src/lib/builder.ts +186 -198
- package/src/lib/config.ts +32 -42
- package/src/lib/middleware/rsc/ssr.ts +359 -0
- package/src/lib/middleware/rsc/utils.ts +22 -32
- package/src/lib/middleware/rsc/worker-api.ts +89 -102
- package/src/lib/middleware/rsc/worker-impl.ts +182 -172
- package/src/lib/middleware/rsc.ts +172 -31
- package/src/lib/vite-plugin/nonjs-resolve-plugin.ts +5 -5
- package/src/lib/vite-plugin/patch-react-refresh.ts +26 -0
- package/src/lib/vite-plugin/rsc-analyze-plugin.ts +80 -14
- package/src/lib/vite-plugin/rsc-delegate-plugin.ts +14 -14
- package/src/lib/vite-plugin/rsc-hmr-plugin.ts +7 -7
- package/src/lib/vite-plugin/rsc-index-plugin.ts +9 -9
- package/src/lib/vite-plugin/rsc-reload-plugin.ts +13 -13
- package/src/lib/vite-plugin/rsc-transform-plugin.ts +10 -10
- package/src/main.ts +3 -8
- package/src/node-loader.ts +8 -6
- package/src/router/client.ts +30 -34
- package/src/router/common.ts +18 -28
- package/src/router/server.ts +28 -60
- package/src/server.ts +9 -32
- package/src/types.d.ts +5 -5
- package/dist/.tsbuildinfo +0 -1
- package/dist/cjs/lib/middleware/devServer.js +0 -288
- package/dist/cjs/lib/middleware/ssr/utils.js +0 -150
- package/dist/cjs/lib/middleware/ssr.js +0 -454
- package/dist/lib/middleware/devServer.d.ts +0 -5
- package/dist/lib/middleware/devServer.js +0 -273
- package/dist/lib/middleware/ssr/utils.d.ts +0 -2
- package/dist/lib/middleware/ssr/utils.js +0 -135
- package/dist/lib/middleware/ssr.d.ts +0 -7
- package/dist/lib/middleware/ssr.js +0 -441
- package/src/lib/middleware/devServer.ts +0 -58
- package/src/lib/middleware/ssr/utils.ts +0 -88
- package/src/lib/middleware/ssr.ts +0 -126
package/README.md
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# Waku
|
|
2
|
+
|
|
3
|
+
⛩️ The minimal React framework
|
|
4
|
+
|
|
5
|
+
[](https://github.com/dai-shi/waku/actions?query=workflow%3ACI)
|
|
6
|
+
[](https://www.npmjs.com/package/waku)
|
|
7
|
+
[](https://discord.gg/MrQdmzd)
|
|
8
|
+
|
|
9
|
+
<!-- [](https://bundlephobia.com/result?p=waku) -->
|
|
10
|
+
|
|
11
|
+
## Project status
|
|
12
|
+
|
|
13
|
+
We are working toward v1-alpha: https://github.com/dai-shi/waku/issues/24
|
|
14
|
+
|
|
15
|
+
Feel free to try it _seriously_ with non-production projects and give us feedback.
|
|
16
|
+
|
|
17
|
+
Playground: https://codesandbox.io/p/sandbox/waku-example-counter-mdc1yb
|
|
18
|
+
|
|
19
|
+
## Introduction
|
|
20
|
+
|
|
21
|
+
Waku is a React framework that supports React Server Components
|
|
22
|
+
(RSCs), a new feature that will be available in a future version of
|
|
23
|
+
React. RSCs allow developers to render UI components on the server,
|
|
24
|
+
improving performance and enabling server-side features. To use RSCs,
|
|
25
|
+
a framework is necessary for bundling, optionally server, router and
|
|
26
|
+
so on.
|
|
27
|
+
|
|
28
|
+
Waku takes a minimalistic approach, providing a minimal API that
|
|
29
|
+
allows for multiple feature implementations and encourages growth in
|
|
30
|
+
the ecosystem. For example, the minimal API is not tied to a specific
|
|
31
|
+
router. This flexibility makes it easier to build new features.
|
|
32
|
+
|
|
33
|
+
Waku uses Vite internally, and while it is still a work in progress,
|
|
34
|
+
it will eventually support all of Vite's features. It can even
|
|
35
|
+
work as a replacement for Vite + React client components. While using
|
|
36
|
+
RSCs is optional, it is highly recommended for improved user and
|
|
37
|
+
developer experiences.
|
|
38
|
+
|
|
39
|
+
## Why develop a React framework?
|
|
40
|
+
|
|
41
|
+
We believe that React Server Components (RSCs) are the future of React.
|
|
42
|
+
The challenge is that we can't utilize RSCs with the React library alone.
|
|
43
|
+
Instead, they require a React framework for bundling, at the very least.
|
|
44
|
+
|
|
45
|
+
Currently, only a few React frameworks support RSCs, and
|
|
46
|
+
they often come with more features than RSCs.
|
|
47
|
+
It would be nice to have a minimal framework that implements RSCs,
|
|
48
|
+
which should help learning how RSCs work.
|
|
49
|
+
|
|
50
|
+
Learning is the start, but it's not what we aim at.
|
|
51
|
+
Our assumption is that RSC best practices are still to explore.
|
|
52
|
+
The minimal implementation should clarify the fundamentals of RSCs
|
|
53
|
+
and enable the creation of additional features.
|
|
54
|
+
Our goal is to establish an ecosystem that covers a broader range of use cases.
|
|
55
|
+
|
|
56
|
+
## How to create a new project
|
|
57
|
+
|
|
58
|
+
To start a new Waku project, you can use any of the following
|
|
59
|
+
commands, depending on your preferred package manager:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm create waku@latest
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
yarn create waku
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pnpm create waku
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
These commands will create an example app that you can use as a
|
|
74
|
+
starting point for your project.
|
|
75
|
+
|
|
76
|
+
Minimum requirement: Node.js 18
|
|
77
|
+
|
|
78
|
+
## Practices
|
|
79
|
+
|
|
80
|
+
### Minimal
|
|
81
|
+
|
|
82
|
+
#### Server API
|
|
83
|
+
|
|
84
|
+
To use React Server Components in Waku, you need to create an
|
|
85
|
+
`entries.ts` file in the project root directory with a
|
|
86
|
+
`renderEntries` function that returns a server component module.
|
|
87
|
+
Here's an example:
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
import { lazy } from 'react';
|
|
91
|
+
import { defineEntries } from 'waku/server';
|
|
92
|
+
|
|
93
|
+
const App = lazy(() => import('./components/App.js'));
|
|
94
|
+
|
|
95
|
+
export default defineEntries(
|
|
96
|
+
// renderEntries
|
|
97
|
+
async (input) => {
|
|
98
|
+
return {
|
|
99
|
+
App: <App name={input || 'Waku'} />,
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The `id` parameter is the ID of the React Server Component
|
|
106
|
+
that you want to load on the server. You specify the RSC ID from the
|
|
107
|
+
client.
|
|
108
|
+
|
|
109
|
+
#### Client API
|
|
110
|
+
|
|
111
|
+
To render a React Server Component on the client, you can use the
|
|
112
|
+
`Root` and `Slot` components from `waku/client` with the RSC
|
|
113
|
+
ID to create a wrapper component. Here's an example:
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
import { createRoot } from 'react-dom/client';
|
|
117
|
+
import { Root, Slot } from 'waku/client';
|
|
118
|
+
|
|
119
|
+
const rootElement = (
|
|
120
|
+
<StrictMode>
|
|
121
|
+
<Root>
|
|
122
|
+
<Slot id="App" />
|
|
123
|
+
</Root>
|
|
124
|
+
</StrictMode>
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
createRoot(document.getElementById('root')!).render(rootElement);
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The `initialInput` prop can be passed to the `Root` Component,
|
|
131
|
+
overriding the default input which is `""`.
|
|
132
|
+
You can also re-render a React Server Component with new input.
|
|
133
|
+
Here's an example just to illustrate the idea:
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
import { useRefetch } from 'waku/client';
|
|
137
|
+
|
|
138
|
+
const Component = () => {
|
|
139
|
+
const refetch = useRefetch();
|
|
140
|
+
const handleClick = () => {
|
|
141
|
+
refetch('...');
|
|
142
|
+
};
|
|
143
|
+
// ...
|
|
144
|
+
};
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### Additional Server API
|
|
148
|
+
|
|
149
|
+
In addition to the `renderEntries` function, you can also
|
|
150
|
+
optionally specify `getBuildConfig` function in
|
|
151
|
+
`entries.ts`. Here's an example:
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
import { defineEntries } from 'waku/server';
|
|
155
|
+
|
|
156
|
+
export default defineEntries(
|
|
157
|
+
// renderEntries
|
|
158
|
+
async (input) => {
|
|
159
|
+
return {
|
|
160
|
+
App: <App name={input || 'Waku'} />,
|
|
161
|
+
};
|
|
162
|
+
},
|
|
163
|
+
// getBuildConfig
|
|
164
|
+
async () => {
|
|
165
|
+
return {
|
|
166
|
+
'/': {
|
|
167
|
+
entries: [['']],
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
},
|
|
171
|
+
);
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The `getBuildConfig` function is used for build-time
|
|
175
|
+
optimization. It renders React Server Components during the build
|
|
176
|
+
process to produce the output that will be sent to the client. Note
|
|
177
|
+
that rendering here means to produce RSC payload not HTML content.
|
|
178
|
+
|
|
179
|
+
#### How to try it
|
|
180
|
+
|
|
181
|
+
If you create a project with something like
|
|
182
|
+
`npm create waku@latest`, it will create the minimal
|
|
183
|
+
example app.
|
|
184
|
+
|
|
185
|
+
### Router
|
|
186
|
+
|
|
187
|
+
Waku provides a router built on top of the minimal API, and it serves
|
|
188
|
+
as a reference implementation.
|
|
189
|
+
|
|
190
|
+
#### Client API
|
|
191
|
+
|
|
192
|
+
To use the router, it is required to use the `Router`
|
|
193
|
+
component instead of using `Root` and `Slot` directly.
|
|
194
|
+
The following code demonstrates how to use
|
|
195
|
+
the `Router` component as the root component:
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
import { createRoot } from 'react-dom/client';
|
|
199
|
+
import { Router } from 'waku/router/client';
|
|
200
|
+
|
|
201
|
+
const root = createRoot(document.getElementById('root')!);
|
|
202
|
+
|
|
203
|
+
root.render(<Router />);
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
The `Router` component internally uses `Root` and `Slot`
|
|
207
|
+
and handles nested routes.
|
|
208
|
+
|
|
209
|
+
#### Server API
|
|
210
|
+
|
|
211
|
+
In `entries.ts`, we use `defineRouter` to export
|
|
212
|
+
`getEntry` and `getBuildConfig` at once.
|
|
213
|
+
Here's a simple example code without builder:
|
|
214
|
+
|
|
215
|
+
```tsx
|
|
216
|
+
import { defineRouter } from 'waku/router/server';
|
|
217
|
+
|
|
218
|
+
export default defineRouter((id) => {
|
|
219
|
+
switch (id) {
|
|
220
|
+
case 'index/page':
|
|
221
|
+
return import('./routes/index.tsx');
|
|
222
|
+
case 'foo/page':
|
|
223
|
+
return import('./routes/foo.tsx');
|
|
224
|
+
default:
|
|
225
|
+
throw new Error('no such route');
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
The implementation of the `defineRouter` is config-based.
|
|
231
|
+
However, it isn't too difficult to make a file-based router.
|
|
232
|
+
Here's a file-based example code with builder:
|
|
233
|
+
|
|
234
|
+
```tsx
|
|
235
|
+
import url from 'node:url';
|
|
236
|
+
import path from 'node:path';
|
|
237
|
+
import { glob } from 'glob';
|
|
238
|
+
import { defineRouter } from 'waku/router/server';
|
|
239
|
+
|
|
240
|
+
const routesDir = path.join(
|
|
241
|
+
path.dirname(url.fileURLToPath(import.meta.url)),
|
|
242
|
+
'routes',
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
export default defineRouter(
|
|
246
|
+
// getComponent (id is '**/layout' or '**/page')
|
|
247
|
+
async (id) => {
|
|
248
|
+
const files = await glob(${'`'}$\{id}.{tsx,js}${'`'}, { cwd: routesDir });
|
|
249
|
+
if (files.length === 0) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
const items = id.split('/');
|
|
253
|
+
switch (items.length) {
|
|
254
|
+
case 1:
|
|
255
|
+
return import(${'`'}./routes/$\{items[0]}.tsx${'`'});
|
|
256
|
+
case 2:
|
|
257
|
+
return import(${'`'}./routes/$\{items[0]}/$\{items[1]}.tsx${'`'});
|
|
258
|
+
case 3:
|
|
259
|
+
return import(${'`'}./routes/$\{items[0]}/$\{items[1]}/$\{items[2]}.tsx${'`'});
|
|
260
|
+
default:
|
|
261
|
+
throw new Error('too deep route');
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
// getPathsForBuild
|
|
265
|
+
async () => {
|
|
266
|
+
const files = await glob('**/page.{tsx,js}', { cwd: routesDir });
|
|
267
|
+
return files.map(
|
|
268
|
+
(file) => '/' + file.slice(0, Math.max(0, file.lastIndexOf('/'))),
|
|
269
|
+
);
|
|
270
|
+
},
|
|
271
|
+
);
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Due to the limitation of bundler, we cannot automatically allow
|
|
275
|
+
infinite depth of routes.
|
|
276
|
+
|
|
277
|
+
#### How to try it
|
|
278
|
+
|
|
279
|
+
You can try an example app in the repository by cloning it and running
|
|
280
|
+
the following commands:
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
git clone https://github.com/dai-shi/waku.git
|
|
284
|
+
cd waku
|
|
285
|
+
npm install
|
|
286
|
+
npm run examples:dev:07_router
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Alternatively, you could create a project with something like
|
|
290
|
+
`npm create waku@latest` and copy files from the example
|
|
291
|
+
folder in the repository.
|
|
292
|
+
|
|
293
|
+
## Tweets
|
|
294
|
+
|
|
295
|
+
<https://github.com/dai-shi/waku/discussions/150>
|
|
296
|
+
|
|
297
|
+
## Diagrams
|
|
298
|
+
|
|
299
|
+
<https://github.com/dai-shi/waku/discussions/151>
|