@webtypen/webframez-react 0.0.2 → 0.0.4
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 +238 -110
- package/bin/webframez-react.mjs +179 -7
- package/defaults/tsconfig.server.example.json +46 -0
- package/defaults/tsconfig.server.json +13 -0
- package/defaults/webpack.client.cjs +4 -1
- package/defaults/webpack.server.cjs +8 -1
- package/dist/client.cjs +1 -1
- package/dist/client.js +1 -1
- package/dist/http.cjs +55 -19
- package/dist/http.d.ts +18 -7
- package/dist/http.js +55 -19
- package/dist/index.cjs +55 -19
- package/dist/index.d.ts +2 -5
- package/dist/index.js +55 -19
- package/dist/router.cjs +30 -10
- package/dist/router.js +30 -10
- package/dist/types.d.ts +25 -8
- package/dist/webframez-core.cjs +55 -19
- package/dist/webframez-core.d.ts +32 -8
- package/dist/webframez-core.js +55 -19
- package/package.json +5 -2
- package/register.cjs +1 -0
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
# webframez-react
|
|
1
|
+
# @webtypen/webframez-react
|
|
2
2
|
|
|
3
3
|
React Server Components (RSC) extension for `@webtypen/webframez-core`.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
`@webtypen/webframez-react` provides:
|
|
7
6
|
- seamless `webframez-core` integration via `initWebframezReact(Route)`
|
|
8
7
|
- file-based routing for `pages/**/index.tsx`
|
|
9
8
|
- layout/error handling with `RouteChildren`
|
|
10
9
|
- client-side navigation (`Link`, `Redirect`, `useRouter`) and cookies (`useCookie`)
|
|
11
10
|
- server-rendered initial HTML plus RSC streaming
|
|
11
|
+
- a small CLI for the standard server/client build pipeline
|
|
12
12
|
|
|
13
13
|
## Requirements
|
|
14
14
|
|
|
@@ -26,111 +26,201 @@ npm i -D typescript webpack webpack-cli ts-loader
|
|
|
26
26
|
## Quick Start with webframez-core
|
|
27
27
|
|
|
28
28
|
```ts
|
|
29
|
-
// server.ts
|
|
29
|
+
// src/server.ts
|
|
30
|
+
import path from "node:path";
|
|
30
31
|
import { BaseKernelWeb, Route, WebApplication } from "@webtypen/webframez-core";
|
|
31
|
-
import { initWebframezReact } from "@webtypen/webframez-react
|
|
32
|
+
import { initWebframezReact } from "@webtypen/webframez-react";
|
|
32
33
|
|
|
33
34
|
class Kernel extends BaseKernelWeb {
|
|
34
35
|
static controller = {};
|
|
35
36
|
static middleware = {};
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
initWebframezReact(Route);
|
|
39
|
+
const ReactRoute = initWebframezReact(Route);
|
|
39
40
|
|
|
40
41
|
const app = new WebApplication();
|
|
41
42
|
app.boot({
|
|
42
43
|
kernel: Kernel,
|
|
43
44
|
routesFunction: () => {
|
|
44
|
-
|
|
45
|
-
distRootDir:
|
|
45
|
+
ReactRoute.renderReact("/react", {
|
|
46
|
+
distRootDir: path.resolve(process.cwd(), "dist"),
|
|
46
47
|
});
|
|
47
48
|
},
|
|
48
49
|
});
|
|
49
50
|
```
|
|
50
51
|
|
|
51
52
|
Notes:
|
|
52
|
-
|
|
53
53
|
- `"/react"` is automatically registered as a catch-all route (`/react/*`).
|
|
54
|
-
- `basePath`, `assetsPrefix`, `rscPath`, and `clientScriptUrl` are derived automatically from the mount path.
|
|
54
|
+
- `basePath`, `assetsPrefix`, `rscPath`, and `clientScriptUrl` are derived automatically from the mount path unless you override them.
|
|
55
|
+
- `initWebframezReact(Route)` returns the extended route facade, which gives you reliable editor autocompletion for `renderReact(...)` even in monorepos or symlinked development setups.
|
|
56
|
+
- The package also ships module augmentation for `Route.renderReact(...)`, but using the returned `ReactRoute` variable is the most robust TypeScript setup.
|
|
55
57
|
|
|
56
|
-
##
|
|
58
|
+
## `Route.renderReact()`
|
|
57
59
|
|
|
58
|
-
|
|
60
|
+
Signature:
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
npm i @webtypen/webframez-react react react-dom react-server-dom-webpack
|
|
64
|
-
npm i -D typescript webpack webpack-cli ts-loader
|
|
62
|
+
```ts
|
|
63
|
+
Route.renderReact(path, options)
|
|
65
64
|
```
|
|
66
65
|
|
|
67
|
-
|
|
66
|
+
Example:
|
|
68
67
|
|
|
69
68
|
```ts
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
class Kernel extends BaseKernelWeb {
|
|
76
|
-
static controller = {};
|
|
77
|
-
static middleware = {};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
initWebframezReact(Route);
|
|
81
|
-
|
|
82
|
-
const app = new WebApplication();
|
|
83
|
-
app.boot({
|
|
84
|
-
kernel: Kernel,
|
|
85
|
-
port: 3000,
|
|
86
|
-
routesFunction: () => {
|
|
87
|
-
// Your existing core routes can stay here.
|
|
88
|
-
Route.renderReact("/app", {
|
|
89
|
-
distRootDir: path.resolve(process.cwd(), "dist"),
|
|
90
|
-
});
|
|
69
|
+
Route.renderReact("/app", {
|
|
70
|
+
distRootDir: path.resolve(process.cwd(), "dist"),
|
|
71
|
+
method: "GET",
|
|
72
|
+
routeOptions: {
|
|
73
|
+
middleware: ["auth"],
|
|
91
74
|
},
|
|
92
75
|
});
|
|
93
76
|
```
|
|
94
77
|
|
|
95
|
-
|
|
78
|
+
### Options
|
|
79
|
+
|
|
80
|
+
`distRootDir`
|
|
81
|
+
- Required.
|
|
82
|
+
- Directory containing the built client assets and generated manifests.
|
|
83
|
+
|
|
84
|
+
`pagesDir`
|
|
85
|
+
- Optional.
|
|
86
|
+
- Directory containing the compiled `pages/**` output.
|
|
87
|
+
- Default: `${distRootDir}/pages`
|
|
88
|
+
|
|
89
|
+
`manifestPath`
|
|
90
|
+
- Optional.
|
|
91
|
+
- Path to the React client manifest.
|
|
92
|
+
- Default: `${distRootDir}/react-client-manifest.json`
|
|
93
|
+
|
|
94
|
+
`assetsPrefix`
|
|
95
|
+
- Optional.
|
|
96
|
+
- Public URL prefix used to serve built client assets.
|
|
97
|
+
- Auto-derived from `path`.
|
|
98
|
+
- Example for `"/react"`: `/react/assets/`
|
|
99
|
+
|
|
100
|
+
`rscPath`
|
|
101
|
+
- Optional.
|
|
102
|
+
- Public URL for the RSC endpoint.
|
|
103
|
+
- Auto-derived from `path`.
|
|
104
|
+
- Example for `"/react"`: `/react/rsc`
|
|
105
|
+
|
|
106
|
+
`clientScriptUrl`
|
|
107
|
+
- Optional.
|
|
108
|
+
- Public URL of the browser client entry bundle.
|
|
109
|
+
- Auto-derived from `path`.
|
|
110
|
+
- Example for `"/react"`: `/react/assets/client.js`
|
|
111
|
+
|
|
112
|
+
`basePath`
|
|
113
|
+
- Optional.
|
|
114
|
+
- Basename mounted in front of all file-router paths.
|
|
115
|
+
- Auto-derived from `path` when `path !== "/"`.
|
|
116
|
+
|
|
117
|
+
`liveReloadPath`
|
|
118
|
+
- Optional.
|
|
119
|
+
- Enables dev live reload on a custom path or disables it explicitly with `false`.
|
|
120
|
+
- Automatically disabled in production mode.
|
|
121
|
+
|
|
122
|
+
`method`
|
|
123
|
+
- Optional.
|
|
124
|
+
- HTTP method or methods used to register the route.
|
|
125
|
+
- Supported values: `"GET" | "POST" | "PUT" | "DELETE"`
|
|
126
|
+
- Default: `"GET"`
|
|
127
|
+
|
|
128
|
+
`routeOptions`
|
|
129
|
+
- Optional.
|
|
130
|
+
- Additional route options forwarded to `webframez-core`.
|
|
131
|
+
- Typical use case: middleware.
|
|
132
|
+
|
|
133
|
+
## Recommended Project Structure
|
|
96
134
|
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
|
|
135
|
+
```txt
|
|
136
|
+
pages/
|
|
137
|
+
layout.tsx
|
|
138
|
+
errors.tsx
|
|
139
|
+
index.tsx
|
|
140
|
+
about/index.tsx
|
|
141
|
+
src/
|
|
142
|
+
server.ts
|
|
143
|
+
client.tsx
|
|
144
|
+
components/
|
|
145
|
+
dist/
|
|
146
|
+
```
|
|
100
147
|
|
|
101
|
-
|
|
102
|
-
import { RouteChildren } from "@webtypen/webframez-react/router";
|
|
148
|
+
## `tsconfig.server.json`
|
|
103
149
|
|
|
104
|
-
|
|
105
|
-
return (
|
|
106
|
-
<main>
|
|
107
|
-
<RouteChildren />
|
|
108
|
-
</main>
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
```
|
|
150
|
+
Compared to a standard `@webtypen/webframez-core` project, the server TypeScript config usually needs a few changes:
|
|
112
151
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
152
|
+
- enable JSX via `"jsx": "react-jsx"`
|
|
153
|
+
- include `pages/**/*.tsx`
|
|
154
|
+
- include `src/components/**/*.tsx`
|
|
155
|
+
- include your server entry (`src/server.ts` or `src/server.tsx`)
|
|
156
|
+
- exclude the browser client entry (`src/client.tsx` by default)
|
|
157
|
+
- add `paths` mappings for the `@webtypen/webframez-react` package and its subpaths
|
|
116
158
|
|
|
117
|
-
|
|
159
|
+
When you use the CLI (`webframez-react build:server` / `watch:server`), it generates a temporary `.webframez-react.tsconfig.server.json` that extends your project `tsconfig.server.json`. That means:
|
|
118
160
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
```
|
|
161
|
+
- your own `tsconfig.server.json` stays the source of truth
|
|
162
|
+
- the CLI only injects the resolved server entry and the standard RSC include/exclude rules
|
|
163
|
+
- you only need to customize the base config when your project structure differs from the defaults
|
|
123
164
|
|
|
124
|
-
|
|
165
|
+
A good starting point is the shipped default config:
|
|
125
166
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
import { mountWebframezClient } from "@webtypen/webframez-react/client";
|
|
167
|
+
- `@webtypen/webframez-react/defaults/tsconfig.server`
|
|
168
|
+
- `@webtypen/webframez-react/defaults/tsconfig.server.example`
|
|
129
169
|
|
|
130
|
-
|
|
170
|
+
Example:
|
|
171
|
+
|
|
172
|
+
```json
|
|
173
|
+
{
|
|
174
|
+
"compilerOptions": {
|
|
175
|
+
"target": "ES2020",
|
|
176
|
+
"module": "CommonJS",
|
|
177
|
+
"moduleResolution": "Node",
|
|
178
|
+
"baseUrl": ".",
|
|
179
|
+
"paths": {
|
|
180
|
+
"@webtypen/webframez-react": [
|
|
181
|
+
"./node_modules/@webtypen/webframez-react/dist/index.d.ts"
|
|
182
|
+
],
|
|
183
|
+
"@webtypen/webframez-react/types": [
|
|
184
|
+
"./node_modules/@webtypen/webframez-react/dist/types.d.ts"
|
|
185
|
+
],
|
|
186
|
+
"@webtypen/webframez-react/router": [
|
|
187
|
+
"./node_modules/@webtypen/webframez-react/dist/router.d.ts"
|
|
188
|
+
],
|
|
189
|
+
"@webtypen/webframez-react/client": [
|
|
190
|
+
"./node_modules/@webtypen/webframez-react/dist/client.d.ts"
|
|
191
|
+
],
|
|
192
|
+
"@webtypen/webframez-react/navigation": [
|
|
193
|
+
"./node_modules/@webtypen/webframez-react/dist/navigation.d.ts"
|
|
194
|
+
],
|
|
195
|
+
"@webtypen/webframez-react/webframez-core": [
|
|
196
|
+
"./node_modules/@webtypen/webframez-react/dist/webframez-core.d.ts"
|
|
197
|
+
]
|
|
198
|
+
},
|
|
199
|
+
"jsx": "react-jsx",
|
|
200
|
+
"strict": true,
|
|
201
|
+
"esModuleInterop": true,
|
|
202
|
+
"skipLibCheck": true,
|
|
203
|
+
"outDir": "dist",
|
|
204
|
+
"rootDir": "."
|
|
205
|
+
},
|
|
206
|
+
"include": [
|
|
207
|
+
"src/server.ts",
|
|
208
|
+
"src/server.tsx",
|
|
209
|
+
"src/components/**/*.tsx",
|
|
210
|
+
"pages/**/*.tsx",
|
|
211
|
+
"src/types.d.ts"
|
|
212
|
+
],
|
|
213
|
+
"exclude": [
|
|
214
|
+
"src/client.tsx",
|
|
215
|
+
"dist",
|
|
216
|
+
"node_modules"
|
|
217
|
+
]
|
|
218
|
+
}
|
|
131
219
|
```
|
|
132
220
|
|
|
133
|
-
|
|
221
|
+
## `package.json` Scripts
|
|
222
|
+
|
|
223
|
+
Recommended scripts:
|
|
134
224
|
|
|
135
225
|
```json
|
|
136
226
|
{
|
|
@@ -138,18 +228,84 @@ mountWebframezClient();
|
|
|
138
228
|
"build:server": "webframez-react build:server",
|
|
139
229
|
"build:client": "webframez-react build:client",
|
|
140
230
|
"build": "npm run build:server && npm run build:client",
|
|
231
|
+
"start": "NODE_OPTIONS='--conditions react-server -r @webtypen/webframez-react/register' node start-server.cjs",
|
|
141
232
|
"watch:server": "webframez-react watch:server",
|
|
142
233
|
"watch:client": "webframez-react watch:client",
|
|
143
|
-
"serve:watch": "
|
|
234
|
+
"serve:watch": "NODE_OPTIONS='--conditions react-server -r @webtypen/webframez-react/register' node --watch start-server.cjs",
|
|
144
235
|
"watch": "sh -c 'npm run watch:server & npm run watch:client & npm run serve:watch & wait'",
|
|
145
236
|
"dev": "sh -c 'npm run watch:server & npm run watch:client & npm run serve:watch & wait'"
|
|
146
237
|
}
|
|
147
238
|
}
|
|
148
239
|
```
|
|
149
240
|
|
|
150
|
-
|
|
241
|
+
Notes:
|
|
242
|
+
- `build` compiles the server output (`pages`, `server.ts`) and the browser client bundle (`client.tsx` + RSC manifests).
|
|
243
|
+
- `start` runs the built app in React Server mode.
|
|
244
|
+
- `watch` / `dev` keep TypeScript and webpack in watch mode and restart Node automatically when server output changes.
|
|
245
|
+
- `@webtypen/webframez-react/register` activates the React Server module register, so `"use client"` modules are treated correctly in Node and in the package's SSR worker.
|
|
246
|
+
|
|
247
|
+
If you run an existing `webframez-core` app directly with `ts-node` or `node`, use the same preload:
|
|
248
|
+
|
|
249
|
+
```json
|
|
250
|
+
{
|
|
251
|
+
"scripts": {
|
|
252
|
+
"start": "NODE_OPTIONS='--conditions react-server -r @webtypen/webframez-react/register' ts-node ./app.ts",
|
|
253
|
+
"watch:app": "nodemon --exec \"NODE_OPTIONS='--conditions react-server -r @webtypen/webframez-react/register' ts-node ./app.ts\""
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Or via the shipped wrapper command:
|
|
259
|
+
|
|
260
|
+
```json
|
|
261
|
+
{
|
|
262
|
+
"scripts": {
|
|
263
|
+
"start": "TS_NODE_FILES=true webframez-react exec -- ts-node ./app.ts",
|
|
264
|
+
"watch:app": "nodemon --exec \"TS_NODE_FILES=true webframez-react exec -- ts-node ./app.ts\""
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## CLI Config and Custom Entry Paths
|
|
270
|
+
|
|
271
|
+
The CLI first checks project override files and then falls back to the package defaults:
|
|
272
|
+
|
|
273
|
+
- `tsconfig.server.json`
|
|
274
|
+
- `webpack.client.cjs`
|
|
275
|
+
- `webpack.server.cjs`
|
|
276
|
+
|
|
277
|
+
If you do not want to create your own webpack config just to move `client.tsx` or `server.ts`, you can use a small project config file:
|
|
278
|
+
|
|
279
|
+
```js
|
|
280
|
+
// webframez-react.config.mjs
|
|
281
|
+
export default {
|
|
282
|
+
clientEntryPath: "src/app/client.tsx",
|
|
283
|
+
serverEntryPath: "src/app/server.tsx",
|
|
284
|
+
};
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Supported file names:
|
|
288
|
+
- `webframez-react.config.mjs`
|
|
289
|
+
- `webframez-react.config.cjs`
|
|
290
|
+
- `webframez-react.config.js`
|
|
291
|
+
- `webframez-react.config.json`
|
|
292
|
+
|
|
293
|
+
You can also override the entry paths per command:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
webframez-react build:client --client-entry=src/app/client.tsx
|
|
297
|
+
webframez-react build:server --server-entry=src/app/server.tsx
|
|
298
|
+
webframez-react watch:client --client-entry=src/app/client.tsx
|
|
299
|
+
webframez-react watch:server --server-entry=src/app/server.tsx
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Notes:
|
|
303
|
+
- The default client entry is `src/client.tsx`.
|
|
304
|
+
- The default server entry is `src/server.ts`, with automatic fallback to `src/server.tsx` if present.
|
|
305
|
+
- `build:server:webpack` and `watch:server:webpack` also respect `serverEntryPath`.
|
|
306
|
+
- `build:server` and `watch:server` generate a temporary `.webframez-react.tsconfig.server.json` so custom server entry paths also work with the TypeScript compiler.
|
|
151
307
|
|
|
152
|
-
##
|
|
308
|
+
## File-Based Routing
|
|
153
309
|
|
|
154
310
|
Example:
|
|
155
311
|
|
|
@@ -205,9 +361,8 @@ export default function AccountPage({ params, abort }: PageProps) {
|
|
|
205
361
|
```
|
|
206
362
|
|
|
207
363
|
Behavior:
|
|
208
|
-
|
|
209
364
|
- default without options: `404` + `"Page not found"`
|
|
210
|
-
- rendered through `pages/errors.tsx`
|
|
365
|
+
- rendered through `pages/errors.tsx`
|
|
211
366
|
- `pathname` is provided automatically by context
|
|
212
367
|
- optional `payload` is forwarded to `errors.tsx`
|
|
213
368
|
|
|
@@ -255,8 +410,7 @@ export function Guard({ loggedIn }: { loggedIn: boolean }) {
|
|
|
255
410
|
}
|
|
256
411
|
```
|
|
257
412
|
|
|
258
|
-
|
|
259
|
-
|
|
413
|
+
Notes:
|
|
260
414
|
- `Link` and `Redirect` automatically use the basename from `Route.renderReact()`.
|
|
261
415
|
- You can override it per usage via `basename`.
|
|
262
416
|
|
|
@@ -288,7 +442,12 @@ export default function LoginAction() {
|
|
|
288
442
|
## Public Entrypoints
|
|
289
443
|
|
|
290
444
|
- `@webtypen/webframez-react`
|
|
291
|
-
- `
|
|
445
|
+
- `initWebframezReact`
|
|
446
|
+
- `createNodeRequestHandler`
|
|
447
|
+
- `createFileRouter`
|
|
448
|
+
- `createHTMLShell`
|
|
449
|
+
- `sendRSC`
|
|
450
|
+
- `createRSCHandler`
|
|
292
451
|
- `@webtypen/webframez-react/webframez-core`
|
|
293
452
|
- `initWebframezReact`
|
|
294
453
|
- `@webtypen/webframez-react/router`
|
|
@@ -298,48 +457,17 @@ export default function LoginAction() {
|
|
|
298
457
|
- `@webtypen/webframez-react/navigation`
|
|
299
458
|
- `Link`, `Redirect`
|
|
300
459
|
- `@webtypen/webframez-react/types`
|
|
301
|
-
-
|
|
302
|
-
|
|
303
|
-
## package.json Scripts
|
|
304
|
-
|
|
305
|
-
Example scripts for a `webframez-react` app:
|
|
306
|
-
|
|
307
|
-
```json
|
|
308
|
-
{
|
|
309
|
-
"scripts": {
|
|
310
|
-
"build:server": "webframez-react build:server",
|
|
311
|
-
"build:client": "webframez-react build:client",
|
|
312
|
-
"build": "npm run build:server && npm run build:client",
|
|
313
|
-
"start": "node --conditions react-server start-server.cjs",
|
|
314
|
-
"watch:server": "webframez-react watch:server",
|
|
315
|
-
"watch:client": "webframez-react watch:client",
|
|
316
|
-
"serve:watch": "node --watch --conditions react-server start-server.cjs",
|
|
317
|
-
"watch": "sh -c 'npm run watch:server & npm run watch:client & npm run serve:watch & wait'",
|
|
318
|
-
"dev": "sh -c 'npm run watch:server & npm run watch:client & npm run serve:watch & wait'"
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
```
|
|
460
|
+
- `RouteContext`, `PageProps`, `ErrorPageProps`, `AbortRouteOptions`, ...
|
|
322
461
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
- `webframez-react` CLI first checks project overrides and falls back to package defaults:
|
|
326
|
-
- `tsconfig.server.json`
|
|
327
|
-
- `webpack.client.cjs`
|
|
328
|
-
- `webpack.server.cjs`
|
|
329
|
-
- `webpack.server.cjs` is optional. Default flow compiles server with `tsc`. Use webpack-server only if you explicitly want a bundled server build:
|
|
330
|
-
- `webframez-react build:server:webpack`
|
|
331
|
-
- `webframez-react watch:server:webpack`
|
|
332
|
-
- `build` compiles server output (`pages`, `server.ts`) and client output (`client.tsx` bundle + RSC manifests).
|
|
333
|
-
- `start` runs the built app in React Server mode.
|
|
334
|
-
- `watch` and `dev` run the same full watch pipeline and restart Node automatically on server output changes.
|
|
462
|
+
## Package Build
|
|
335
463
|
|
|
336
|
-
|
|
464
|
+
Build the package itself:
|
|
337
465
|
|
|
338
466
|
```bash
|
|
339
467
|
npm run build
|
|
340
468
|
```
|
|
341
469
|
|
|
342
|
-
Watch mode:
|
|
470
|
+
Watch mode for package development:
|
|
343
471
|
|
|
344
472
|
```bash
|
|
345
473
|
npm run build:watch
|