diff --git a/node-custom-server/Dockerfile b/node-custom-server/Dockerfile index d7526ff..f13f218 100644 --- a/node-custom-server/Dockerfile +++ b/node-custom-server/Dockerfile @@ -15,8 +15,8 @@ WORKDIR /app RUN npm run build FROM node:20-alpine -COPY ./package.json package-lock.json server.js /app/ +COPY ./package.json package-lock.json server.prod.js /app/ COPY --from=production-dependencies-env /app/node_modules /app/node_modules COPY --from=build-env /app/build /app/build WORKDIR /app -CMD ["npm", "run", "start"] \ No newline at end of file +CMD ["npm", "run", "start"] diff --git a/node-custom-server/Dockerfile.bun b/node-custom-server/Dockerfile.bun index aa1b075..8900507 100644 --- a/node-custom-server/Dockerfile.bun +++ b/node-custom-server/Dockerfile.bun @@ -18,8 +18,8 @@ WORKDIR /app RUN bun run build FROM dependencies-env -COPY ./package.json bun.lockb server.js /app/ +COPY ./package.json bun.lockb server.prod.js /app/ COPY --from=production-dependencies-env /app/node_modules /app/node_modules COPY --from=build-env /app/build /app/build WORKDIR /app -CMD ["bun", "run", "start"] \ No newline at end of file +CMD ["bun", "run", "start"] diff --git a/node-custom-server/Dockerfile.pnpm b/node-custom-server/Dockerfile.pnpm index 4e66f30..06a93b5 100644 --- a/node-custom-server/Dockerfile.pnpm +++ b/node-custom-server/Dockerfile.pnpm @@ -19,8 +19,8 @@ WORKDIR /app RUN pnpm build FROM dependencies-env -COPY ./package.json pnpm-lock.yaml server.js /app/ +COPY ./package.json pnpm-lock.yaml server.prod.js /app/ COPY --from=production-dependencies-env /app/node_modules /app/node_modules COPY --from=build-env /app/build /app/build WORKDIR /app -CMD ["pnpm", "start"] \ No newline at end of file +CMD ["pnpm", "start"] diff --git a/node-custom-server/README.md b/node-custom-server/README.md index 72d9bc0..3e80d69 100644 --- a/node-custom-server/README.md +++ b/node-custom-server/README.md @@ -84,7 +84,7 @@ Make sure to deploy the output of `npm run build` ``` ├── package.json ├── package-lock.json (or pnpm-lock.yaml, or bun.lockb) -├── server.js +├── server.prod.js ├── build/ │ ├── client/ # Static assets │ └── server/ # Server-side code diff --git a/node-custom-server/package.json b/node-custom-server/package.json index 0fa84ee..77cd80c 100644 --- a/node-custom-server/package.json +++ b/node-custom-server/package.json @@ -3,8 +3,8 @@ "type": "module", "scripts": { "build": "react-router build", - "dev": "cross-env NODE_ENV=development node server.js", - "start": "node server.js", + "dev": "cross-env NODE_ENV=development node server.dev.js", + "start": "node server.prod.js", "typecheck": "react-router typegen && tsc" }, "dependencies": { diff --git a/node-custom-server/server.dev.js b/node-custom-server/server.dev.js new file mode 100644 index 0000000..acf59be --- /dev/null +++ b/node-custom-server/server.dev.js @@ -0,0 +1,35 @@ +import * as vite from "vite"; +import express from "express"; + +const PORT = Number.parseInt(process.env.PORT || "3000"); +const app = express(); + +console.log("Starting development server"); + +const viteDevServer = await vite.createServer({ + server: { middlewareMode: true }, +}); +// `vite.middlewares` is a Connect instance which can be used as a middleware +// in any connect-compatible Node.js framework. +// https://vite.dev/guide/ssr#setting-up-the-dev-server +app.use(viteDevServer.middlewares); + +viteDevServer + // `ssrLoadModule` automatically transforms ESM source code without bundling, + // enabling live refresh of changed modules similar to Hot Module Reloading. + .ssrLoadModule("./server/app.ts") + .then((source) => { + // Here, `source` is effectively behaving like: + // `import * as source from 'server/app.ts'` + app.use(source.app); + }) + .catch((error) => { + if (typeof error === "object" && error instanceof Error) { + viteDevServer.ssrFixStacktrace(error); + } + console.log({ error }); + }); + +app.listen(PORT, () => { + console.log(`Server is running on http://localhost:${PORT}`); +}); diff --git a/node-custom-server/server.js b/node-custom-server/server.js deleted file mode 100644 index 2f65aa7..0000000 --- a/node-custom-server/server.js +++ /dev/null @@ -1,48 +0,0 @@ -import compression from "compression"; -import express from "express"; -import morgan from "morgan"; - -// Short-circuit the type-checking of the built output. -const BUILD_PATH = "./build/server/index.js"; -const DEVELOPMENT = process.env.NODE_ENV === "development"; -const PORT = Number.parseInt(process.env.PORT || "3000"); - -const app = express(); - -app.use(compression()); -app.disable("x-powered-by"); - -if (DEVELOPMENT) { - console.log("Starting development server"); - const viteDevServer = await import("vite").then((vite) => - vite.createServer({ - server: { middlewareMode: true }, - }) - ); - app.use(viteDevServer.middlewares); - app.use(async (req, res, next) => { - try { - const source = await viteDevServer.ssrLoadModule("./server/app.ts"); - return await source.app(req, res, next); - } catch (error) { - if (typeof error === "object" && error instanceof Error) { - viteDevServer.ssrFixStacktrace(error); - } - next(error); - } - }); -} else { - console.log("Starting production server"); - app.use( - "/assets", - express.static("build/client/assets", { immutable: true, maxAge: "1y" }) - ); - app.use(express.static("build/client", { maxAge: "1h" })); - app.use(await import(BUILD_PATH).then((mod) => mod.app)); -} - -app.use(morgan("tiny")); - -app.listen(PORT, () => { - console.log(`Server is running on http://localhost:${PORT}`); -}); diff --git a/node-custom-server/server.prod.js b/node-custom-server/server.prod.js new file mode 100644 index 0000000..de5a761 --- /dev/null +++ b/node-custom-server/server.prod.js @@ -0,0 +1,18 @@ +import express from "express"; + +import { app as rrApp } from "./build/server/index.js"; + +const PORT = Number.parseInt(process.env.PORT || "3000"); +const app = express(); + +console.log("Starting production server"); +app.use( + "/assets", + express.static("build/client/assets", { immutable: true, maxAge: "1y" }), +); +app.use(express.static("build/client", { maxAge: "1h" })); +app.use(rrApp); + +app.listen(PORT, () => { + console.log(`Server is running on http://localhost:${PORT}`); +}); diff --git a/node-custom-server/server/app.ts b/node-custom-server/server/app.ts index f84a0ba..eeeb96c 100644 --- a/node-custom-server/server/app.ts +++ b/node-custom-server/server/app.ts @@ -1,8 +1,12 @@ import "react-router"; import { createRequestHandler } from "@react-router/express"; import express from "express"; +import morgan from "morgan"; +import compression from "compression"; declare module "react-router" { + // If you need to pass data through from this script into React Router, this + // will give you type safety throughout our app. interface AppLoadContext { VALUE_FROM_EXPRESS: string; } @@ -10,14 +14,20 @@ declare module "react-router" { export const app = express(); +app.use(compression()); +app.disable("x-powered-by"); + app.use( createRequestHandler({ // @ts-expect-error - virtual module provided by React Router at build time build: () => import("virtual:react-router/server-build"), getLoadContext() { + // This is where we'll return the actual values we defined the types for + // earlier in the file. return { VALUE_FROM_EXPRESS: "Hello from Express", }; }, - }) + }), ); +app.use(morgan("tiny"));