~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


2989f2ef pyrossh

2 years ago
add SEO render
Files changed (2) hide show
  1. example/pages/page.jsx +1 -1
  2. index.js +54 -51
example/pages/page.jsx CHANGED
@@ -1,4 +1,4 @@
1
- import { useEffect } from 'react';
1
+ import React, { useEffect } from 'react';
2
2
  import { useRouter } from "edge-city";
3
3
  import Layout from '@/components/Layout/Layout';
4
4
  import Counter from "@/components/Counter/Counter";
index.js CHANGED
@@ -1,9 +1,7 @@
1
1
  import React, {
2
2
  Suspense, createContext, useContext, useState, useEffect, useTransition, useCallback
3
3
  } from "react";
4
- import { jsx as _jsx } from "react/jsx-runtime";
5
- import { jsxs as _jsxs } from "react/jsx-runtime";
6
- import { Fragment as _Fragment } from "react/jsx-runtime";
4
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
7
5
  import { HelmetProvider } from 'react-helmet-async';
8
6
  import { ErrorBoundary } from "react-error-boundary";
9
7
  import { createMemoryHistory, createBrowserHistory } from "history";
@@ -121,7 +119,7 @@ export const useMutation = (fn) => {
121
119
 
122
120
  export const RouterContext = createContext(undefined);
123
121
 
124
- export const App = ({ history, router, rpcCache, helmetContext }) => {
122
+ export const App = ({ history, router, rpcContext, helmetContext }) => {
125
123
  const [_, startTransition] = useTransition();
126
124
  const [pathname, setPathname] = useState(history.location.pathname);
127
125
  const match = router.lookup(pathname) || router.lookup("/_404");
@@ -135,7 +133,7 @@ export const App = ({ history, router, rpcCache, helmetContext }) => {
135
133
  return _jsx(HelmetProvider, {
136
134
  context: helmetContext,
137
135
  children: _jsx(RpcContext.Provider, {
138
- value: rpcCache,
136
+ value: rpcContext,
139
137
  children: _jsx(RouterContext.Provider, {
140
138
  value: {
141
139
  history: history,
@@ -197,9 +195,26 @@ export const NavLink = ({ children, className, activeClassName, ...props }) => {
197
195
  })
198
196
  }
199
197
 
198
+ export const hydrateApp = async () => {
200
- /**
199
+ const module = await import("react-dom/client");
200
+ const history = createBrowserHistory();
201
- * SSR related functions
201
+ const router = createRouter({
202
+ strictTrailingSlash: true,
203
+ routes: Object.keys(routemap).reduce((acc, r) => {
204
+ acc[r] = React.lazy(() => import(routemap[r]));
205
+ return acc;
202
- */
206
+ }, {}),
207
+ });
208
+ const root = document.getElementById("root");
209
+ module.default.hydrateRoot(root, React.createElement(App, {
210
+ history,
211
+ router,
212
+ rpcContext: {},
213
+ helmetContext: {},
214
+ }));
215
+ }
216
+
217
+ // TODO: move this to cli.js or another file
203
218
  export const renderPage = async (Page, req) => {
204
219
  const { renderToReadableStream } = await import("react-dom/server");
205
220
  const { default: isbot } = await import("isbot");
@@ -215,8 +230,9 @@ export const renderPage = async (Page, req) => {
215
230
  }, {}),
216
231
  });
217
232
  const jsScript = url.pathname === "/" ? "/index" : url.pathname;
218
- const helmetContext = {}
233
+ const helmetContext = {};
234
+ const rpcContext = {};
219
- if (isbot(req.headers.get('User-Agent'))) {
235
+ if (isbot(req.headers.get('User-Agent')) || url.search.includes("ec_is_bot=true")) {
220
236
  const stream = await renderToReadableStream(_jsx("body", {
221
237
  children: _jsxs("div", {
222
238
  id: "root",
@@ -235,27 +251,33 @@ export const renderPage = async (Page, req) => {
235
251
  })
236
252
  }))
237
253
  await stream.allReady;
238
- // const data = [];
239
- // const reader = stream.getReader();
240
- // await reader.read().then(function pump({ done, value }) {
241
- // if (done) {
242
- // return;
254
+ let isFirstChunk = true;
243
- // }
244
- // data.push(value);
245
- // return reader.read().then(pump);
246
- // });
247
- // return new Response(`
255
+ // TODO: add rpcContext
256
+ const transformStream = new TransformStream({
257
+ transform(chunk, controller) {
258
+ if (isFirstChunk) {
259
+ isFirstChunk = false;
260
+ controller.enqueue(new TextEncoder().encode(`
248
- // <!DOCTYPE html>
261
+ <!DOCTYPE html>
249
- // <html lang="en">
262
+ <html lang="en">
250
- // <head>
263
+ <head>
251
- // ${helmetContext.helmet.title.toString()}
264
+ ${helmetContext.helmet.title.toString()}
265
+ ${helmetContext.helmet.meta.toString()}
266
+ <link rel="stylesheet" href="/css/app.css">
252
- // </head>
267
+ </head>
253
- // ${res}
268
+ `));
269
+ }
254
- // </html>
270
+ controller.enqueue(chunk);
271
+ },
255
- // `, {
272
+ flush(controller) {
273
+ controller.enqueue(new TextEncoder().encode(`</html>`));
274
+ controller.terminate();
275
+ },
276
+ });
277
+ return new Response(stream.pipeThrough(transformStream), {
256
- // headers: { 'Content-Type': 'text/html' },
278
+ headers: { 'Content-Type': 'text/html' },
257
- // status: 200,
279
+ status: 200,
258
- // });
280
+ });
259
281
  }
260
282
  const stream = await renderToReadableStream(
261
283
  _jsxs("html", {
@@ -273,7 +295,7 @@ export const renderPage = async (Page, req) => {
273
295
  children: [_jsx(App, {
274
296
  history,
275
297
  router,
276
- rpcCache: {},
298
+ rpcContext,
277
299
  helmetContext,
278
300
  }), _jsx(_Fragment, {
279
301
  children: _jsx("script", {
@@ -290,22 +312,3 @@ export const renderPage = async (Page, req) => {
290
312
  status: 200,
291
313
  });
292
314
  }
293
-
294
- export const hydrateApp = async () => {
295
- const module = await import("react-dom/client");
296
- const history = createBrowserHistory();
297
- const router = createRouter({
298
- strictTrailingSlash: true,
299
- routes: Object.keys(routemap).reduce((acc, r) => {
300
- acc[r] = React.lazy(() => import(routemap[r]));
301
- return acc;
302
- }, {}),
303
- });
304
- const root = document.getElementById("root");
305
- module.default.hydrateRoot(root, React.createElement(App, {
306
- history,
307
- router,
308
- rpcCache: {},
309
- helmetContext: {},
310
- }));
311
- }