edge-city

#react#js#ssr

git clone https://git.pyrossh.dev/edge-city

edge-city is a next level meta-framework for react that runs only on edge runtimes


c7f7584 — pyrossh 2023-06-03T00:40:04+05:30
refactor dir

Generated files not shown: pnpm-lock.yaml

example/package.json CHANGED
@@ -8,6 +8,7 @@
8
8
  "dev:wrangler": "cd build && wrangler pages dev static",
9
9
  "deploy:wrangler": "cd build && wrangler pages deploy static",
10
10
  "build": " edge-city build -p cloudflare",
11
+ "build-2": "vite build",
11
12
  "generate": "drizzle-kit generate:pg --out migrations --schema migrations/schema.js",
12
13
  "migrate": "node scripts/migrate.js",
13
14
  "test": "jest",
@@ -33,7 +34,10 @@
33
34
  "eslint-config-react-app": "^7.0.1",
34
35
  "postgres": "3.3.4",
35
36
  "prettier": "^2.8.8",
36
- "wrangler": "3.0.1"
37
+ "wrangler": "3.0.1",
38
+ "vite": "4.3.9",
39
+ "vite-plugin-pages": "0.30.1",
40
+ "@vitejs/plugin-react": "4.0.0"
37
41
  },
38
42
  "prettier": {
39
43
  "printWidth": 100,
example/src/components/Layout/Layout.jsx CHANGED
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { Link } from "edge-city";
2
+ import Link from "edge-city/link";
3
3
  import "./Layout.css";
4
4
 
5
5
  const Layout = ({ children }) => {
example/src/pages/about/page.jsx CHANGED
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
+ import Link from "edge-city/link";
2
- import { Link, useRouter } from "edge-city";
3
+ import { useRouter } from "edge-city/router";
3
4
  import { Helmet } from "react-helmet-async";
4
5
  import "./page.css";
5
6
 
example/src/pages/page.jsx CHANGED
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { useRouter } from "edge-city";
2
+ import { useRouter } from "edge-city/router";
3
3
  import Counter from "@/components/Counter/Counter";
4
4
  import { Helmet } from "react-helmet-async";
5
5
  import "./page.css";
example/src/pages/todos/Todo.jsx CHANGED
@@ -1,7 +1,7 @@
1
1
  import { useState } from "react";
2
2
  import { useForm } from "react-hook-form";
3
3
  import { TextField, Input } from "react-aria-components";
4
- import { cache, useMutation } from "edge-city";
4
+ import { cache, useMutation } from "edge-city/data";
5
5
  import { updateTodo, deleteTodo } from "@/services/todos.service";
6
6
  import "./Todo.css";
7
7
 
example/src/pages/todos/TodoList.jsx CHANGED
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { useQuery } from "edge-city";
2
+ import { useQuery } from "edge-city/data";
3
3
  import { getTodos } from "@/services/todos.service";
4
4
  import Spinner from "@/components/Spinner/Spinner";
5
5
  import Todo from "./Todo";
example/src/pages/todos/page.jsx CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Suspense } from "react";
2
2
  import { ErrorBoundary } from "react-error-boundary";
3
3
  import { Helmet } from "react-helmet-async";
4
- import { cache, useMutation } from "edge-city";
4
+ import { cache, useMutation } from "edge-city/data";
5
5
  import { useForm } from "react-hook-form";
6
6
  import { Button, TextField, Input } from "react-aria-components";
7
7
  import Spinner from "@/components/Spinner/Spinner";
example/vite.config.js ADDED
@@ -0,0 +1,55 @@
1
+ import { defineConfig } from 'vite';
2
+
3
+ function pagesPlugin(userOptions) {
4
+ return {
5
+ name: 'vite-plugin-pages',
6
+ enforce: 'pre',
7
+ async configResolved(config) {
8
+ userOptions.resolver = 'react'
9
+ ctx = new PageContext(userOptions, config.root)
10
+ ctx.setLogger(config.logger)
11
+ await ctx.searchGlob()
12
+ },
13
+ api: {
14
+ getResolvedRoutes() {
15
+ return ctx.options.resolver.getComputedRoutes(ctx)
16
+ },
17
+ },
18
+ configureServer(server) {
19
+ ctx.setupViteServer(server)
20
+ },
21
+ resolveId(id) {
22
+ if (ctx.options.moduleIds.includes(id))
23
+ return `${MODULE_ID_VIRTUAL}?id=${id}`
24
+
25
+ if (routeBlockQueryRE.test(id))
26
+ return ROUTE_BLOCK_ID_VIRTUAL
27
+
28
+ return null
29
+ },
30
+ async load(id) {
31
+ const {
32
+ moduleId,
33
+ pageId,
34
+ } = parsePageRequest(id)
35
+
36
+ if (moduleId === MODULE_ID_VIRTUAL && pageId && ctx.options.moduleIds.includes(pageId))
37
+ return ctx.resolveRoutes()
38
+
39
+ if (id === ROUTE_BLOCK_ID_VIRTUAL) {
40
+ return {
41
+ code: 'export default {};',
42
+ map: null,
43
+ }
44
+ }
45
+
46
+ return null
47
+ },
48
+ }
49
+ }
50
+
51
+ // https://vitejs.dev/config/
52
+ export default defineConfig({
53
+ plugins: [
54
+ ],
55
+ })
index.js DELETED
@@ -1,201 +0,0 @@
1
- import React, {
2
- Suspense, createContext, useContext, useState, useEffect, useTransition, useCallback
3
- } from "react";
4
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
5
- import { HelmetProvider } from 'react-helmet-async';
6
- import { createBrowserHistory } from "history";
7
- import { createRouter } from "radix3";
8
- import routemap from '/routemap.json' assert {type: 'json'};
9
-
10
- export const isProd = () => process.env.NODE_ENV === "production";
11
- export const isClient = () => typeof window !== 'undefined';
12
- export const domain = () => isClient() ? window.origin : "http://0.0.0.0:3000";
13
-
14
- export const defineRpc = (serviceName) => async (params = {}) => {
15
- const res = await fetch(`${domain()}/_rpc/${serviceName}`, {
16
- method: "POST",
17
- headers: {
18
- "Accept": "application/json",
19
- "Content-Type": "application/json",
20
- },
21
- body: JSON.stringify(params),
22
- })
23
- return await res.json();
24
- }
25
-
26
- export const cache = {
27
- get: (k) => globalThis._EDGE_DATA_.data[k],
28
- set: (k, v) => {
29
- globalThis._EDGE_DATA_.data[k] = v;
30
- },
31
- invalidate: (k, setRefetch) => Promise.all(Array.from(globalThis._EDGE_DATA_.subs[k]).map((cb) => cb(setRefetch))),
32
- subscribe: (k, cb) => {
33
- if (!globalThis._EDGE_DATA_.subs[k]) {
34
- globalThis._EDGE_DATA_.subs[k] = new Set();
35
- }
36
- globalThis._EDGE_DATA_.subs[k].add(cb)
37
- return () => globalThis._EDGE_DATA_.subs[k].delete(cb);
38
- }
39
- }
40
-
41
- /**
42
- *
43
- * @param {*} fn
44
- * @param {*} params
45
- * @returns
46
- */
47
- export const useQuery = (key, fn) => {
48
- const [, toggle] = useState(false);
49
- const [isRefetching, setIsRefetching] = useState(false);
50
- const [err, setErr] = useState(null);
51
- const refetch = useCallback(async (setRefetch = true) => {
52
- try {
53
- if (setRefetch) {
54
- setIsRefetching(true);
55
- }
56
- setErr(null);
57
- cache.set(key, await fn());
58
- } catch (err) {
59
- setErr(err);
60
- throw err;
61
- } finally {
62
- if (setRefetch) {
63
- setIsRefetching(false);
64
- } else {
65
- toggle((v) => !v);
66
- }
67
- }
68
- }, [fn]);
69
- useEffect(() => {
70
- return cache.subscribe(key, refetch);
71
- }, [key])
72
- const value = cache.get(key);
73
- if (value) {
74
- if (value instanceof Promise) {
75
- throw value;
76
- } else if (value instanceof Error) {
77
- throw value;
78
- }
79
- return { data: value, isRefetching, err, refetch };
80
- }
81
- cache.set(key, fn().then((v) => cache.set(key, v)));
82
- throw cache.get(key);
83
- }
84
-
85
- export const useMutation = (fn) => {
86
- const [isMutating, setIsMutating] = useState(false);
87
- const [err, setErr] = useState(null);
88
- const mutate = useCallback(async (params) => {
89
- try {
90
- setIsMutating(true);
91
- setErr(null);
92
- await fn(params);
93
- } catch (err) {
94
- setErr(err)
95
- throw err;
96
- } finally {
97
- setIsMutating(false);
98
- }
99
- }, [fn])
100
- return {
101
- mutate,
102
- isMutating,
103
- err,
104
- }
105
- }
106
-
107
- export const RouterContext = createContext(undefined);
108
- export const RouterProvider = ({ router, history, helmetContext, App }) => {
109
- const [_, startTransition] = useTransition();
110
- const [pathname, setPathname] = useState(history.location.pathname);
111
- const page = router.lookup(pathname) || router.lookup("/_404");
112
- useEffect(() => {
113
- return history.listen(({ location }) => {
114
- // this causes 2 renders to happen but stops jitter or flash due to React.lazy
115
- startTransition(() => {
116
- setPathname(location.pathname);
117
- })
118
- });
119
- }, []);
120
- return _jsx(RouterContext.Provider, {
121
- value: {
122
- history,
123
- params: page.params || {},
124
- },
125
- children: _jsx(Suspense, {
126
- fallback: _jsx("div", {}, "Routing...."),
127
- children: _jsx(HelmetProvider, {
128
- context: helmetContext,
129
- children: _jsx(App, {
130
- children: _jsx(page, {}),
131
- })
132
- }),
133
- }),
134
- })
135
- }
136
-
137
- export const useRouter = () => {
138
- const { history, params } = useContext(RouterContext);
139
- return {
140
- pathname: history.location.pathname,
141
- query: new URLSearchParams(history.location.search),
142
- params,
143
- push: history.push,
144
- replace: history.replace,
145
- forward: history.forward,
146
- back: history.back,
147
- reload: () => window.location.reload(),
148
- };
149
- }
150
-
151
- export const Link = (props) => {
152
- const router = useRouter();
153
- return _jsx("a", {
154
- ...props,
155
- onMouseOver: (e) => {
156
- // Simple prefetching for now will work only with cache headers
157
- // fetch(getCssUrl(props.href));
158
- // fetch(getCssUrl(props.href).replace("css", "jsx"));
159
- },
160
- onClick: (e) => {
161
- e.preventDefault();
162
- if (props && props.onClick) {
163
- props.onClick(e);
164
- }
165
- router.push(props.href);
166
- },
167
- })
168
- }
169
-
170
- export const NavLink = ({ children, className, activeClassName, ...props }) => {
171
- const { pathname } = useRouter();
172
- const classNames = pathname === props.href ? [activeClassName, className] : [className];
173
- return _jsx(Link, {
174
- children,
175
- className: classNames,
176
- ...props,
177
- })
178
- }
179
-
180
- export const hydrateApp = async (App) => {
181
- if (!isProd()) {
182
- console.log("hydrating with", window._EDGE_DATA_);
183
- }
184
- const module = await import("react-dom/client");
185
- const history = createBrowserHistory();
186
- const router = createRouter({
187
- strictTrailingSlash: true,
188
- routes: Object.keys(routemap).reduce((acc, r) => {
189
- acc[r] = React.lazy(() => import(routemap[r]));
190
- return acc;
191
- }, {}),
192
- });
193
- const root = document.getElementById("root");
194
- module.default.hydrateRoot(root, _jsx(RouterProvider, {
195
- history,
196
- router,
197
- rpcContext: window._EDGE_DATA_ || {},
198
- helmetContext: {},
199
- App,
200
- }));
201
- }
cli.js → lib/bin/cli.js RENAMED
@@ -62,7 +62,7 @@ const bundleJs = async ({ entryPoints, isServer, outfile, ...options }, plg) =>
62
62
  entryPoints,
63
63
  outfile,
64
64
  format: 'esm',
65
- external: ["node:*"],
65
+ external: isServer ? [] : ["node:*"], // TODO: "react", "react-dom/client", "react-aria-components" "react-error-boundary" "react-helmet-async" "react-hook-form" "zod" "lodash" "date-fns"
66
66
  color: true,
67
67
  keepNames: !isProd,
68
68
  minify: isProd,
@@ -112,7 +112,7 @@ const bundlePages = async () => {
112
112
  build.onLoad({ filter: /\\*.page.jsx/, namespace: undefined }, (args) => {
113
113
  const data = fs.readFileSync(args.path);
114
114
  const newSrc = `
115
- import renderPage from "edge-city/renderPage";
115
+ import renderPage from "edge-city/server/renderPage";
116
116
  import init from "@/init";
117
117
  ${importAppComp}
118
118
 
@@ -156,14 +156,14 @@ const bundlePages = async () => {
156
156
  build.onLoad({ filter: /\\*.page.jsx/, namespace: undefined }, (args) => {
157
157
  const data = fs.readFileSync(args.path);
158
158
  const newSrc = `
159
- import { hydrateApp } from "edge-city";
159
+ import hydratePage from "edge-city/client/hydratePage";
160
160
  ${importAppComp}
161
161
 
162
162
  ${data.toString()}
163
163
 
164
164
  const searchParams = new URL(import.meta.url).searchParams;
165
165
  if (searchParams.get("hydrate") === "true") {
166
- hydrateApp(App)
166
+ hydratePage(App)
167
167
  }
168
168
  `
169
169
  return {
@@ -182,7 +182,7 @@ const bundlePages = async () => {
182
182
  const svcName = args.path.replace(srcDir, "").replace("/services/", "").replace(".service.js", "");
183
183
  const funcs = parseExports(src);
184
184
  const newSrc = `
185
- import { defineRpc } from "edge-city";
185
+ import { defineRpc } from "edge-city/data";
186
186
  ${funcs.map((f) => `export const ${f} = defineRpc("${svcName}/${f}")`).join("\n")}
187
187
  `
188
188
  return {
@@ -217,7 +217,7 @@ const bundleServices = async () => {
217
217
  setup(build) {
218
218
  build.onLoad({ filter: /\\*.service.js/, namespace: undefined }, async (args) => {
219
219
  const newSrc = `
220
- import renderApi from "edge-city/renderApi";
220
+ import renderApi from "edge-city/server/renderApi";
221
221
  import init from "@/init";
222
222
  ${src.toString()}
223
223
 
lib/client/hydratePage.js ADDED
@@ -0,0 +1,27 @@
1
+ import React from "react";
2
+ import { hydrateRoot } from "react-dom/client";
3
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
4
+ import { createBrowserHistory } from "history";
5
+ import { createRouter } from "radix3";
6
+ import { RouterProvider } from "../router";
7
+ import routemap from '/routemap.json' assert {type: 'json'};
8
+
9
+ const hydratePage = (App) => {
10
+ const history = createBrowserHistory();
11
+ const router = createRouter({
12
+ strictTrailingSlash: true,
13
+ routes: Object.keys(routemap).reduce((acc, r) => {
14
+ acc[r] = React.lazy(() => import(routemap[r]));
15
+ return acc;
16
+ }, {}),
17
+ });
18
+ const root = document.getElementById("root");
19
+ hydrateRoot(root, _jsx(RouterProvider, {
20
+ history,
21
+ router,
22
+ rpcContext: window._EDGE_DATA_ || {},
23
+ helmetContext: {},
24
+ App,
25
+ }));
26
+ }
27
+ export default hydratePage;
lib/data.js ADDED
@@ -0,0 +1,95 @@
1
+ import { useState, useEffect, useCallback } from "react";
2
+
3
+ export const defineRpc = (serviceName) => async (params = {}) => {
4
+ const res = await fetch(`/_rpc/${serviceName}`, {
5
+ method: "POST",
6
+ headers: {
7
+ "Accept": "application/json",
8
+ "Content-Type": "application/json",
9
+ },
10
+ body: JSON.stringify(params),
11
+ })
12
+ return await res.json();
13
+ }
14
+
15
+
16
+ export const cache = {
17
+ get: (k) => globalThis._EDGE_DATA_.data[k],
18
+ set: (k, v) => {
19
+ globalThis._EDGE_DATA_.data[k] = v;
20
+ },
21
+ invalidate: (k, setRefetch) => Promise.all(Array.from(globalThis._EDGE_DATA_.subs[k]).map((cb) => cb(setRefetch))),
22
+ subscribe: (k, cb) => {
23
+ if (!globalThis._EDGE_DATA_.subs[k]) {
24
+ globalThis._EDGE_DATA_.subs[k] = new Set();
25
+ }
26
+ globalThis._EDGE_DATA_.subs[k].add(cb)
27
+ return () => globalThis._EDGE_DATA_.subs[k].delete(cb);
28
+ }
29
+ }
30
+
31
+ /**
32
+ *
33
+ * @param {*} fn
34
+ * @param {*} params
35
+ * @returns
36
+ */
37
+ export const useQuery = (key, fn) => {
38
+ const [, toggle] = useState(false);
39
+ const [isRefetching, setIsRefetching] = useState(false);
40
+ const [err, setErr] = useState(null);
41
+ const refetch = useCallback(async (setRefetch = true) => {
42
+ try {
43
+ if (setRefetch) {
44
+ setIsRefetching(true);
45
+ }
46
+ setErr(null);
47
+ cache.set(key, await fn());
48
+ } catch (err) {
49
+ setErr(err);
50
+ throw err;
51
+ } finally {
52
+ if (setRefetch) {
53
+ setIsRefetching(false);
54
+ } else {
55
+ toggle((v) => !v);
56
+ }
57
+ }
58
+ }, [fn]);
59
+ useEffect(() => {
60
+ return cache.subscribe(key, refetch);
61
+ }, [key])
62
+ const value = cache.get(key);
63
+ if (value) {
64
+ if (value instanceof Promise) {
65
+ throw value;
66
+ } else if (value instanceof Error) {
67
+ throw value;
68
+ }
69
+ return { data: value, isRefetching, err, refetch };
70
+ }
71
+ cache.set(key, fn().then((v) => cache.set(key, v)));
72
+ throw cache.get(key);
73
+ }
74
+
75
+ export const useMutation = (fn) => {
76
+ const [isMutating, setIsMutating] = useState(false);
77
+ const [err, setErr] = useState(null);
78
+ const mutate = useCallback(async (params) => {
79
+ try {
80
+ setIsMutating(true);
81
+ setErr(null);
82
+ await fn(params);
83
+ } catch (err) {
84
+ setErr(err)
85
+ throw err;
86
+ } finally {
87
+ setIsMutating(false);
88
+ }
89
+ }, [fn])
90
+ return {
91
+ mutate,
92
+ isMutating,
93
+ err,
94
+ }
95
+ }
lib/index.js ADDED
@@ -0,0 +1,29 @@
1
+ import React from "react";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { createBrowserHistory } from "history";
4
+ import { createRouter } from "radix3";
5
+ import routemap from '/routemap.json' assert {type: 'json'};
6
+
7
+ export const isProd = () => process.env.NODE_ENV === "production";
8
+ export const hydrateApp = async (App) => {
9
+ if (!isProd()) {
10
+ console.log("hydrating with", window._EDGE_DATA_);
11
+ }
12
+ const module = await import("react-dom/client");
13
+ const history = createBrowserHistory();
14
+ const router = createRouter({
15
+ strictTrailingSlash: true,
16
+ routes: Object.keys(routemap).reduce((acc, r) => {
17
+ acc[r] = React.lazy(() => import(routemap[r]));
18
+ return acc;
19
+ }, {}),
20
+ });
21
+ const root = document.getElementById("root");
22
+ module.default.hydrateRoot(root, _jsx(RouterProvider, {
23
+ history,
24
+ router,
25
+ rpcContext: window._EDGE_DATA_ || {},
26
+ helmetContext: {},
27
+ App,
28
+ }));
29
+ }
index.test.js → lib/index.test.js RENAMED
File without changes
lib/link.js ADDED
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useRouter } from "./router";
3
+
4
+ const Link = (props) => {
5
+ const router = useRouter();
6
+ return _jsx("a", {
7
+ ...props,
8
+ onMouseOver: (e) => {
9
+ // Simple prefetching for now will work only with cache headers
10
+ // fetch(getCssUrl(props.href));
11
+ // fetch(getCssUrl(props.href).replace("css", "jsx"));
12
+ },
13
+ onClick: (e) => {
14
+ e.preventDefault();
15
+ if (props && props.onClick) {
16
+ props.onClick(e);
17
+ }
18
+ router.push(props.href);
19
+ },
20
+ })
21
+ }
22
+
23
+ export const NavLink = ({ children, className, activeClassName, ...props }) => {
24
+ const { pathname } = useRouter();
25
+ const classNames = pathname === props.href ? [activeClassName, className] : [className];
26
+ return _jsx(Link, {
27
+ children,
28
+ className: classNames,
29
+ ...props,
30
+ })
31
+ }
32
+
33
+ export default Link;
package.json → lib/package.json RENAMED
@@ -29,6 +29,9 @@
29
29
  "walkdir": "0.4.1",
30
30
  "esbuild-plugin-resolve": "2.0.0",
31
31
  "wrangler": "3.0.1",
32
+ "miniflare": "3.0.1",
33
+ "vite": "4.3.9",
34
+ "express": "4.18.2",
32
35
  "node-watch": "0.7.3",
33
36
  "dotenv": "16.0.3",
34
37
  "jest": "29.5.0"
@@ -40,7 +43,7 @@
40
43
  "react-helmet-async": "*"
41
44
  },
42
45
  "bin": {
43
- "edge-city": "cli.js"
46
+ "edge-city": "bin/cli.js"
44
47
  },
45
48
  "jest": {
46
49
  "verbose": true
lib/router.js ADDED
@@ -0,0 +1,49 @@
1
+ import {
2
+ Suspense, createContext, useContext, useState, useEffect, useTransition, useCallback
3
+ } from "react";
4
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
5
+ import { HelmetProvider } from 'react-helmet-async';
6
+
7
+ export const RouterContext = createContext(undefined);
8
+ export const RouterProvider = ({ router, history, helmetContext, App }) => {
9
+ const [_, startTransition] = useTransition();
10
+ const [pathname, setPathname] = useState(history.location.pathname);
11
+ const page = router.lookup(pathname) || router.lookup("/_404");
12
+ useEffect(() => {
13
+ return history.listen(({ location }) => {
14
+ // this causes 2 renders to happen but stops jitter or flash due to React.lazy
15
+ startTransition(() => {
16
+ setPathname(location.pathname);
17
+ })
18
+ });
19
+ }, []);
20
+ return _jsx(RouterContext.Provider, {
21
+ value: {
22
+ history,
23
+ params: page.params || {},
24
+ },
25
+ children: _jsx(Suspense, {
26
+ fallback: _jsx("div", {}, "Routing...."),
27
+ children: _jsx(HelmetProvider, {
28
+ context: helmetContext,
29
+ children: _jsx(App, {
30
+ children: _jsx(page, {}),
31
+ })
32
+ }),
33
+ }),
34
+ })
35
+ }
36
+
37
+ export const useRouter = () => {
38
+ const { history, params } = useContext(RouterContext);
39
+ return {
40
+ pathname: history.location.pathname,
41
+ query: new URLSearchParams(history.location.search),
42
+ params,
43
+ push: history.push,
44
+ replace: history.replace,
45
+ forward: history.forward,
46
+ back: history.back,
47
+ reload: () => window.location.reload(),
48
+ };
49
+ }
lib/server/dev-server.js ADDED
@@ -0,0 +1,18 @@
1
+ import http from "http";
2
+
3
+ const server = http.createServer((req, res) => {
4
+ res.writeHead(200, { 'Content-Type': 'application/json' });
5
+ res.end(JSON.stringify({
6
+ data: 'Hello World!',
7
+ }))
8
+ })
9
+ server.listen(3000);
10
+ server.on('error', (e) => {
11
+ if (e.code === 'EADDRINUSE') {
12
+ console.log('Address in use, retrying...');
13
+ setTimeout(() => {
14
+ server.close();
15
+ server.listen(PORT, HOST);
16
+ }, 1000);
17
+ }
18
+ });
renderApi.js → lib/server/renderApi.js RENAMED
File without changes
renderPage.js → lib/server/renderPage.js RENAMED
@@ -5,7 +5,7 @@ import { createRouter } from "radix3";
5
5
  import { renderToReadableStream } from "react-dom/server";
6
6
  import isbot from "isbot";
7
7
  import routemap from '/routemap.json' assert {type: 'json'};
8
- import { RouterProvider } from "./index";
8
+ import { RouterProvider } from "../router";
9
9
 
10
10
  const stringToStream = (str) => {
11
11
  return new ReadableStream({
pnpm-workspace.yaml CHANGED
@@ -1,4 +1,4 @@
1
1
  packages:
2
- - "./"
2
+ - "lib"
3
3
  - "example"
4
4
  - "docs"