@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 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
- ## @lazarv/react-server-router
94
+ ## File-system based routing
95
95
 
96
- To enable file-system based routing, you need to install the `@lazarv/react-server-router` package and you no longer need to specify and entrypoint for your app.
96
+ To enable file-system based routing, you just omit the entrypoint when running a `@lazarv/react-server` app.
97
97
 
98
- ```sh
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": "src"
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 `./src/page.tsx` to transform it into a page.
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 using an entrypoint.
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 install the ${colors.cyan("@lazarv/react-server-router")} package by running ${colors.cyan(`${installCommand} @lazarv/react-server-router`)} to use the optional file-system based routing then omit the ${colors.cyan("<root>")} in the above commands. Learn more at ${colors.cyan("https://react-server.dev/router")}.
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
@@ -22,6 +22,7 @@ export async function loadConfig(initialConfig, options = {}) {
22
22
  [
23
23
  "**/{react-server,+*,vite}.config.{json,js,ts,mjs,mts,ts.mjs,mts.mjs}",
24
24
  "!**/node_modules",
25
+ "!*/**/vite.config.{json,js,ts,mjs,mts,ts.mjs,mts.mjs}",
25
26
  ],
26
27
  {
27
28
  cwd,
@@ -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
- let reactServerRouterModule;
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
- ...(reactServerRouterModule &&
178
- (!root || root === "@lazarv/react-server-router")
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
  ],
@@ -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 || (!process.stdin.isTTY && !process.env.CI)
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 { createRequire, register } from "node:module";
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
- ...(reactServerRouterModule &&
135
- (!root || root === "@lazarv/react-server-router")
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.push(...importedStyles.map((mod) => mod.url));
435
+ styles.unshift(...importedStyles.map((mod) => mod.url));
459
436
  imports.forEach((mod) => mod.id && collectCss(mod.id));
460
437
  }
461
438
  }
@@ -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
- : reactServerRouterModule
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
+ }