@lazarv/react-server 0.0.0-experimental-43e79e6-20230928

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 (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +5 -0
  3. package/bin/cli.mjs +93 -0
  4. package/bin/commands/build.mjs +19 -0
  5. package/bin/commands/dev.mjs +23 -0
  6. package/bin/commands/start.mjs +16 -0
  7. package/bin/loader.mjs +38 -0
  8. package/client/ActionState.mjs +16 -0
  9. package/client/ClientOnly.jsx +14 -0
  10. package/client/ClientProvider.jsx +243 -0
  11. package/client/ErrorBoundary.jsx +45 -0
  12. package/client/FlightContext.mjs +3 -0
  13. package/client/Link.jsx +59 -0
  14. package/client/Params.mjs +15 -0
  15. package/client/ReactServerComponent.jsx +77 -0
  16. package/client/Refresh.jsx +52 -0
  17. package/client/components.mjs +28 -0
  18. package/client/context.mjs +6 -0
  19. package/client/entry.client.jsx +146 -0
  20. package/client/index.jsx +6 -0
  21. package/client/navigation.jsx +4 -0
  22. package/config/context.mjs +37 -0
  23. package/config/index.mjs +114 -0
  24. package/lib/build/action.mjs +57 -0
  25. package/lib/build/banner.mjs +13 -0
  26. package/lib/build/chunks.mjs +26 -0
  27. package/lib/build/client.mjs +114 -0
  28. package/lib/build/custom-logger.mjs +13 -0
  29. package/lib/build/dependencies.mjs +54 -0
  30. package/lib/build/resolve.mjs +101 -0
  31. package/lib/build/server.mjs +142 -0
  32. package/lib/build/static.mjs +89 -0
  33. package/lib/dev/action.mjs +63 -0
  34. package/lib/dev/create-logger.mjs +52 -0
  35. package/lib/dev/create-server.mjs +208 -0
  36. package/lib/dev/modules.mjs +20 -0
  37. package/lib/dev/ssr-handler.mjs +135 -0
  38. package/lib/handlers/error.mjs +153 -0
  39. package/lib/handlers/not-found.mjs +5 -0
  40. package/lib/handlers/redirect.mjs +1 -0
  41. package/lib/handlers/rewrite.mjs +1 -0
  42. package/lib/handlers/static.mjs +120 -0
  43. package/lib/handlers/trailing-slash.mjs +12 -0
  44. package/lib/plugins/react-server.mjs +73 -0
  45. package/lib/plugins/use-client.mjs +135 -0
  46. package/lib/plugins/use-server.mjs +175 -0
  47. package/lib/start/action.mjs +110 -0
  48. package/lib/start/create-server.mjs +111 -0
  49. package/lib/start/manifest.mjs +104 -0
  50. package/lib/start/ssr-handler.mjs +134 -0
  51. package/lib/sys.mjs +49 -0
  52. package/lib/utils/merge.mjs +31 -0
  53. package/lib/utils/server-address.mjs +14 -0
  54. package/memory-cache/index.mjs +125 -0
  55. package/package.json +81 -0
  56. package/react-server.d.ts +209 -0
  57. package/server/ErrorBoundary.jsx +14 -0
  58. package/server/RemoteComponent.jsx +210 -0
  59. package/server/Route.jsx +108 -0
  60. package/server/actions.mjs +72 -0
  61. package/server/cache.mjs +19 -0
  62. package/server/client-component.mjs +62 -0
  63. package/server/context.mjs +32 -0
  64. package/server/cookies.mjs +14 -0
  65. package/server/entry.server.jsx +972 -0
  66. package/server/error-boundary.jsx +2 -0
  67. package/server/http-headers.mjs +8 -0
  68. package/server/http-status.mjs +6 -0
  69. package/server/index.mjs +14 -0
  70. package/server/logger.mjs +15 -0
  71. package/server/module-loader.mjs +20 -0
  72. package/server/redirects.mjs +45 -0
  73. package/server/remote-component.jsx +2 -0
  74. package/server/request.mjs +37 -0
  75. package/server/revalidate.mjs +22 -0
  76. package/server/rewrites.mjs +0 -0
  77. package/server/router.jsx +6 -0
  78. package/server/runtime.mjs +32 -0
  79. package/server/symbols.mjs +24 -0
@@ -0,0 +1,104 @@
1
+ import { createRequire } from "node:module";
2
+
3
+ import { getContext } from "../../server/context.mjs";
4
+ import { runtime$ } from "../../server/runtime.mjs";
5
+ import {
6
+ COLLECT_STYLESHEETS,
7
+ HTTP_CONTEXT,
8
+ MAIN_MODULE,
9
+ MANIFEST,
10
+ MODULE_LOADER,
11
+ } from "../../server/symbols.mjs";
12
+
13
+ const __require = createRequire(import.meta.url);
14
+
15
+ export async function init$() {
16
+ const serverManifest = __require.resolve(
17
+ "./.react-server/server/manifest.json",
18
+ {
19
+ paths: [process.cwd()],
20
+ }
21
+ );
22
+ const clientManifest = __require.resolve(
23
+ "./.react-server/client/manifest.json",
24
+ {
25
+ paths: [process.cwd()],
26
+ }
27
+ );
28
+ const [{ default: server }, { default: client }] = await Promise.all([
29
+ import(serverManifest, { assert: { type: "json" } }),
30
+ import(clientManifest, { assert: { type: "json" } }),
31
+ ]);
32
+ const manifest = {
33
+ server,
34
+ client,
35
+ };
36
+ runtime$(MANIFEST, manifest);
37
+
38
+ const mainModule = `/${
39
+ Object.values(manifest.client).find(
40
+ (entry) => entry.src === "../../client/entry.client.jsx"
41
+ ).file
42
+ }`;
43
+ runtime$(MAIN_MODULE, [mainModule]);
44
+
45
+ const entryCache = new Map();
46
+ function ssrLoadModule($$id) {
47
+ const httpContext = getContext(HTTP_CONTEXT);
48
+ const [id /*, name*/] = $$id
49
+ .replace(
50
+ `${
51
+ httpContext?.request?.headers?.get("x-forwarded-for") ||
52
+ new URL(httpContext?.url).origin
53
+ }/`,
54
+ ""
55
+ )
56
+ .split("::");
57
+ try {
58
+ const moduleUri = new URL(id);
59
+ if (moduleUri.protocol === "http:" || moduleUri.protocol === "https:") {
60
+ return import(id);
61
+ }
62
+ } catch (e) {
63
+ // noop
64
+ }
65
+ if (entryCache.has(id)) {
66
+ return import(entryCache.get(id));
67
+ }
68
+ const clientEntry = Object.values(manifest.client).find((entry) =>
69
+ entry.file.endsWith(id)
70
+ );
71
+ const serverEntry = Object.values(manifest.server).find((entry) =>
72
+ entry.src.endsWith(clientEntry.src)
73
+ );
74
+ const specifier = __require.resolve(`./.react-server/${serverEntry.file}`, {
75
+ paths: [process.cwd()],
76
+ });
77
+ entryCache.set(id, specifier);
78
+ return import(specifier);
79
+ }
80
+ runtime$(MODULE_LOADER, ssrLoadModule);
81
+
82
+ function collectStylesheets(rootModule) {
83
+ if (!rootModule) return [];
84
+ const rootManifest = Array.from(Object.values(manifest.server)).find(
85
+ (entry) =>
86
+ rootModule.endsWith(entry.file) || entry.src.endsWith(rootModule)
87
+ );
88
+ const styles = [];
89
+ function collectCss(entry) {
90
+ if (!entry) return styles;
91
+ if (entry.css) {
92
+ styles.push(...entry.css.map((href) => `/${href}`));
93
+ }
94
+ if (entry.imports) {
95
+ entry.imports.forEach((imported) =>
96
+ collectCss(manifest.server[imported])
97
+ );
98
+ }
99
+ }
100
+ collectCss(rootManifest);
101
+ return styles;
102
+ }
103
+ runtime$(COLLECT_STYLESHEETS, collectStylesheets);
104
+ }
@@ -0,0 +1,134 @@
1
+ import { createRequire } from "node:module";
2
+
3
+ import { forChild } from "../../config/index.mjs";
4
+ import { init$ as memory_cache_init$ } from "../../memory-cache/index.mjs";
5
+ import { ContextStorage, getContext } from "../../server/context.mjs";
6
+ import { logger } from "../../server/logger.mjs";
7
+ import { init$ as module_loader_init$ } from "../../server/module-loader.mjs";
8
+ import { getRuntime } from "../../server/runtime.mjs";
9
+ import {
10
+ COLLECT_STYLESHEETS,
11
+ CONFIG_CONTEXT,
12
+ CONFIG_ROOT,
13
+ ERROR_CONTEXT,
14
+ FORM_DATA_PARSER,
15
+ HTTP_CONTEXT,
16
+ LOGGER_CONTEXT,
17
+ MAIN_MODULE,
18
+ MANIFEST,
19
+ MEMORY_CACHE_CONTEXT,
20
+ MODULE_LOADER,
21
+ REDIRECT_CONTEXT,
22
+ SERVER_CONTEXT,
23
+ STYLES_CONTEXT,
24
+ } from "../../server/symbols.mjs";
25
+ import errorHandler from "../handlers/error.mjs";
26
+ import { init$ as manifest_init$ } from "./manifest.mjs";
27
+
28
+ const __require = createRequire(import.meta.url);
29
+
30
+ const defaultRoot = `${process.cwd()}/.react-server/server/index.mjs`;
31
+ export default async function ssrHandler(root) {
32
+ const config = getRuntime(CONFIG_CONTEXT);
33
+ const configRoot = config?.[CONFIG_ROOT] ?? {};
34
+
35
+ await manifest_init$();
36
+
37
+ const entryModule = __require.resolve(
38
+ `${process.cwd()}/.react-server/server/entry.mjs`
39
+ );
40
+ const rootModule = __require.resolve(
41
+ root ?? configRoot.entry ?? defaultRoot,
42
+ {
43
+ paths: [process.cwd()],
44
+ }
45
+ );
46
+ const { render } = await import(entryModule);
47
+ const { default: Component, init$: root_init$ } = await import(rootModule);
48
+ const collectStylesheets = getRuntime(COLLECT_STYLESHEETS);
49
+ const styles = getRuntime(COLLECT_STYLESHEETS)?.(rootModule) ?? [];
50
+ const mainModule = getRuntime(MAIN_MODULE);
51
+ const formDataParser = getRuntime(FORM_DATA_PARSER);
52
+ const moduleLoader = getRuntime(MODULE_LOADER);
53
+ const memoryCache = getRuntime(MEMORY_CACHE_CONTEXT);
54
+ const manifest = getRuntime(MANIFEST);
55
+ await module_loader_init$(moduleLoader);
56
+
57
+ return async (httpContext) => {
58
+ // const accept = httpContext.request.headers.get("accept");
59
+ // if (
60
+ // !accept ||
61
+ // !(accept.includes("text/html") || accept.includes("text/x-component"))
62
+ // ) {
63
+ // return;
64
+ // }
65
+ return new Promise((resolve, reject) => {
66
+ try {
67
+ ContextStorage.run(
68
+ {
69
+ [SERVER_CONTEXT]: getRuntime(SERVER_CONTEXT),
70
+ [CONFIG_CONTEXT]: config,
71
+ [HTTP_CONTEXT]: httpContext,
72
+ [ERROR_CONTEXT]: errorHandler,
73
+ [LOGGER_CONTEXT]: logger,
74
+ [MAIN_MODULE]: mainModule,
75
+ [FORM_DATA_PARSER]: formDataParser,
76
+ [MODULE_LOADER]: moduleLoader,
77
+ [MEMORY_CACHE_CONTEXT]: memoryCache,
78
+ [MANIFEST]: manifest,
79
+ [REDIRECT_CONTEXT]: {},
80
+ [COLLECT_STYLESHEETS]: collectStylesheets,
81
+ [STYLES_CONTEXT]: styles,
82
+ },
83
+ async () => {
84
+ const cacheModule = forChild(httpContext.url)?.cache?.module;
85
+
86
+ if (cacheModule) {
87
+ const { init$: cache_init$ } = await import(
88
+ __require.resolve(cacheModule, {
89
+ paths: [process.cwd()],
90
+ })
91
+ );
92
+ await cache_init$?.();
93
+ } else {
94
+ await memory_cache_init$?.();
95
+ }
96
+
97
+ try {
98
+ const middlewares = await root_init$?.();
99
+ if (middlewares) {
100
+ const response = await middlewares(httpContext);
101
+ if (response) {
102
+ return resolve(response);
103
+ }
104
+ }
105
+ } catch (e) {
106
+ const redirect = getContext(REDIRECT_CONTEXT);
107
+ if (redirect?.response) {
108
+ return resolve(redirect.response);
109
+ }
110
+ }
111
+
112
+ const accept = httpContext.request.headers.get("accept");
113
+ if (
114
+ !accept ||
115
+ !(
116
+ accept.includes("text/html") ||
117
+ accept.includes("text/x-component") ||
118
+ accept.includes("application/json")
119
+ )
120
+ ) {
121
+ return resolve();
122
+ }
123
+
124
+ const styles = getContext(STYLES_CONTEXT);
125
+ render(Component, styles).then(resolve, reject);
126
+ }
127
+ );
128
+ } catch (e) {
129
+ logger.error(e);
130
+ errorHandler(e).then(resolve);
131
+ }
132
+ });
133
+ };
134
+ }
package/lib/sys.mjs ADDED
@@ -0,0 +1,49 @@
1
+ export function cwd() {
2
+ return typeof Deno !== "undefined" ? Deno.cwd() : process.cwd();
3
+ }
4
+
5
+ export function argv() {
6
+ return typeof Deno !== "undefined"
7
+ ? [Deno.execPath(), Deno.mainModule, ...Deno.args]
8
+ : process.argv;
9
+ }
10
+
11
+ export function exit(code) {
12
+ typeof Deno !== "undefined" ? Deno.exit(code) : process.exit(code);
13
+ }
14
+
15
+ export function getEnv(name) {
16
+ return typeof Deno !== "undefined" ? Deno.env.get(name) : process.env[name];
17
+ }
18
+
19
+ export function setEnv(name, value) {
20
+ typeof Deno !== "undefined"
21
+ ? Deno.env.set(name, value)
22
+ : (process.env[name] = value);
23
+ }
24
+
25
+ export function copyBytesFrom(buffer) {
26
+ return typeof Deno !== "undefined"
27
+ ? new Uint8Array(buffer)
28
+ : Buffer.copyBytesFrom(buffer);
29
+ }
30
+
31
+ export function concat(buffers) {
32
+ return typeof Deno !== "undefined"
33
+ ? new Uint8Array(buffers.reduce((acc, buf) => [...acc, ...buf], []))
34
+ : Buffer.concat(buffers);
35
+ }
36
+
37
+ export function immediate(fn) {
38
+ return typeof Deno !== "undefined" ? fn() : setImmediate(fn);
39
+ }
40
+
41
+ if (typeof Deno !== "undefined") {
42
+ globalThis.process = {
43
+ env: Deno.env.toObject(),
44
+ cwd: Deno.cwd,
45
+ argv: [Deno.execPath(), Deno.mainModule, ...Deno.args],
46
+ exit: Deno.exit,
47
+ emit: function () {},
48
+ };
49
+ }
@@ -0,0 +1,31 @@
1
+ function isObject(item) {
2
+ return (
3
+ item &&
4
+ typeof item === "object" &&
5
+ !Array.isArray(item) &&
6
+ Object.getPrototypeOf(item) === Object.prototype
7
+ );
8
+ }
9
+
10
+ export default function mergeDeep(target, ...sources) {
11
+ if (!sources.length) return target;
12
+ const source = sources.shift();
13
+
14
+ if (isObject(target) && isObject(source)) {
15
+ for (const key in source) {
16
+ if (isObject(source[key])) {
17
+ if (!target[key])
18
+ Object.assign(target, {
19
+ [key]: {},
20
+ });
21
+ mergeDeep(target[key], source[key]);
22
+ } else {
23
+ Object.assign(target, {
24
+ [key]: source[key],
25
+ });
26
+ }
27
+ }
28
+ }
29
+
30
+ return mergeDeep(target, ...sources);
31
+ }
@@ -0,0 +1,14 @@
1
+ import { networkInterfaces } from "node:os";
2
+
3
+ export default function getServerAddresses(server) {
4
+ const serverAddress = server.address();
5
+ const addresses = Object.values(networkInterfaces());
6
+ return (
7
+ addresses.find((addresses) =>
8
+ addresses.some((address) => address.address === serverAddress.address),
9
+ ) ||
10
+ addresses.flatMap((addresses) =>
11
+ addresses.filter((address) => address.family === "IPv4"),
12
+ )
13
+ ).filter((address) => address.family === "IPv4");
14
+ }
@@ -0,0 +1,125 @@
1
+ import { context$, getContext } from "../server/context.mjs";
2
+ import { CACHE_CONTEXT, MEMORY_CACHE_CONTEXT } from "../server/symbols.mjs";
3
+
4
+ export class MemoryCache {
5
+ constructor() {
6
+ this.cache = new Map();
7
+ this.expiry = new Map();
8
+ }
9
+
10
+ async get(keys) {
11
+ const now = Date.now();
12
+
13
+ const expiryEntries = this.expiry.entries();
14
+ const deleteQueue = [];
15
+ for (const [expiryKeys, expiry] of expiryEntries) {
16
+ if (expiry < now) {
17
+ this.expiry.delete(expiryKeys);
18
+ deleteQueue.push(this.delete(expiryKeys));
19
+ }
20
+ }
21
+ await Promise.all(deleteQueue);
22
+
23
+ const cacheKeys = this.cache.keys();
24
+ for (const entryKeys of cacheKeys) {
25
+ if (
26
+ keys.every((key, keyIndex) => entryKeys[keyIndex] === key?.toString())
27
+ ) {
28
+ return this.cache.get(entryKeys);
29
+ }
30
+ }
31
+
32
+ return null;
33
+ }
34
+
35
+ async set(keys, value) {
36
+ if (await this.hasExpiry(keys)) {
37
+ const cacheKeys = this.cache.keys();
38
+ for (const entryKeys of cacheKeys) {
39
+ if (
40
+ keys.every((key, keyIndex) => entryKeys[keyIndex] === key?.toString())
41
+ ) {
42
+ this.cache.set(entryKeys, value);
43
+ return;
44
+ }
45
+ }
46
+ this.cache.set(
47
+ keys.map((key) => key?.toString()),
48
+ value
49
+ );
50
+ }
51
+ }
52
+
53
+ async has(keys) {
54
+ const cacheKeys = this.cache.keys();
55
+ for (const entryKeys of cacheKeys) {
56
+ if (
57
+ keys.every((key, keyIndex) => entryKeys[keyIndex] === key?.toString())
58
+ ) {
59
+ return true;
60
+ }
61
+ }
62
+
63
+ return false;
64
+ }
65
+
66
+ async setExpiry(keys, expiry) {
67
+ const expiryKeys = this.expiry.keys();
68
+ for (const entryKeys of expiryKeys) {
69
+ if (
70
+ keys.every((key, keyIndex) => entryKeys[keyIndex] === key?.toString())
71
+ ) {
72
+ this.expiry.set(entryKeys, expiry);
73
+ return;
74
+ }
75
+ }
76
+ this.expiry.set(
77
+ keys.map((key) => key?.toString()),
78
+ expiry
79
+ );
80
+ }
81
+
82
+ async hasExpiry(keys) {
83
+ const expiryKeys = this.expiry.keys();
84
+ for (const entryKeys of expiryKeys) {
85
+ for (let keyIndex = 0; keyIndex < entryKeys.length; keyIndex++) {
86
+ const key = keys[keyIndex];
87
+ if (entryKeys[keyIndex] !== key?.toString()) {
88
+ break;
89
+ }
90
+ if (keyIndex === entryKeys.length - 1) {
91
+ return true;
92
+ }
93
+ }
94
+ }
95
+
96
+ return false;
97
+ }
98
+
99
+ async delete(keys) {
100
+ const cacheKeys = this.cache.keys();
101
+ for (const entryKeys of cacheKeys) {
102
+ if (
103
+ keys.every((key, keyIndex) => entryKeys[keyIndex] === key?.toString())
104
+ ) {
105
+ this.cache.delete(entryKeys);
106
+ }
107
+ }
108
+ }
109
+ }
110
+
111
+ const cache = new MemoryCache();
112
+ export async function init$() {
113
+ return context$(CACHE_CONTEXT, cache);
114
+ }
115
+
116
+ export async function useCache(keys, promise, ttl = Infinity, force = false) {
117
+ const cache = getContext(MEMORY_CACHE_CONTEXT);
118
+ let result = await cache.get(keys);
119
+ if (force || result === null) {
120
+ result = typeof promise === "function" ? await promise() : promise;
121
+ await cache.setExpiry(keys, Date.now() + ttl);
122
+ await cache.set(keys, result);
123
+ }
124
+ return result;
125
+ }
package/package.json ADDED
@@ -0,0 +1,81 @@
1
+ {
2
+ "name": "@lazarv/react-server",
3
+ "version": "0.0.0-experimental-43e79e6-20230928",
4
+ "description": "Experimental React meta-framework using Vite",
5
+ "type": "module",
6
+ "module": "server/index.mjs",
7
+ "bin": "bin/cli.mjs",
8
+ "types": "react-server.d.ts",
9
+ "exports": {
10
+ ".": "./server/index.mjs",
11
+ "./client": "./client/index.jsx",
12
+ "./config": "./config/index.mjs",
13
+ "./error-boundary": "./server/error-boundary.jsx",
14
+ "./memory-cache": "./memory-cache/index.mjs",
15
+ "./navigation": "./client/navigation.jsx",
16
+ "./remote-component": "./server/remote-component.jsx",
17
+ "./router": "./server/router.jsx",
18
+ "./client/*": "./client/*",
19
+ "./server/*": "./server/*",
20
+ "./lib/*": "./lib/*",
21
+ "./*": "./*"
22
+ },
23
+ "keywords": [
24
+ "react",
25
+ "ssr",
26
+ "esm",
27
+ "server"
28
+ ],
29
+ "author": "lazarv",
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/lazarv/react-server.git"
34
+ },
35
+ "bugs": {
36
+ "url": "https://github.com/lazarv/react-server/issues"
37
+ },
38
+ "dependencies": {
39
+ "@hattip/adapter-node": "^0.0.34",
40
+ "@hattip/compose": "^0.0.34",
41
+ "@hattip/cookie": "^0.0.34",
42
+ "@hattip/core": "^0.0.34",
43
+ "@hattip/cors": "^0.0.34",
44
+ "@hattip/headers": "^0.0.34",
45
+ "@hattip/multipart": "^0.0.34",
46
+ "@rollup/plugin-replace": "^5.0.2",
47
+ "@vitejs/plugin-react": "^4.0.1",
48
+ "acorn": "^8.10.0",
49
+ "cac": "^6.7.14",
50
+ "esbuild": "^0.19.3",
51
+ "escodegen": "^2.1.0",
52
+ "estraverse": "^5.3.0",
53
+ "fast-glob": "^3.2.12",
54
+ "filesize": "^10.0.12",
55
+ "mime": "^3.0.0",
56
+ "module-alias": "^2.2.3",
57
+ "open": "^9.1.0",
58
+ "picocolors": "^1.0.0",
59
+ "pino": "^8.14.1",
60
+ "rimraf": "^5.0.1",
61
+ "sass": "^1.63.6",
62
+ "strip-ansi": "^7.1.0",
63
+ "vite": "^4.4.9"
64
+ },
65
+ "peerDependencies": {
66
+ "react": "0.0.0-experimental-41f0e9dae-20230907",
67
+ "react-dom": "0.0.0-experimental-41f0e9dae-20230907",
68
+ "react-error-boundary": "^4.0.10",
69
+ "react-server-dom-webpack": "0.0.0-experimental-41f0e9dae-20230907"
70
+ },
71
+ "engines": {
72
+ "node": ">=20.0.0"
73
+ },
74
+ "devDependencies": {
75
+ "react": "0.0.0-experimental-41f0e9dae-20230907",
76
+ "react-dom": "0.0.0-experimental-41f0e9dae-20230907",
77
+ "react-error-boundary": "^4.0.10",
78
+ "react-server-dom-webpack": "0.0.0-experimental-41f0e9dae-20230907"
79
+ },
80
+ "scripts": {}
81
+ }