Skip to content

Commit 11516fb

Browse files
committed
feat: bump deps, clean up code, serve static assets correctly
1 parent b98b9b7 commit 11516fb

15 files changed

+1518
-1644
lines changed

gpg

-1
This file was deleted.

index.html

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!DOCTYPE html>
1+
<!doctype html>
22
<html lang="en">
33
<head>
44
<meta charset="UTF-8" />
@@ -8,18 +8,18 @@
88
name="viewport"
99
/>
1010
<meta content="text/html; charset=utf-8" http-equiv="content-type" />
11-
11+
<!--head-->
1212
<!-- ### Manifest and icons ### -->
1313
<!-- General -->
14-
<link rel="manifest" href="/static/site.webmanifest" />
14+
<link rel="manifest" href="/site.webmanifest" />
1515
<meta name="theme-color" content="#ffffff" />
1616
<meta name="application-name" content="App" />
17-
<link rel="shortcut icon" href="/static/favicon.ico" />
18-
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicon-32x32.png" />
19-
<link rel="icon" type="image/png" sizes="16x16" href="/static/favicon-16x16.png" />
17+
<link rel="shortcut icon" href="/favicon.ico" />
18+
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
19+
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
2020
<!-- Apple -->
2121
<meta name="apple-mobile-web-app-title" content="App" />
22-
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png" />
22+
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
2323
</head>
2424
<body>
2525
<div id="app" style="height: 100%; width: 100%"><!--app-html--></div>

package.json

