@lazarv/react-server 0.0.0-experimental-eed22ac-20240913-e818c1a8 → 0.0.0-experimental-059f540-20240914-36104f27
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 +14 -10
- package/bin/help.mjs +1 -8
- package/config/index.mjs +1 -0
- package/lib/build/server.mjs +6 -25
- package/lib/dev/action.mjs +6 -1
- package/lib/dev/create-server.mjs +6 -29
- package/lib/dev/modules.mjs +1 -12
- package/lib/plugins/file-router/entrypoint.jsx +100 -0
- package/lib/plugins/file-router/plugin.mjs +1246 -0
- package/lib/plugins/file-router/react-server-router.d.ts +208 -0
- package/lib/start/manifest.mjs +1 -1
- package/package.json +8 -1
package/README.md
CHANGED
|
@@ -91,32 +91,36 @@ You can unleash cluster mode by using the `REACT_SERVER_CLUSTER` environment var
|
|
|
91
91
|
REACT_SERVER_CLUSTER=8 pnpm exec react-server start
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
-
##
|
|
94
|
+
## File-system based routing
|
|
95
95
|
|
|
96
|
-
To enable file-system based routing, you
|
|
96
|
+
To enable file-system based routing, you just omit the entrypoint when running a `@lazarv/react-server` app.
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
pnpm add @lazarv/react-server-router
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
Create a `@lazarv/react-server` configuration file in your project root to specify where the router should start processing files. By default every file are included in the routing, but you can include/exclude using arrays of glob patterns.
|
|
98
|
+
Create a `@lazarv/react-server` configuration file in your project root to specify where the router should start processing files by using the `root` property. By default every file are included in the routing, but you can include/exclude using arrays of glob patterns. The following example will only include `page.tsx` files as pages and `layout.tsx` files as layouts, emulating the behavior of Next.js.
|
|
103
99
|
|
|
104
100
|
#### `react-server.config.json`
|
|
105
101
|
|
|
106
102
|
```json
|
|
107
103
|
{
|
|
108
|
-
"root": "
|
|
104
|
+
"root": "app",
|
|
105
|
+
"page": {
|
|
106
|
+
"include": ["**/page.tsx"],
|
|
107
|
+
},
|
|
108
|
+
"layout": {
|
|
109
|
+
"include": ["**/layout.tsx"],
|
|
110
|
+
}
|
|
109
111
|
}
|
|
110
112
|
```
|
|
111
113
|
|
|
112
|
-
Move your entrypoint from `./App.tsx` to `./
|
|
114
|
+
Move your entrypoint component from `./App.tsx` to `./app/layout.tsx` and `./app/page.tsx` to transform it into a page with a layout.
|
|
113
115
|
|
|
114
|
-
Just start `react-server` without
|
|
116
|
+
Just start `react-server` without specifying an entrypoint.
|
|
115
117
|
|
|
116
118
|
```sh
|
|
117
119
|
pnpm exec react-server --open
|
|
118
120
|
```
|
|
119
121
|
|
|
122
|
+
Read more about file-system based routing at [react-server.dev/router](https://react-server.dev/router).
|
|
123
|
+
|
|
120
124
|
## Documentation
|
|
121
125
|
|
|
122
126
|
Check out the full documentation at [react-server.dev](https://react-server.dev).
|
package/bin/help.mjs
CHANGED
|
@@ -144,13 +144,6 @@ export default async function help() {
|
|
|
144
144
|
) {
|
|
145
145
|
const packageManager = await detectPackageManager();
|
|
146
146
|
|
|
147
|
-
const installCommand =
|
|
148
|
-
packageManager === "yarn"
|
|
149
|
-
? "yarn add"
|
|
150
|
-
: packageManager === "pnpm"
|
|
151
|
-
? "pnpm add"
|
|
152
|
-
: "npm install";
|
|
153
|
-
|
|
154
147
|
const execCommand =
|
|
155
148
|
packageManager === "yarn "
|
|
156
149
|
? "yarn exec "
|
|
@@ -176,7 +169,7 @@ You don't need to do anything else to get started, just create a ${colors.cyan("
|
|
|
176
169
|
|
|
177
170
|
Start the development server with ${colors.cyan(`${execCommand}react-server <root>`)} or build your project with ${colors.cyan(`${execCommand}react-server build <root>`)} then start the production server with ${colors.cyan(`${execCommand}react-server start`)} where ${colors.cyan("<root>")} is your entrypoint (like ${colors.cyan("./App.jsx")}). See all available commands by running ${colors.cyan(`${execCommand}react-server --help`)} or read more on how to use ${colors.cyan("@lazarv/react-server")} at ${colors.cyan("https://react-server.dev")}.
|
|
178
171
|
|
|
179
|
-
Alternatively you can
|
|
172
|
+
Alternatively you can use the built-in file-system based routing by omitting the ${colors.cyan("<root>")} in the above commands. Learn more at ${colors.cyan("https://react-server.dev/router")}.
|
|
180
173
|
`,
|
|
181
174
|
maxLineWidth
|
|
182
175
|
)
|
package/config/index.mjs
CHANGED
package/lib/build/server.mjs
CHANGED
|
@@ -14,6 +14,7 @@ import rollupUseClient from "../plugins/use-client.mjs";
|
|
|
14
14
|
import rollupUseServerInline from "../plugins/use-server-inline.mjs";
|
|
15
15
|
import rollupUseServer from "../plugins/use-server.mjs";
|
|
16
16
|
import rootModule from "../plugins/root-module.mjs";
|
|
17
|
+
import fileRouter from "../plugins/file-router/plugin.mjs";
|
|
17
18
|
import * as sys from "../sys.mjs";
|
|
18
19
|
import {
|
|
19
20
|
filterOutVitePluginReact,
|
|
@@ -27,15 +28,7 @@ const __require = createRequire(import.meta.url);
|
|
|
27
28
|
const cwd = sys.cwd();
|
|
28
29
|
|
|
29
30
|
export default async function serverBuild(root, options) {
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
reactServerRouterModule = __require.resolve("@lazarv/react-server-router", {
|
|
33
|
-
paths: [cwd],
|
|
34
|
-
});
|
|
35
|
-
} catch {
|
|
36
|
-
// ignore
|
|
37
|
-
root ||= "virtual:react-server-eval.jsx";
|
|
38
|
-
}
|
|
31
|
+
root ||= "@lazarv/react-server/file-router";
|
|
39
32
|
|
|
40
33
|
banner("server", options.dev);
|
|
41
34
|
const config = forRoot();
|
|
@@ -122,7 +115,7 @@ export default async function serverBuild(root, options) {
|
|
|
122
115
|
: root?.startsWith("virtual:")
|
|
123
116
|
? root
|
|
124
117
|
: __require.resolve(
|
|
125
|
-
root?.split("#")?.[0] ?? "@lazarv/react-server-router",
|
|
118
|
+
root?.split("#")?.[0] ?? "@lazarv/react-server/file-router",
|
|
126
119
|
{
|
|
127
120
|
paths: [cwd],
|
|
128
121
|
}
|
|
@@ -174,21 +167,9 @@ export default async function serverBuild(root, options) {
|
|
|
174
167
|
},
|
|
175
168
|
},
|
|
176
169
|
plugins: [
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
(async () =>
|
|
181
|
-
(
|
|
182
|
-
await import(
|
|
183
|
-
pathToFileURL(
|
|
184
|
-
__require.resolve("@lazarv/react-server-router/plugin", {
|
|
185
|
-
paths: [cwd],
|
|
186
|
-
})
|
|
187
|
-
)
|
|
188
|
-
)
|
|
189
|
-
).default())(options),
|
|
190
|
-
]
|
|
191
|
-
: []),
|
|
170
|
+
!root || root === "@lazarv/react-server/file-router"
|
|
171
|
+
? fileRouter(options)
|
|
172
|
+
: [],
|
|
192
173
|
reactServerEval(options),
|
|
193
174
|
...buildPlugins,
|
|
194
175
|
],
|
package/lib/dev/action.mjs
CHANGED
|
@@ -32,8 +32,13 @@ export default async function dev(root, options) {
|
|
|
32
32
|
try {
|
|
33
33
|
runtime$(CONFIG_CONTEXT, config);
|
|
34
34
|
|
|
35
|
+
const isNonInteractiveEnvironment =
|
|
36
|
+
!process.stdin.isTTY ||
|
|
37
|
+
process.env.CI === "true" ||
|
|
38
|
+
process.env.DOCKER_CONTAINER === "true";
|
|
39
|
+
|
|
35
40
|
const server = await createServer(
|
|
36
|
-
options.eval ||
|
|
41
|
+
options.eval || isNonInteractiveEnvironment
|
|
37
42
|
? "virtual:react-server-eval.jsx"
|
|
38
43
|
: root,
|
|
39
44
|
options
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { rm } from "node:fs/promises";
|
|
2
|
-
import {
|
|
2
|
+
import { register } from "node:module";
|
|
3
3
|
import { join, relative } from "node:path";
|
|
4
|
-
import { pathToFileURL } from "node:url";
|
|
5
4
|
import { format } from "node:util";
|
|
6
5
|
import { Worker } from "node:worker_threads";
|
|
7
6
|
|
|
@@ -40,6 +39,7 @@ import trailingSlashHandler from "../handlers/trailing-slash.mjs";
|
|
|
40
39
|
import { alias, moduleAliases } from "../loader/module-alias.mjs";
|
|
41
40
|
import { applyAlias } from "../loader/utils.mjs";
|
|
42
41
|
import asset from "../plugins/asset.mjs";
|
|
42
|
+
import fileRouter from "../plugins/file-router/plugin.mjs";
|
|
43
43
|
import optimizeDeps from "../plugins/optimize-deps.mjs";
|
|
44
44
|
import reactServerEval from "../plugins/react-server-eval.mjs";
|
|
45
45
|
import reactServerRuntime from "../plugins/react-server-runtime.mjs";
|
|
@@ -61,7 +61,6 @@ import ssrHandler from "./ssr-handler.mjs";
|
|
|
61
61
|
alias("react-server");
|
|
62
62
|
register("../loader/node-loader.react-server.mjs", import.meta.url);
|
|
63
63
|
|
|
64
|
-
const __require = createRequire(import.meta.url);
|
|
65
64
|
const cwd = sys.cwd();
|
|
66
65
|
const workspaceRoot = findPackageRoot(join(cwd, "..")) ?? cwd;
|
|
67
66
|
|
|
@@ -70,16 +69,6 @@ export default async function createServer(root, options) {
|
|
|
70
69
|
options.outDir = ".react-server";
|
|
71
70
|
}
|
|
72
71
|
const config = getRuntime(CONFIG_CONTEXT)?.[CONFIG_ROOT];
|
|
73
|
-
let reactServerRouterModule;
|
|
74
|
-
try {
|
|
75
|
-
reactServerRouterModule = __require.resolve("@lazarv/react-server-router", {
|
|
76
|
-
paths: [cwd],
|
|
77
|
-
});
|
|
78
|
-
} catch {
|
|
79
|
-
// ignore
|
|
80
|
-
root ||= "virtual:react-server-eval.jsx";
|
|
81
|
-
}
|
|
82
|
-
|
|
83
72
|
const worker = new Worker(new URL("./render-stream.mjs", import.meta.url));
|
|
84
73
|
runtime$(WORKER_THREAD, worker);
|
|
85
74
|
|
|
@@ -131,21 +120,9 @@ export default async function createServer(root, options) {
|
|
|
131
120
|
postcss: cwd,
|
|
132
121
|
},
|
|
133
122
|
plugins: [
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
(async () =>
|
|
138
|
-
(
|
|
139
|
-
await import(
|
|
140
|
-
pathToFileURL(
|
|
141
|
-
__require.resolve("@lazarv/react-server-router/plugin", {
|
|
142
|
-
paths: [cwd],
|
|
143
|
-
})
|
|
144
|
-
)
|
|
145
|
-
)
|
|
146
|
-
).default())(options),
|
|
147
|
-
]
|
|
148
|
-
: []),
|
|
123
|
+
!root || root === "@lazarv/react-server/file-router"
|
|
124
|
+
? fileRouter(options)
|
|
125
|
+
: [],
|
|
149
126
|
resolveWorkspace(),
|
|
150
127
|
reactServerEval(options),
|
|
151
128
|
reactServerRuntime(),
|
|
@@ -455,7 +432,7 @@ export default async function createServer(root, options) {
|
|
|
455
432
|
(mod) => !/\.(css|scss|less)/.test(mod.id)
|
|
456
433
|
);
|
|
457
434
|
|
|
458
|
-
styles.
|
|
435
|
+
styles.unshift(...importedStyles.map((mod) => mod.url));
|
|
459
436
|
imports.forEach((mod) => mod.id && collectCss(mod.id));
|
|
460
437
|
}
|
|
461
438
|
}
|
package/lib/dev/modules.mjs
CHANGED
|
@@ -6,15 +6,6 @@ import { cwd, rootDir } from "../sys.mjs";
|
|
|
6
6
|
const __require = createRequire(import.meta.url);
|
|
7
7
|
|
|
8
8
|
export default function getModules(root) {
|
|
9
|
-
let reactServerRouterModule;
|
|
10
|
-
try {
|
|
11
|
-
reactServerRouterModule = __require.resolve("@lazarv/react-server-router", {
|
|
12
|
-
paths: [cwd()],
|
|
13
|
-
});
|
|
14
|
-
} catch (e) {
|
|
15
|
-
// ignore
|
|
16
|
-
}
|
|
17
|
-
|
|
18
9
|
const entryModule = `${rootDir}/server/render-rsc.jsx`;
|
|
19
10
|
let rootModule;
|
|
20
11
|
const [module, name] = root?.split("#") ?? [];
|
|
@@ -23,9 +14,7 @@ export default function getModules(root) {
|
|
|
23
14
|
? __require.resolve(module, {
|
|
24
15
|
paths: [cwd()],
|
|
25
16
|
})
|
|
26
|
-
:
|
|
27
|
-
? "@lazarv/react-server-router"
|
|
28
|
-
: "virtual:react-server-eval.jsx";
|
|
17
|
+
: "@lazarv/react-server/file-router";
|
|
29
18
|
} catch {
|
|
30
19
|
rootModule = "virtual:react-server-eval.jsx";
|
|
31
20
|
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { dirname } from "node:path";
|
|
2
|
+
|
|
3
|
+
import { status, useOutlet } from "@lazarv/react-server";
|
|
4
|
+
import {
|
|
5
|
+
middlewares,
|
|
6
|
+
pages,
|
|
7
|
+
routes,
|
|
8
|
+
} from "@lazarv/react-server/file-router/manifest";
|
|
9
|
+
import { useMatch } from "@lazarv/react-server/router";
|
|
10
|
+
import { context$ } from "@lazarv/react-server/server/context.mjs";
|
|
11
|
+
import { ROUTE_MATCH } from "@lazarv/react-server/server/symbols.mjs";
|
|
12
|
+
|
|
13
|
+
export async function init$() {
|
|
14
|
+
return async (context) => {
|
|
15
|
+
for (const handler of middlewares) {
|
|
16
|
+
const response = await handler(context);
|
|
17
|
+
if (response) {
|
|
18
|
+
return response;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let match = null;
|
|
23
|
+
let route = null;
|
|
24
|
+
for (const [method, path, _route] of routes) {
|
|
25
|
+
match =
|
|
26
|
+
method === "*" || method === context.request.method
|
|
27
|
+
? useMatch(path, { exact: true })
|
|
28
|
+
: null;
|
|
29
|
+
if (match) {
|
|
30
|
+
route = _route;
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (route) {
|
|
36
|
+
context$(ROUTE_MATCH, match);
|
|
37
|
+
context.request.params = match;
|
|
38
|
+
|
|
39
|
+
const handler = await route();
|
|
40
|
+
return await (
|
|
41
|
+
handler[context.request.method] ??
|
|
42
|
+
handler.default ??
|
|
43
|
+
(() => {})
|
|
44
|
+
)(context);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default async function App() {
|
|
50
|
+
let match = null;
|
|
51
|
+
let Page = () => {
|
|
52
|
+
status(404);
|
|
53
|
+
return null;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const reactServerOutlet = useOutlet();
|
|
57
|
+
if (reactServerOutlet && reactServerOutlet !== "PAGE_ROOT") {
|
|
58
|
+
const outlets = pages.filter(
|
|
59
|
+
([, type, outlet]) => type === "page" && outlet === reactServerOutlet
|
|
60
|
+
);
|
|
61
|
+
for (const [path, , , , , lazy] of outlets) {
|
|
62
|
+
const match = useMatch(path, { exact: true });
|
|
63
|
+
if (match) {
|
|
64
|
+
const { default: Component, init$: page_init$ } = await lazy();
|
|
65
|
+
await page_init$?.();
|
|
66
|
+
return <Component {...match} />;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
for (const [path, type, outlet, lazy, src] of pages) {
|
|
73
|
+
match = type === "page" && !outlet ? useMatch(path, { exact: true }) : null;
|
|
74
|
+
if (match) {
|
|
75
|
+
const { default: Component, init$: page_init$ } = await lazy();
|
|
76
|
+
Page = Component;
|
|
77
|
+
await page_init$?.();
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
match = type === "page" && outlet ? useMatch(path, { exact: true }) : null;
|
|
82
|
+
if (match) {
|
|
83
|
+
const [, , , lazy] =
|
|
84
|
+
pages.find(
|
|
85
|
+
([, type, outlet, , pageSrc]) =>
|
|
86
|
+
type === "page" &&
|
|
87
|
+
!outlet &&
|
|
88
|
+
dirname(src).includes(dirname(pageSrc))
|
|
89
|
+
) ?? [];
|
|
90
|
+
if (lazy) {
|
|
91
|
+
const { default: Component, init$: page_init$ } = await lazy();
|
|
92
|
+
Page = Component;
|
|
93
|
+
await page_init$?.();
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return <Page {...match} />;
|
|
100
|
+
}
|