waku 0.19.1 → 0.19.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/README.md +49 -6
  2. package/cli.js +4 -0
  3. package/dist/cli.d.ts +0 -1
  4. package/dist/cli.js +23 -24
  5. package/dist/client.d.ts +1 -1
  6. package/dist/client.js +2 -2
  7. package/dist/config.d.ts +5 -0
  8. package/dist/lib/builder/build.d.ts +1 -1
  9. package/dist/lib/builder/build.js +172 -84
  10. package/dist/lib/builder/output-aws-lambda.d.ts +2 -0
  11. package/dist/lib/builder/output-aws-lambda.js +7 -0
  12. package/dist/lib/builder/output-cloudflare.js +3 -2
  13. package/dist/lib/builder/output-netlify.d.ts +2 -0
  14. package/dist/lib/builder/output-netlify.js +17 -0
  15. package/dist/lib/builder/output-vercel.d.ts +1 -1
  16. package/dist/lib/builder/output-vercel.js +6 -19
  17. package/dist/lib/builder/serve-aws-lambda.d.ts +1 -0
  18. package/dist/lib/builder/serve-aws-lambda.js +19 -0
  19. package/dist/lib/builder/serve-cloudflare.d.ts +1 -1
  20. package/dist/lib/builder/serve-cloudflare.js +6 -2
  21. package/dist/lib/builder/serve-deno.js +3 -3
  22. package/dist/lib/builder/serve-netlify.d.ts +3 -0
  23. package/dist/lib/builder/serve-netlify.js +14 -0
  24. package/dist/lib/builder/serve-vercel.js +11 -25
  25. package/dist/lib/config.d.ts +1 -0
  26. package/dist/lib/config.js +1 -0
  27. package/dist/lib/handlers/dev-worker-api.d.ts +4 -11
  28. package/dist/lib/handlers/dev-worker-api.js +3 -23
  29. package/dist/lib/handlers/dev-worker-impl.js +17 -24
  30. package/dist/lib/handlers/handler-dev.js +12 -13
  31. package/dist/lib/handlers/handler-prd.js +51 -52
  32. package/dist/lib/plugins/vite-plugin-rsc-delegate.d.ts +2 -2
  33. package/dist/lib/plugins/vite-plugin-rsc-delegate.js +58 -14
  34. package/dist/lib/plugins/vite-plugin-rsc-hmr.d.ts +19 -7
  35. package/dist/lib/plugins/vite-plugin-rsc-hmr.js +118 -51
  36. package/dist/lib/plugins/vite-plugin-rsc-serve.d.ts +1 -0
  37. package/dist/lib/plugins/vite-plugin-rsc-serve.js +10 -0
  38. package/dist/lib/plugins/vite-plugin-rsc-transform.d.ts +2 -0
  39. package/dist/lib/plugins/vite-plugin-rsc-transform.js +3 -0
  40. package/dist/lib/renderers/html-renderer.d.ts +0 -1
  41. package/dist/lib/renderers/html-renderer.js +1 -1
  42. package/dist/lib/renderers/rsc-renderer.d.ts +4 -2
  43. package/dist/lib/renderers/rsc-renderer.js +11 -14
  44. package/dist/lib/renderers/utils.d.ts +2 -0
  45. package/dist/lib/renderers/utils.js +11 -0
  46. package/dist/lib/utils/node-fs.d.ts +2 -0
  47. package/dist/lib/utils/node-fs.js +2 -0
  48. package/dist/lib/utils/path.d.ts +13 -0
  49. package/dist/lib/utils/path.js +66 -0
  50. package/dist/router/client.d.ts +1 -1
  51. package/dist/router/client.js +21 -5
  52. package/dist/router/server.d.ts +6 -2
  53. package/dist/router/server.js +79 -138
  54. package/dist/server.d.ts +4 -4
  55. package/package.json +21 -13
  56. package/src/cli.ts +23 -24
  57. package/src/client.ts +3 -3
  58. package/src/config.ts +5 -0
  59. package/src/lib/builder/build.ts +214 -97
  60. package/src/lib/builder/output-aws-lambda.ts +10 -0
  61. package/src/lib/builder/output-cloudflare.ts +3 -2
  62. package/src/lib/builder/output-netlify.ts +27 -0
  63. package/src/lib/builder/output-vercel.ts +6 -24
  64. package/src/lib/builder/serve-aws-lambda.ts +18 -0
  65. package/src/lib/builder/serve-cloudflare.ts +4 -1
  66. package/src/lib/builder/serve-deno.ts +1 -1
  67. package/src/lib/builder/serve-netlify.ts +14 -0
  68. package/src/lib/builder/serve-vercel.ts +8 -26
  69. package/src/lib/config.ts +1 -0
  70. package/src/lib/handlers/dev-worker-api.ts +6 -32
  71. package/src/lib/handlers/dev-worker-impl.ts +27 -20
  72. package/src/lib/handlers/handler-dev.ts +12 -15
  73. package/src/lib/handlers/handler-prd.ts +56 -55
  74. package/src/lib/middleware/hono-utils.ts +1 -1
  75. package/src/lib/plugins/vite-plugin-rsc-delegate.ts +54 -15
  76. package/src/lib/plugins/vite-plugin-rsc-hmr.ts +145 -57
  77. package/src/lib/plugins/vite-plugin-rsc-serve.ts +17 -0
  78. package/src/lib/plugins/vite-plugin-rsc-transform.ts +5 -0
  79. package/src/lib/renderers/html-renderer.ts +2 -2
  80. package/src/lib/renderers/rsc-renderer.ts +24 -28
  81. package/src/lib/renderers/utils.ts +13 -0
  82. package/src/lib/utils/node-fs.ts +6 -0
  83. package/src/lib/utils/path.ts +81 -0
  84. package/src/router/client.ts +29 -4
  85. package/src/router/server.ts +77 -146
  86. package/src/server.ts +5 -4
  87. package/dist/lib/plugins/vite-plugin-rsc-reload.d.ts +0 -2
  88. package/dist/lib/plugins/vite-plugin-rsc-reload.js +0 -43
  89. package/src/lib/plugins/vite-plugin-rsc-reload.ts +0 -51
