~repos /edge-city

#react#js#ssr

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

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


8cd9e9ae pyrossh

2 years ago
Move router to the top
Files changed (3) hide show
  1. index.js +16 -16
  2. readme.md +10 -8
  3. renderPage.js +41 -43
index.js CHANGED
@@ -11,7 +11,7 @@ import routemap from '/routemap.json' assert {type: 'json'};
11
11
  export const isClient = () => typeof window !== 'undefined';
12
12
  export const domain = () => isClient() ? window.origin : "http://0.0.0.0:3000";
13
13
 
14
- export const rpc = (serviceName) => async (params = {}) => {
14
+ export const defineRpc = (serviceName) => async (params = {}) => {
15
15
  const res = await fetch(`${domain()}/_rpc/${serviceName}`, {
16
16
  method: "POST",
17
17
  headers: {
@@ -114,33 +114,33 @@ export const useMutation = (fn) => {
114
114
  }
115
115
 
116
116
  export const RouterContext = createContext(undefined);
117
-
118
- export const App = ({ history, router, rpcContext, helmetContext }) => {
117
+ export const RouterProvider = ({ router, history, rpcContext, helmetContext }) => {
119
118
  const [_, startTransition] = useTransition();
120
119
  const [pathname, setPathname] = useState(history.location.pathname);
121
- const match = router.lookup(pathname) || router.lookup("/_404");
120
+ const page = router.lookup(pathname) || router.lookup("/_404");
122
121
  useEffect(() => {
123
122
  return history.listen(({ location }) => {
124
- startTransition(() => { // this causes 2 renders to happen but stops jitter or layout reflow due to React.lazy
123
+ // this causes 2 renders to happen but stops jitter or flash due to React.lazy
124
+ startTransition(() => {
125
125
  setPathname(location.pathname);
126
126
  })
127
127
  });
128
128
  }, []);
129
+ return _jsx(RouterContext.Provider, {
130
+ value: {
131
+ history,
132
+ params: page.params || {},
133
+ },
129
- return _jsx(HelmetProvider, {
134
+ children: _jsx(HelmetProvider, {
130
- context: helmetContext,
135
+ context: helmetContext,
131
- children: _jsx(RpcContext.Provider, {
136
+ children: _jsx(RpcContext.Provider, {
132
- value: rpcContext,
137
+ value: rpcContext,
133
- children: _jsx(RouterContext.Provider, {
134
- value: {
135
- history: history,
136
- params: match.params || {},
137
- },
138
138
  children: _jsx(ErrorBoundary, {
139
139
  onError: (err) => console.log(err),
140
140
  fallback: _jsx("p", {}, "Oops something went wrong"),
141
141
  children: _jsx(Suspense, {
142
142
  fallback: _jsx("p", {}, "Loading..."),
143
- children: _jsx(match, {}),
143
+ children: _jsx(page, {}),
144
144
  }),
145
145
  }),
146
146
  }),
@@ -202,7 +202,7 @@ export const hydrateApp = async () => {
202
202
  }, {}),
203
203
  });
204
204
  const root = document.getElementById("root");
205
- module.default.hydrateRoot(root, React.createElement(App, {
205
+ module.default.hydrateRoot(root, _jsx(RouterProvider, {
206
206
  history,
207
207
  router,
208
208
  rpcContext: {},
readme.md CHANGED
@@ -39,13 +39,15 @@ which are compatible with these API's. Here is a list of some of them,
39
39
  1. `node >= v20.2.0`
40
40
  2. `pnpm >= v8.5.1`
41
41
 
42
- ### Todo
42
+ ### Todo[General]
43
43
  1. Hydrate rpc cache
44
44
  2. Build a docs website
45
+ 4. Add tests for bot
45
- 3. Fix 404/500 pages not routing
46
+ 5. Add tests for runtime
46
- 4. Add Env variables `PUBLIC_` for client
47
+ 6. Add Env variables `PUBLIC_` for client
47
- 5. Add tests for bot
48
- 6. Add tests for runtime
48
+ 7. Add E2E tests for example
49
- 7. Maybe move to vite for HMR goodness
49
+ 8. Maybe move to vite for HMR goodness
50
+
51
+ ### Todo[Cloudflare]
50
- 8. Add E2E tests for example
52
+ 1. Fix 404/500 pages not routing on server
51
- 9. Fix todos sql library
53
+ 2. Fix todos service not loading
renderPage.js CHANGED
@@ -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 { App } from "./index";
8
+ import { RouterProvider } from "./index";
9
9
 
10
10
  const renderPage = async (Page, req) => {
11
11
  const url = new URL(req.url);
@@ -24,20 +24,14 @@ const renderPage = async (Page, req) => {
24
24
  const rpcContext = {};
25
25
  if (isbot(req.headers.get('User-Agent')) || url.search.includes("ec_is_bot=true")) {
26
26
  const stream = await renderToReadableStream(_jsx("body", {
27
- children: _jsxs("div", {
27
+ children: _jsx("div", {
28
28
  id: "root",
29
- children: [_jsx(App, {
29
+ children: _jsx(RouterProvider, {
30
30
  history,
31
31
  router,
32
32
  rpcCache: {},
33
33
  helmetContext,
34
- }), _jsx(_Fragment, {
35
- children: _jsx("script", {
36
- type: "module",
37
- defer: true,
38
- src: `/js${jsScript}.js?hydrate=true`,
39
- })
40
- })]
34
+ }),
41
35
  })
42
36
  }))
43
37
  await stream.allReady;
@@ -47,15 +41,14 @@ const renderPage = async (Page, req) => {
47
41
  transform(chunk, controller) {
48
42
  if (isFirstChunk) {
49
43
  isFirstChunk = false;
50
- controller.enqueue(new TextEncoder().encode(`
44
+ const encoder = new TextEncoder();
51
- <!DOCTYPE html>
52
- <html lang="en">
45
+ controller.enqueue(encoder.encode(`<!DOCTYPE html><html lang="en"><head>`));
53
- <head>
54
- ${helmetContext.helmet.title.toString()}
46
+ for (const key of Object.keys(helmetContext.helmet)) {
55
- ${helmetContext.helmet.meta.toString()}
47
+ controller.enqueue(encoder.encode(helmetContext.helmet[key].toString()));
48
+ }
56
- <link rel="stylesheet" href="/css/app.css">
49
+ controller.enqueue(encoder.encode(`<link rel="stylesheet" href="/css/app.css">`));
50
+ controller.enqueue(encoder.encode(`<script type="module" src="/js${jsScript}.js?hydrate=true" defer></script>`));
57
- </head>
51
+ controller.enqueue(encoder.encode(`</head>`));
58
- `));
59
52
  }
60
53
  controller.enqueue(chunk);
61
54
  },
@@ -72,30 +65,35 @@ const renderPage = async (Page, req) => {
72
65
  const stream = await renderToReadableStream(
73
66
  _jsxs("html", {
74
67
  lang: "en",
75
- children: [_jsxs("head", {
76
- children: [
68
+ children: [
69
+ _jsxs("head", {
70
+ children: [
77
- _jsx("link", {
71
+ _jsx("link", {
78
- rel: "stylesheet",
72
+ rel: "stylesheet",
79
- href: "/css/app.css"
73
+ href: "/css/app.css"
80
- }),
74
+ }),
81
- ]
75
+ ]
76
+ }),
82
- }), _jsx("body", {
77
+ _jsx("body", {
83
- children: _jsxs("div", {
78
+ children: _jsxs("div", {
84
- id: "root",
79
+ id: "root",
85
- children: [_jsx(App, {
80
+ children: [
81
+ _jsx(RouterProvider, {
86
- history,
82
+ history,
87
- router,
83
+ router,
88
- rpcContext,
84
+ rpcContext,
89
- helmetContext,
85
+ helmetContext,
86
+ }),
90
- }), _jsx(_Fragment, {
87
+ _jsx(_Fragment, {
91
- children: _jsx("script", {
88
+ children: _jsx("script", {
92
- type: "module",
89
+ type: "module",
93
- defer: true,
90
+ defer: true,
94
- src: `/js${jsScript}.js?hydrate=true`,
91
+ src: `/js${jsScript}.js?hydrate=true`,
95
- })
92
+ })
93
+ }),
94
+ ]
95
+ })
96
- })]
96
+ })]
97
- })
98
- })]
99
97
  }));
100
98
  return new Response(stream, {
101
99
  headers: { 'Content-Type': 'text/html' },