ssr
This commit is contained in:
+39
-12
@@ -2,36 +2,63 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Pathway+Gothic+One&display=swap" rel="stylesheet">
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Pathway+Gothic+One&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
<link href="https://mst.fntz.net/@alex" rel="me" />
|
<link href="https://mst.fntz.net/@alex" rel="me" />
|
||||||
|
|
||||||
<!-- opengraph tags -->
|
<!-- opengraph tags -->
|
||||||
<meta property="og:image" content="https://storage.alexav.gg/content/picturev2.jpg" />
|
<meta
|
||||||
|
property="og:image"
|
||||||
|
content="https://storage.alexav.gg/content/picturev2.jpg"
|
||||||
|
/>
|
||||||
<meta property="og:title" content="alex" />
|
<meta property="og:title" content="alex" />
|
||||||
<meta property="og:description" content="hi, i'm alex! i'm obsessed with the last of us and i do a lot of code things. about it tbh" />
|
<meta
|
||||||
|
property="og:description"
|
||||||
|
content="hi, i'm alex! i'm obsessed with the last of us and i do a lot of code things. about it tbh"
|
||||||
|
/>
|
||||||
<meta property="og:url" content="https://alexav.gg" />
|
<meta property="og:url" content="https://alexav.gg" />
|
||||||
<meta property="og:site_name" content="alex" />
|
<meta property="og:site_name" content="alex" />
|
||||||
|
|
||||||
<!-- twitter tags -->
|
<!-- twitter tags -->
|
||||||
<meta name="twitter:title" content="alex" />
|
<meta name="twitter:title" content="alex" />
|
||||||
<meta name="twitter:description" content="hi, i'm alex! i'm obsessed with the last of us and i do a lot of code things. about it tbh" />
|
<meta
|
||||||
|
name="twitter:description"
|
||||||
|
content="hi, i'm alex! i'm obsessed with the last of us and i do a lot of code things. about it tbh"
|
||||||
|
/>
|
||||||
<meta name="twitter:url" content="https://alexav.gg" />
|
<meta name="twitter:url" content="https://alexav.gg" />
|
||||||
<meta name="twitter:image" content="https://storage.alexav.gg/content/picturev2.jpg" />
|
<meta
|
||||||
|
name="twitter:image"
|
||||||
|
content="https://storage.alexav.gg/content/picturev2.jpg"
|
||||||
|
/>
|
||||||
<meta name="twitter:card" content="summary" />
|
<meta name="twitter:card" content="summary" />
|
||||||
|
|
||||||
<!-- metadata -->
|
<!-- metadata -->
|
||||||
<meta name="title" content="alex" />
|
<meta name="title" content="alex" />
|
||||||
<meta name="description" content="hi, i'm alex! i'm obsessed with the last of us and i do a lot of code things. about it tbh" />
|
<meta
|
||||||
<meta name="keywords" content="alex frantz, frantz, alex, qa, javascript, html, react, python, java, alexavier frantz, alexavier, alexav, alexfrantz" />
|
name="description"
|
||||||
|
content="hi, i'm alex! i'm obsessed with the last of us and i do a lot of code things. about it tbh"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="keywords"
|
||||||
|
content="alex frantz, frantz, alex, qa, javascript, html, react, python, java, alexavier frantz, alexavier, alexav, alexfrantz"
|
||||||
|
/>
|
||||||
|
|
||||||
<title>alex</title>
|
<title>alex</title>
|
||||||
|
<!--app-head-->
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"><!--app-html--></div>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="/src/entry-client.tsx"></script>
|
||||||
<script defer data-domain="alexav.gg" src="https://metrics.alexav.gg/js/script.js"></script>
|
<script
|
||||||
|
defer
|
||||||
|
data-domain="alexav.gg"
|
||||||
|
src="https://metrics.alexav.gg/js/script.js"
|
||||||
|
></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+13
-5
@@ -4,25 +4,33 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "node server",
|
||||||
"dev:expose": "vite --host",
|
"build": "npm run build:client && npm run build:server",
|
||||||
"build": "tsc -b && vite build",
|
"build:client": "vite build --outDir dist/client",
|
||||||
"lint": "eslint .",
|
"build:server": "vite build --ssr src/entry-server.tsx --outDir dist/server",
|
||||||
"preview": "vite preview"
|
"preview": "cross-env NODE_ENV=production node server",
|
||||||
|
"start": "cross-env NODE_ENV=production node server"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/vite": "4.1.4",
|
"@tailwindcss/vite": "4.1.4",
|
||||||
|
"compression": "^1.8.1",
|
||||||
|
"express": "^5.2.1",
|
||||||
"lucide-react": "0.488.0",
|
"lucide-react": "0.488.0",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
"react-router-dom": "7.6.2",
|
"react-router-dom": "7.6.2",
|
||||||
|
"sirv": "^3.0.2",
|
||||||
"tailwindcss": "4.1.4"
|
"tailwindcss": "4.1.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "9.22.0",
|
"@eslint/js": "9.22.0",
|
||||||
|
"@types/compression": "^1.8.1",
|
||||||
|
"@types/express": "^5.0.6",
|
||||||
|
"@types/node": "^26.1.1",
|
||||||
"@types/react": "19.0.10",
|
"@types/react": "19.0.10",
|
||||||
"@types/react-dom": "19.0.4",
|
"@types/react-dom": "19.0.4",
|
||||||
"@vitejs/plugin-react": "4.3.4",
|
"@vitejs/plugin-react": "4.3.4",
|
||||||
|
"cross-env": "^10.1.0",
|
||||||
"eslint": "9.22.0",
|
"eslint": "9.22.0",
|
||||||
"eslint-plugin-react-hooks": "5.2.0",
|
"eslint-plugin-react-hooks": "5.2.0",
|
||||||
"eslint-plugin-react-refresh": "0.4.19",
|
"eslint-plugin-react-refresh": "0.4.19",
|
||||||
|
|||||||
Generated
+749
-8
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
|||||||
|
allowBuilds:
|
||||||
|
esbuild: false
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import fs from "node:fs/promises";
|
||||||
|
import express from "express";
|
||||||
|
|
||||||
|
const isProduction = process.env.NODE_ENV === "production";
|
||||||
|
const port = process.env.PORT || 5173;
|
||||||
|
const base = process.env.BASE || "/";
|
||||||
|
|
||||||
|
const templateHtml = isProduction
|
||||||
|
? await fs.readFile("./dist/client/index.html", "utf-8")
|
||||||
|
: "";
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
let vite;
|
||||||
|
if (!isProduction) {
|
||||||
|
const { createServer } = await import("vite");
|
||||||
|
vite = await createServer({
|
||||||
|
server: { middlewareMode: true },
|
||||||
|
appType: "custom",
|
||||||
|
base,
|
||||||
|
});
|
||||||
|
app.use(vite.middlewares);
|
||||||
|
} else {
|
||||||
|
const compression = (await import("compression")).default;
|
||||||
|
const sirv = (await import("sirv")).default;
|
||||||
|
app.use(compression());
|
||||||
|
app.use(base, sirv("./dist/client", { extensions: [] }));
|
||||||
|
}
|
||||||
|
|
||||||
|
app.use("*all", async (req, res) => {
|
||||||
|
try {
|
||||||
|
const url = req.originalUrl.replace(base, "");
|
||||||
|
|
||||||
|
let template;
|
||||||
|
let render;
|
||||||
|
if (!isProduction) {
|
||||||
|
template = await fs.readFile("./index.html", "utf-8");
|
||||||
|
template = await vite.transformIndexHtml(url, template);
|
||||||
|
render = (await vite.ssrLoadModule("/src/entry-server.tsx")).render;
|
||||||
|
} else {
|
||||||
|
template = templateHtml;
|
||||||
|
render = (await import("./dist/server/entry-server.js")).render;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rendered = await render(url);
|
||||||
|
const html = template
|
||||||
|
?.replace("<!--app-head-->", rendered.head ?? "")
|
||||||
|
.replace("<!--app-html-->", rendered.html ?? "");
|
||||||
|
res.status(200).set({ "Content-Type": "text/html" }).send(html);
|
||||||
|
} catch (e) {
|
||||||
|
vite?.ssrFixStacktrace(e);
|
||||||
|
console.log(e.stack);
|
||||||
|
res.status(500).end(e.stack);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(port, () => {
|
||||||
|
console.log(`Server started at http://localhost:${port}`);
|
||||||
|
});
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { hydrateRoot } from "react-dom/client";
|
||||||
|
import { BrowserRouter } from "react-router-dom";
|
||||||
|
import "./assets/main.css";
|
||||||
|
import App from "./pages/App.tsx";
|
||||||
|
|
||||||
|
hydrateRoot(
|
||||||
|
document.getElementById("root")!,
|
||||||
|
<BrowserRouter>
|
||||||
|
<App />
|
||||||
|
</BrowserRouter>,
|
||||||
|
);
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { renderToString } from "react-dom/server";
|
||||||
|
import * as RouterExports from "react-router-dom";
|
||||||
|
import App from "./pages/App";
|
||||||
|
|
||||||
|
// Vite's SSR runner resolves react-router-dom to its CommonJS build, so the
|
||||||
|
// named exports land on `.default`; fall back to the namespace when not.
|
||||||
|
const { StaticRouter } =
|
||||||
|
(RouterExports as { default?: typeof RouterExports }).default ?? RouterExports;
|
||||||
|
|
||||||
|
export function render(url: string) {
|
||||||
|
const html = renderToString(
|
||||||
|
<StaticRouter location={url}>
|
||||||
|
<App />
|
||||||
|
</StaticRouter>,
|
||||||
|
);
|
||||||
|
return { html };
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import { createRoot } from "react-dom/client";
|
|
||||||
import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
|
||||||
import "./assets/main.css";
|
|
||||||
import App from "./pages/App.tsx";
|
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
|
||||||
{
|
|
||||||
path: "/",
|
|
||||||
element: <App />,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
createRoot(document.getElementById("root")!).render(
|
|
||||||
<RouterProvider router={router} />
|
|
||||||
);
|
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
"moduleDetection": "force",
|
"moduleDetection": "force",
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
|
"types": ["node"],
|
||||||
|
|
||||||
/* Linting */
|
/* Linting */
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"moduleDetection": "force",
|
"moduleDetection": "force",
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
|
"types": ["node"],
|
||||||
|
|
||||||
/* Linting */
|
/* Linting */
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
|||||||
Reference in New Issue
Block a user