package/README.md CHANGED
@@ -27,17 +27,21 @@ Start a new Waku project with the `create` command for your preferred package ma
27
27
  npm create waku@latest
28
28
  ```
29
29
 
30
+ **Node.js version requirement:** `^20.8.0 || ^18.16.0`
31
+
30
32
  ## Rendering
31
33
 
32
- Let's face it: React is getting complicated. But not without good reason!
34
+ While there's a bit of a learning curve to modern React rendering, it introduces powerful new patterns of full-stack composability that are only possible with the advent of [server components](https://github.com/reactjs/rfcs/blob/main/text/0188-server-components.md).
35
+
36
+ So please don't be intimidated by the `'use client'` directive! Once you get the hang of it, you'll appreciate how awesome it is to flexibly move server-client boundaries with a single line of code as your full-stack React codebase evolves over time. It's way simpler than maintaining separate codebases for your backend and frontend.
33
37
 
34
- While there's a bit of a learning curve to modern React rendering, it introduces powerful new patterns of composability that are only possible with the advent of React server components. So stick with us.
38
+ And please don't fret about client components! Even if you only lightly optimize towards server components, your client bundle size will be smaller than traditional React frameworks, which are always 100% client components.
35
39
 
36
- Future versions of Waku may provide additional APIs to abstract away some of the complexity for an improved developer experience.
40
+ > Future versions of Waku may provide additional opt-in APIs to abstract some of the complexity away for an improved developer experience.
37
41
 
38
42
  #### Server components
39
43
 
40
- Waku supports [server components](https://github.com/reactjs/rfcs/blob/main/text/0188-server-components.md) and [server actions](https://react.dev/reference/react/use-server). Server components can be made async and can securely perform server-side logic and data fetching. Feel free to use heavy dependencies since they aren't included in the client bundle. They have no interactivity or access to browser APIs since they run exclusively on the server.
44
+ Server components can be made async and can securely perform server-side logic and data fetching. Feel free to access the local file-system and import heavy dependencies since they aren't included in the client bundle. They have no state, interactivity, or access to browser APIs since they run exclusively on the server.
41
45
 
42
46
  ```tsx
