~repos /edge-city
git clone
https://pyrossh.dev/repos/edge-city.git
Discussions:
https://groups.google.com/g/rust-embed-devs
edge-city is a next level meta-framework for react that runs only on edge runtimes
update app
- example/src/components/Counter/Counter.jsx +2 -2
- example/src/pages/app.jsx +14 -6
- lib/bin/cli.js +1 -1
- lib/{client/hydratePage.js → hydratePage.js} +7 -7
- lib/index.js +0 -29
- lib/index.test.js +0 -15
- lib/link.js +1 -1
- lib/package.json +1 -1
- lib/router.js +13 -21
- lib/server/renderPage.js +8 -10
example/src/components/Counter/Counter.jsx
CHANGED
|
@@ -8,11 +8,11 @@ const Counter = () => {
|
|
|
8
8
|
const decrement = () => setCount(count + 1);
|
|
9
9
|
return (
|
|
10
10
|
<div className="counter">
|
|
11
|
-
<Button
|
|
11
|
+
<Button onPress={increment}>
|
|
12
12
|
-
|
|
13
13
|
</Button>
|
|
14
14
|
<span className="count">{count}</span>
|
|
15
|
-
<Button
|
|
15
|
+
<Button onPress={decrement}>
|
|
16
16
|
+
|
|
17
17
|
</Button>
|
|
18
18
|
</div>
|
example/src/pages/app.jsx
CHANGED
|
@@ -1,15 +1,23 @@
|
|
|
1
|
+
import { Suspense } from "react";
|
|
1
2
|
import { SSRProvider } from "react-aria";
|
|
3
|
+
import { HelmetProvider } from 'react-helmet-async';
|
|
4
|
+
import { usePage } from "edge-city/router";
|
|
2
5
|
import Layout from "@/components/Layout/Layout";
|
|
3
6
|
import "./normalize.css";
|
|
4
7
|
import "./spectrum.css";
|
|
5
8
|
import "./app.css";
|
|
6
9
|
|
|
7
|
-
export default function App({
|
|
10
|
+
export default function App({ helmetContext }) {
|
|
11
|
+
const Page = usePage();
|
|
8
12
|
return (
|
|
13
|
+
<HelmetProvider context={helmetContext}>
|
|
9
|
-
|
|
14
|
+
<SSRProvider>
|
|
10
|
-
|
|
15
|
+
<Layout>
|
|
16
|
+
<Suspense fallback={<p>Routing....</p>}>
|
|
11
|
-
|
|
17
|
+
<Page />
|
|
18
|
+
</Suspense>
|
|
12
|
-
|
|
19
|
+
</Layout>
|
|
13
|
-
|
|
20
|
+
</SSRProvider>
|
|
21
|
+
</HelmetProvider>
|
|
14
22
|
);
|
|
15
23
|
}
|
lib/bin/cli.js
CHANGED
|
@@ -156,7 +156,7 @@ 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 hydratePage from "edge-city/
|
|
159
|
+
import hydratePage from "edge-city/hydratePage";
|
|
160
160
|
${importAppComp}
|
|
161
161
|
|
|
162
162
|
${data.toString()}
|
lib/{client/hydratePage.js → hydratePage.js}
RENAMED
|
@@ -3,7 +3,7 @@ import { hydrateRoot } from "react-dom/client";
|
|
|
3
3
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
4
4
|
import { createBrowserHistory } from "history";
|
|
5
5
|
import { createRouter } from "radix3";
|
|
6
|
-
import {
|
|
6
|
+
import { state } from "./router";
|
|
7
7
|
import routemap from '/routemap.json' assert {type: 'json'};
|
|
8
8
|
|
|
9
9
|
const hydratePage = (App) => {
|
|
@@ -15,13 +15,13 @@ const hydratePage = (App) => {
|
|
|
15
15
|
return acc;
|
|
16
16
|
}, {}),
|
|
17
17
|
});
|
|
18
|
-
const root = document.getElementById("root");
|
|
19
|
-
hydrateRoot(root, _jsx(RouterProvider, {
|
|
20
|
-
|
|
18
|
+
state.set({
|
|
21
19
|
router,
|
|
22
|
-
|
|
20
|
+
history,
|
|
23
21
|
helmetContext: {},
|
|
24
|
-
App,
|
|
25
|
-
})
|
|
22
|
+
})
|
|
23
|
+
const root = document.getElementById("root");
|
|
24
|
+
hydrateRoot(root, _jsx(App, { helmetContext: {} }));
|
|
26
25
|
}
|
|
26
|
+
|
|
27
27
|
export default hydratePage;
|
lib/index.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
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
|
-
}
|
lib/index.test.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
describe('A thing', () => {
|
|
2
|
-
it('should work', () => {
|
|
3
|
-
expect(1).toEqual(1);
|
|
4
|
-
});
|
|
5
|
-
|
|
6
|
-
it('should be ok', () => {
|
|
7
|
-
expect(1).toEqual(1);
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
describe('a nested thing', () => {
|
|
11
|
-
it('should work', () => {
|
|
12
|
-
expect(1).toEqual(1);
|
|
13
|
-
});
|
|
14
|
-
});
|
|
15
|
-
});
|
lib/link.js
CHANGED
|
@@ -20,7 +20,7 @@ const Link = (props) => {
|
|
|
20
20
|
})
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export const
|
|
23
|
+
export const StyleLink = ({ children, className, activeClassName, ...props }) => {
|
|
24
24
|
const { pathname } = useRouter();
|
|
25
25
|
const classNames = pathname === props.href ? [activeClassName, className] : [className];
|
|
26
26
|
return _jsx(Link, {
|
lib/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "edge-city",
|
|
3
3
|
"version": "0.5.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "client",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">= 20"
|
|
8
8
|
},
|
lib/router.js
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
useState, useEffect, useTransition
|
|
3
3
|
} from "react";
|
|
4
4
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
5
|
-
import { HelmetProvider } from 'react-helmet-async';
|
|
6
5
|
|
|
6
|
+
export let state = {
|
|
7
|
+
get: () => globalThis._EDGE_ROUTER_STATE_,
|
|
8
|
+
set: (v) => {
|
|
9
|
+
globalThis._EDGE_ROUTER_STATE_ = v;
|
|
10
|
+
},
|
|
11
|
+
}
|
|
12
|
+
|
|
7
|
-
export const
|
|
13
|
+
export const usePage = () => {
|
|
8
|
-
|
|
14
|
+
const { history, router } = state.get();
|
|
9
15
|
const [_, startTransition] = useTransition();
|
|
10
16
|
const [pathname, setPathname] = useState(history.location.pathname);
|
|
11
|
-
const
|
|
17
|
+
const Page = router.lookup(pathname) || router.lookup("/_404");
|
|
12
18
|
useEffect(() => {
|
|
13
19
|
return history.listen(({ location }) => {
|
|
14
20
|
// this causes 2 renders to happen but stops jitter or flash due to React.lazy
|
|
@@ -17,25 +23,11 @@ export const RouterProvider = ({ router, history, helmetContext, App }) => {
|
|
|
17
23
|
})
|
|
18
24
|
});
|
|
19
25
|
}, []);
|
|
20
|
-
return _jsx(RouterContext.Provider, {
|
|
21
|
-
|
|
26
|
+
return Page;
|
|
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
27
|
}
|
|
36
28
|
|
|
37
29
|
export const useRouter = () => {
|
|
38
|
-
const { history, params } =
|
|
30
|
+
const { history, params } = state.get();
|
|
39
31
|
return {
|
|
40
32
|
pathname: history.location.pathname,
|
|
41
33
|
query: new URLSearchParams(history.location.search),
|
lib/server/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 {
|
|
8
|
+
import { state } from "../router";
|
|
9
9
|
|
|
10
10
|
const stringToStream = (str) => {
|
|
11
11
|
return new ReadableStream({
|
|
@@ -116,6 +116,11 @@ const renderPage = async (Page, App, req) => {
|
|
|
116
116
|
const jsScript = url.pathname === "/" ? "/index" : url.pathname;
|
|
117
117
|
const helmetContext = {};
|
|
118
118
|
globalThis._EDGE_DATA_ = { data: {}, subs: {} };
|
|
119
|
+
state.set({
|
|
120
|
+
router,
|
|
121
|
+
history,
|
|
122
|
+
helmetContext,
|
|
123
|
+
})
|
|
119
124
|
const stream = await render(
|
|
120
125
|
_jsxs("html", {
|
|
121
126
|
lang: "en",
|
|
@@ -129,16 +134,9 @@ const renderPage = async (Page, App, req) => {
|
|
|
129
134
|
]
|
|
130
135
|
}),
|
|
131
136
|
_jsx("body", {
|
|
132
|
-
children:
|
|
137
|
+
children: _jsx("div", {
|
|
133
138
|
id: "root",
|
|
134
|
-
children: [
|
|
135
|
-
_jsx(RouterProvider, {
|
|
136
|
-
history,
|
|
137
|
-
router,
|
|
138
|
-
|
|
139
|
+
children: _jsx(App, { helmetContext }),
|
|
139
|
-
App,
|
|
140
|
-
}),
|
|
141
|
-
]
|
|
142
140
|
})
|
|
143
141
|
})]
|
|
144
142
|
}), {
|