waku 0.20.0-beta.2 → 0.20.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.
- package/README.md +164 -48
- package/dist/lib/plugins/vite-plugin-rsc-delegate.js +31 -20
- package/dist/lib/plugins/vite-plugin-rsc-hmr.js +22 -10
- package/dist/lib/renderers/html-renderer.js +8 -7
- package/dist/main.d.ts +1 -1
- package/dist/main.js +1 -1
- package/dist/router/client.d.ts +12 -12
- package/dist/router/client.js +121 -97
- package/dist/router/define-router.js +1 -1
- package/package.json +7 -10
- package/src/lib/plugins/vite-plugin-rsc-delegate.ts +35 -26
- package/src/lib/plugins/vite-plugin-rsc-hmr.ts +26 -10
- package/src/lib/renderers/html-renderer.ts +9 -11
- package/src/main.ts +1 -1
- package/src/router/client.ts +145 -112
- package/src/router/define-router.ts +1 -1
package/README.md
CHANGED
|
@@ -49,11 +49,11 @@ import db from 'some-db';
|
|
|
49
49
|
|
|
50
50
|
import { Gallery } from '../components/gallery.js';
|
|
51
51
|
|
|
52
|
-
export
|
|
52
|
+
export const Store = async () => {
|
|
53
53
|
const products = await db.query('SELECT * FROM products');
|
|
54
54
|
|
|
55
55
|
return <Gallery products={products} />;
|
|
56
|
-
}
|
|
56
|
+
};
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
#### Client components
|
|
@@ -145,11 +145,11 @@ To learn more about the modern React architecture, we recommend [Making Sense of
|
|
|
145
145
|
|
|
146
146
|
Waku provides a familiar file-based “pages router” experience built for the modern React server components era.
|
|
147
147
|
|
|
148
|
-
Its underlying [low-level API](https://github.com/dai-shi/waku/blob/main/docs/
|
|
148
|
+
Its underlying [low-level API](https://github.com/dai-shi/waku/blob/main/docs/create-pages.mdx) is also available for those that prefer programmatic routing. This documentation covers file-based routing since many React developers prefer it, but please feel free to try both and see which you prefer.
|
|
149
149
|
|
|
150
150
|
### Overview
|
|
151
151
|
|
|
152
|
-
The directory for file-based routing in Waku projects is `./src/pages`. Layouts and pages can be created
|
|
152
|
+
The directory for file-based routing in Waku projects is `./src/pages`. Layouts and pages can be created by making a new file with two exports: a default function for the React component and a named `getConfig` function that returns a configuration object including the render method and other options.
|
|
153
153
|
|
|
154
154
|
Waku currently supports two rendering options: `'static'` for static prerendering (SSG) or `'dynamic'` for server-side rendering (SSR).
|
|
155
155
|
|
|
@@ -158,6 +158,9 @@ For example, you can statically prerender a global header and footer in the root
|
|
|
158
158
|
```tsx
|
|
159
159
|
// ./src/pages/_layout.tsx
|
|
160
160
|
|
|
161
|
+
import { Header } from '../components/header.js';
|
|
162
|
+
import { Footer } from '../components/footer.js';
|
|
163
|
+
|
|
161
164
|
// Create root layout
|
|
162
165
|
export default async function RootLayout({ children }) {
|
|
163
166
|
return (
|
|
@@ -186,6 +189,7 @@ export default async function HomePage() {
|
|
|
186
189
|
return (
|
|
187
190
|
<div>
|
|
188
191
|
<h1>{data.someDynamicTitle}</h1>
|
|
192
|
+
<div>{data.someDynamicContent}</div>
|
|
189
193
|
</div>
|
|
190
194
|
);
|
|
191
195
|
}
|
|
@@ -205,7 +209,7 @@ export const getConfig = async () => {
|
|
|
205
209
|
|
|
206
210
|
#### Single routes
|
|
207
211
|
|
|
208
|
-
Pages can be rendered as a single route (e.g., `/about
|
|
212
|
+
Pages can be rendered as a single route (e.g., `/about` or `/blog`).
|
|
209
213
|
|
|
210
214
|
```tsx
|
|
211
215
|
// ./src/pages/about.tsx
|
|
@@ -223,7 +227,7 @@ export const getConfig = async () => {
|
|
|
223
227
|
```
|
|
224
228
|
|
|
225
229
|
```tsx
|
|
226
|
-
// ./src/pages/blog.tsx
|
|
230
|
+
// ./src/pages/blog/index.tsx
|
|
227
231
|
|
|
228
232
|
// Create blog index page
|
|
229
233
|
export default async function BlogIndexPage() {
|
|
@@ -239,7 +243,7 @@ export const getConfig = async () => {
|
|
|
239
243
|
|
|
240
244
|
#### Segment routes
|
|
241
245
|
|
|
242
|
-
Pages can also render a segment route (e.g., `/blog/[slug]
|
|
246
|
+
Pages can also render a segment route (e.g., `/blog/[slug]`). The rendered React component automatically receives a prop named by the segment (e.g, `slug`) with the value of the rendered segment (e.g., `'introducing-waku'`). If statically prerendering a segment route at build time, a `staticPaths` array must also be provided.
|
|
243
247
|
|
|
244
248
|
```tsx
|
|
245
249
|
// ./src/pages/blog/[slug].tsx
|
|
@@ -264,7 +268,7 @@ export const getConfig = async () => {
|
|
|
264
268
|
```
|
|
265
269
|
|
|
266
270
|
```tsx
|
|
267
|
-
// ./src/pages/shop/[category].tsx
|
|
271
|
+
// ./src/pages/shop/[category]/index.tsx
|
|
268
272
|
|
|
269
273
|
// Create product category pages
|
|
270
274
|
export default async function ProductCategoryPage({ category }) {
|
|
@@ -316,7 +320,7 @@ const getStaticPaths = async () => {
|
|
|
316
320
|
|
|
317
321
|
#### Nested segment routes
|
|
318
322
|
|
|
319
|
-
Routes can contain multiple segments (e.g., `/shop/[category]/[product]
|
|
323
|
+
Routes can contain multiple segments (e.g., `/shop/[category]/[product]`).
|
|
320
324
|
|
|
321
325
|
```tsx
|
|
322
326
|
// ./src/pages/shop/[category]/[product].tsx
|
|
@@ -347,8 +351,8 @@ export const getConfig = async () => {
|
|
|
347
351
|
return {
|
|
348
352
|
render: 'static',
|
|
349
353
|
staticPaths: [
|
|
350
|
-
['
|
|
351
|
-
['
|
|
354
|
+
['some-category', 'some-product'],
|
|
355
|
+
['some-category', 'another-product'],
|
|
352
356
|
],
|
|
353
357
|
};
|
|
354
358
|
};
|
|
@@ -356,7 +360,7 @@ export const getConfig = async () => {
|
|
|
356
360
|
|
|
357
361
|
#### Catch-all routes
|
|
358
362
|
|
|
359
|
-
Catch-all or "wildcard" routes (e.g., `/app/[...catchAll]
|
|
363
|
+
Catch-all or "wildcard" routes (e.g., `/app/[...catchAll]`) have indefinite segments. Wildcard routes receive a prop with segment values as an ordered array.
|
|
360
364
|
|
|
361
365
|
For example, the `/app/profile/settings` route would receive a `catchAll` prop with the value `['profile', 'settings']`. These values can then be used to determine what to render in the component.
|
|
362
366
|
|
|
@@ -381,7 +385,7 @@ Layouts are created with a special `_layout.tsx` file name and wrap the entire r
|
|
|
381
385
|
|
|
382
386
|
#### Root layout
|
|
383
387
|
|
|
384
|
-
The root layout placed at `./
|
|
388
|
+
The root layout placed at `./pages/_layout.tsx` is especially useful. It can be used for setting global styles, global metadata, global providers, global data, and global components, such as a header and footer.
|
|
385
389
|
|
|
386
390
|
```tsx
|
|
387
391
|
// ./src/pages/_layout.tsx
|
|
@@ -395,14 +399,20 @@ import { Footer } from '../components/footer.js';
|
|
|
395
399
|
export default async function RootLayout({ children }) {
|
|
396
400
|
return (
|
|
397
401
|
<Providers>
|
|
398
|
-
<meta property="og:image" content="/images/preview.png" />
|
|
399
402
|
<link rel="icon" type="image/png" href="/images/favicon.png" />
|
|
403
|
+
<meta property="og:image" content="/images/opengraph.png" />
|
|
400
404
|
<Header />
|
|
401
405
|
<main>{children}</main>
|
|
402
406
|
<Footer />
|
|
403
407
|
</Providers>
|
|
404
408
|
);
|
|
405
409
|
}
|
|
410
|
+
|
|
411
|
+
export const getConfig = async () => {
|
|
412
|
+
return {
|
|
413
|
+
render: 'static',
|
|
414
|
+
};
|
|
415
|
+
};
|
|
406
416
|
```
|
|
407
417
|
|
|
408
418
|
```tsx
|
|
@@ -420,7 +430,7 @@ export const Providers = ({ children }) => {
|
|
|
420
430
|
|
|
421
431
|
#### Other layouts
|
|
422
432
|
|
|
423
|
-
Layouts are also helpful further down the tree. For example, you could add a layout at `./
|
|
433
|
+
Layouts are also helpful further down the tree. For example, you could add a layout at `./pages/blog/_layout.tsx` to add a sidebar to both the blog index and all blog article pages.
|
|
424
434
|
|
|
425
435
|
```tsx
|
|
426
436
|
// ./src/pages/blog/_layout.tsx
|
|
@@ -435,10 +445,18 @@ export default async function BlogLayout({ children }) {
|
|
|
435
445
|
</div>
|
|
436
446
|
);
|
|
437
447
|
}
|
|
448
|
+
|
|
449
|
+
export const getConfig = async () => {
|
|
450
|
+
return {
|
|
451
|
+
render: 'static',
|
|
452
|
+
};
|
|
453
|
+
};
|
|
438
454
|
```
|
|
439
455
|
|
|
440
456
|
## Navigation
|
|
441
457
|
|
|
458
|
+
### Link
|
|
459
|
+
|
|
442
460
|
Internal links should be made with the Waku `<Link />` component. It accepts a `to` prop for the destination, which is automatically prefetched ahead of the navigation.
|
|
443
461
|
|
|
444
462
|
```tsx
|
|
@@ -455,56 +473,64 @@ export default async function HomePage() {
|
|
|
455
473
|
}
|
|
456
474
|
```
|
|
457
475
|
|
|
458
|
-
|
|
476
|
+
### useRouter
|
|
459
477
|
|
|
460
|
-
|
|
478
|
+
The `useRouter` hook can be used to inspect the current route or perform programmatic navigation.
|
|
461
479
|
|
|
462
|
-
|
|
480
|
+
#### router properties
|
|
463
481
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
Files placed in a special `./private` folder of the Waku project root directory can be securely accessed in React server components.
|
|
482
|
+
The `router` object has a `value` property with two additional properties related to the current route: `path` (string) and `searchParams` ([URLSearchParams](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)).
|
|
467
483
|
|
|
468
484
|
```tsx
|
|
469
|
-
|
|
470
|
-
const file = readFileSync('./private/README.md', 'utf8');
|
|
471
|
-
/* ... */
|
|
472
|
-
}
|
|
473
|
-
```
|
|
474
|
-
|
|
475
|
-
## Data fetching
|
|
476
|
-
|
|
477
|
-
### Server
|
|
478
|
-
|
|
479
|
-
All of the wonderful patterns of React server components are supported. For example, you can compile MDX files or perform code syntax highlighting on the server with zero impact on the client bundle size.
|
|
485
|
+
'use client';
|
|
480
486
|
|
|
481
|
-
|
|
482
|
-
// ./src/pages/blog/[slug].tsx
|
|
483
|
-
import { MDX } from '../../components/mdx.js';
|
|
484
|
-
import { getArticle } from '../../lib/get-article.js';
|
|
487
|
+
import { useRouter_UNSTABLE as useRouter } from 'waku';
|
|
485
488
|
|
|
486
|
-
export
|
|
487
|
-
const
|
|
489
|
+
export const Component = () => {
|
|
490
|
+
const router = useRouter();
|
|
491
|
+
const { path, searchParams } = router.value;
|
|
488
492
|
|
|
489
493
|
return (
|
|
490
494
|
<>
|
|
491
|
-
<
|
|
492
|
-
<
|
|
493
|
-
<MDX>{article.content}</MDX>
|
|
495
|
+
<div>current path: {path}</div>
|
|
496
|
+
<div>current searchParams: {searchParams.toString()}</div>
|
|
494
497
|
</>
|
|
495
498
|
);
|
|
496
|
-
}
|
|
499
|
+
};
|
|
497
500
|
```
|
|
498
501
|
|
|
499
|
-
|
|
502
|
+
#### router methods
|
|
500
503
|
|
|
501
|
-
|
|
504
|
+
The `router` object also contains several methods for programmatic navigation:
|
|
502
505
|
|
|
503
|
-
|
|
506
|
+
- `router.push(to: string)` - navigate to the provided route
|
|
504
507
|
|
|
505
|
-
|
|
508
|
+
- `router.prefetch(to: string)` - prefetch the provided route
|
|
506
509
|
|
|
507
|
-
|
|
510
|
+
- `router.replace(to: string)` - replace the current history entry
|
|
511
|
+
|
|
512
|
+
- `router.reload()` - reload the current route
|
|
513
|
+
|
|
514
|
+
- `router.back()` - navigate to the previous entry in the session history
|
|
515
|
+
|
|
516
|
+
- `router.forward()` - navigate to the next entry in the session history
|
|
517
|
+
|
|
518
|
+
```tsx
|
|
519
|
+
'use client';
|
|
520
|
+
|
|
521
|
+
import { useRouter_UNSTABLE as useRouter } from 'waku';
|
|
522
|
+
|
|
523
|
+
export const Component = () => {
|
|
524
|
+
const router = useRouter();
|
|
525
|
+
|
|
526
|
+
return (
|
|
527
|
+
<>
|
|
528
|
+
<button onClick={() => router.push('/')}>Home</button>
|
|
529
|
+
<button onClick={() => router.back()}>Back</button>
|
|
530
|
+
</>
|
|
531
|
+
);
|
|
532
|
+
};
|
|
533
|
+
```
|
|
508
534
|
|
|
509
535
|
## Metadata
|
|
510
536
|
|
|
@@ -515,12 +541,18 @@ Waku automatically hoists any title, meta, and link tags to the document head. S
|
|
|
515
541
|
export default async function RootLayout({ children }) {
|
|
516
542
|
return (
|
|
517
543
|
<>
|
|
518
|
-
<meta property="og:image" content="/images/preview.png" />
|
|
519
544
|
<link rel="icon" type="image/png" href="/images/favicon.png" />
|
|
545
|
+
<meta property="og:image" content="/images/opengraph.png" />
|
|
520
546
|
{children}
|
|
521
547
|
</>
|
|
522
548
|
);
|
|
523
549
|
}
|
|
550
|
+
|
|
551
|
+
export const getConfig = async () => {
|
|
552
|
+
return {
|
|
553
|
+
render: 'static',
|
|
554
|
+
};
|
|
555
|
+
};
|
|
524
556
|
```
|
|
525
557
|
|
|
526
558
|
```tsx
|
|
@@ -535,6 +567,12 @@ export default async function HomePage() {
|
|
|
535
567
|
</>
|
|
536
568
|
);
|
|
537
569
|
}
|
|
570
|
+
|
|
571
|
+
export const getConfig = async () => {
|
|
572
|
+
return {
|
|
573
|
+
render: 'static',
|
|
574
|
+
};
|
|
575
|
+
};
|
|
538
576
|
```
|
|
539
577
|
|
|
540
578
|
Metadata could also be generated programmatically.
|
|
@@ -564,6 +602,12 @@ const Head = async () => {
|
|
|
564
602
|
const getMetadata = async () => {
|
|
565
603
|
/* ... */
|
|
566
604
|
};
|
|
605
|
+
|
|
606
|
+
export const getConfig = async () => {
|
|
607
|
+
return {
|
|
608
|
+
render: 'static',
|
|
609
|
+
};
|
|
610
|
+
};
|
|
567
611
|
```
|
|
568
612
|
|
|
569
613
|
## Styling
|
|
@@ -579,6 +623,12 @@ import '../styles.css';
|
|
|
579
623
|
export default async function RootLayout({ children }) {
|
|
580
624
|
return <main>{children}</main>;
|
|
581
625
|
}
|
|
626
|
+
|
|
627
|
+
export const getConfig = async () => {
|
|
628
|
+
return {
|
|
629
|
+
render: 'static',
|
|
630
|
+
};
|
|
631
|
+
};
|
|
582
632
|
```
|
|
583
633
|
|
|
584
634
|
```css
|
|
@@ -605,6 +655,72 @@ export default {
|
|
|
605
655
|
};
|
|
606
656
|
```
|
|
607
657
|
|
|
658
|
+
## Static assets
|
|
659
|
+
|
|
660
|
+
Static assets such as images, fonts, stylesheets, and scripts can be placed in a special `./public` folder of the Waku project root directory. The public directory structure is served relative to the `/` base path.
|
|
661
|
+
|
|
662
|
+
For example, an image added to `./public/images/logo.svg` can be rendered via `<img src="/images/logo.svg" />`.
|
|
663
|
+
|
|
664
|
+
## File system
|
|
665
|
+
|
|
666
|
+
Files placed in a special `./private` folder of the Waku project root directory can be securely accessed in React server components.
|
|
667
|
+
|
|
668
|
+
```tsx
|
|
669
|
+
export default async function HomePage() {
|
|
670
|
+
const file = readFileSync('./private/README.md', 'utf8');
|
|
671
|
+
/* ... */
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
export const getConfig = async () => {
|
|
675
|
+
return {
|
|
676
|
+
render: 'static',
|
|
677
|
+
};
|
|
678
|
+
};
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
## Data fetching
|
|
682
|
+
|
|
683
|
+
### Server
|
|
684
|
+
|
|
685
|
+
All of the wonderful patterns of React server components are supported. For example, you can compile MDX files or perform code syntax highlighting on the server with zero impact on the client bundle size.
|
|
686
|
+
|
|
687
|
+
```tsx
|
|
688
|
+
// ./src/pages/blog/[slug].tsx
|
|
689
|
+
import { MDX } from '../../components/mdx.js';
|
|
690
|
+
import { getArticle, getStaticPaths } from '../../lib/blog.js';
|
|
691
|
+
|
|
692
|
+
export default async function BlogArticlePage({ slug }) {
|
|
693
|
+
const article = await getArticle(slug);
|
|
694
|
+
|
|
695
|
+
return (
|
|
696
|
+
<>
|
|
697
|
+
<title>{article.frontmatter.title}</title>
|
|
698
|
+
<h1>{article.frontmatter.title}</h1>
|
|
699
|
+
<MDX>{article.content}</MDX>
|
|
700
|
+
</>
|
|
701
|
+
);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
export const getConfig = async () => {
|
|
705
|
+
const staticPaths = await getStaticPaths();
|
|
706
|
+
|
|
707
|
+
return {
|
|
708
|
+
render: 'static',
|
|
709
|
+
staticPaths,
|
|
710
|
+
};
|
|
711
|
+
};
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
### Client
|
|
715
|
+
|
|
716
|
+
Data should be fetched on the server when possible for the best user experience, but all data fetching libraries such as React Query should be compatible with Waku.
|
|
717
|
+
|
|
718
|
+
## State management
|
|
719
|
+
|
|
720
|
+
We recommend [Jotai](https://jotai.org) for global React state management based on the atomic model's performance and scalability, but Waku should be compatible with all React state management libraries such as Zustand and Valtio.
|
|
721
|
+
|
|
722
|
+
We're exploring a deeper integration of atomic state management into Waku to achieve the performance and developer experience of signals while preserving React's declarative programming model.
|
|
723
|
+
|
|
608
724
|
## Environment variables
|
|
609
725
|
|
|
610
726
|
It's important to distinguish environment variables that must be kept secret from those that can be made public.
|
|
@@ -27,6 +27,34 @@ export function rscDelegatePlugin(callback) {
|
|
|
27
27
|
let mode = 'development';
|
|
28
28
|
let base = '/';
|
|
29
29
|
let server;
|
|
30
|
+
const updateStyle = async (id, importer)=>{
|
|
31
|
+
const resolvedSource = await server.pluginContainer.resolveId(id, importer, {
|
|
32
|
+
ssr: true
|
|
33
|
+
});
|
|
34
|
+
if (resolvedSource?.id) {
|
|
35
|
+
const { default: source } = await server.ssrLoadModule(resolvedSource.id);
|
|
36
|
+
const transformedResult = await server.transformRequest(resolvedSource.id);
|
|
37
|
+
if (transformedResult) {
|
|
38
|
+
moduleImports.add(resolvedSource.id);
|
|
39
|
+
callback({
|
|
40
|
+
type: 'custom',
|
|
41
|
+
event: 'module-import',
|
|
42
|
+
data: {
|
|
43
|
+
...transformedResult,
|
|
44
|
+
source,
|
|
45
|
+
id: resolvedSource.id,
|
|
46
|
+
css: true
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const styleFiles = new Map(); // id -> importer
|
|
53
|
+
const updateAllStyles = async ()=>{
|
|
54
|
+
for (const [id, importer] of styleFiles){
|
|
55
|
+
await updateStyle(id, importer);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
30
58
|
return {
|
|
31
59
|
name: 'rsc-delegate-plugin',
|
|
32
60
|
configResolved (config) {
|
|
@@ -38,6 +66,7 @@ export function rscDelegatePlugin(callback) {
|
|
|
38
66
|
},
|
|
39
67
|
async handleHotUpdate (ctx) {
|
|
40
68
|
if (mode === 'development') {
|
|
69
|
+
await updateAllStyles(); // FIXME is this too aggressive?
|
|
41
70
|
if (moduleImports.has(ctx.file)) {
|
|
42
71
|
// re-inject
|
|
43
72
|
const transformedResult = await server.transformRequest(ctx.file);
|
|
@@ -84,26 +113,8 @@ export function rscDelegatePlugin(callback) {
|
|
|
84
113
|
data: source
|
|
85
114
|
});
|
|
86
115
|
} else if (CSS_LANGS_RE.test(item.source.value)) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
});
|
|
90
|
-
if (resolvedSource?.id) {
|
|
91
|
-
const { default: source } = await server.ssrLoadModule(resolvedSource.id);
|
|
92
|
-
const transformedResult = await server.transformRequest(resolvedSource.id);
|
|
93
|
-
if (transformedResult) {
|
|
94
|
-
moduleImports.add(resolvedSource.id);
|
|
95
|
-
callback({
|
|
96
|
-
type: 'custom',
|
|
97
|
-
event: 'module-import',
|
|
98
|
-
data: {
|
|
99
|
-
...transformedResult,
|
|
100
|
-
source,
|
|
101
|
-
id: resolvedSource.id,
|
|
102
|
-
css: true
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
}
|
|
116
|
+
styleFiles.set(item.source.value, id);
|
|
117
|
+
await updateStyle(item.source.value, id);
|
|
107
118
|
}
|
|
108
119
|
}
|
|
109
120
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { joinPath, fileURLToFilePath, decodeFilePathFromAbsolute } from '../utils/path.js';
|
|
1
|
+
import { joinPath, fileURLToFilePath, decodeFilePathFromAbsolute, filePathToFileURL } from '../utils/path.js';
|
|
2
2
|
const injectingHmrCode = `
|
|
3
3
|
import { createHotContext as __vite__createHotContext } from "/@vite/client";
|
|
4
4
|
import.meta.hot = __vite__createHotContext(import.meta.url);
|
|
@@ -22,7 +22,7 @@ if (import.meta.hot && !globalThis.__WAKU_HMR_CONFIGURED__) {
|
|
|
22
22
|
document.head.appendChild(script);
|
|
23
23
|
// avoid HMR flash by first applying the new and removing the old styles
|
|
24
24
|
if (style) {
|
|
25
|
-
queueMicrotask(style.
|
|
25
|
+
queueMicrotask(() => style.parentElement?.removeChild(style));
|
|
26
26
|
}
|
|
27
27
|
});
|
|
28
28
|
}
|
|
@@ -91,6 +91,18 @@ export function rscHmrPlugin() {
|
|
|
91
91
|
}
|
|
92
92
|
`);
|
|
93
93
|
}
|
|
94
|
+
},
|
|
95
|
+
handleHotUpdate ({ file }) {
|
|
96
|
+
const moduleLoading = globalThis.__webpack_module_loading__;
|
|
97
|
+
const moduleCache = globalThis.__webpack_module_cache__;
|
|
98
|
+
const id = filePathToFileURL(file);
|
|
99
|
+
if (moduleLoading.has(id) && moduleCache.has(id)) {
|
|
100
|
+
moduleLoading.delete(id);
|
|
101
|
+
moduleCache.delete(id);
|
|
102
|
+
moduleLoading.set(id, viteServer.ssrLoadModule(file).then((m)=>{
|
|
103
|
+
moduleCache.set(id, m);
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
94
106
|
}
|
|
95
107
|
};
|
|
96
108
|
}
|
|
@@ -124,12 +136,12 @@ function hotImport(viteServer, source) {
|
|
|
124
136
|
const modulePendingMap = new WeakMap();
|
|
125
137
|
function moduleImport(viteServer, result) {
|
|
126
138
|
const hot = viteHot(viteServer);
|
|
127
|
-
let
|
|
128
|
-
if (!
|
|
129
|
-
|
|
130
|
-
modulePendingMap.set(hot,
|
|
139
|
+
let sources = modulePendingMap.get(hot);
|
|
140
|
+
if (!sources) {
|
|
141
|
+
sources = new Map();
|
|
142
|
+
modulePendingMap.set(hot, sources);
|
|
131
143
|
}
|
|
132
|
-
|
|
144
|
+
sources.set(result.id, result);
|
|
133
145
|
hot.send({
|
|
134
146
|
type: 'custom',
|
|
135
147
|
event: 'module-import',
|
|
@@ -138,13 +150,13 @@ function moduleImport(viteServer, result) {
|
|
|
138
150
|
}
|
|
139
151
|
async function generateInitialScripts(viteServer) {
|
|
140
152
|
const hot = viteHot(viteServer);
|
|
141
|
-
const
|
|
142
|
-
if (!
|
|
153
|
+
const sources = modulePendingMap.get(hot);
|
|
154
|
+
if (!sources) {
|
|
143
155
|
return [];
|
|
144
156
|
}
|
|
145
157
|
const scripts = [];
|
|
146
158
|
let injectedBlockingViteClient = false;
|
|
147
|
-
for (const result of
|
|
159
|
+
for (const result of sources.values()){
|
|
148
160
|
// CSS modules do not support result.source (empty) since ssr-transforming them gives the css keys
|
|
149
161
|
// and client-transforming them gives the script tag for injecting them.
|
|
150
162
|
if (result.id.endsWith('.module.css')) {
|
|
@@ -148,13 +148,14 @@ export const renderHtml = async (opts)=>{
|
|
|
148
148
|
get (_target, filePath) {
|
|
149
149
|
return new Proxy({}, {
|
|
150
150
|
get (_target, name) {
|
|
151
|
-
const file = filePath.slice(config.basePath.length);
|
|
152
|
-
// TODO too long, we need to refactor this logic
|
|
153
151
|
if (isDev) {
|
|
154
|
-
|
|
152
|
+
// TODO too long, we need to refactor this logic
|
|
153
|
+
let file = filePath.slice(config.basePath.length);
|
|
154
|
+
file = file.split('?')[0];
|
|
155
|
+
file = file.startsWith('@fs/') ? file.slice('@fs'.length) : encodeFilePathToAbsolute(joinPath(opts.rootDir, file));
|
|
155
156
|
const wakuDist = joinPath(fileURLToFilePath(import.meta.url), '../../..');
|
|
156
|
-
if (
|
|
157
|
-
const id = 'waku' +
|
|
157
|
+
if (file.startsWith(wakuDist)) {
|
|
158
|
+
const id = 'waku' + file.slice(wakuDist.length).replace(/\.\w+$/, '');
|
|
158
159
|
if (!moduleLoading.has(id)) {
|
|
159
160
|
moduleLoading.set(id, import(/* @vite-ignore */ id).then((m)=>{
|
|
160
161
|
moduleCache.set(id, m);
|
|
@@ -168,7 +169,7 @@ export const renderHtml = async (opts)=>{
|
|
|
168
169
|
name
|
|
169
170
|
};
|
|
170
171
|
}
|
|
171
|
-
const id = filePathToFileURL(
|
|
172
|
+
const id = filePathToFileURL(file);
|
|
172
173
|
if (!moduleLoading.has(id)) {
|
|
173
174
|
moduleLoading.set(id, opts.loadServerFile(id).then((m)=>{
|
|
174
175
|
moduleCache.set(id, m);
|
|
@@ -183,7 +184,7 @@ export const renderHtml = async (opts)=>{
|
|
|
183
184
|
};
|
|
184
185
|
}
|
|
185
186
|
// !isDev
|
|
186
|
-
const id =
|
|
187
|
+
const id = filePath.slice(config.basePath.length);
|
|
187
188
|
if (!moduleLoading.has(id)) {
|
|
188
189
|
moduleLoading.set(id, opts.loadModule(joinPath(config.ssrDir, id)).then((m)=>{
|
|
189
190
|
moduleCache.set(id, m);
|
package/dist/main.d.ts
CHANGED
package/dist/main.js
CHANGED
package/dist/router/client.d.ts
CHANGED
|
@@ -5,15 +5,15 @@ declare global {
|
|
|
5
5
|
readonly env: Record<string, string>;
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
|
-
declare
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
export declare function useRouter_UNSTABLE(): {
|
|
9
|
+
value: RouteProps;
|
|
10
|
+
push: (to: string) => void;
|
|
11
|
+
replace: (to: string) => void;
|
|
12
|
+
reload: () => void;
|
|
13
|
+
back: () => void;
|
|
14
|
+
forward: () => void;
|
|
15
|
+
prefetch: (to: string) => void;
|
|
16
|
+
};
|
|
17
17
|
export type LinkProps = {
|
|
18
18
|
to: string;
|
|
19
19
|
pending?: ReactNode;
|
|
@@ -24,7 +24,7 @@ export type LinkProps = {
|
|
|
24
24
|
export declare function Link({ to, children, pending, notPending, unstable_prefetchOnEnter, ...props }: LinkProps): ReactElement;
|
|
25
25
|
type RouterData = [
|
|
26
26
|
shouldSkip?: ShouldSkip,
|
|
27
|
-
locationListners?: Set<(pathname: string,
|
|
27
|
+
locationListners?: Set<(pathname: string, searchParamsString: string) => void>
|
|
28
28
|
];
|
|
29
29
|
export declare function Router({ routerData }: {
|
|
30
30
|
routerData?: RouterData | undefined;
|
|
@@ -39,9 +39,9 @@ export declare function Router({ routerData }: {
|
|
|
39
39
|
* ServerRouter for SSR
|
|
40
40
|
* This is not a public API.
|
|
41
41
|
*/
|
|
42
|
-
export declare function ServerRouter({ children,
|
|
42
|
+
export declare function ServerRouter({ children, route, }: {
|
|
43
43
|
children: ReactNode;
|
|
44
|
-
|
|
44
|
+
route: RouteProps;
|
|
45
45
|
}): import("react").FunctionComponentElement<{
|
|
46
46
|
children?: ReactNode;
|
|
47
47
|
}>;
|