43
47
  // server component
@@ -115,7 +119,17 @@ export const Providers = ({ children }) => {
115
119
 
116
120
  #### Server-side rendering
117
121
 
118
- Waku provides static prerendering (SSG) or server-side rendering (SSR) options for layouts and pages including both their server and client components.
122
+ SSR is a distinct concept from RSC. Waku provides static prerendering (SSG) or server-side rendering (SSR) options for both layouts and pages including all of their server _and_ client components.
123
+
124
+ #### tl;dr:
125
+
126
+ Each layout and page in Waku is composed of a React component heirarchy.
127
+
128
+ It begins with a server component at the top of the tree. Then at points down the heirarchy, you'll eventually import a component that needs client component APIs. Mark this file with a `'use client'` directive at the top. When imported into a server component, it will create a server-client boundary. Below this point in the component heirarchy, all imported components are hydrated and will run in the browser as well.
129
+
130
+ Server components can still be rendered below this boundary, but only via composition (e.g., `children` props). They form [a new layer](https://github.com/reactwg/server-components/discussions/4) that run _before_ any client code.
131
+
132
+ Client components are still server-side rendered. SSR is seperate from RSC. See the [linked diagrams](https://github.com/reactwg/server-components/discussions/4) for a helpful visual.
119
133
 
120
134
  #### Further reading
121
135
 
@@ -362,7 +376,7 @@ export const Providers = ({ children }) => {
362
376
 
363
377
  #### Other layouts
364
378
 
365
- Layouts are also helpful further down the tree. For example, you could add a layout at `path: '/blog` to add a sidebar to both the blog index and all blog article pages.
379
+ Layouts are also helpful further down the tree. For example, you could add a layout at `path: '/blog'` to add a sidebar to both the blog index and all blog article pages.
366
380
 
367
381
  ```tsx
368
382
  // ./src/entries.tsx
@@ -635,6 +649,27 @@ Adding the `--with-vercel-static` flag to the build script will produce static s
635
649
  }
636
650
  ```
637
651
 
652
+ ### Netlify
653
+
654
+ Waku projects can be deployed to Netlify with the [Netlify CLI](https://docs.netlify.com/cli/get-started/).
655
+
656
+ ```
657
+ npm run build -- --with-netlify
658
+ netlify deploy --dir=dist/public
659
+ ```
660
+
661
+ #### Pure SSG
662
+
663
+ Adding the `--with-netlify-static` flag to the build script will produce static sites without Netlify functions.
664
+
665
+ ```
666
+ {
667
+ "scripts": {
668
+ "build": "waku build --with-ssr --with-netlify-static"
669
+ }
670
+ }
671
+ ```
672
+
638
673
  ### Cloudflare (experimental)
639
674
 
640
675
  ```
@@ -649,6 +684,14 @@ npm run build -- --with-deno
649
684
  DENO_DEPLOY_TOKEN=... deployctl deploy --project=... --prod dist/serve.js --exclude node_modules
650
685
  ```
651
686
 
687
+ ### AWS Lambda (experimental)
688
+
689
+ ```
690
+ npm run build -- --with-aws-lambda
691
+ ```
692
+
693
+ The handler entrypoint is `dist/serve.js` - see [Hono AWS Lambda Deploy Docs](https://hono.dev/getting-started/aws-lambda#_3-deploy)
694
+
652
695
  ## Community
653
696
 
654
697
  Please join our friendly [GitHub discussions](https://github.com/dai-shi/waku/discussions) or [Discord server](https://discord.gg/MrQdmzd) to participate in the Waku community. Hope to see you there!
package/cli.js ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+
3
+ // eslint-disable-next-line import/no-unresolved
4
+ import './dist/cli.js';
package/dist/cli.d.ts CHANGED
@@ -1,2 +1 @@
1
- #!/usr/bin/env node
2
1
  export {};
package/dist/cli.js CHANGED
@@ -1,6 +1,5 @@
1
- #!/usr/bin/env node
2
1
  import path from 'node:path';
3
- import { existsSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs';
2
+ import { existsSync, writeFileSync, unlinkSync } from 'node:fs';
4
3
  import { pathToFileURL } from 'node:url';
5
4
  import { parseArgs } from 'node:util';
6
5
  import { createRequire } from 'node:module';
@@ -9,11 +8,18 @@ import { Hono } from 'hono';
9
8
  import { serve } from '@hono/node-server';
10
9
  import { serveStatic } from '@hono/node-server/serve-static';
11
10
  import * as swc from '@swc/core';
11
+ import * as dotenv from 'dotenv';
12
12
  import { resolveConfig } from './lib/config.js';
13
13
  import { honoMiddleware as honoDevMiddleware } from './lib/middleware/hono-dev.js';
14
14
  import { honoMiddleware as honoPrdMiddleware } from './lib/middleware/hono-prd.js';
15
15
  import { build } from './lib/builder/build.js';
16
16
  const require = createRequire(new URL('.', import.meta.url));
17
+ dotenv.config({
18
+ path: [
19
+ '.env.local',
20
+ '.env'
21
+ ]
22
+ });
17
23
  const { values, positionals } = parseArgs({
18
24
  args: process.argv.slice(2),
19
25
  allowPositionals: true,
@@ -33,6 +39,15 @@ const { values, positionals } = parseArgs({
33
39
  'with-deno': {
34
40
  type: 'boolean'
35
41
  },
42
+ 'with-netlify': {
43
+ type: 'boolean'
44
+ },
45
+ 'with-netlify-static': {
46
+ type: 'boolean'
47
+ },
48
+ 'with-aws-lambda': {
49
+ type: 'boolean'
50
+ },
36
51
  version: {
37
52
  type: 'boolean',
38
53
  short: 'v'
@@ -43,7 +58,6 @@ const { values, positionals } = parseArgs({
43
58
  }
44
59
  }
45
60
  });
46
- loadEnv();
47
61
  const config = await loadConfig();
48
62
  const cmd = positionals[0];
49
63
  if (values.version) {
@@ -92,22 +106,22 @@ async function runBuild(options) {
92
106
  ...options,
93
107
  config,
94
108
  env: process.env,
95
- deploy: (values['with-vercel'] ?? !!process.env.VERCEL ? values['with-vercel-static'] ? 'vercel-static' : 'vercel-serverless' : undefined) || (values['with-cloudflare'] ? 'cloudflare' : undefined) || (values['with-deno'] ? 'deno' : undefined)
109
+ deploy: (values['with-vercel'] ?? !!process.env.VERCEL ? values['with-vercel-static'] ? 'vercel-static' : 'vercel-serverless' : undefined) || (values['with-cloudflare'] ? 'cloudflare' : undefined) || (values['with-deno'] ? 'deno' : undefined) || (values['with-netlify'] ?? !!process.env.NETLIFY ? values['with-netlify-static'] ? 'netlify-static' : 'netlify-functions' : undefined) || (values['with-aws-lambda'] ? 'aws-lambda' : undefined)
96
110
  });
97
111
  }
98
112
  async function runStart(options) {
99
113
  const { distDir, publicDir, entriesJs } = await resolveConfig(config);
100
114
  const loadEntries = ()=>import(pathToFileURL(path.resolve(distDir, entriesJs)).toString());
101
115
  const app = new Hono();
116
+ app.use('*', serveStatic({
117
+ root: path.join(distDir, publicDir)
118
+ }));
102
119
  app.use('*', honoPrdMiddleware({
103
120
  ...options,
104
121
  config,
105
122
  loadEntries,
106
123
  env: process.env
107
124
  }));
108
- app.use('*', serveStatic({
109
- root: path.join(distDir, publicDir)
110
- }));
111
125
  const port = parseInt(process.env.PORT || '8080', 10);
112
126
  startServer(app, port);
113
127
  }
@@ -141,27 +155,12 @@ Options:
141
155
  --with-vercel Output for Vercel on build
142
156
  --with-cloudflare Output for Cloudflare on build
143
157
  --with-deno Output for Deno on build
158
+ --with-netlify Output for Netlify on build
159
+ --with-aws-lambda Output for AWS Lambda on build
144
160
  -v, --version Display the version number
145
161
  -h, --help Display this help message
146
162
  `);
147
163
  }
148
- // TODO consider using a library such as `dotenv`
149
- function loadEnv() {
150
- if (existsSync('.env.local')) {
151
- for (const line of readFileSync('.env.local', 'utf8').split('\n')){
152
- const [key, value] = line.split('=');
153
- if (key && value) {
154
- if (value.startsWith('"') && value.endsWith('"')) {
155
- process.env[key.trim()] = value.slice(1, -1);
156
- } else if (value.startsWith("'") && value.endsWith("'")) {
157
- process.env[key.trim()] = value.slice(1, -1);
158
- } else {
159
- process.env[key.trim()] = value.trim();
160
- }
161
- }
162
- }
163
- }
164
- }
165
164
  // TODO is this a good idea?
166
165
  async function loadConfig() {
167
166
  if (!existsSync('waku.config.ts')) {
package/dist/client.d.ts CHANGED
@@ -5,7 +5,7 @@ declare global {
5
5
  }
6
6
  }
7
7
  type Elements = Promise<Record<string, ReactNode>>;
8
- type SetElements = (fn: (prev: Elements) => Elements) => void;
8
+ type SetElements = (updater: Elements | ((prev: Elements) => Elements)) => void;
9
9
  type CacheEntry = [
10
10
  input: string,
11
11
  searchParamsString: string,
package/dist/client.js CHANGED
@@ -2,7 +2,7 @@
2
2
  'use client';
3
3
  import { createContext, createElement, memo, use, useCallback, useState, startTransition } from 'react';
4
4
  import RSDWClient from 'react-server-dom-webpack/client';
5
- import { encodeInput } from './lib/renderers/utils.js';
5
+ import { encodeInput, encodeActionId } from './lib/renderers/utils.js';
6
6
  const { createFromFetch, encodeReply } = RSDWClient;
7
7
  const BASE_PATH = `${import.meta.env?.WAKU_CONFIG_BASE_PATH}${import.meta.env?.WAKU_CONFIG_RSC_PATH}/`;
8
8
  const checkStatus = async (responsePromise)=>{
@@ -37,7 +37,7 @@ export const fetchRSC = (input, searchParamsString, setElements, cache = fetchCa
37
37
  }
38
38
  const options = {
39
39
  async callServer (actionId, args) {
40
- const response = fetch(BASE_PATH + encodeInput(encodeURIComponent(actionId)), {
40
+ const response = fetch(BASE_PATH + encodeInput(encodeActionId(actionId)), {
41
41
  method: 'POST',
42
42
  body: await encodeReply(args)
43
43
  });
package/dist/config.d.ts CHANGED
@@ -27,6 +27,11 @@ export interface Config {
27
27
  * Defaults to "assets".
28
28
  */
29
29
  assetsDir?: string;
30
+ /**
31
+ * The SSR directory relative to distDir.
32
+ * Defaults to "ssr".
33
+ */
34
+ ssrDir?: string;
30
35
  /**
31
36
  * The index.html file for any directories.
32
37
  * Defaults to "index.html".
@@ -3,5 +3,5 @@ export declare function build(options: {
3
3
  config?: Config;
4
4
  ssr?: boolean;
5
5
  env?: Record<string, string>;
6
- deploy?: 'vercel-static' | 'vercel-serverless' | 'cloudflare' | 'deno' | undefined;
6
+ deploy?: 'vercel-static' | 'vercel-serverless' | 'cloudflare' | 'deno' | 'netlify-static' | 'netlify-functions' | 'aws-lambda' | undefined;
7
7
  }): Promise<void>;