+31-31
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"name": "vite-typescript-ssr-react",
33
"version": "1.0.1",
4+
"type": "module",
45
"description": "Boilerplate for a modern web stack",
56
"repository": {
67
"type": "git",
@@ -9,61 +10,60 @@
910
"author": "JonLuca DeCaro",
1011
"license": "MIT",
1112
"scripts": {
12-
"dev:server": "nodemon --watch server.ts --watch src/server --exec 'ts-node server.ts'",
13+
"dev:server": "tsx server.ts",
1314
"dev:client": "yarn build:client && vite --config vite.config.ts dev",
14-
"build": "rimraf dist && tsc -p tsconfig.prod.json && yarn build:client && yarn build:server && yarn copy-files",
15+
"build": "rimraf dist && tsc -p tsconfig.json && yarn build:client && yarn build:server && yarn copy-files",
1516
"build:client": "vite build --outDir dist/client --ssrManifest",
1617
"build:server": "vite build --ssr src/client/entry-server.tsx --outDir dist/server",
1718
"test": "vitest",
1819
"test:watch": "vitest --watch",
1920
"coverage": "vitest --coverage",
2021
"typecheck": "tsc --noEmit",
21-
"serve": "yarn build && cross-env NODE_ENV=production yarn ts-node ./server",
22+
"serve": "yarn build && cross-env NODE_ENV=production node ./dist/server",
2223
"serve:local": "vite serve",
2324
"clean": "rimraf dist/",
24-
"copy-files": "copyfiles static/* dist/assets && copyfiles index.html dist && copyfiles -f dist/client/assets/* dist/assets",
25+
"copy-files": "copyfiles \"public/**/*\" dist && copyfiles -u 2 \"dist/client/**/*\" dist && copyfiles -u 2 \"dist/client/assets/**/*\" dist/public",
2526
"format": "prettier --write ."
2627
},
2728
"dependencies": {
28-
"autoprefixer": "^10.4.14",
29+
"autoprefixer": "^10.4.16",
2930
"compression": "1.7.4",
3031
"cross-env": "^7.0.3",
3132
"express": "4.18.2",
32-
"nodemon": "^2.0.22",
3333
"react": "^18.2.0",
3434
"react-dom": "^18.2.0",
35-
"react-router-dom": "^6.10.0",
35+
"react-router-dom": "^6.16.0",
3636
"serve-static": "^1.15.0",
37-
"ts-node": "^10.9.1"
37+
"tsx": "^3.13.0"
3838
},
3939
"devDependencies": {
40-
"@testing-library/jest-dom": "^5.16.5",
40+
"@testing-library/jest-dom": "^6.1.3",
4141
"@testing-library/react": "^14.0.0",
42-
"@types/compression": "1.7.2",
42+
"@types/compression": "1.7.3",
4343
"@types/concurrently": "6.4.0",
44-
"@types/eslint": "8.37.0",
45-
"@types/express": "^4.17.17",
46-
"@types/node": "18.15.12",
47-
"@types/react": "^18.0.37",
48-
"@types/react-dom": "^18.0.11",
44+
"@types/eslint": "8.44.3",
45+
"@types/express": "^4.17.18",
46+
"@types/node": "20.8.2",
47+
"@types/react": "^18.2.24",
48+
"@types/react-dom": "^18.2.8",
4949
"@types/react-router-dom": "^5.3.3",
50-
"@typescript-eslint/eslint-plugin": "^5.59.0",
51-
"@typescript-eslint/parser": "^5.59.0",
52-
"@vitejs/plugin-react": "^4.0.0",
53-
"concurrently": "8.0.1",
50+
"@typescript-eslint/eslint-plugin": "^6.7.4",
51+
"@typescript-eslint/parser": "^6.7.4",
52+
"@vitejs/plugin-react": "^4.1.0",
53+
"concurrently": "8.2.1",
5454
"copyfiles": "^2.4.1",
55-
"eslint": "^8.38.0",
56-
"eslint-config-prettier": "^8.8.0",
57-
"eslint-config-standard": "^17.0.0",
58-
"eslint-plugin-react": "^7.32.2",
55+
"eslint": "^8.50.0",
56+
"eslint-config-prettier": "^9.0.0",
57+
"eslint-config-standard": "^17.1.0",
58+
"eslint-plugin-react": "^7.33.2",
5959
"eslint-plugin-react-hooks": "^4.6.0",
60-
"jsdom": "^21.1.1",
61-
"postcss": "8.4.23",
62-
"prettier": "^2.8.7",
63-
"rimraf": "^5.0.0",
64-
"tailwindcss": "3.3.1",
65-
"typescript": "5.0.4",
66-
"vite": "4.3.0",
67-
"vitest": "^0.30.1"
60+
"jsdom": "^22.1.0",
61+
"postcss": "8.4.31",
62+
"prettier": "^3.0.3",
63+
"rimraf": "^5.0.5",
64+
"tailwindcss": "3.3.3",
65+
"typescript": "5.2.2",
66+
"vite": "4.4.10",
67+
"vitest": "^0.34.6"
6868
}
6969
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

server.ts

+22-14
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
import type { Request, Response, NextFunction } from "express";
22
import fs from "fs/promises";
3-
import path from "path";
3+
import path, { dirname } from "path";
44
import express from "express";
55
import compression from "compression";
66
import serveStatic from "serve-static";
77
import { createServer as createViteServer } from "vite";
8+
import { fileURLToPath } from "url";
89
const isTest = process.env.NODE_ENV === "test" || !!process.env.VITE_TEST_BUILD;
910

11+
const __filename = fileURLToPath(import.meta.url);
12+
const __dirname = dirname(__filename);
13+
1014
const resolve = (p: string) => path.resolve(__dirname, p);
1115

1216
const getStyleSheets = async () => {
1317
try {
14-
const assetpath = resolve("dist/assets");
18+
const assetpath = resolve("public");
1519
const files = await fs.readdir(assetpath);
1620
const cssAssets = files.filter(l => l.endsWith(".css"));
1721
const allContent = [];
@@ -34,48 +38,52 @@ async function createServer(isProd = process.env.NODE_ENV === "production") {
3438
server: { middlewareMode: true },
3539
appType: "custom",
3640
logLevel: isTest ? "error" : "info",
41+
root: isProd ? "dist" : "",
42+
optimizeDeps: { include: [] },
3743
});
3844

3945
// use vite's connect instance as middleware
4046
// if you use your own express router (express.Router()), you should use router.use
4147
app.use(vite.middlewares);
42-
const requestHandler = express.static(resolve("assets"));
48+
const assetsDir = resolve("public");
49+
const requestHandler = express.static(assetsDir);
4350
app.use(requestHandler);
44-
app.use("/assets", requestHandler);
51+
app.use("/public", requestHandler);
4552

4653
if (isProd) {
4754
app.use(compression());
4855
app.use(
49-
serveStatic(resolve("dist/client"), {
56+
serveStatic(resolve("client"), {
5057
index: false,
5158
}),
5259
);
5360
}
5461
const stylesheets = getStyleSheets();
62+
63+
// 1. Read index.html
64+
const baseTemplate = await fs.readFile(isProd ? resolve("client/index.html") : resolve("index.html"), "utf-8");
65+
const productionBuildPath = path.join(__dirname, "./server/entry-server.js");
66+
const devBuildPath = path.join(__dirname, "./src/client/entry-server.tsx");
67+
const buildModule = isProd ? productionBuildPath : devBuildPath;
68+
const { render } = await vite.ssrLoadModule(buildModule);
69+
5570
app.use("*", async (req: Request, res: Response, next: NextFunction) => {
5671
const url = req.originalUrl;
5772

5873
try {
59-
// 1. Read index.html
60-
let template = await fs.readFile(isProd ? resolve("dist/client/index.html") : resolve("index.html"), "utf-8");
61-
6274
// 2. Apply Vite HTML transforms. This injects the Vite HMR client, and
6375
// also applies HTML transforms from Vite plugins, e.g. global preambles
6476
// from @vitejs/plugin-react
65-
template = await vite.transformIndexHtml(url, template);
66-
77+
const template = await vite.transformIndexHtml(url, baseTemplate);
6778
// 3. Load the server entry. vite.ssrLoadModule automatically transforms
6879
// your ESM source code to be usable in Node.js! There is no bundling
6980
// required, and provides efficient invalidation similar to HMR.
70-
let productionBuildPath = path.join(__dirname, "./dist/server/entry-server.mjs");
71-
let devBuildPath = path.join(__dirname, "./src/client/entry-server.tsx");
72-
const { render } = await vite.ssrLoadModule(isProd ? productionBuildPath : devBuildPath);
7381

7482
// 4. render the app HTML. This assumes entry-server.js's exported `render`
7583
// function calls appropriate framework SSR APIs,
7684
// e.g. ReactDOMServer.renderToString()
7785
const appHtml = await render(url);
78-
const cssAssets = isProd ? "" : await stylesheets;
86+
const cssAssets = await stylesheets;
7987

8088
// 5. Inject the app-rendered HTML into the template.
8189
const html = template.replace(`<!--app-html-->`, appHtml).replace(`<!--head-->`, cssAssets);

tsconfig.prod.json

-6
This file was deleted.

vite.config.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,12 @@ const test = {
1212
} as UserConfig["test"];
1313

1414
// https://vitejs.dev/config/
15+
const isProd = process.env.NODE_ENV === "production";
1516
export default defineConfig({
1617
plugins: [react()],
1718
server: { port: 3000 },
1819
build: {
1920
minify: false,
2021
},
21-
root: "",
22-
// @ts-ignore
2322
test,
2423
});

0 commit comments

Comments
 (0)