~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


fbdae633 Peter John

2 years ago
add atom
Files changed (5) hide show
  1. bun.lockb +0 -0
  2. index.js +20 -36
  3. jsxPlugin.js +1 -2
  4. package.json +0 -1
  5. router.js +67 -21
bun.lockb CHANGED
Binary file
index.js CHANGED
@@ -1,8 +1,7 @@
1
1
  import './jsxPlugin.js';
2
2
  import path from 'path';
3
- import { SWRConfig } from 'swr';
4
3
  import { renderToReadableStream } from 'react-dom/server';
5
- import { RouterProvider } from './router.js';
4
+ import { routerAtom } from './router.js';
6
5
  import packageJson from "./package.json";
7
6
 
8
7
  const transpiler = new Bun.Transpiler({
@@ -32,15 +31,13 @@ const renderPage = async (filePath, url, params) => {
32
31
  pathname: url.pathname,
33
32
  }
34
33
  console.log('filePath', filePath);
34
+ routerAtom.update(() => initialRouteValue);
35
35
  const routeImport = await import(filePath);
36
36
  const Page = routeImport.default;
37
37
  const stream = await renderToReadableStream(
38
38
  <html lang="en">
39
39
  <head>
40
- <link rel="stylesheet" href="/pages/index.css" />
40
+ <link rel="stylesheet" href="/routes/index/page.css" />
41
- <script id="initial_route_context" type='application/json' dangerouslySetInnerHTML={{
42
- __html: JSON.stringify(initialRouteValue)
43
- }} />
44
41
  <script type="importmap" dangerouslySetInnerHTML={{
45
42
  __html: JSON.stringify(
46
43
  {
@@ -49,7 +46,7 @@ const renderPage = async (filePath, url, params) => {
49
46
  "react-dom/client": "https://esm.sh/react-dom@18.2.0/client?dev",
50
47
  "react/jsx-dev-runtime": "https://esm.sh/react@18.2.0/jsx-dev-runtime?dev",
51
48
  "@/router.js": "/assets/js/src/router.js",
52
- "@/pages/index.jsx": "/pages/index.js",
49
+ "@/routes/index/page.jsx": "/routes/index/page.js",
53
50
  "@/components/Todo.jsx": "/components/Todo.js",
54
51
  "@/containers/TodoList.jsx": "/containers/TodoList.js"
55
52
  }
@@ -59,30 +56,19 @@ const renderPage = async (filePath, url, params) => {
59
56
  </script>
60
57
  <script type="module" defer dangerouslySetInnerHTML={{
61
58
  __html: `
62
- import* as JSX from "react/jsx-dev-runtime";
59
+ import React from 'react';
63
- var $jsx = JSX.jsxDEV;
64
60
  import { hydrateRoot } from 'react-dom/client';
65
- import { SWRConfig } from 'swr';
66
- import {RouterProvider} from "@/router.js";
61
+ import { routerAtom } from "@/router.js";
67
- import Page from "@/pages/index.jsx";
62
+ import Page from "@/routes/index/page.jsx";
68
-
63
+
69
- const initialRouteValue = JSON.parse(document.getElementById('initial_route_context').textContent);
64
+ routerAtom.update(() => (${JSON.stringify(initialRouteValue)}));
65
+
70
- const root = hydrateRoot(document.getElementById("root"), $jsx(SWRConfig, {
66
+ hydrateRoot(document.getElementById("root"), React.createElement(Page, {}, undefined, false, undefined, this));
71
- value: { suspense: true },
72
- children: $jsx(RouterProvider, {
73
- value: initialRouteValue,
74
- children: $jsx(Page, {}, undefined, false, undefined, this)
75
- }, undefined, false, undefined, this)
76
- }, undefined, false, undefined, this));
77
67
  `}}></script>
78
68
  </head>
79
69
  <body>
80
70
  <div id="root">
81
- <SWRConfig value={{ suspense: true }}>
82
- <RouterProvider value={initialRouteValue}>
83
- <Page />
71
+ <Page />
84
- </RouterProvider>
85
- </SWRConfig>
86
72
  </div>
87
73
  </body>
88
74
  </html >
@@ -126,26 +112,24 @@ export default {
126
112
  port: 3000,
127
113
  async fetch(req) {
128
114
  const url = new URL(req.url);
129
- console.log(req.method, url.pathname)
130
- if (url.pathname.includes("/components/") || url.pathname.includes("/containers/") || url.pathname.includes("/pages/")) {
115
+ if (url.pathname.includes("/components/") || url.pathname.includes("/containers/") || url.pathname.includes("/routes/")) {
131
116
  return sendFile(url);
132
117
  }
133
118
  if (url.pathname.includes("/assets/js")) {
134
119
  return renderJs(url);
135
120
  }
136
- if (url.pathname.includes("/api")) {
137
- return renderApi(url, req);
138
- }
139
121
  if (url.pathname.includes("/favicon")) {
140
122
  return new Response(`Not Found`, {
141
123
  headers: { 'Content-Type': 'text/html' },
142
124
  status: 404,
143
125
  });
144
126
  }
127
+ if (url.pathname.includes("/")) {
145
- return renderPage("./pages/index.jsx", url, {});
128
+ return renderPage("./routes/index/page.jsx", url, {});
146
- // const route = router.match(url.pathname);
147
- // if (route) {
129
+ }
148
- // return renderPage(route, url);
130
+ return new Response(`Not Found`, {
131
+ headers: { 'Content-Type': 'text/html' },
149
- // }
132
+ status: 404,
133
+ });
150
134
  },
151
135
  };
jsxPlugin.js CHANGED
@@ -24,9 +24,8 @@ plugin({
24
24
  const outputFile = path.join(outputFolder, filename);
25
25
  const jsxCode = readFileSync(args.path, "utf8");
26
26
  const code = await transpiler.transform(jsxCode);
27
- // console.log('code', code);
28
27
  if (!existsSync(outputFolder)) {
29
- mkdirSync(outputFolder);
28
+ mkdirSync(outputFolder, { recursive: true });
30
29
  }
31
30
  writeFileSync(outputFile, code);
32
31
  if (existsSync(cssFile)) {
package.json CHANGED
@@ -19,7 +19,6 @@
19
19
  "react": "18.2.0",
20
20
  "react-dom": "^18.2.0",
21
21
  "react-streaming": "^0.3.5",
22
- "swr": "^2.1.0",
23
22
  "zod": "^3.21.0"
24
23
  }
25
24
  }
router.js CHANGED
@@ -1,26 +1,72 @@
1
- import { createContext, useContext } from 'react';
1
+ import { useState, useEffect } from 'react';
2
2
 
3
- const Context = createContext(undefined)
4
-
5
- export const RouterProvider = ({ value, children }) => {
3
+ export const atom = (initial) => {
4
+ let value;
6
- return <Context.Provider value={value}>{children}</Context.Provider>
5
+ const isDerived = typeof initial === 'function';
6
+ const subs = new Set();
7
+ const get = (a) => {
8
+ a.subscribe(compute);
9
+ return a.getValue();
10
+ };
11
+ const compute = () => {
12
+ value = initial(get);
13
+ subs.forEach((sub) => {
14
+ sub(value);
15
+ });
16
+ };
17
+ if (isDerived) {
18
+ compute();
19
+ } else {
20
+ value = initial;
7
- }
21
+ }
8
-
9
- export const useRouter = () => {
10
- const state = useContext(Context);
11
22
  return {
12
- query: state.query,
13
- pathname: state.pathname,
14
- params: state.params,
15
- push: () => {
16
- },
17
- replace: () => {
23
+ getValue() {
24
+ return value;
18
25
  },
26
+ subscribe(fn) {
27
+ subs.add(fn);
19
- prefetch: () => {
28
+ return () => subs.delete(fn);
20
29
  },
30
+ update(fn) {
31
+ value = fn(value);
21
- beforePopState: () => {
32
+ subs.forEach((sub) => {
33
+ sub(value);
34
+ });
22
35
  },
36
+ };
37
+ };
38
+
39
+ export const useAtom = (atom) => {
40
+ const [data, setData] = useState(atom.getValue());
23
- back: () => { },
41
+ useEffect(() => {
42
+ return atom.subscribe((value) => {
43
+ setData(value);
44
+ });
45
+ }, []);
46
+ return data;
47
+ };
48
+
49
+ export const routerAtom = atom({
50
+ pathname: "/",
51
+ query: {},
52
+ params: {},
53
+ })
54
+
24
- reload: () => window.location.reload(),
55
+ export const useRouter = () => useAtom(routerAtom);
56
+
57
+ export const push = () => {
25
- }
58
+ }
59
+
60
+ export const replace = () => {
26
- }
61
+ }
62
+
63
+ export const prefetch = () => {
64
+ }
65
+
66
+ export const beforePopState = () => {
67
+ }
68
+
69
+ export const back = () => {
70
+ }
71
+
72
+ export const reload = () => window.location.reload()