rasengan 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/lib/cli/dirname.d.ts +2 -0
- package/lib/cli/dirname.js +6 -0
- package/lib/cli/dirname.js.map +1 -0
- package/lib/cli/index.d.ts +2 -0
- package/lib/cli/index.js +101 -0
- package/lib/cli/index.js.map +1 -0
- package/lib/config/index.d.ts +39 -0
- package/lib/config/index.js +57 -0
- package/lib/config/index.js.map +1 -0
- package/lib/config/type.d.ts +68 -0
- package/lib/config/type.js +2 -0
- package/lib/config/type.js.map +1 -0
- package/lib/core/components/index.d.ts +26 -0
- package/lib/core/components/index.js +72 -0
- package/lib/core/components/index.js.map +1 -0
- package/lib/core/index.d.ts +2 -0
- package/lib/core/index.js +2 -0
- package/lib/core/index.js.map +1 -0
- package/lib/core/interfaces.d.ts +76 -0
- package/lib/core/interfaces.js +91 -0
- package/lib/core/interfaces.js.map +1 -0
- package/lib/core/types.d.ts +45 -0
- package/lib/core/types.js +2 -0
- package/lib/core/types.js.map +1 -0
- package/lib/decorators/index.d.ts +2 -0
- package/lib/decorators/index.js +3 -0
- package/lib/decorators/index.js.map +1 -0
- package/lib/decorators/route.d.ts +7 -0
- package/lib/decorators/route.js +25 -0
- package/lib/decorators/route.js.map +1 -0
- package/lib/decorators/router.d.ts +7 -0
- package/lib/decorators/router.js +24 -0
- package/lib/decorators/router.js.map +1 -0
- package/lib/decorators/types.d.ts +47 -0
- package/lib/decorators/types.js +2 -0
- package/lib/decorators/types.js.map +1 -0
- package/lib/entries/entry-client.d.ts +1 -0
- package/lib/entries/entry-client.js +15 -0
- package/lib/entries/entry-client.js.map +1 -0
- package/lib/entries/entry-server.d.ts +6 -0
- package/lib/entries/entry-server.js +20 -0
- package/lib/entries/entry-server.js.map +1 -0
- package/lib/hooks/index.d.ts +0 -0
- package/lib/hooks/index.js +1 -0
- package/lib/hooks/index.js.map +1 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.js +10 -0
- package/lib/index.js.map +1 -0
- package/lib/routing/components/index.d.ts +32 -0
- package/lib/routing/components/index.js +69 -0
- package/lib/routing/components/index.js.map +1 -0
- package/lib/routing/index.d.ts +3 -0
- package/lib/routing/index.js +4 -0
- package/lib/routing/index.js.map +1 -0
- package/lib/routing/interfaces.d.ts +67 -0
- package/lib/routing/interfaces.js +88 -0
- package/lib/routing/interfaces.js.map +1 -0
- package/lib/routing/types.d.ts +4 -0
- package/lib/routing/types.js +2 -0
- package/lib/routing/types.js.map +1 -0
- package/lib/routing/utils/index.d.ts +40 -0
- package/lib/routing/utils/index.js +256 -0
- package/lib/routing/utils/index.js.map +1 -0
- package/lib/server/functions/vercel/api/index.d.ts +2 -0
- package/lib/server/functions/vercel/api/index.js +91 -0
- package/lib/server/functions/vercel/api/index.js.map +1 -0
- package/lib/server/functions/vercel/vercel.json +12 -0
- package/lib/server/utils/createFetchRequest.d.ts +5 -0
- package/lib/server/utils/createFetchRequest.js +34 -0
- package/lib/server/utils/createFetchRequest.js.map +1 -0
- package/lib/server/utils/getIp.d.ts +1 -0
- package/lib/server/utils/getIp.js +30 -0
- package/lib/server/utils/getIp.js.map +1 -0
- package/lib/server/utils/handleError.d.ts +2 -0
- package/lib/server/utils/handleError.js +28 -0
- package/lib/server/utils/handleError.js.map +1 -0
- package/lib/server/utils/index.d.ts +5 -0
- package/lib/server/utils/index.js +8 -0
- package/lib/server/utils/index.js.map +1 -0
- package/lib/server/utils/log.d.ts +6 -0
- package/lib/server/utils/log.js +69 -0
- package/lib/server/utils/log.js.map +1 -0
- package/package.json +75 -0
- package/server.js +229 -0
- package/src/cli/dirname.ts +7 -0
- package/src/cli/index.ts +134 -0
- package/src/config/index.ts +67 -0
- package/src/config/type.ts +76 -0
- package/src/core/components/index.tsx +111 -0
- package/src/core/index.ts +14 -0
- package/src/core/interfaces.tsx +129 -0
- package/src/core/types.ts +43 -0
- package/src/decorators/index.ts +2 -0
- package/src/decorators/route.ts +32 -0
- package/src/decorators/router.ts +30 -0
- package/src/decorators/types.ts +54 -0
- package/src/entries/entry-client.tsx +33 -0
- package/src/entries/entry-server.tsx +50 -0
- package/src/hooks/index.ts +0 -0
- package/src/index.ts +11 -0
- package/src/routing/components/index.tsx +125 -0
- package/src/routing/index.ts +23 -0
- package/src/routing/interfaces.ts +105 -0
- package/src/routing/types.ts +3 -0
- package/src/routing/utils/index.tsx +342 -0
- package/src/server/functions/vercel/api/index.ts +122 -0
- package/src/server/functions/vercel/vercel.json +12 -0
- package/src/server/utils/createFetchRequest.ts +40 -0
- package/src/server/utils/getIp.ts +37 -0
- package/src/server/utils/handleError.ts +36 -0
- package/src/server/utils/index.ts +15 -0
- package/src/server/utils/log.ts +115 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +30 -0
- package/tsconfig.lib.json +41 -0
- package/tsconfig.node.json +11 -0
- package/vite.config.ts +45 -0
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { RouterComponent } from "../interfaces.js";
|
|
2
|
+
import { RouterProvider, createBrowserRouter } from "react-router-dom";
|
|
3
|
+
import {
|
|
4
|
+
RouteDecoratorProps,
|
|
5
|
+
RouteLayoutDecoratorProps,
|
|
6
|
+
RouterDecoratorProps,
|
|
7
|
+
} from "../../decorators/types.js";
|
|
8
|
+
import { DefaultLayout, LayoutComponent, PageComponent } from "../../index.js";
|
|
9
|
+
import {
|
|
10
|
+
ClientComponent,
|
|
11
|
+
NotFoundComponentContainer,
|
|
12
|
+
NotFoundPageComponent,
|
|
13
|
+
ServerComponent,
|
|
14
|
+
} from "../components/index.js";
|
|
15
|
+
import { RouteErrorBoundary as ErrorBoundary } from "../../core/components/index.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* This function receives a router component and get a formated router first
|
|
19
|
+
* and then return a router.
|
|
20
|
+
*/
|
|
21
|
+
export const getRouter = (router: RouterComponent) => {
|
|
22
|
+
const routes = generateBrowserRoutes(router);
|
|
23
|
+
|
|
24
|
+
let Router = createBrowserRouter(routes);
|
|
25
|
+
|
|
26
|
+
return () => (
|
|
27
|
+
<RouterProvider fallbackElement={<>Not Found</>} router={Router} />
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* This function receives a router component and return a formated router.
|
|
33
|
+
*/
|
|
34
|
+
const generateBrowserRoutes = (router: RouterComponent, isRoot = true) => {
|
|
35
|
+
// Initialization of the list of routes
|
|
36
|
+
const routes = [] as any;
|
|
37
|
+
|
|
38
|
+
// Get information about the layout and the path
|
|
39
|
+
const layout = router.layout;
|
|
40
|
+
const LayoutToRender = layout.render;
|
|
41
|
+
|
|
42
|
+
const route = {
|
|
43
|
+
path: layout.path,
|
|
44
|
+
elementError: <ErrorBoundary />,
|
|
45
|
+
element: <LayoutToRender />,
|
|
46
|
+
children: [] as unknown as any,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Defining the page not found route
|
|
50
|
+
if (isRoot) {
|
|
51
|
+
routes.push({
|
|
52
|
+
path: "*",
|
|
53
|
+
element: (
|
|
54
|
+
<NotFoundComponentContainer content={router.notFoundComponent} />
|
|
55
|
+
),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Get informations about pages
|
|
60
|
+
const pages = router.pages.map((page) => {
|
|
61
|
+
// Get the path of the page
|
|
62
|
+
const path = page.path === "/" ? layout.path : page.path;
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
path,
|
|
66
|
+
loader: async ({ params, request }: any) => {
|
|
67
|
+
// Get the response from the loader
|
|
68
|
+
const response = await page.loader({ params, request });
|
|
69
|
+
|
|
70
|
+
// Handle redirection
|
|
71
|
+
if (response.redirect) {
|
|
72
|
+
const formData = new FormData();
|
|
73
|
+
|
|
74
|
+
formData.append("redirect", response.redirect);
|
|
75
|
+
|
|
76
|
+
return new Response(formData, {
|
|
77
|
+
status: 302,
|
|
78
|
+
headers: {
|
|
79
|
+
Location: response.redirect,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return response;
|
|
85
|
+
},
|
|
86
|
+
element: (
|
|
87
|
+
<ClientComponent page={page} loader={router.loaderComponent({})} />
|
|
88
|
+
),
|
|
89
|
+
elementError: <ErrorBoundary />,
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Add pages into children of the current route
|
|
94
|
+
pages.forEach((page) => {
|
|
95
|
+
route.children.push(page);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Loop throug sub routers in order to apply the same thing.
|
|
99
|
+
// for (const SubRouter of router.routers) {
|
|
100
|
+
// const subRouter = new SubRouter();
|
|
101
|
+
|
|
102
|
+
// const subRoutes = generateBrowserRoutes(subRouter);
|
|
103
|
+
|
|
104
|
+
// // Add sub routes into the lists of route
|
|
105
|
+
// route.children.push(subRoutes);
|
|
106
|
+
// }
|
|
107
|
+
|
|
108
|
+
routes.push(route);
|
|
109
|
+
|
|
110
|
+
// Loop throug besides routers in order to apply the same thing.
|
|
111
|
+
for (const besideRouter of router.routers) {
|
|
112
|
+
const besidesRoutes = generateBrowserRoutes(besideRouter, false);
|
|
113
|
+
|
|
114
|
+
// Add besides routes into the lists of route
|
|
115
|
+
routes.push(...besidesRoutes);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Return the formated router
|
|
119
|
+
return routes;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* This function receives a router component and return a formated router for static routing
|
|
124
|
+
* @param router Represents the router component
|
|
125
|
+
* @returns
|
|
126
|
+
*/
|
|
127
|
+
export const generateStaticRoutes = (
|
|
128
|
+
router: RouterComponent,
|
|
129
|
+
isRoot = true
|
|
130
|
+
) => {
|
|
131
|
+
// Initialization of the list of routes
|
|
132
|
+
const routes = [] as any;
|
|
133
|
+
|
|
134
|
+
// Get information about the layout and the path
|
|
135
|
+
const layout = router.layout;
|
|
136
|
+
const LayoutToRender = layout.render;
|
|
137
|
+
|
|
138
|
+
const route = {
|
|
139
|
+
path: layout.path,
|
|
140
|
+
elementError: <ErrorBoundary />,
|
|
141
|
+
element: <LayoutToRender />,
|
|
142
|
+
children: [] as unknown as any,
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// Defining the page not found route
|
|
146
|
+
if (isRoot) {
|
|
147
|
+
routes.push({
|
|
148
|
+
path: "*",
|
|
149
|
+
element: (
|
|
150
|
+
<NotFoundComponentContainer content={router.notFoundComponent} />
|
|
151
|
+
),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Get informations about pages
|
|
156
|
+
const pages = router.pages.map((page) => {
|
|
157
|
+
// Get the path of the page
|
|
158
|
+
const path = page.path === "/" ? layout.path : page.path;
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
path,
|
|
162
|
+
async loader({ params, request }: any) {
|
|
163
|
+
// Get the response from the loader
|
|
164
|
+
const response = await page.loader({ params, request });
|
|
165
|
+
|
|
166
|
+
// Handle redirection
|
|
167
|
+
if (response.redirect) {
|
|
168
|
+
const formData = new FormData();
|
|
169
|
+
|
|
170
|
+
formData.append("redirect", response.redirect);
|
|
171
|
+
|
|
172
|
+
return new Response(formData, {
|
|
173
|
+
status: 302,
|
|
174
|
+
headers: {
|
|
175
|
+
Location: response.redirect,
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return response;
|
|
181
|
+
},
|
|
182
|
+
Component() {
|
|
183
|
+
return (
|
|
184
|
+
<ServerComponent page={page} loader={router.loaderComponent({})} />
|
|
185
|
+
);
|
|
186
|
+
},
|
|
187
|
+
elementError: <ErrorBoundary />,
|
|
188
|
+
};
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Add pages into children of the current route
|
|
192
|
+
pages.forEach((page) => {
|
|
193
|
+
route.children.push(page);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Loop throug sub routers in order to apply the same thing.
|
|
197
|
+
// for (const SubRouter of router.routers) {
|
|
198
|
+
// const subRouter = new SubRouter();
|
|
199
|
+
// const subRoutes = generateStaticRoutes(subRouter);
|
|
200
|
+
|
|
201
|
+
// // Add sub routes into the lists of route
|
|
202
|
+
// route.children.push(subRoutes);
|
|
203
|
+
// }
|
|
204
|
+
|
|
205
|
+
routes.push(route);
|
|
206
|
+
|
|
207
|
+
// Loop throug besides routers in order to apply the same thing.
|
|
208
|
+
for (const besideRouter of router.routers) {
|
|
209
|
+
const besidesRoutes = generateStaticRoutes(besideRouter, false);
|
|
210
|
+
|
|
211
|
+
// Add besides routes into the lists of route
|
|
212
|
+
routes.push(...besidesRoutes);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Return the formated router
|
|
216
|
+
return routes;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* This function receives a router component and extract all metadatas of all pages
|
|
221
|
+
* and put all of them inside a map function in order to be used to enhance ssr
|
|
222
|
+
*/
|
|
223
|
+
export const extractPageMetadata = (router: RouterComponent) => {
|
|
224
|
+
// Initialisation of the Map of metadata
|
|
225
|
+
const metadatas = new Map<string, { title: string; description: string }>();
|
|
226
|
+
|
|
227
|
+
// Get base url
|
|
228
|
+
const baseURL = router.layout.path;
|
|
229
|
+
|
|
230
|
+
// Get informations about pages of the main router
|
|
231
|
+
router.pages.forEach((page) => {
|
|
232
|
+
// Add the first slash if not exists from the page path
|
|
233
|
+
const pagePath = page.path[0] === "/" ? page.path : "/" + page.path;
|
|
234
|
+
|
|
235
|
+
// Remove the last slash if exists from the base url
|
|
236
|
+
const finalBaseURL =
|
|
237
|
+
baseURL === "/"
|
|
238
|
+
? baseURL
|
|
239
|
+
: baseURL[baseURL.length - 1] === "/"
|
|
240
|
+
? baseURL.slice(0, -1)
|
|
241
|
+
: baseURL;
|
|
242
|
+
|
|
243
|
+
// Get the path of the page
|
|
244
|
+
const path =
|
|
245
|
+
pagePath === "/"
|
|
246
|
+
? finalBaseURL
|
|
247
|
+
: finalBaseURL === "/"
|
|
248
|
+
? pagePath
|
|
249
|
+
: finalBaseURL + "/" + pagePath;
|
|
250
|
+
|
|
251
|
+
// Add metadata
|
|
252
|
+
metadatas.set(path, {
|
|
253
|
+
title: page.title,
|
|
254
|
+
description: page.description,
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Loop through the others routers recursively
|
|
259
|
+
for (let besidesRouter of router.routers) {
|
|
260
|
+
const data = extractPageMetadata(besidesRouter);
|
|
261
|
+
|
|
262
|
+
// Copy metadata from data to metadatas map
|
|
263
|
+
data.forEach((value, key) => {
|
|
264
|
+
metadatas.set(key, value);
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return metadatas;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* This function adds metadata to a page or a layout
|
|
273
|
+
* @param option
|
|
274
|
+
* @returns
|
|
275
|
+
*/
|
|
276
|
+
export const defineRoutePage = (option: RouteDecoratorProps) => {
|
|
277
|
+
const { path, title, description } = option;
|
|
278
|
+
|
|
279
|
+
return (Component: new () => PageComponent) => {
|
|
280
|
+
if (!path) throw new Error("You must provide a path to the page");
|
|
281
|
+
|
|
282
|
+
// Create a new instance of the component
|
|
283
|
+
const component = new Component();
|
|
284
|
+
|
|
285
|
+
// Set properties
|
|
286
|
+
component.path = path;
|
|
287
|
+
component.title = title || Component.name;
|
|
288
|
+
component.description = description || "";
|
|
289
|
+
|
|
290
|
+
return component;
|
|
291
|
+
};
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* This function adds metadata to a page or a layout
|
|
296
|
+
* @param option
|
|
297
|
+
* @returns
|
|
298
|
+
*/
|
|
299
|
+
export const defineRouteLayout = (option: RouteLayoutDecoratorProps) => {
|
|
300
|
+
const { path } = option;
|
|
301
|
+
|
|
302
|
+
return (Component: new () => LayoutComponent) => {
|
|
303
|
+
if (!path) throw new Error("You must provide a path to the layout");
|
|
304
|
+
|
|
305
|
+
// Create a new instance of the component
|
|
306
|
+
const component = new Component();
|
|
307
|
+
|
|
308
|
+
// Set properties
|
|
309
|
+
component.path = path;
|
|
310
|
+
|
|
311
|
+
return component;
|
|
312
|
+
};
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* This function adds metadata to a router
|
|
317
|
+
* @param option
|
|
318
|
+
* @returns
|
|
319
|
+
*/
|
|
320
|
+
export const defineRouter = (option: RouterDecoratorProps) => {
|
|
321
|
+
const { imports, layout, pages, loaderComponent, notFoundComponent } = option;
|
|
322
|
+
|
|
323
|
+
return (Component: new () => RouterComponent) => {
|
|
324
|
+
// Handle errors
|
|
325
|
+
if (!option.pages)
|
|
326
|
+
throw new Error(
|
|
327
|
+
"You must provide a list of pages in the router decorator"
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
// Create router
|
|
331
|
+
const router = new Component();
|
|
332
|
+
|
|
333
|
+
// Set properties
|
|
334
|
+
router.routers = imports || [];
|
|
335
|
+
router.layout = layout || new DefaultLayout();
|
|
336
|
+
router.pages = pages;
|
|
337
|
+
router.loaderComponent = loaderComponent || (() => null);
|
|
338
|
+
router.notFoundComponent = notFoundComponent || NotFoundPageComponent;
|
|
339
|
+
|
|
340
|
+
return router;
|
|
341
|
+
};
|
|
342
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path, { join, dirname } from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import type { VercelRequest, VercelResponse } from "@vercel/node";
|
|
5
|
+
import {
|
|
6
|
+
StaticHandlerContext,
|
|
7
|
+
createStaticHandler,
|
|
8
|
+
createStaticRouter,
|
|
9
|
+
} from "react-router-dom/server.js";
|
|
10
|
+
// @ts-ignore
|
|
11
|
+
import { createFetchRequest } from "rasengan";
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = dirname(__filename);
|
|
15
|
+
|
|
16
|
+
// Create server for production only
|
|
17
|
+
export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|
18
|
+
try {
|
|
19
|
+
// Get URL
|
|
20
|
+
const url = req.url;
|
|
21
|
+
const host = req.headers.host;
|
|
22
|
+
|
|
23
|
+
// Get app path
|
|
24
|
+
const appPath = join(__dirname, "..");
|
|
25
|
+
|
|
26
|
+
// ! Favicon Fix
|
|
27
|
+
if (url === "/favicon.ico") {
|
|
28
|
+
return res.send(path.resolve(join(appPath, "dist/client/rasengan.png")));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ! Robots Fix
|
|
32
|
+
if (url === "/robots.txt") {
|
|
33
|
+
return res.send(`
|
|
34
|
+
user-agent: *
|
|
35
|
+
disallow: /downloads/
|
|
36
|
+
disallow: /private/
|
|
37
|
+
allow: /
|
|
38
|
+
|
|
39
|
+
user-agent: magicsearchbot
|
|
40
|
+
disallow: /uploads/
|
|
41
|
+
`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ! Sitemap Fix
|
|
45
|
+
if (url === "/sitemap.xml") {
|
|
46
|
+
return res.send(path.resolve(join(appPath, "dist/client/sitemap.xml")));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ! Manifest Fix
|
|
50
|
+
if (url === "/manifest.json") {
|
|
51
|
+
return res.send(path.resolve(join(appPath, "dist/client/manifest.json")));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let template;
|
|
55
|
+
let entry;
|
|
56
|
+
let manifest;
|
|
57
|
+
|
|
58
|
+
// Always read fresh template in development
|
|
59
|
+
const htmlFilePath = join(appPath, "dist/client/index.html");
|
|
60
|
+
const serverFilePath = join(appPath, "dist/server/entry-server.js");
|
|
61
|
+
const ssrManifestFilePath = join(
|
|
62
|
+
appPath,
|
|
63
|
+
"dist/client/.vite/ssr-manifest.json"
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// Read template, server-renderer and manifest in production
|
|
67
|
+
template = await fs.readFile(htmlFilePath, "utf-8");
|
|
68
|
+
entry = await import(serverFilePath);
|
|
69
|
+
manifest = await fs.readFile(ssrManifestFilePath, "utf-8");
|
|
70
|
+
|
|
71
|
+
// Extract render and staticRoutes from entry
|
|
72
|
+
const { render, staticRoutes } = entry;
|
|
73
|
+
|
|
74
|
+
// Create static handler
|
|
75
|
+
let handler = createStaticHandler(staticRoutes);
|
|
76
|
+
|
|
77
|
+
// Create fetch request for static routing
|
|
78
|
+
let fetchRequest = createFetchRequest(req, host);
|
|
79
|
+
let context = await handler.query(fetchRequest);
|
|
80
|
+
|
|
81
|
+
// Handle redirects
|
|
82
|
+
const status = (context as Response).status;
|
|
83
|
+
|
|
84
|
+
if (status === 302) {
|
|
85
|
+
const redirect = (context as Response).headers.get("Location");
|
|
86
|
+
|
|
87
|
+
if (redirect) return res.redirect(redirect);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Helmet context
|
|
91
|
+
const helmetContext = {} as { helmet: any };
|
|
92
|
+
|
|
93
|
+
// Create static router
|
|
94
|
+
let router = createStaticRouter(
|
|
95
|
+
handler.dataRoutes,
|
|
96
|
+
context as StaticHandlerContext
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const rendered = await render(router, context, helmetContext);
|
|
100
|
+
|
|
101
|
+
// Get metadata
|
|
102
|
+
const helmet = helmetContext.helmet;
|
|
103
|
+
|
|
104
|
+
let head = `
|
|
105
|
+
${helmet.title.toString()}
|
|
106
|
+
${helmet.meta.toString()}
|
|
107
|
+
`;
|
|
108
|
+
|
|
109
|
+
let html = template
|
|
110
|
+
.replace(`<!--app-head-->`, head ?? "")
|
|
111
|
+
.replace(`<!--app-html-->`, rendered.html ?? "");
|
|
112
|
+
|
|
113
|
+
res
|
|
114
|
+
.status(200)
|
|
115
|
+
.setHeader("Content-Type", "text/html")
|
|
116
|
+
.setHeader("Cache-Control", "max-age=31536000")
|
|
117
|
+
.end(html);
|
|
118
|
+
} catch (e: any) {
|
|
119
|
+
console.log(e.stack);
|
|
120
|
+
res.status(500).end(e.stack);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Request } from "express";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This function is used to create a fetch request from an express request.
|
|
5
|
+
*/
|
|
6
|
+
export default function createFetchRequest(req: Request, host: string) {
|
|
7
|
+
let origin = `${req.protocol}://${host}`;
|
|
8
|
+
// Note: This had to take originalUrl into account for presumably vite's proxying
|
|
9
|
+
let url = new URL(req.originalUrl || req.url, origin);
|
|
10
|
+
|
|
11
|
+
let controller = new AbortController();
|
|
12
|
+
req.on("close", () => controller.abort());
|
|
13
|
+
|
|
14
|
+
let headers = new Headers();
|
|
15
|
+
|
|
16
|
+
for (let [key, values] of Object.entries(req.headers)) {
|
|
17
|
+
if (values) {
|
|
18
|
+
if (Array.isArray(values)) {
|
|
19
|
+
for (let value of values) {
|
|
20
|
+
headers.append(key, value);
|
|
21
|
+
}
|
|
22
|
+
} else {
|
|
23
|
+
headers.set(key, values);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let init = {
|
|
29
|
+
method: req.method,
|
|
30
|
+
headers,
|
|
31
|
+
signal: controller.signal,
|
|
32
|
+
body: null,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
36
|
+
init.body = req.body;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return new Request(url.href, init);
|
|
40
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
|
|
3
|
+
// Get local IP
|
|
4
|
+
export default function getIP() {
|
|
5
|
+
// Get network interfaces
|
|
6
|
+
const networkInterfaces = os.networkInterfaces();
|
|
7
|
+
|
|
8
|
+
// Find the IPv4 address for the default network interface
|
|
9
|
+
let ipAddress = "";
|
|
10
|
+
|
|
11
|
+
// Loop through the network interfaces
|
|
12
|
+
for (const interfaceName in networkInterfaces) {
|
|
13
|
+
// Get the network interface
|
|
14
|
+
const iface = networkInterfaces[interfaceName];
|
|
15
|
+
|
|
16
|
+
// Skip when there is no network interface
|
|
17
|
+
if (!iface) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Loop through the interface addresses
|
|
22
|
+
for (let i = 0; i < iface.length; i++) {
|
|
23
|
+
const alias = iface[i];
|
|
24
|
+
|
|
25
|
+
if (alias.family === "IPv4" && !alias.internal) {
|
|
26
|
+
ipAddress = alias.address;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (ipAddress) {
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return ipAddress;
|
|
37
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import path, { join } from "node:path";
|
|
2
|
+
import { Response } from "express";
|
|
3
|
+
|
|
4
|
+
export function fix404(url: string, res: Response, appPath: string) {
|
|
5
|
+
// ! Favicon Fix
|
|
6
|
+
if (url === "/favicon.ico") {
|
|
7
|
+
return res.sendFile(
|
|
8
|
+
path.resolve(join(appPath, "dist/client/rasengan.png"))
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// ! Robots Fix
|
|
13
|
+
if (url === "/robots.txt") {
|
|
14
|
+
return res.send(`
|
|
15
|
+
user-agent: *
|
|
16
|
+
disallow: /downloads/
|
|
17
|
+
disallow: /private/
|
|
18
|
+
allow: /
|
|
19
|
+
|
|
20
|
+
user-agent: magicsearchbot
|
|
21
|
+
disallow: /uploads/
|
|
22
|
+
`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ! Sitemap Fix
|
|
26
|
+
if (url === "/sitemap.xml") {
|
|
27
|
+
return res.sendFile(path.resolve(join(appPath, "dist/client/sitemap.xml")));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ! Manifest Fix
|
|
31
|
+
if (url === "/manifest.json") {
|
|
32
|
+
return res.sendFile(
|
|
33
|
+
path.resolve(join(appPath, "dist/client/manifest.json"))
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Import section
|
|
2
|
+
|
|
3
|
+
import createFetchRequest from "./createFetchRequest.js";
|
|
4
|
+
import getIP from "./getIp.js";
|
|
5
|
+
import { logServerInfo } from "./log.js";
|
|
6
|
+
import { fix404 } from "./handleError.js";
|
|
7
|
+
|
|
8
|
+
// Export section
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
createFetchRequest,
|
|
12
|
+
getIP,
|
|
13
|
+
logServerInfo,
|
|
14
|
+
fix404,
|
|
15
|
+
}
|