ssr
This commit is contained in:
+39
-12
@@ -2,36 +2,63 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<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" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<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 rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<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://mst.fntz.net/@alex" rel="me" />
|
||||
|
||||
<!-- 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: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:site_name" content="alex" />
|
||||
|
||||
<!-- twitter tags -->
|
||||
<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: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" />
|
||||
|
||||
<!-- metadata -->
|
||||
<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 name="keywords" content="alex frantz, frantz, alex, qa, javascript, html, react, python, java, alexavier frantz, alexavier, alexav, alexfrantz" />
|
||||
<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
|
||||
name="keywords"
|
||||
content="alex frantz, frantz, alex, qa, javascript, html, react, python, java, alexavier frantz, alexavier, alexav, alexfrantz"
|
||||
/>
|
||||
|
||||
<title>alex</title>
|
||||
<!--app-head-->
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
<script defer data-domain="alexav.gg" src="https://metrics.alexav.gg/js/script.js"></script>
|
||||
<div id="root"><!--app-html--></div>
|
||||
<script type="module" src="/src/entry-client.tsx"></script>
|
||||
<script
|
||||
defer
|
||||
data-domain="alexav.gg"
|
||||
src="https://metrics.alexav.gg/js/script.js"
|
||||
></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+14
-6
@@ -4,25 +4,33 @@
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev:expose": "vite --host",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
"dev": "node server",
|
||||
"build": "npm run build:client && npm run build:server",
|
||||
"build:client": "vite build --outDir dist/client",
|
||||
"build:server": "vite build --ssr src/entry-server.tsx --outDir dist/server",
|
||||
"preview": "cross-env NODE_ENV=production node server",
|
||||
"start": "cross-env NODE_ENV=production node server"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/vite": "4.1.4",
|
||||
"compression": "^1.8.1",
|
||||
"express": "^5.2.1",
|
||||
"lucide-react": "0.488.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-router-dom": "7.6.2",
|
||||
"sirv": "^3.0.2",
|
||||
"tailwindcss": "4.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@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-dom": "19.0.4",
|
||||
"@vitejs/plugin-react": "4.3.4",
|
||||
"cross-env": "^10.1.0",
|
||||
"eslint": "9.22.0",
|
||||
"eslint-plugin-react-hooks": "5.2.0",
|
||||
"eslint-plugin-react-refresh": "0.4.19",
|
||||
@@ -32,4 +40,4 @@
|
||||
"typescript-eslint": "8.26.1",
|
||||
"vite": "6.3.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"types": ["node"],
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"types": ["node"],
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
|
||||
Reference in New Issue
Block a user