Skip to content

Commit ae1daec

Browse files
committed
feat: enhance server-side rendering and context management and initial props load
1 parent 713232a commit ae1daec

File tree

8 files changed

+77
-50
lines changed

8 files changed

+77
-50
lines changed

examples/basic/src/pages/about/about.module.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
font-weight: bold;
44
font-style: italic;
55
color: orange;
6-
}
6+
}
Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
1-
import { GetStaticPathsFunction } from "pranx";
1+
import { GetServerSidePropsFunction } from "pranx";
22
import { Header } from "src/components/Header";
33

4-
export default function ProductIdPage() {
4+
export default function ProductIdPage(props: { id: string; name: string }) {
55
return (
66
<div>
77
<Header />
88
<h1>Products id</h1>
9+
<div className={"flex flex-col gap-2 font-bold"}>
10+
<span>id: {props.id}</span>
11+
<span>Name: {props.name}</span>
12+
</div>
913
</div>
1014
);
1115
}
1216

13-
export const getStaticPaths: GetStaticPathsFunction<{ id: string; name: string }> = async () => {
17+
export const getServerSideProps: GetServerSidePropsFunction<{
18+
id: string;
19+
name: string;
20+
}> = async () => {
1421
return {
15-
paths: [{ params: { name: "Pedro", id: "1" } }, { params: { name: "Juan", id: "2" } }],
16-
fallback: false,
22+
id: Math.random().toFixed(10),
23+
name: "Pedro",
1724
};
1825
};
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
h2 {
22
color: blueviolet;
3-
}
3+
}
Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
11
@import "tailwindcss";
22

33
body {
4-
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
4+
font-family:
5+
system-ui,
6+
-apple-system,
7+
BlinkMacSystemFont,
8+
"Segoe UI",
9+
Roboto,
10+
"Helvetica Neue",
11+
Arial,
12+
"Noto Sans",
13+
sans-serif,
14+
"Apple Color Emoji",
15+
"Segoe UI Emoji",
16+
"Segoe UI Symbol",
17+
"Noto Color Emoji";
518
box-sizing: border-box;
619
background-color: #0c131a;
720
color: #9bafdc;
8-
}
21+
}
Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,52 @@
11
import { createContext } from "preact";
22
import type { PropsWithChildren } from "preact/compat";
3-
import { useContext, useState } from "preact/hooks";
3+
import { useCallback, useContext, useState } from "preact/hooks";
4+
import { find_current_route } from "./StartApp.js";
45

56
export type AppContext = {
67
_params: Record<string, string> | null;
78
_props: Record<string, any> | null;
89
set(key: "params" | "props", data: any): void;
910
};
1011

