waku 0.26.0 → 0.26.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 +217 -6
- package/dist/lib/middleware/context.js +5 -32
- package/dist/lib/middleware/context.js.map +1 -1
- package/dist/lib/renderers/utils.d.ts +0 -2
- package/dist/lib/renderers/utils.js +0 -26
- package/dist/lib/renderers/utils.js.map +1 -1
- package/dist/lib/utils/stream.d.ts +0 -2
- package/dist/lib/utils/stream.js +0 -29
- package/dist/lib/utils/stream.js.map +1 -1
- package/dist/lib/vite-plugins/fs-router-typegen.js +6 -0
- package/dist/lib/vite-plugins/fs-router-typegen.js.map +1 -1
- package/dist/lib/vite-rsc/cli.js +1 -1
- package/dist/lib/vite-rsc/cli.js.map +1 -1
- package/dist/lib/vite-rsc/deploy/aws-lambda/plugin.js +1 -1
- package/dist/lib/vite-rsc/deploy/aws-lambda/plugin.js.map +1 -1
- package/dist/lib/vite-rsc/deploy/cloudflare/plugin.js +1 -1
- package/dist/lib/vite-rsc/deploy/cloudflare/plugin.js.map +1 -1
- package/dist/lib/vite-rsc/deploy/deno/plugin.js +1 -1
- package/dist/lib/vite-rsc/deploy/deno/plugin.js.map +1 -1
- package/dist/lib/vite-rsc/deploy/netlify/plugin.js +1 -1
- package/dist/lib/vite-rsc/deploy/netlify/plugin.js.map +1 -1
- package/dist/lib/vite-rsc/deploy/partykit/plugin.js +1 -1
- package/dist/lib/vite-rsc/deploy/partykit/plugin.js.map +1 -1
- package/dist/lib/vite-rsc/deploy/vercel/plugin.js +17 -8
- package/dist/lib/vite-rsc/deploy/vercel/plugin.js.map +1 -1
- package/dist/lib/vite-rsc/plugin.js +9 -12
- package/dist/lib/vite-rsc/plugin.js.map +1 -1
- package/dist/lib/vite-rsc/ssr.js +6 -2
- package/dist/lib/vite-rsc/ssr.js.map +1 -1
- package/dist/lib/vite-types.d.js.map +1 -1
- package/dist/minimal/client.js +1 -1
- package/dist/minimal/client.js.map +1 -1
- package/dist/router/client.js +18 -6
- package/dist/router/client.js.map +1 -1
- package/dist/router/common.js +8 -2
- package/dist/router/common.js.map +1 -1
- package/dist/router/create-pages.js +1 -1
- package/dist/router/create-pages.js.map +1 -1
- package/dist/types.d.js.map +1 -1
- package/package.json +9 -9
- package/dist/lib/utils/swc.d.ts +0 -9
- package/dist/lib/utils/swc.js +0 -16
- package/dist/lib/utils/swc.js.map +0 -1
package/README.md
CHANGED
|
@@ -415,6 +415,107 @@ export const getConfig = async () => {
|
|
|
415
415
|
};
|
|
416
416
|
```
|
|
417
417
|
|
|
418
|
+
#### Group routes
|
|
419
|
+
|
|
420
|
+
Group routes allow you to organize routes into logical groups without affecting the URL structure. They're created by wrapping directory names in parentheses (e.g., `(group)`). This is particularly useful for sharing layouts across multiple routes while keeping the URL clean.
|
|
421
|
+
|
|
422
|
+
For example, you might want a home page at `/` that doesn't use a shared layout, but all other routes should share a common layout. This can be achieved by grouping those routes:
|
|
423
|
+
|
|
424
|
+
```
|
|
425
|
+
├── (main)
|
|
426
|
+
│ ├── _layout.tsx
|
|
427
|
+
│ ├── about.tsx
|
|
428
|
+
│ └── contact.tsx
|
|
429
|
+
└── index.tsx
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
In this structure, `/about` and `/contact` will use the layout from `(main)/_layout.tsx`, but `/` (from `index.tsx`) will not.
|
|
433
|
+
|
|
434
|
+
```tsx
|
|
435
|
+
// ./src/pages/(main)/_layout.tsx
|
|
436
|
+
import { Header } from '../../components/header';
|
|
437
|
+
import { Footer } from '../../components/footer';
|
|
438
|
+
|
|
439
|
+
// Create shared layout for main pages
|
|
440
|
+
export default async function MainLayout({ children }) {
|
|
441
|
+
return (
|
|
442
|
+
<>
|
|
443
|
+
<Header />
|
|
444
|
+
<main>{children}</main>
|
|
445
|
+
<Footer />
|
|
446
|
+
</>
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
export const getConfig = async () => {
|
|
451
|
+
return {
|
|
452
|
+
render: 'static',
|
|
453
|
+
} as const;
|
|
454
|
+
};
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
```tsx
|
|
458
|
+
// ./src/pages/(main)/about.tsx
|
|
459
|
+
export default async function AboutPage() {
|
|
460
|
+
return <h1>About Us</h1>;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
export const getConfig = async () => {
|
|
464
|
+
return {
|
|
465
|
+
render: 'static',
|
|
466
|
+
} as const;
|
|
467
|
+
};
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
Group routes can be nested to create complex layout compositions. For instance, you could have a static layout at the group level and a dynamic layout nested within:
|
|
471
|
+
|
|
472
|
+
```
|
|
473
|
+
(main)
|
|
474
|
+
├── (dynamic)
|
|
475
|
+
│ ├── _layout.tsx # dynamic layout
|
|
476
|
+
│ ├── dashboard.tsx
|
|
477
|
+
│ └── profile.tsx
|
|
478
|
+
└── _layout.tsx # static layout
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
This allows for fine-grained control over rendering modes - some work can be done at build time (`static`) while other work happens at runtime (`dynamic`).
|
|
482
|
+
|
|
483
|
+
```tsx
|
|
484
|
+
// ./src/pages/(main)/_layout.tsx
|
|
485
|
+
// Static layout - runs at build time
|
|
486
|
+
export default async function MainLayout({ children }) {
|
|
487
|
+
return <div className="main-container">{children}</div>;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
export const getConfig = async () => {
|
|
491
|
+
return {
|
|
492
|
+
render: 'static',
|
|
493
|
+
} as const;
|
|
494
|
+
};
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
```tsx
|
|
498
|
+
// ./src/pages/(main)/(dynamic)/_layout.tsx
|
|
499
|
+
// Dynamic layout - runs at request time
|
|
500
|
+
export default async function DynamicLayout({ children }) {
|
|
501
|
+
const userData = await fetchUserData(); // Dynamic data fetching
|
|
502
|
+
|
|
503
|
+
return (
|
|
504
|
+
<div className="dynamic-container">
|
|
505
|
+
<UserContext.Provider value={userData}>{children}</UserContext.Provider>
|
|
506
|
+
</div>
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
export const getConfig = async () => {
|
|
511
|
+
return {
|
|
512
|
+
render: 'dynamic',
|
|
513
|
+
} as const;
|
|
514
|
+
};
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
Group routes are especially powerful for organizing complex applications where different sections need different layouts, state management, or data requirements while maintaining clean URLs.
|
|
518
|
+
|
|
418
519
|
### Layouts
|
|
419
520
|
|
|
420
521
|
Layouts are created with a special `_layout.tsx` file name and wrap the entire route and its descendents. They must accept a `children` prop of type `ReactNode`. While not required, you will typically want at least a root layout.
|
|
@@ -513,6 +614,113 @@ export const getConfig = async () => {
|
|
|
513
614
|
};
|
|
514
615
|
```
|
|
515
616
|
|
|
617
|
+
### Slices
|
|
618
|
+
|
|
619
|
+
Slices are reusable components that are defined in the `src/pages/_slices` directory. They allow you to compose pages by assembling components like normal React components while specifying alternate rendering patterns.
|
|
620
|
+
|
|
621
|
+
#### Creating slices
|
|
622
|
+
|
|
623
|
+
Slices are created by placing files in the `src/pages/_slices` directory. The slice ID corresponds to the filename, and nested slices use the full path as the ID.
|
|
624
|
+
|
|
625
|
+
```
|
|
626
|
+
src/pages
|
|
627
|
+
├── _slices
|
|
628
|
+
│ ├── one.tsx
|
|
629
|
+
│ ├── two.tsx
|
|
630
|
+
│ └── nested
|
|
631
|
+
│ └── three.tsx
|
|
632
|
+
└── some-page.tsx
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
Each slice file exports a default React component and a `getConfig` function that specifies the render method.
|
|
636
|
+
|
|
637
|
+
```tsx
|
|
638
|
+
// ./src/pages/_slices/one.tsx
|
|
639
|
+
|
|
640
|
+
// Create slice component
|
|
641
|
+
export default function SliceOne() {
|
|
642
|
+
return <p>🍕</p>;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
export const getConfig = () => {
|
|
646
|
+
return {
|
|
647
|
+
render: 'static', // default is 'static'
|
|
648
|
+
};
|
|
649
|
+
};
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
```tsx
|
|
653
|
+
// ./src/pages/_slices/nested/three.tsx
|
|
654
|
+
|
|
655
|
+
// Create nested slice component
|
|
656
|
+
export default function SliceThree() {
|
|
657
|
+
return <p>🍰</p>;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
export const getConfig = () => {
|
|
661
|
+
return {
|
|
662
|
+
render: 'dynamic',
|
|
663
|
+
};
|
|
664
|
+
};
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
#### Using slices
|
|
668
|
+
|
|
669
|
+
Slices are used in pages and layouts by importing the `Slice` component from Waku and specifying the slice ID. The `slices` array in the page's `getConfig` must include all slice IDs used on that page.
|
|
670
|
+
|
|
671
|
+
```tsx
|
|
672
|
+
// ./src/pages/some-page.tsx
|
|
673
|
+
import { Slice } from 'waku';
|
|
674
|
+
|
|
675
|
+
// Create page with slices
|
|
676
|
+
export default function SomePage() {
|
|
677
|
+
return (
|
|
678
|
+
<div>
|
|
679
|
+
<Slice id="one" />
|
|
680
|
+
<Slice id="two" />
|
|
681
|
+
<Slice id="nested/three" />
|
|
682
|
+
</div>
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
export const getConfig = () => {
|
|
687
|
+
return {
|
|
688
|
+
render: 'static',
|
|
689
|
+
slices: ['one', 'two', 'nested/three'],
|
|
690
|
+
};
|
|
691
|
+
};
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
#### Lazy slices
|
|
695
|
+
|
|
696
|
+
Lazy slices allow components to be requested independently from the page they are used on, similar to Astro's server islands feature. This is useful for components that will be dynamically rendered on otherwise static pages.
|
|
697
|
+
|
|
698
|
+
Lazy slices are marked with the `lazy` prop and can include a `fallback` component to display while loading.
|
|
699
|
+
|
|
700
|
+
```tsx
|
|
701
|
+
// ./src/pages/some-page.tsx
|
|
702
|
+
import { Slice } from 'waku';
|
|
703
|
+
|
|
704
|
+
// Create page with lazy slice
|
|
705
|
+
export default function SomePage() {
|
|
706
|
+
return (
|
|
707
|
+
<div>
|
|
708
|
+
<Slice id="one" />
|
|
709
|
+
<Slice id="two" lazy fallback={<p>Two is loading...</p>} />
|
|
710
|
+
</div>
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
export const getConfig = () => {
|
|
715
|
+
return {
|
|
716
|
+
render: 'static',
|
|
717
|
+
slices: ['one'], // Note: 'two' is lazy, so it is not included
|
|
718
|
+
};
|
|
719
|
+
};
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
This allows you to have a `dynamic` slice component while keeping the rest of the page static.
|
|
723
|
+
|
|
516
724
|
## Navigation
|
|
517
725
|
|
|
518
726
|
### Link
|
|
@@ -673,7 +881,7 @@ export const getConfig = async () => {
|
|
|
673
881
|
|
|
674
882
|
### Global styles
|
|
675
883
|
|
|
676
|
-
Install any required dev dependencies (e.g., `npm i -D tailwindcss @tailwindcss/
|
|
884
|
+
Install any required dev dependencies (e.g., `npm i -D tailwindcss @tailwindcss/vite`) and set up any required configuration (e.g., `waku.config.ts`). Then create your global stylesheet (e.g., `./src/styles.css`) and import it into the root layout.
|
|
677
885
|
|
|
678
886
|
```tsx
|
|
679
887
|
// ./src/pages/_layout.tsx
|
|
@@ -696,12 +904,15 @@ export const getConfig = async () => {
|
|
|
696
904
|
```
|
|
697
905
|
|
|
698
906
|
```js
|
|
699
|
-
// ./
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
907
|
+
// ./waku.config.ts
|
|
908
|
+
import { defineConfig } from 'waku/config';
|
|
909
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
910
|
+
|
|
911
|
+
export default defineConfig({
|
|
912
|
+
vite: {
|
|
913
|
+
plugins: [tailwindcss()],
|
|
703
914
|
},
|
|
704
|
-
};
|
|
915
|
+
});
|
|
705
916
|
```
|
|
706
917
|
|
|
707
918
|
## Static assets
|
|
@@ -1,50 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
};
|
|
4
|
-
const getContextStorage = ()=>{
|
|
5
|
-
return globalThis.__WAKU_MIDDLEWARE_CONTEXT_STORAGE__;
|
|
6
|
-
};
|
|
7
|
-
try {
|
|
8
|
-
const { AsyncLocalStorage } = await import('node:async_hooks');
|
|
9
|
-
setContextStorage(new AsyncLocalStorage());
|
|
10
|
-
} catch {
|
|
11
|
-
console.warn('AsyncLocalStorage is not available');
|
|
12
|
-
}
|
|
13
|
-
let previousContext;
|
|
14
|
-
let currentContext;
|
|
15
|
-
const runWithContext = (context, fn)=>{
|
|
16
|
-
const contextStorage = getContextStorage();
|
|
17
|
-
if (contextStorage) {
|
|
18
|
-
return contextStorage.run(context, fn);
|
|
19
|
-
}
|
|
20
|
-
previousContext = currentContext;
|
|
21
|
-
currentContext = context;
|
|
22
|
-
try {
|
|
23
|
-
return fn();
|
|
24
|
-
} finally{
|
|
25
|
-
currentContext = previousContext;
|
|
26
|
-
}
|
|
27
|
-
};
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
const contextStorage = new AsyncLocalStorage();
|
|
28
3
|
export const context = ()=>{
|
|
29
4
|
return async (ctx, next)=>{
|
|
30
5
|
const context = {
|
|
31
6
|
req: ctx.req,
|
|
32
7
|
data: ctx.data
|
|
33
8
|
};
|
|
34
|
-
return
|
|
9
|
+
return contextStorage.run(context, next);
|
|
35
10
|
};
|
|
36
11
|
};
|
|
37
12
|
export function getContext() {
|
|
38
|
-
const
|
|
39
|
-
const context = contextStorage?.getStore() ?? currentContext;
|
|
13
|
+
const context = contextStorage.getStore();
|
|
40
14
|
if (!context) {
|
|
41
15
|
throw new Error('Context is not available. Make sure to use the context middleware. For now, Context is not available during the build time.');
|
|
42
16
|
}
|
|
43
17
|
return context;
|
|
44
18
|
}
|
|
45
19
|
export function getContextData() {
|
|
46
|
-
const
|
|
47
|
-
const context = contextStorage?.getStore() ?? currentContext;
|
|
20
|
+
const context = contextStorage.getStore();
|
|
48
21
|
if (!context) {
|
|
49
22
|
return {};
|
|
50
23
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/middleware/context.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/middleware/context.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { Middleware } from './types.js';\n\ntype Context = {\n readonly req: Request;\n readonly data: Record<string, unknown>;\n};\n\nconst contextStorage = new AsyncLocalStorage<Context>();\n\nexport const context: Middleware = () => {\n return async (ctx, next) => {\n const context: Context = {\n req: ctx.req,\n data: ctx.data,\n };\n return contextStorage.run(context, next);\n };\n};\n\nexport function getContext() {\n const context = contextStorage.getStore();\n if (!context) {\n throw new Error(\n 'Context is not available. Make sure to use the context middleware. For now, Context is not available during the build time.',\n );\n }\n return context;\n}\n\nexport function getContextData(): Record<string, unknown> {\n const context = contextStorage.getStore();\n if (!context) {\n return {};\n }\n return context.data;\n}\n"],"names":["AsyncLocalStorage","contextStorage","context","ctx","next","req","data","run","getContext","getStore","Error","getContextData"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,mBAAmB;AAQrD,MAAMC,iBAAiB,IAAID;AAE3B,OAAO,MAAME,UAAsB;IACjC,OAAO,OAAOC,KAAKC;QACjB,MAAMF,UAAmB;YACvBG,KAAKF,IAAIE,GAAG;YACZC,MAAMH,IAAIG,IAAI;QAChB;QACA,OAAOL,eAAeM,GAAG,CAACL,SAASE;IACrC;AACF,EAAE;AAEF,OAAO,SAASI;IACd,MAAMN,UAAUD,eAAeQ,QAAQ;IACvC,IAAI,CAACP,SAAS;QACZ,MAAM,IAAIQ,MACR;IAEJ;IACA,OAAOR;AACT;AAEA,OAAO,SAASS;IACd,MAAMT,UAAUD,eAAeQ,QAAQ;IACvC,IAAI,CAACP,SAAS;QACZ,OAAO,CAAC;IACV;IACA,OAAOA,QAAQI,IAAI;AACrB"}
|
|
@@ -2,5 +2,3 @@ export declare const encodeRscPath: (rscPath: string) => string;
|
|
|
2
2
|
export declare const decodeRscPath: (rscPath: string) => string;
|
|
3
3
|
export declare const encodeFuncId: (funcId: string) => string;
|
|
4
4
|
export declare const decodeFuncId: (encoded: string) => string | null;
|
|
5
|
-
export declare const generatePrefetchCode: (basePrefix: string, rscPaths: Iterable<string>, moduleIds: Iterable<string>) => string;
|
|
6
|
-
export declare const deepFreeze: (x: unknown) => void;
|
|
@@ -58,31 +58,5 @@ export const decodeFuncId = (encoded)=>{
|
|
|
58
58
|
}
|
|
59
59
|
return file + '#' + name;
|
|
60
60
|
};
|
|
61
|
-
export const generatePrefetchCode = (basePrefix, rscPaths, moduleIds)=>{
|
|
62
|
-
const rscPathArray = Array.from(rscPaths);
|
|
63
|
-
let code = '';
|
|
64
|
-
if (rscPathArray.length) {
|
|
65
|
-
code += `
|
|
66
|
-
globalThis.__WAKU_PREFETCHED__ = {
|
|
67
|
-
${rscPathArray.map((rscPath)=>{
|
|
68
|
-
const url = basePrefix + encodeRscPath(rscPath);
|
|
69
|
-
return ` '${rscPath}': fetch('${url}'),`;
|
|
70
|
-
}).join('\n')}
|
|
71
|
-
};`;
|
|
72
|
-
}
|
|
73
|
-
for (const moduleId of moduleIds){
|
|
74
|
-
code += `
|
|
75
|
-
import('${moduleId}');`;
|
|
76
|
-
}
|
|
77
|
-
return code;
|
|
78
|
-
};
|
|
79
|
-
export const deepFreeze = (x)=>{
|
|
80
|
-
if (typeof x === 'object' && x !== null) {
|
|
81
|
-
Object.freeze(x);
|
|
82
|
-
for (const value of Object.values(x)){
|
|
83
|
-
deepFreeze(value);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
61
|
|
|
88
62
|
//# sourceMappingURL=utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/renderers/utils.ts"],"sourcesContent":["// This file should not include Node specific code.\n\nexport const encodeRscPath = (rscPath: string) => {\n if (rscPath.startsWith('_')) {\n throw new Error('rscPath must not start with `_`: ' + rscPath);\n }\n if (rscPath.endsWith('_')) {\n throw new Error('rscPath must not end with `_`: ' + rscPath);\n }\n if (rscPath === '') {\n rscPath = '_';\n }\n if (rscPath.startsWith('/')) {\n rscPath = '_' + rscPath;\n }\n if (rscPath.endsWith('/')) {\n rscPath += '_';\n }\n return rscPath + '.txt';\n};\n\nexport const decodeRscPath = (rscPath: string) => {\n if (!rscPath.endsWith('.txt')) {\n const err = new Error('Invalid encoded rscPath');\n (err as any).statusCode = 400;\n throw err;\n }\n rscPath = rscPath.slice(0, -'.txt'.length);\n if (rscPath.startsWith('_')) {\n rscPath = rscPath.slice(1);\n }\n if (rscPath.endsWith('_')) {\n rscPath = rscPath.slice(0, -1);\n }\n return rscPath;\n};\n\nconst FUNC_PREFIX = 'F/';\n\nexport const encodeFuncId = (funcId: string) => {\n const [file, name] = funcId.split('#') as [string, string];\n if (name.includes('/')) {\n throw new Error('Function name must not include `/`: ' + name);\n }\n if (file.startsWith('_')) {\n throw new Error('File must not start with `_`: ' + file);\n }\n if (file.startsWith('/')) {\n return FUNC_PREFIX + '_' + file + '/' + name;\n }\n return FUNC_PREFIX + file + '/' + name;\n};\n\nexport const decodeFuncId = (encoded: string) => {\n if (!encoded.startsWith(FUNC_PREFIX)) {\n return null;\n }\n const index = encoded.lastIndexOf('/');\n const file = encoded.slice(FUNC_PREFIX.length, index);\n const name = encoded.slice(index + 1);\n if (file.startsWith('_')) {\n return file.slice(1) + '#' + name;\n }\n return file + '#' + name;\n};\n
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/renderers/utils.ts"],"sourcesContent":["// This file should not include Node specific code.\n\nexport const encodeRscPath = (rscPath: string) => {\n if (rscPath.startsWith('_')) {\n throw new Error('rscPath must not start with `_`: ' + rscPath);\n }\n if (rscPath.endsWith('_')) {\n throw new Error('rscPath must not end with `_`: ' + rscPath);\n }\n if (rscPath === '') {\n rscPath = '_';\n }\n if (rscPath.startsWith('/')) {\n rscPath = '_' + rscPath;\n }\n if (rscPath.endsWith('/')) {\n rscPath += '_';\n }\n return rscPath + '.txt';\n};\n\nexport const decodeRscPath = (rscPath: string) => {\n if (!rscPath.endsWith('.txt')) {\n const err = new Error('Invalid encoded rscPath');\n (err as any).statusCode = 400;\n throw err;\n }\n rscPath = rscPath.slice(0, -'.txt'.length);\n if (rscPath.startsWith('_')) {\n rscPath = rscPath.slice(1);\n }\n if (rscPath.endsWith('_')) {\n rscPath = rscPath.slice(0, -1);\n }\n return rscPath;\n};\n\nconst FUNC_PREFIX = 'F/';\n\nexport const encodeFuncId = (funcId: string) => {\n const [file, name] = funcId.split('#') as [string, string];\n if (name.includes('/')) {\n throw new Error('Function name must not include `/`: ' + name);\n }\n if (file.startsWith('_')) {\n throw new Error('File must not start with `_`: ' + file);\n }\n if (file.startsWith('/')) {\n return FUNC_PREFIX + '_' + file + '/' + name;\n }\n return FUNC_PREFIX + file + '/' + name;\n};\n\nexport const decodeFuncId = (encoded: string) => {\n if (!encoded.startsWith(FUNC_PREFIX)) {\n return null;\n }\n const index = encoded.lastIndexOf('/');\n const file = encoded.slice(FUNC_PREFIX.length, index);\n const name = encoded.slice(index + 1);\n if (file.startsWith('_')) {\n return file.slice(1) + '#' + name;\n }\n return file + '#' + name;\n};\n"],"names":["encodeRscPath","rscPath","startsWith","Error","endsWith","decodeRscPath","err","statusCode","slice","length","FUNC_PREFIX","encodeFuncId","funcId","file","name","split","includes","decodeFuncId","encoded","index","lastIndexOf"],"mappings":"AAAA,mDAAmD;AAEnD,OAAO,MAAMA,gBAAgB,CAACC;IAC5B,IAAIA,QAAQC,UAAU,CAAC,MAAM;QAC3B,MAAM,IAAIC,MAAM,sCAAsCF;IACxD;IACA,IAAIA,QAAQG,QAAQ,CAAC,MAAM;QACzB,MAAM,IAAID,MAAM,oCAAoCF;IACtD;IACA,IAAIA,YAAY,IAAI;QAClBA,UAAU;IACZ;IACA,IAAIA,QAAQC,UAAU,CAAC,MAAM;QAC3BD,UAAU,MAAMA;IAClB;IACA,IAAIA,QAAQG,QAAQ,CAAC,MAAM;QACzBH,WAAW;IACb;IACA,OAAOA,UAAU;AACnB,EAAE;AAEF,OAAO,MAAMI,gBAAgB,CAACJ;IAC5B,IAAI,CAACA,QAAQG,QAAQ,CAAC,SAAS;QAC7B,MAAME,MAAM,IAAIH,MAAM;QACrBG,IAAYC,UAAU,GAAG;QAC1B,MAAMD;IACR;IACAL,UAAUA,QAAQO,KAAK,CAAC,GAAG,CAAC,OAAOC,MAAM;IACzC,IAAIR,QAAQC,UAAU,CAAC,MAAM;QAC3BD,UAAUA,QAAQO,KAAK,CAAC;IAC1B;IACA,IAAIP,QAAQG,QAAQ,CAAC,MAAM;QACzBH,UAAUA,QAAQO,KAAK,CAAC,GAAG,CAAC;IAC9B;IACA,OAAOP;AACT,EAAE;AAEF,MAAMS,cAAc;AAEpB,OAAO,MAAMC,eAAe,CAACC;IAC3B,MAAM,CAACC,MAAMC,KAAK,GAAGF,OAAOG,KAAK,CAAC;IAClC,IAAID,KAAKE,QAAQ,CAAC,MAAM;QACtB,MAAM,IAAIb,MAAM,yCAAyCW;IAC3D;IACA,IAAID,KAAKX,UAAU,CAAC,MAAM;QACxB,MAAM,IAAIC,MAAM,mCAAmCU;IACrD;IACA,IAAIA,KAAKX,UAAU,CAAC,MAAM;QACxB,OAAOQ,cAAc,MAAMG,OAAO,MAAMC;IAC1C;IACA,OAAOJ,cAAcG,OAAO,MAAMC;AACpC,EAAE;AAEF,OAAO,MAAMG,eAAe,CAACC;IAC3B,IAAI,CAACA,QAAQhB,UAAU,CAACQ,cAAc;QACpC,OAAO;IACT;IACA,MAAMS,QAAQD,QAAQE,WAAW,CAAC;IAClC,MAAMP,OAAOK,QAAQV,KAAK,CAACE,YAAYD,MAAM,EAAEU;IAC/C,MAAML,OAAOI,QAAQV,KAAK,CAACW,QAAQ;IACnC,IAAIN,KAAKX,UAAU,CAAC,MAAM;QACxB,OAAOW,KAAKL,KAAK,CAAC,KAAK,MAAMM;IAC/B;IACA,OAAOD,OAAO,MAAMC;AACtB,EAAE"}
|
package/dist/lib/utils/stream.js
CHANGED
|
@@ -1,33 +1,4 @@
|
|
|
1
1
|
// Utility functions for web streams (not Node.js streams)
|
|
2
|
-
export const concatUint8Arrays = (arrs)=>{
|
|
3
|
-
const len = arrs.reduce((acc, arr)=>acc + arr.length, 0);
|
|
4
|
-
const array = new Uint8Array(len);
|
|
5
|
-
let offset = 0;
|
|
6
|
-
for (const arr of arrs){
|
|
7
|
-
array.set(arr, offset);
|
|
8
|
-
offset += arr.length;
|
|
9
|
-
}
|
|
10
|
-
return array;
|
|
11
|
-
};
|
|
12
|
-
export const streamToString = async (stream)=>{
|
|
13
|
-
const decoder = new TextDecoder();
|
|
14
|
-
const reader = stream.getReader();
|
|
15
|
-
const outs = [];
|
|
16
|
-
let result;
|
|
17
|
-
do {
|
|
18
|
-
result = await reader.read();
|
|
19
|
-
if (result.value) {
|
|
20
|
-
if (!(result.value instanceof Uint8Array)) {
|
|
21
|
-
throw new Error('Unexepected buffer type');
|
|
22
|
-
}
|
|
23
|
-
outs.push(decoder.decode(result.value, {
|
|
24
|
-
stream: true
|
|
25
|
-
}));
|
|
26
|
-
}
|
|
27
|
-
}while (!result.done)
|
|
28
|
-
outs.push(decoder.decode());
|
|
29
|
-
return outs.join('');
|
|
30
|
-
};
|
|
31
2
|
export const stringToStream = (str)=>{
|
|
32
3
|
const encoder = new TextEncoder();
|
|
33
4
|
return new ReadableStream({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/utils/stream.ts"],"sourcesContent":["// Utility functions for web streams (not Node.js streams)\n\nexport const
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/utils/stream.ts"],"sourcesContent":["// Utility functions for web streams (not Node.js streams)\n\nexport const stringToStream = (str: string): ReadableStream => {\n const encoder = new TextEncoder();\n return new ReadableStream({\n start(controller) {\n controller.enqueue(encoder.encode(str));\n controller.close();\n },\n });\n};\n"],"names":["stringToStream","str","encoder","TextEncoder","ReadableStream","start","controller","enqueue","encode","close"],"mappings":"AAAA,0DAA0D;AAE1D,OAAO,MAAMA,iBAAiB,CAACC;IAC7B,MAAMC,UAAU,IAAIC;IACpB,OAAO,IAAIC,eAAe;QACxBC,OAAMC,UAAU;YACdA,WAAWC,OAAO,CAACL,QAAQM,MAAM,CAACP;YAClCK,WAAWG,KAAK;QAClB;IACF;AACF,EAAE"}
|
|
@@ -183,6 +183,12 @@ declare module 'waku/router' {
|
|
|
183
183
|
}
|
|
184
184
|
await updateGeneratedFile();
|
|
185
185
|
});
|
|
186
|
+
server.watcher.on('unlink', async (file)=>{
|
|
187
|
+
if (!outputFile || outputFile.endsWith(file)) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
await updateGeneratedFile();
|
|
191
|
+
});
|
|
186
192
|
void updateGeneratedFile();
|
|
187
193
|
}
|
|
188
194
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/vite-plugins/fs-router-typegen.ts"],"sourcesContent":["import type { Plugin } from 'vite';\nimport { readdir, writeFile } from 'node:fs/promises';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { SRC_SERVER_ENTRY, EXTENSIONS } from '../builder/constants.js';\nimport { joinPath } from '../utils/path.js';\nimport { isIgnoredPath } from '../utils/fs-router.js';\nimport { getGrouplessPath } from '../utils/create-pages.js';\nimport * as swc from '@swc/core';\n\nconst SRC_PAGES = 'pages';\n\n// https://tc39.es/ecma262/multipage/ecmascript-language-lexical-grammar.html#sec-names-and-keywords\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#identifiers\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#reserved_words\nexport function toIdentifier(input: string): string {\n // Strip the file extension\n let identifier = input.includes('.')\n ? input.split('.').slice(0, -1).join('.')\n : input;\n // Replace any characters besides letters, numbers, underscores, and dollar signs with underscores\n identifier = identifier.replace(/[^\\p{L}\\p{N}_$]/gu, '_');\n // Ensure it starts with a letter\n if (/^\\d/.test(identifier)) {\n identifier = '_' + identifier;\n }\n // Turn it into PascalCase\n // Since the first letter is uppercased, it will not be a reserved word\n return (\n 'File_' +\n identifier\n .split('_')\n .map((part) => {\n if (part[0] === undefined) {\n return '';\n }\n return part[0].toUpperCase() + part.slice(1);\n })\n .join('')\n );\n}\n\nexport function getImportModuleNames(filePaths: string[]): {\n [k: string]: string;\n} {\n const moduleNameCount: { [k: string]: number } = {};\n const moduleNames: { [k: string]: string } = {};\n for (const filePath of filePaths) {\n let identifier = toIdentifier(filePath);\n moduleNameCount[identifier] = (moduleNameCount[identifier] ?? -1) + 1;\n if (moduleNameCount[identifier]) {\n identifier = `${identifier}_${moduleNameCount[identifier]}`;\n }\n try {\n moduleNames[filePath.replace(/^\\//, '')] = identifier;\n } catch (e) {\n console.log(e);\n }\n }\n return moduleNames;\n}\n\nexport const fsRouterTypegenPlugin = (opts: { srcDir: string }): Plugin => {\n let entriesFilePossibilities: string[] | undefined;\n let pagesDir: string | undefined;\n let outputFile: string | undefined;\n\n return {\n name: 'vite-plugin-fs-router-typegen',\n apply: 'serve',\n async configResolved(config) {\n pagesDir = joinPath(config.root, opts.srcDir, SRC_PAGES);\n entriesFilePossibilities = EXTENSIONS.map((ext) =>\n joinPath(config.root, opts.srcDir, SRC_SERVER_ENTRY + ext),\n );\n outputFile = joinPath(config.root, opts.srcDir, 'pages.gen.ts');\n },\n configureServer(server) {\n if (\n !entriesFilePossibilities ||\n !pagesDir ||\n !outputFile ||\n entriesFilePossibilities.some((entriesFile) =>\n existsSync(entriesFile),\n ) ||\n !existsSync(pagesDir)\n ) {\n return;\n }\n\n // Recursively collect `.tsx` files in the given directory\n const collectFiles = async (\n dir: string,\n files: string[] = [],\n ): Promise<string[]> => {\n // TODO revisit recursive option for readdir once more stable\n // https://nodejs.org/docs/latest-v20.x/api/fs.html#direntparentpath\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = joinPath(dir, entry.name);\n if (entry.isDirectory()) {\n await collectFiles(fullPath, files);\n } else {\n if (entry.name.endsWith('.tsx')) {\n files.push(pagesDir ? fullPath.slice(pagesDir.length) : fullPath);\n }\n }\n }\n return files;\n };\n\n const fileExportsGetConfig = (filePath: string) => {\n if (!pagesDir) {\n return false;\n }\n const file = swc.parseSync(readFileSync(pagesDir + filePath, 'utf8'), {\n syntax: 'typescript',\n tsx: true,\n });\n\n return file.body.some((node) => {\n if (node.type === 'ExportNamedDeclaration') {\n return node.specifiers.some(\n (specifier) =>\n specifier.type === 'ExportSpecifier' &&\n !specifier.isTypeOnly &&\n ((!specifier.exported &&\n specifier.orig.value === 'getConfig') ||\n specifier.exported?.value === 'getConfig'),\n );\n }\n\n return (\n node.type === 'ExportDeclaration' &&\n ((node.declaration.type === 'VariableDeclaration' &&\n node.declaration.declarations.some(\n (decl) =>\n decl.id.type === 'Identifier' &&\n decl.id.value === 'getConfig',\n )) ||\n (node.declaration.type === 'FunctionDeclaration' &&\n node.declaration.identifier.value === 'getConfig'))\n );\n });\n };\n\n const generateFile = (filePaths: string[]): string | null => {\n const fileInfo: { path: string; src: string; hasGetConfig: boolean }[] =\n [];\n const moduleNames = getImportModuleNames(filePaths);\n\n for (const filePath of filePaths) {\n // where to import the component from\n const src = filePath.replace(/^\\//, '');\n let hasGetConfig = false;\n try {\n hasGetConfig = fileExportsGetConfig(filePath);\n } catch {\n return null;\n }\n\n if (\n filePath.endsWith('/_layout.tsx') ||\n isIgnoredPath(filePath.split('/'))\n ) {\n continue;\n } else if (filePath.endsWith('/index.tsx')) {\n const path = filePath.slice(0, -'/index.tsx'.length);\n fileInfo.push({\n path: getGrouplessPath(path) || '/',\n src,\n hasGetConfig,\n });\n } else {\n fileInfo.push({\n path: getGrouplessPath(filePath.replace('.tsx', '')),\n src,\n hasGetConfig,\n });\n }\n }\n\n let result = `// deno-fmt-ignore-file\n// biome-ignore format: generated types do not need formatting\n// prettier-ignore\nimport type { PathsForPages, GetConfigResponse } from 'waku/router';\\n\\n`;\n\n for (const file of fileInfo) {\n const moduleName = moduleNames[file.src];\n if (file.hasGetConfig) {\n result += `// prettier-ignore\\nimport type { getConfig as ${moduleName}_getConfig } from './${SRC_PAGES}/${file.src.replace('.tsx', '')}';\\n`;\n }\n }\n\n result += `\\n// prettier-ignore\\ntype Page =\\n`;\n\n for (const file of fileInfo) {\n const moduleName = moduleNames[file.src];\n if (file.hasGetConfig) {\n result += `| ({ path: '${file.path}' } & GetConfigResponse<typeof ${moduleName}_getConfig>)\\n`;\n } else {\n result += `| { path: '${file.path}'; render: 'dynamic' }\\n`;\n }\n }\n\n result =\n result.slice(0, -1) +\n `;\n\n// prettier-ignore\ndeclare module 'waku/router' {\n interface RouteConfig {\n paths: PathsForPages<Page>;\n }\n interface CreatePagesConfig {\n pages: Page;\n }\n}\n`;\n\n return result;\n };\n\n const updateGeneratedFile = async () => {\n if (!pagesDir || !outputFile) {\n return;\n }\n const files = await collectFiles(pagesDir);\n if (!files.length) {\n return;\n }\n const generation = generateFile(files);\n if (!generation) {\n // skip failures\n return;\n }\n await writeFile(outputFile, generation, 'utf-8');\n };\n\n server.watcher.on('change', async (file) => {\n if (!outputFile || outputFile.endsWith(file)) {\n return;\n }\n\n await updateGeneratedFile();\n });\n server.watcher.on('add', async (file) => {\n if (!outputFile || outputFile.endsWith(file)) {\n return;\n }\n\n await updateGeneratedFile();\n });\n\n void updateGeneratedFile();\n },\n };\n};\n"],"names":["readdir","writeFile","existsSync","readFileSync","SRC_SERVER_ENTRY","EXTENSIONS","joinPath","isIgnoredPath","getGrouplessPath","swc","SRC_PAGES","toIdentifier","input","identifier","includes","split","slice","join","replace","test","map","part","undefined","toUpperCase","getImportModuleNames","filePaths","moduleNameCount","moduleNames","filePath","e","console","log","fsRouterTypegenPlugin","opts","entriesFilePossibilities","pagesDir","outputFile","name","apply","configResolved","config","root","srcDir","ext","configureServer","server","some","entriesFile","collectFiles","dir","files","entries","withFileTypes","entry","fullPath","isDirectory","endsWith","push","length","fileExportsGetConfig","file","parseSync","syntax","tsx","body","node","type","specifiers","specifier","isTypeOnly","exported","orig","value","declaration","declarations","decl","id","generateFile","fileInfo","src","hasGetConfig","path","result","moduleName","updateGeneratedFile","generation","watcher","on"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/vite-plugins/fs-router-typegen.ts"],"sourcesContent":["import type { Plugin } from 'vite';\nimport { readdir, writeFile } from 'node:fs/promises';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { SRC_SERVER_ENTRY, EXTENSIONS } from '../builder/constants.js';\nimport { joinPath } from '../utils/path.js';\nimport { isIgnoredPath } from '../utils/fs-router.js';\nimport { getGrouplessPath } from '../utils/create-pages.js';\nimport * as swc from '@swc/core';\n\nconst SRC_PAGES = 'pages';\n\n// https://tc39.es/ecma262/multipage/ecmascript-language-lexical-grammar.html#sec-names-and-keywords\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#identifiers\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#reserved_words\nexport function toIdentifier(input: string): string {\n // Strip the file extension\n let identifier = input.includes('.')\n ? input.split('.').slice(0, -1).join('.')\n : input;\n // Replace any characters besides letters, numbers, underscores, and dollar signs with underscores\n identifier = identifier.replace(/[^\\p{L}\\p{N}_$]/gu, '_');\n // Ensure it starts with a letter\n if (/^\\d/.test(identifier)) {\n identifier = '_' + identifier;\n }\n // Turn it into PascalCase\n // Since the first letter is uppercased, it will not be a reserved word\n return (\n 'File_' +\n identifier\n .split('_')\n .map((part) => {\n if (part[0] === undefined) {\n return '';\n }\n return part[0].toUpperCase() + part.slice(1);\n })\n .join('')\n );\n}\n\nexport function getImportModuleNames(filePaths: string[]): {\n [k: string]: string;\n} {\n const moduleNameCount: { [k: string]: number } = {};\n const moduleNames: { [k: string]: string } = {};\n for (const filePath of filePaths) {\n let identifier = toIdentifier(filePath);\n moduleNameCount[identifier] = (moduleNameCount[identifier] ?? -1) + 1;\n if (moduleNameCount[identifier]) {\n identifier = `${identifier}_${moduleNameCount[identifier]}`;\n }\n try {\n moduleNames[filePath.replace(/^\\//, '')] = identifier;\n } catch (e) {\n console.log(e);\n }\n }\n return moduleNames;\n}\n\nexport const fsRouterTypegenPlugin = (opts: { srcDir: string }): Plugin => {\n let entriesFilePossibilities: string[] | undefined;\n let pagesDir: string | undefined;\n let outputFile: string | undefined;\n\n return {\n name: 'vite-plugin-fs-router-typegen',\n apply: 'serve',\n async configResolved(config) {\n pagesDir = joinPath(config.root, opts.srcDir, SRC_PAGES);\n entriesFilePossibilities = EXTENSIONS.map((ext) =>\n joinPath(config.root, opts.srcDir, SRC_SERVER_ENTRY + ext),\n );\n outputFile = joinPath(config.root, opts.srcDir, 'pages.gen.ts');\n },\n configureServer(server) {\n if (\n !entriesFilePossibilities ||\n !pagesDir ||\n !outputFile ||\n entriesFilePossibilities.some((entriesFile) =>\n existsSync(entriesFile),\n ) ||\n !existsSync(pagesDir)\n ) {\n return;\n }\n\n // Recursively collect `.tsx` files in the given directory\n const collectFiles = async (\n dir: string,\n files: string[] = [],\n ): Promise<string[]> => {\n // TODO revisit recursive option for readdir once more stable\n // https://nodejs.org/docs/latest-v20.x/api/fs.html#direntparentpath\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = joinPath(dir, entry.name);\n if (entry.isDirectory()) {\n await collectFiles(fullPath, files);\n } else {\n if (entry.name.endsWith('.tsx')) {\n files.push(pagesDir ? fullPath.slice(pagesDir.length) : fullPath);\n }\n }\n }\n return files;\n };\n\n const fileExportsGetConfig = (filePath: string) => {\n if (!pagesDir) {\n return false;\n }\n const file = swc.parseSync(readFileSync(pagesDir + filePath, 'utf8'), {\n syntax: 'typescript',\n tsx: true,\n });\n\n return file.body.some((node) => {\n if (node.type === 'ExportNamedDeclaration') {\n return node.specifiers.some(\n (specifier) =>\n specifier.type === 'ExportSpecifier' &&\n !specifier.isTypeOnly &&\n ((!specifier.exported &&\n specifier.orig.value === 'getConfig') ||\n specifier.exported?.value === 'getConfig'),\n );\n }\n\n return (\n node.type === 'ExportDeclaration' &&\n ((node.declaration.type === 'VariableDeclaration' &&\n node.declaration.declarations.some(\n (decl) =>\n decl.id.type === 'Identifier' &&\n decl.id.value === 'getConfig',\n )) ||\n (node.declaration.type === 'FunctionDeclaration' &&\n node.declaration.identifier.value === 'getConfig'))\n );\n });\n };\n\n const generateFile = (filePaths: string[]): string | null => {\n const fileInfo: { path: string; src: string; hasGetConfig: boolean }[] =\n [];\n const moduleNames = getImportModuleNames(filePaths);\n\n for (const filePath of filePaths) {\n // where to import the component from\n const src = filePath.replace(/^\\//, '');\n let hasGetConfig = false;\n try {\n hasGetConfig = fileExportsGetConfig(filePath);\n } catch {\n return null;\n }\n\n if (\n filePath.endsWith('/_layout.tsx') ||\n isIgnoredPath(filePath.split('/'))\n ) {\n continue;\n } else if (filePath.endsWith('/index.tsx')) {\n const path = filePath.slice(0, -'/index.tsx'.length);\n fileInfo.push({\n path: getGrouplessPath(path) || '/',\n src,\n hasGetConfig,\n });\n } else {\n fileInfo.push({\n path: getGrouplessPath(filePath.replace('.tsx', '')),\n src,\n hasGetConfig,\n });\n }\n }\n\n let result = `// deno-fmt-ignore-file\n// biome-ignore format: generated types do not need formatting\n// prettier-ignore\nimport type { PathsForPages, GetConfigResponse } from 'waku/router';\\n\\n`;\n\n for (const file of fileInfo) {\n const moduleName = moduleNames[file.src];\n if (file.hasGetConfig) {\n result += `// prettier-ignore\\nimport type { getConfig as ${moduleName}_getConfig } from './${SRC_PAGES}/${file.src.replace('.tsx', '')}';\\n`;\n }\n }\n\n result += `\\n// prettier-ignore\\ntype Page =\\n`;\n\n for (const file of fileInfo) {\n const moduleName = moduleNames[file.src];\n if (file.hasGetConfig) {\n result += `| ({ path: '${file.path}' } & GetConfigResponse<typeof ${moduleName}_getConfig>)\\n`;\n } else {\n result += `| { path: '${file.path}'; render: 'dynamic' }\\n`;\n }\n }\n\n result =\n result.slice(0, -1) +\n `;\n\n// prettier-ignore\ndeclare module 'waku/router' {\n interface RouteConfig {\n paths: PathsForPages<Page>;\n }\n interface CreatePagesConfig {\n pages: Page;\n }\n}\n`;\n\n return result;\n };\n\n const updateGeneratedFile = async () => {\n if (!pagesDir || !outputFile) {\n return;\n }\n const files = await collectFiles(pagesDir);\n if (!files.length) {\n return;\n }\n const generation = generateFile(files);\n if (!generation) {\n // skip failures\n return;\n }\n await writeFile(outputFile, generation, 'utf-8');\n };\n\n server.watcher.on('change', async (file) => {\n if (!outputFile || outputFile.endsWith(file)) {\n return;\n }\n\n await updateGeneratedFile();\n });\n server.watcher.on('add', async (file) => {\n if (!outputFile || outputFile.endsWith(file)) {\n return;\n }\n\n await updateGeneratedFile();\n });\n server.watcher.on('unlink', async (file) => {\n if (!outputFile || outputFile.endsWith(file)) {\n return;\n }\n\n await updateGeneratedFile();\n });\n\n void updateGeneratedFile();\n },\n };\n};\n"],"names":["readdir","writeFile","existsSync","readFileSync","SRC_SERVER_ENTRY","EXTENSIONS","joinPath","isIgnoredPath","getGrouplessPath","swc","SRC_PAGES","toIdentifier","input","identifier","includes","split","slice","join","replace","test","map","part","undefined","toUpperCase","getImportModuleNames","filePaths","moduleNameCount","moduleNames","filePath","e","console","log","fsRouterTypegenPlugin","opts","entriesFilePossibilities","pagesDir","outputFile","name","apply","configResolved","config","root","srcDir","ext","configureServer","server","some","entriesFile","collectFiles","dir","files","entries","withFileTypes","entry","fullPath","isDirectory","endsWith","push","length","fileExportsGetConfig","file","parseSync","syntax","tsx","body","node","type","specifiers","specifier","isTypeOnly","exported","orig","value","declaration","declarations","decl","id","generateFile","fileInfo","src","hasGetConfig","path","result","moduleName","updateGeneratedFile","generation","watcher","on"],"mappings":"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"}
|
package/dist/lib/vite-rsc/cli.js
CHANGED
|
@@ -64,7 +64,7 @@ export async function cli(cmd, flags) {
|
|
|
64
64
|
const port = parseInt(flags.port || '8080', 10);
|
|
65
65
|
const { serve } = await import('@hono/node-server');
|
|
66
66
|
const distDir = rscPluginOptions.config?.distDir ?? 'dist';
|
|
67
|
-
const entry = await import(pathToFileURL(path.resolve(distDir, '
|
|
67
|
+
const entry = await import(pathToFileURL(path.resolve(distDir, 'server', 'index.js')).href);
|
|
68
68
|
await startServer(port);
|
|
69
69
|
function startServer(port) {
|
|
70
70
|
return new Promise((resolve, reject)=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/vite-rsc/cli.ts"],"sourcesContent":["import * as vite from 'vite';\nimport { rscPlugin } from './plugin.js';\nimport type { Flags, RscPluginOptions } from './plugin.js';\nimport type { Config } from '../../config.js';\nimport { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nasync function loadConfig(): Promise<Config | undefined> {\n let config: Config | undefined;\n if (existsSync('waku.config.ts') || existsSync('waku.config.js')) {\n const imported = await vite.runnerImport<{ default: Config }>(\n '/waku.config',\n );\n config = imported.module.default;\n }\n return config;\n}\n\nasync function startDevServer(\n port: number,\n rscPluginOptions: RscPluginOptions,\n) {\n const server = await vite.createServer({\n configFile: false,\n plugins: [rscPlugin(rscPluginOptions)],\n server: { port },\n });\n await server.listen();\n const url = server.resolvedUrls!['local'];\n port = Number(url[0]?.match(/:(\\d+)/)?.[1]) || port;\n console.log(`ready: Listening on ${url}`);\n const watcher = server.watcher;\n watcher.on('change', handleConfigChange);\n watcher.on('unlink', handleConfigChange);\n watcher.on('add', handleConfigChange);\n\n async function handleConfigChange(changedFile: string) {\n const dirname = path.dirname(changedFile);\n const filename = path.basename(changedFile);\n if (\n dirname === process.cwd() &&\n (filename === 'waku.config.ts' || filename === 'waku.config.js')\n ) {\n console.log(`Waku configuration file changed, restarting server...`);\n await server.close();\n await startDevServer(port, {\n ...rscPluginOptions,\n config: await loadConfig(),\n });\n }\n }\n}\n\nexport async function cli(\n cmd: 'dev' | 'build' | 'start',\n flags: { port?: string } & Flags,\n) {\n // set NODE_ENV before runnerImport https://github.com/vitejs/vite/issues/20299\n process.env.NODE_ENV ??= cmd === 'dev' ? 'development' : 'production';\n\n const rscPluginOptions: RscPluginOptions = {\n flags,\n config: await loadConfig(),\n };\n\n if (cmd === 'dev') {\n const port = parseInt(flags.port || '3000', 10);\n await startDevServer(port, rscPluginOptions);\n } else if (cmd === 'build') {\n const builder = await vite.createBuilder({\n configFile: false,\n plugins: [rscPlugin(rscPluginOptions)],\n });\n await builder.buildApp();\n } else if (cmd === 'start') {\n const port = parseInt(flags.port || '8080', 10);\n const { serve } = await import('@hono/node-server');\n const distDir = rscPluginOptions.config?.distDir ?? 'dist';\n const entry: typeof import('../vite-entries/entry.server.js') =\n await import(\n pathToFileURL(path.resolve(distDir, '
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/vite-rsc/cli.ts"],"sourcesContent":["import * as vite from 'vite';\nimport { rscPlugin } from './plugin.js';\nimport type { Flags, RscPluginOptions } from './plugin.js';\nimport type { Config } from '../../config.js';\nimport { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nasync function loadConfig(): Promise<Config | undefined> {\n let config: Config | undefined;\n if (existsSync('waku.config.ts') || existsSync('waku.config.js')) {\n const imported = await vite.runnerImport<{ default: Config }>(\n '/waku.config',\n );\n config = imported.module.default;\n }\n return config;\n}\n\nasync function startDevServer(\n port: number,\n rscPluginOptions: RscPluginOptions,\n) {\n const server = await vite.createServer({\n configFile: false,\n plugins: [rscPlugin(rscPluginOptions)],\n server: { port },\n });\n await server.listen();\n const url = server.resolvedUrls!['local'];\n port = Number(url[0]?.match(/:(\\d+)/)?.[1]) || port;\n console.log(`ready: Listening on ${url}`);\n const watcher = server.watcher;\n watcher.on('change', handleConfigChange);\n watcher.on('unlink', handleConfigChange);\n watcher.on('add', handleConfigChange);\n\n async function handleConfigChange(changedFile: string) {\n const dirname = path.dirname(changedFile);\n const filename = path.basename(changedFile);\n if (\n dirname === process.cwd() &&\n (filename === 'waku.config.ts' || filename === 'waku.config.js')\n ) {\n console.log(`Waku configuration file changed, restarting server...`);\n await server.close();\n await startDevServer(port, {\n ...rscPluginOptions,\n config: await loadConfig(),\n });\n }\n }\n}\n\nexport async function cli(\n cmd: 'dev' | 'build' | 'start',\n flags: { port?: string } & Flags,\n) {\n // set NODE_ENV before runnerImport https://github.com/vitejs/vite/issues/20299\n process.env.NODE_ENV ??= cmd === 'dev' ? 'development' : 'production';\n\n const rscPluginOptions: RscPluginOptions = {\n flags,\n config: await loadConfig(),\n };\n\n if (cmd === 'dev') {\n const port = parseInt(flags.port || '3000', 10);\n await startDevServer(port, rscPluginOptions);\n } else if (cmd === 'build') {\n const builder = await vite.createBuilder({\n configFile: false,\n plugins: [rscPlugin(rscPluginOptions)],\n });\n await builder.buildApp();\n } else if (cmd === 'start') {\n const port = parseInt(flags.port || '8080', 10);\n const { serve } = await import('@hono/node-server');\n const distDir = rscPluginOptions.config?.distDir ?? 'dist';\n const entry: typeof import('../vite-entries/entry.server.js') =\n await import(\n pathToFileURL(path.resolve(distDir, 'server', 'index.js')).href\n );\n await startServer(port);\n function startServer(port: number) {\n return new Promise<void>((resolve, reject) => {\n const server = serve({ fetch: entry.default, port }, () => {\n console.log(`ready: Listening on http://localhost:${port}/`);\n resolve();\n });\n server.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n console.log(\n `warn: Port ${port} is in use, trying ${port + 1} instead.`,\n );\n startServer(port + 1)\n .then(resolve)\n .catch(reject);\n } else {\n console.error(`Failed to start server: ${err.message}`);\n }\n });\n });\n }\n }\n}\n"],"names":["vite","rscPlugin","existsSync","path","pathToFileURL","loadConfig","config","imported","runnerImport","module","default","startDevServer","port","rscPluginOptions","server","createServer","configFile","plugins","listen","url","resolvedUrls","Number","match","console","log","watcher","on","handleConfigChange","changedFile","dirname","filename","basename","process","cwd","close","cli","cmd","flags","env","NODE_ENV","parseInt","builder","createBuilder","buildApp","serve","distDir","entry","resolve","href","startServer","Promise","reject","fetch","err","code","then","catch","error","message"],"mappings":"AAAA,YAAYA,UAAU,OAAO;AAC7B,SAASC,SAAS,QAAQ,cAAc;AAGxC,SAASC,UAAU,QAAQ,UAAU;AACrC,OAAOC,UAAU,YAAY;AAC7B,SAASC,aAAa,QAAQ,WAAW;AAEzC,eAAeC;IACb,IAAIC;IACJ,IAAIJ,WAAW,qBAAqBA,WAAW,mBAAmB;QAChE,MAAMK,WAAW,MAAMP,KAAKQ,YAAY,CACtC;QAEFF,SAASC,SAASE,MAAM,CAACC,OAAO;IAClC;IACA,OAAOJ;AACT;AAEA,eAAeK,eACbC,IAAY,EACZC,gBAAkC;IAElC,MAAMC,SAAS,MAAMd,KAAKe,YAAY,CAAC;QACrCC,YAAY;QACZC,SAAS;YAAChB,UAAUY;SAAkB;QACtCC,QAAQ;YAAEF;QAAK;IACjB;IACA,MAAME,OAAOI,MAAM;IACnB,MAAMC,MAAML,OAAOM,YAAY,AAAC,CAAC,QAAQ;IACzCR,OAAOS,OAAOF,GAAG,CAAC,EAAE,EAAEG,MAAM,WAAW,CAAC,EAAE,KAAKV;IAC/CW,QAAQC,GAAG,CAAC,CAAC,oBAAoB,EAAEL,KAAK;IACxC,MAAMM,UAAUX,OAAOW,OAAO;IAC9BA,QAAQC,EAAE,CAAC,UAAUC;IACrBF,QAAQC,EAAE,CAAC,UAAUC;IACrBF,QAAQC,EAAE,CAAC,OAAOC;IAElB,eAAeA,mBAAmBC,WAAmB;QACnD,MAAMC,UAAU1B,KAAK0B,OAAO,CAACD;QAC7B,MAAME,WAAW3B,KAAK4B,QAAQ,CAACH;QAC/B,IACEC,YAAYG,QAAQC,GAAG,MACtBH,CAAAA,aAAa,oBAAoBA,aAAa,gBAAe,GAC9D;YACAP,QAAQC,GAAG,CAAC,CAAC,qDAAqD,CAAC;YACnE,MAAMV,OAAOoB,KAAK;YAClB,MAAMvB,eAAeC,MAAM;gBACzB,GAAGC,gBAAgB;gBACnBP,QAAQ,MAAMD;YAChB;QACF;IACF;AACF;AAEA,OAAO,eAAe8B,IACpBC,GAA8B,EAC9BC,KAAgC;IAEhC,+EAA+E;IAC/EL,QAAQM,GAAG,CAACC,QAAQ,KAAKH,QAAQ,QAAQ,gBAAgB;IAEzD,MAAMvB,mBAAqC;QACzCwB;QACA/B,QAAQ,MAAMD;IAChB;IAEA,IAAI+B,QAAQ,OAAO;QACjB,MAAMxB,OAAO4B,SAASH,MAAMzB,IAAI,IAAI,QAAQ;QAC5C,MAAMD,eAAeC,MAAMC;IAC7B,OAAO,IAAIuB,QAAQ,SAAS;QAC1B,MAAMK,UAAU,MAAMzC,KAAK0C,aAAa,CAAC;YACvC1B,YAAY;YACZC,SAAS;gBAAChB,UAAUY;aAAkB;QACxC;QACA,MAAM4B,QAAQE,QAAQ;IACxB,OAAO,IAAIP,QAAQ,SAAS;QAC1B,MAAMxB,OAAO4B,SAASH,MAAMzB,IAAI,IAAI,QAAQ;QAC5C,MAAM,EAAEgC,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC;QAC/B,MAAMC,UAAUhC,iBAAiBP,MAAM,EAAEuC,WAAW;QACpD,MAAMC,QACJ,MAAM,MAAM,CACV1C,cAAcD,KAAK4C,OAAO,CAACF,SAAS,UAAU,aAAaG,IAAI;QAEnE,MAAMC,YAAYrC;QAClB,SAASqC,YAAYrC,IAAY;YAC/B,OAAO,IAAIsC,QAAc,CAACH,SAASI;gBACjC,MAAMrC,SAAS8B,MAAM;oBAAEQ,OAAON,MAAMpC,OAAO;oBAAEE;gBAAK,GAAG;oBACnDW,QAAQC,GAAG,CAAC,CAAC,qCAAqC,EAAEZ,KAAK,CAAC,CAAC;oBAC3DmC;gBACF;gBACAjC,OAAOY,EAAE,CAAC,SAAS,CAAC2B;oBAClB,IAAIA,IAAIC,IAAI,KAAK,cAAc;wBAC7B/B,QAAQC,GAAG,CACT,CAAC,WAAW,EAAEZ,KAAK,mBAAmB,EAAEA,OAAO,EAAE,SAAS,CAAC;wBAE7DqC,YAAYrC,OAAO,GAChB2C,IAAI,CAACR,SACLS,KAAK,CAACL;oBACX,OAAO;wBACL5B,QAAQkC,KAAK,CAAC,CAAC,wBAAwB,EAAEJ,IAAIK,OAAO,EAAE;oBACxD;gBACF;YACF;QACF;IACF;AACF"}
|
|
@@ -37,7 +37,7 @@ export function deployAwsLambdaPlugin(deployOptions) {
|
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
async function build({ opts }) {
|
|
40
|
-
writeFileSync(path.join(opts.distDir, SERVE_JS), `export { handler } from './
|
|
40
|
+
writeFileSync(path.join(opts.distDir, SERVE_JS), `export { handler } from './server/index.js';\n`);
|
|
41
41
|
writeFileSync(path.join(opts.distDir, 'package.json'), JSON.stringify({
|
|
42
42
|
type: 'module'
|
|
43
43
|
}, null, 2));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/lib/vite-rsc/deploy/aws-lambda/plugin.ts"],"sourcesContent":["import { type Plugin, type ResolvedConfig } from 'vite';\nimport { writeFileSync } from 'node:fs';\nimport path from 'node:path';\nimport type { Config } from '../../../../config.js';\nimport { fileURLToPath } from 'node:url';\n\nconst __dirname = fileURLToPath(new URL('.', import.meta.url));\nconst SERVER_ENTRY = path.join(__dirname, 'entry.js');\nconst SERVE_JS = 'serve-aws-lambda.js';\n\nexport function deployAwsLambdaPlugin(deployOptions: {\n config: Required<Config>;\n streaming: boolean;\n}): Plugin {\n return {\n name: 'waku:deploy-aws-lambda',\n config() {\n return {\n environments: {\n rsc: {\n build: {\n rollupOptions: {\n input: {\n index: SERVER_ENTRY,\n },\n },\n },\n define: {\n 'import.meta.env.WAKU_AWS_LAMBDA_STREAMING': JSON.stringify(\n deployOptions.streaming,\n ),\n },\n },\n },\n };\n },\n buildApp: {\n order: 'post',\n async handler(builder) {\n await build({\n config: builder.config,\n opts: deployOptions.config,\n });\n },\n },\n };\n}\n\nasync function build({\n opts,\n}: {\n config: ResolvedConfig;\n opts: Required<Config>;\n}) {\n writeFileSync(\n path.join(opts.distDir, SERVE_JS),\n `export { handler } from './
|
|
1
|
+
{"version":3,"sources":["../../../../../src/lib/vite-rsc/deploy/aws-lambda/plugin.ts"],"sourcesContent":["import { type Plugin, type ResolvedConfig } from 'vite';\nimport { writeFileSync } from 'node:fs';\nimport path from 'node:path';\nimport type { Config } from '../../../../config.js';\nimport { fileURLToPath } from 'node:url';\n\nconst __dirname = fileURLToPath(new URL('.', import.meta.url));\nconst SERVER_ENTRY = path.join(__dirname, 'entry.js');\nconst SERVE_JS = 'serve-aws-lambda.js';\n\nexport function deployAwsLambdaPlugin(deployOptions: {\n config: Required<Config>;\n streaming: boolean;\n}): Plugin {\n return {\n name: 'waku:deploy-aws-lambda',\n config() {\n return {\n environments: {\n rsc: {\n build: {\n rollupOptions: {\n input: {\n index: SERVER_ENTRY,\n },\n },\n },\n define: {\n 'import.meta.env.WAKU_AWS_LAMBDA_STREAMING': JSON.stringify(\n deployOptions.streaming,\n ),\n },\n },\n },\n };\n },\n buildApp: {\n order: 'post',\n async handler(builder) {\n await build({\n config: builder.config,\n opts: deployOptions.config,\n });\n },\n },\n };\n}\n\nasync function build({\n opts,\n}: {\n config: ResolvedConfig;\n opts: Required<Config>;\n}) {\n writeFileSync(\n path.join(opts.distDir, SERVE_JS),\n `export { handler } from './server/index.js';\\n`,\n );\n writeFileSync(\n path.join(opts.distDir, 'package.json'),\n JSON.stringify({ type: 'module' }, null, 2),\n );\n}\n"],"names":["writeFileSync","path","fileURLToPath","__dirname","URL","url","SERVER_ENTRY","join","SERVE_JS","deployAwsLambdaPlugin","deployOptions","name","config","environments","rsc","build","rollupOptions","input","index","define","JSON","stringify","streaming","buildApp","order","handler","builder","opts","distDir","type"],"mappings":"AACA,SAASA,aAAa,QAAQ,UAAU;AACxC,OAAOC,UAAU,YAAY;AAE7B,SAASC,aAAa,QAAQ,WAAW;AAEzC,MAAMC,YAAYD,cAAc,IAAIE,IAAI,KAAK,YAAYC,GAAG;AAC5D,MAAMC,eAAeL,KAAKM,IAAI,CAACJ,WAAW;AAC1C,MAAMK,WAAW;AAEjB,OAAO,SAASC,sBAAsBC,aAGrC;IACC,OAAO;QACLC,MAAM;QACNC;YACE,OAAO;gBACLC,cAAc;oBACZC,KAAK;wBACHC,OAAO;4BACLC,eAAe;gCACbC,OAAO;oCACLC,OAAOZ;gCACT;4BACF;wBACF;wBACAa,QAAQ;4BACN,6CAA6CC,KAAKC,SAAS,CACzDX,cAAcY,SAAS;wBAE3B;oBACF;gBACF;YACF;QACF;QACAC,UAAU;YACRC,OAAO;YACP,MAAMC,SAAQC,OAAO;gBACnB,MAAMX,MAAM;oBACVH,QAAQc,QAAQd,MAAM;oBACtBe,MAAMjB,cAAcE,MAAM;gBAC5B;YACF;QACF;IACF;AACF;AAEA,eAAeG,MAAM,EACnBY,IAAI,EAIL;IACC3B,cACEC,KAAKM,IAAI,CAACoB,KAAKC,OAAO,EAAEpB,WACxB,CAAC,8CAA8C,CAAC;IAElDR,cACEC,KAAKM,IAAI,CAACoB,KAAKC,OAAO,EAAE,iBACxBR,KAAKC,SAAS,CAAC;QAAEQ,MAAM;IAAS,GAAG,MAAM;AAE7C"}
|
|
@@ -56,7 +56,7 @@ async function build({ config, opts }) {
|
|
|
56
56
|
const outDir = path.join(rootDir, opts.distDir);
|
|
57
57
|
const assetsDistDir = path.join(outDir, 'assets');
|
|
58
58
|
const workerDistDir = path.join(outDir, 'worker');
|
|
59
|
-
writeFileSync(path.join(outDir, SERVE_JS), `export { default } from './
|
|
59
|
+
writeFileSync(path.join(outDir, SERVE_JS), `export { default } from './server/index.js';\n`);
|
|
60
60
|
separatePublicAssetsFromFunctions({
|
|
61
61
|
outDir,
|
|
62
62
|
assetsDir: assetsDistDir,
|