~repos /edge-city
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
164eba9f
—
pyrossh 2 years ago
Fix hydration issue
- cli.js +11 -5
- example/package.json +3 -0
- example/scripts/migrate.js +1 -1
- example/src/pages/app.jsx +11 -0
- example/src/pages/todos/page.jsx +6 -8
- example/src/services/db.js +1 -1
- index.js +12 -5
- pnpm-lock.yaml +3 -0
- readme.md +0 -1
- renderPage.js +7 -4
cli.js
CHANGED
|
@@ -21,6 +21,7 @@ dotenv.config();
|
|
|
21
21
|
|
|
22
22
|
let isProd = false;
|
|
23
23
|
const srcDir = path.join(process.cwd(), "src");
|
|
24
|
+
const pagesDir = path.join(srcDir, "pages");
|
|
24
25
|
const inputStaticDir = path.join(srcDir, "static");
|
|
25
26
|
const buildDir = path.join(process.cwd(), "build");
|
|
26
27
|
const staticDir = path.join(buildDir, "static");
|
|
@@ -36,13 +37,13 @@ const recordSize = (buildStart, dest) => {
|
|
|
36
37
|
let generatedCss = ``;
|
|
37
38
|
const cssCache = [];
|
|
38
39
|
const serverEnvs = Object.keys(process.env)
|
|
39
|
-
.filter((k) => k.startsWith("
|
|
40
|
+
.filter((k) => k.startsWith("EDGE_") || k === "NODE_ENV")
|
|
40
41
|
.reduce((acc, k) => {
|
|
41
42
|
acc[`process.env.${k}`] = JSON.stringify(process.env[k]);
|
|
42
43
|
return acc
|
|
43
44
|
}, {});
|
|
44
45
|
const clientEnvs = Object.keys(process.env)
|
|
45
|
-
.filter((k) => k.startsWith("
|
|
46
|
+
.filter((k) => k.startsWith("EDGE_PUBLIC") || k === "NODE_ENV")
|
|
46
47
|
.reduce((acc, k) => {
|
|
47
48
|
acc[`process.env.${k}`] = JSON.stringify(process.env[k]);
|
|
48
49
|
return acc
|
|
@@ -94,7 +95,9 @@ const buildRouteMap = (routes) => {
|
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
const bundlePages = async () => {
|
|
97
|
-
const
|
|
98
|
+
const appExists = fs.existsSync(path.join(pagesDir, "app.jsx"));
|
|
99
|
+
const importAppComp = appExists ? `import App from "@/pages/app";` : ""
|
|
100
|
+
const routes = walkdir.sync(pagesDir)
|
|
98
101
|
.filter((p) => p.includes("page.jsx"))
|
|
99
102
|
.map((r) => ({
|
|
100
103
|
in: r,
|
|
@@ -111,11 +114,12 @@ const bundlePages = async () => {
|
|
|
111
114
|
const data = fs.readFileSync(args.path);
|
|
112
115
|
const newSrc = `
|
|
113
116
|
import renderPage from "edge-city/renderPage";
|
|
117
|
+
${importAppComp}
|
|
114
118
|
|
|
115
119
|
${data.toString()}
|
|
116
120
|
|
|
117
121
|
export function onRequest(context) {
|
|
118
|
-
return renderPage(Page, context.request);
|
|
122
|
+
return renderPage(Page, App, context.request);
|
|
119
123
|
}
|
|
120
124
|
`
|
|
121
125
|
return {
|
|
@@ -155,11 +159,13 @@ const bundlePages = async () => {
|
|
|
155
159
|
const data = fs.readFileSync(args.path);
|
|
156
160
|
const newSrc = `
|
|
157
161
|
import { hydrateApp } from "edge-city";
|
|
162
|
+
${importAppComp}
|
|
163
|
+
|
|
158
164
|
${data.toString()}
|
|
159
165
|
|
|
160
166
|
const searchParams = new URL(import.meta.url).searchParams;
|
|
161
167
|
if (searchParams.get("hydrate") === "true") {
|
|
162
|
-
hydrateApp()
|
|
168
|
+
hydrateApp(App)
|
|
163
169
|
}
|
|
164
170
|
`
|
|
165
171
|
return {
|
example/package.json
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
"type": "module",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "edge-city dev -p cloudflare",
|
|
6
|
+
"dev:wrangler": "cd build && wrangler pages dev static",
|
|
7
|
+
"deploy:wrangler": "cd build && wrangler pages deploy static",
|
|
6
8
|
"build": " edge-city build -p cloudflare",
|
|
7
9
|
"generate": "drizzle-kit generate:pg --out migrations --schema migrations/schema.js",
|
|
8
10
|
"migrate": "node scripts/migrate.js",
|
|
@@ -29,6 +31,7 @@
|
|
|
29
31
|
"postgres": "3.3.4",
|
|
30
32
|
"@playwright/test": "^1.31.2",
|
|
31
33
|
"eslint": "^8.35.0",
|
|
34
|
+
"wrangler": "3.0.1",
|
|
32
35
|
"eslint-config-react-app": "^7.0.1"
|
|
33
36
|
},
|
|
34
37
|
"prettier": {
|
example/scripts/migrate.js
CHANGED
|
@@ -6,7 +6,7 @@ dotenv.config();
|
|
|
6
6
|
|
|
7
7
|
const main = async () => {
|
|
8
8
|
console.log("migration started");
|
|
9
|
-
const client = postgres(process.env.
|
|
9
|
+
const client = postgres(process.env.EDGE_PG_CONN_URL + "?sslmode=require", { max: 1 });
|
|
10
10
|
await migrate(drizzle(client), { migrationsFolder: './migrations' });
|
|
11
11
|
console.log("migration complete");
|
|
12
12
|
}
|
example/src/pages/app.jsx
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SSRProvider } from 'react-aria';
|
|
2
|
+
|
|
3
|
+
const App = ({ children }) => {
|
|
4
|
+
return (
|
|
5
|
+
<SSRProvider>
|
|
6
|
+
{children}
|
|
7
|
+
</SSRProvider>
|
|
8
|
+
)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default App;
|
example/src/pages/todos/page.jsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { Suspense
|
|
1
|
+
import React, { Suspense } from 'react';
|
|
2
2
|
import { Helmet } from 'react-helmet-async';
|
|
3
3
|
import { useQuery, useMutation } from "edge-city";
|
|
4
4
|
import { useForm } from 'react-hook-form';
|
|
@@ -29,7 +29,6 @@ export default Page;
|
|
|
29
29
|
|
|
30
30
|
const TodoList = () => {
|
|
31
31
|
const { data, refetch } = useQuery("todos", () => getTodos());
|
|
32
|
-
console.log("data", data);
|
|
33
32
|
const { mutate, isMutating, err } = useMutation(async ({ text }) => {
|
|
34
33
|
await createTodo({
|
|
35
34
|
text,
|
|
@@ -38,12 +37,11 @@ const TodoList = () => {
|
|
|
38
37
|
await refetch();
|
|
39
38
|
});
|
|
40
39
|
const { register, handleSubmit, formState: { errors } } = useForm();
|
|
41
|
-
console.log('err', err, errors);
|
|
42
|
-
useEffect(() => {
|
|
40
|
+
// useEffect(() => {
|
|
43
|
-
|
|
41
|
+
// setTimeout(() => {
|
|
44
|
-
|
|
42
|
+
// refetch();
|
|
45
|
-
|
|
43
|
+
// }, 3000)
|
|
46
|
-
}, [])
|
|
44
|
+
// }, [])
|
|
47
45
|
return (
|
|
48
46
|
<div>
|
|
49
47
|
<ul>
|
example/src/services/db.js
CHANGED
|
@@ -2,7 +2,7 @@ import { drizzle } from 'drizzle-orm/neon-serverless';
|
|
|
2
2
|
import { Pool } from '@neondatabase/serverless';
|
|
3
3
|
import { highlight } from 'sql-highlight';
|
|
4
4
|
|
|
5
|
-
export const pool = new Pool({ connectionString: process.env.
|
|
5
|
+
export const pool = new Pool({ connectionString: process.env.EDGE_PG_CONN_URL });
|
|
6
6
|
const db = drizzle(pool, {
|
|
7
7
|
logger: {
|
|
8
8
|
logQuery: (query, params) => {
|
index.js
CHANGED
|
@@ -8,6 +8,7 @@ import { createBrowserHistory } from "history";
|
|
|
8
8
|
import { createRouter } from "radix3";
|
|
9
9
|
import routemap from '/routemap.json' assert {type: 'json'};
|
|
10
10
|
|
|
11
|
+
export const isProd = () => process.env.NODE_ENV === "production";
|
|
11
12
|
export const isClient = () => typeof window !== 'undefined';
|
|
12
13
|
export const domain = () => isClient() ? window.origin : "http://0.0.0.0:3000";
|
|
13
14
|
|
|
@@ -114,7 +115,7 @@ export const useMutation = (fn) => {
|
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
export const RouterContext = createContext(undefined);
|
|
117
|
-
export const RouterProvider = ({ router, history, rpcContext, helmetContext }) => {
|
|
118
|
+
export const RouterProvider = ({ router, history, rpcContext, helmetContext, App }) => {
|
|
118
119
|
const [_, startTransition] = useTransition();
|
|
119
120
|
const [pathname, setPathname] = useState(history.location.pathname);
|
|
120
121
|
const page = router.lookup(pathname) || router.lookup("/_404");
|
|
@@ -140,12 +141,14 @@ export const RouterProvider = ({ router, history, rpcContext, helmetContext }) =
|
|
|
140
141
|
fallback: _jsx("p", {}, "Oops something went wrong"),
|
|
141
142
|
children: _jsx(Suspense, {
|
|
142
143
|
fallback: _jsx("p", {}, "Loading..."),
|
|
144
|
+
children: _jsx(App, {
|
|
143
|
-
|
|
145
|
+
children: _jsx(page, {}),
|
|
146
|
+
})
|
|
144
147
|
}),
|
|
145
148
|
}),
|
|
146
149
|
}),
|
|
147
150
|
}),
|
|
148
|
-
})
|
|
151
|
+
})
|
|
149
152
|
}
|
|
150
153
|
|
|
151
154
|
export const useRouter = () => {
|
|
@@ -191,7 +194,10 @@ export const NavLink = ({ children, className, activeClassName, ...props }) => {
|
|
|
191
194
|
})
|
|
192
195
|
}
|
|
193
196
|
|
|
194
|
-
export const hydrateApp = async () => {
|
|
197
|
+
export const hydrateApp = async (App) => {
|
|
198
|
+
if (!isProd()) {
|
|
199
|
+
console.log("hydrating with", window._EDGE_DATA_);
|
|
200
|
+
}
|
|
195
201
|
const module = await import("react-dom/client");
|
|
196
202
|
const history = createBrowserHistory();
|
|
197
203
|
const router = createRouter({
|
|
@@ -205,7 +211,8 @@ export const hydrateApp = async () => {
|
|
|
205
211
|
module.default.hydrateRoot(root, _jsx(RouterProvider, {
|
|
206
212
|
history,
|
|
207
213
|
router,
|
|
208
|
-
rpcContext: window.
|
|
214
|
+
rpcContext: window._EDGE_DATA_ || {},
|
|
209
215
|
helmetContext: {},
|
|
216
|
+
App,
|
|
210
217
|
}));
|
|
211
218
|
}
|
pnpm-lock.yaml
CHANGED
|
@@ -141,6 +141,9 @@ importers:
|
|
|
141
141
|
postgres:
|
|
142
142
|
specifier: 3.3.4
|
|
143
143
|
version: 3.3.4
|
|
144
|
+
wrangler:
|
|
145
|
+
specifier: 3.0.1
|
|
146
|
+
version: 3.0.1
|
|
144
147
|
|
|
145
148
|
packages:
|
|
146
149
|
|
readme.md
CHANGED
|
@@ -45,7 +45,6 @@ which are compatible with these API's. Here is a list of some of them,
|
|
|
45
45
|
3. Add tests for runtime
|
|
46
46
|
4. Add E2E tests for example
|
|
47
47
|
5. Maybe move to vite for HMR goodness
|
|
48
|
-
6. Fix todos service not loading
|
|
49
48
|
|
|
50
49
|
### Todo[Cloudflare]
|
|
51
50
|
1. Fix 404/500 pages not routing on server
|
renderPage.js
CHANGED
|
@@ -101,7 +101,7 @@ const render = async (children, {
|
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
const renderPage = async (Page, req) => {
|
|
104
|
+
const renderPage = async (Page, App, req) => {
|
|
105
105
|
const url = new URL(req.url);
|
|
106
106
|
const history = createMemoryHistory({
|
|
107
107
|
initialEntries: [url.pathname + url.search],
|
|
@@ -137,6 +137,7 @@ const renderPage = async (Page, req) => {
|
|
|
137
137
|
router,
|
|
138
138
|
rpcContext,
|
|
139
139
|
helmetContext,
|
|
140
|
+
App,
|
|
140
141
|
}),
|
|
141
142
|
]
|
|
142
143
|
})
|
|
@@ -147,9 +148,11 @@ const renderPage = async (Page, req) => {
|
|
|
147
148
|
Object.keys(helmetContext.helmet)
|
|
148
149
|
.map((k) => helmetContext.helmet[k].toString())
|
|
149
150
|
.join(''),
|
|
150
|
-
|
|
151
|
+
injectOnEnd: () => {
|
|
152
|
+
return ''
|
|
151
|
-
|
|
153
|
+
+ `<script>window._EDGE_DATA_ = ${JSON.stringify(rpcContext)};</script>`
|
|
152
|
-
|
|
154
|
+
+ `<script type="module" src="/js${jsScript}.js?hydrate=true" defer></script>`
|
|
155
|
+
}
|
|
153
156
|
});
|
|
154
157
|
return new Response(stream, {
|
|
155
158
|
headers: { 'Content-Type': 'text/html' },
|