11-
const AppContextInstance = createContext<AppContext>({
12+
const _app_context = createContext<AppContext>({
1213
_params: {},
1314
_props: {},
1415
set() {},
1516
});
1617

1718
export const useAppContext = () => {
18-
const c = useContext(AppContextInstance);
19+
const c = useContext(_app_context);
1920
if (!c) {
2021
throw new Error("useAppContext must be used within a AppContextProvider");
2122
}
2223
return c;
2324
};
2425

2526
export const AppContextProvider = (props: PropsWithChildren) => {
26-
const [current_props, setCurrentProps] = useState<AppContext["_props"]>(null);
27+
const [current_props, setCurrentProps] = useState<AppContext["_props"]>(() => {
28+
const current_route = find_current_route();
29+
return current_route?.props || null;
30+
});
2731
const [current_params, setCurrentParams] = useState<AppContext["_params"]>(null);
2832

33+
const set: AppContext["set"] = useCallback((key, data) => {
34+
if (key === "params") {
35+
setCurrentParams(data);
36+
} else {
37+
setCurrentProps(data);
38+
}
39+
}, []);
40+
2941
return (
30-
<AppContextInstance.Provider
42+
<_app_context.Provider
3143
value={{
3244
_params: current_params,
3345
_props: current_props,
34-
set(key, data) {
35-
if (key === "params") {
36-
setCurrentParams(data);
37-
} else {
38-
setCurrentProps(data);
39-
}
40-
},
46+
set,
4147
}}
4248
>
4349
{props.children}
44-
</AppContextInstance.Provider>
50+
</_app_context.Provider>
4551
);
4652
};

packages/pranx/src/client/ServerSidePage.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@ export const ServerSidePage = (props: PropsWithChildren & { route_data: HydrateD
2222
signal: abortController.signal,
2323
});
2424

25-
setTimeout(() => {
26-
set("props", props_result.props);
27-
}, 2000);
25+
set("props", props_result.props);
2826
} catch (error) {
2927
if (!(error instanceof Error)) return;
3028

@@ -41,5 +39,5 @@ export const ServerSidePage = (props: PropsWithChildren & { route_data: HydrateD
4139
props_fetch_handler();
4240
}, []);
4341

44-
return cloneElement(child as VNode, _props || props.route_data.props);
42+
return cloneElement(child as VNode, _props ?? props.route_data.props);
4543
};

packages/pranx/src/client/StartApp.tsx

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,13 @@ import { ServerSidePage } from "./ServerSidePage.js";
99
let headUsed: ReturnType<typeof useHead> | null = null;
1010

1111
export function StartApp() {
12-
const HYDRATE_DATA = window.__PRANX_HYDRATE_DATA__;
13-
1412
return (
1513
<AppContextProvider>
1614
<LocationProvider>
1715
<ErrorBoundary onError={console.error}>
1816
<Router
1917
onRouteChange={() => {
20-
let current_route: HydrateDataRoute | null = null;
21-
22-
for (const r of window.__PRANX_HYDRATE_DATA__.routes) {
23-
const exec_result = exec_route_match(
24-
window.location.pathname,
25-
r.path_parsed_for_routing
26-
);
27-
28-
if (exec_result) {
29-
current_route = r;
30-
break;
31-
}
32-
}
18+
const current_route = find_current_route();
3319

3420
const head_css_config_links = current_route?.css.map((p) => {
3521
return {
@@ -49,7 +35,7 @@ export function StartApp() {
4935
}
5036
}}
5137
>
52-
{HYDRATE_DATA.routes.map((r) => {
38+
{window.__PRANX_HYDRATE_DATA__.routes.map((r) => {
5339
const Page = lazy(() => import(r.module));
5440

5541
return (
@@ -92,3 +78,18 @@ export function StartApp() {
9278
</AppContextProvider>
9379
);
9480
}
81+
82+
export const find_current_route = () => {
83+
let current_route: HydrateDataRoute | null = null;
84+
85+
for (const r of window.__PRANX_HYDRATE_DATA__.routes) {
86+
const exec_result = exec_route_match(window.location.pathname, r.path_parsed_for_routing);
87+
88+
if (exec_result) {
89+
current_route = r;
90+
break;
91+
}
92+
}
93+
94+
return current_route;
95+
};

packages/pranx/src/cmd/start.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ export async function start() {
3838
const file_absolute = resolve(join(OUTPUT_BUNDLE_SERVER_DIR, "pages", route.module));
3939
const { default: page, getServerSideProps } = (await import(file_absolute)) as PageModule;
4040

41+
const hydrate_data = (await fse.readJSON(SITE_MANIFEST_OUTPUT_PATH)) as HYDRATE_DATA;
42+
4143
app.on(
4244
"GET",
4345
filePathToRoutingPath(route.path, false),
@@ -49,30 +51,30 @@ export async function start() {
4951
const params = new URLSearchParams(url_parsed.search);
5052
const return_only_props = Boolean(params.get("props"));
5153

52-
let props = {};
54+
let props_to_return = {};
5355

5456
if (getServerSideProps) {
55-
props = await getServerSideProps();
57+
props_to_return = await getServerSideProps();
5658
}
5759

5860
if (return_only_props) {
5961
return {
60-
props,
62+
props: props_to_return,
6163
};
6264
}
6365

64-
const hydrate_data = (await fse.readJSON(SITE_MANIFEST_OUTPUT_PATH)) as HYDRATE_DATA;
65-
66-
const target_route = hydrate_data.routes.find((r) => r.path === route.path);
66+
const target_route_index = hydrate_data.routes.findIndex((r) => r.path === route.path);
6767

68-
if (!target_route) {
68+
if (target_route_index === -1 || !hydrate_data.routes[target_route_index]) {
6969
logger.error(`Route not found in hydrate data: ${route.path}`);
7070
event.res.status = 500;
7171
return html(event, "Internal Server Error");
7272
}
7373

74+
hydrate_data.routes[target_route_index].props = props_to_return;
75+
7476
const page_prerendered = await renderToStringAsync(
75-
h(server_entry_module?.default || Fragment, {}, h(page, props, null))
77+
h(server_entry_module?.default || Fragment, {}, h(page, props_to_return, null))
7678
);
7779

7880
const html_string = generate_html_template({

0 commit comments

Comments
 (0)