Skip to content

Commit

Permalink
Web/document server (#1129)
Browse files Browse the repository at this point in the history
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
suddenlyGiovanni and github-actions[bot] authored Dec 18, 2024
1 parent dab046e commit 71e8e07
Show file tree
Hide file tree
Showing 16 changed files with 741 additions and 91 deletions.
2 changes: 2 additions & 0 deletions apps/web/.development.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
NODE_ENV=development
NODE_OPTIONS="--trace-warnings --inspect --experimental-strip-types --experimental-transform-types --watch-preserve-output"
2 changes: 2 additions & 0 deletions apps/web/.production.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
NODE_ENV=production
NODE_OPTIONS=--trace-warnings --inspect --experimental-strip-types --experimental-transform-types
2 changes: 1 addition & 1 deletion apps/web/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,4 @@ USER node

EXPOSE 5173

CMD ["node", "--experimental-strip-types", "--experimental-transform-types", "server/server.ts", "build/server/index.js"]
CMD ["node", "--experimental-strip-types", "--experimental-transform-types", "server/server.ts"]
21 changes: 21 additions & 0 deletions apps/web/biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,27 @@
}
},
"overrides": [
{
"include": ["./server/**/*.ts"],
"linter": {
"rules": {
"style": {
"useNamingConvention": "info"
},
"suspicious": {
"noConsole": "off",
"noConsoleLog": "off"
},
"correctness": {
"noNodejsModules": "off"
},
"nursery": {
"useExplicitType": "off"
}
}
}
},

{
"include": ["app/routes/**/*.tsx"],
"linter": {
Expand Down
7 changes: 7 additions & 0 deletions apps/web/mocks/handlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { http, type HttpHandler } from 'msw'

export const handlers = [
http.all('*', ({ request }) => {
console.log(request.method, request.url)
}),
] satisfies HttpHandler[]
5 changes: 5 additions & 0 deletions apps/web/mocks/node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { setupServer } from 'msw/node'

import { handlers } from './handlers.ts'

export const server = setupServer(...handlers)
5 changes: 3 additions & 2 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@vitest/ui": "3.0.0-beta.2",
"babel-plugin-react-compiler": "19.0.0-beta-df7b47d-20241124",
"globals": "15.13.0",
"msw": "2.7.0",
"react-router-devtools": "2.0.0-beta.0",
"tailwindcss": "4.0.0-beta.8",
"tailwindcss-animate": "1.0.7",
Expand All @@ -69,11 +70,11 @@
"check": "biome check .",
"check:fix": "biome check --apply .",
"clean": "scripty",
"dev": "NODE_ENV=development node --watch-path=./server --watch-preserve-output --inspect --experimental-network-inspection --experimental-strip-types --experimental-transform-types --trace-warnings server/server.ts build/server/index.js",
"dev": "node --env-file=.development.env --experimental-network-inspection server/server.ts",
"format": "biome check --vcs-enabled=true --vcs-use-ignore-file=true --formatter-enabled=true --linter-enabled=false --organize-imports-enabled=true .",
"format:write": "biome check --vcs-enabled=true --vcs-use-ignore-file=true --formatter-enabled=true --linter-enabled=false --organize-imports-enabled=true --write .",
"lint": "biome ci --vcs-enabled=true --vcs-use-ignore-file=true --vcs-root='../../' --formatter-enabled=false --linter-enabled=true --organize-imports-enabled=false --no-errors-on-unmatched .",
"start": "node --experimental-strip-types --experimental-transform-types server/server.ts build/server/index.js",
"start": "node --env-file=.production.env server/server.ts",
"test": "vitest",
"test:unit": "vitest",
"typecheck": "react-router typegen && tsc"
Expand Down
1 change: 1 addition & 0 deletions apps/web/react-router.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Config } from '@react-router/dev/config'
export default {
appDirectory: 'src',
serverModuleFormat: 'esm',
serverBuildFile: 'index.js',
buildDirectory: 'build',
ssr: true,
future: {
Expand Down
150 changes: 150 additions & 0 deletions apps/web/server/server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Server Setup Documentation

## Overview

The server is an Express application that serves a React Router build. It supports both development and production environments, dynamically configuring the server port and host.

## Key Components

### Configuration (`Config` class)

- Manages environment variables and server build path.
- Uses `effect` library for schema validation.

### Source Map Support

- Uses `source-map-support` to enhance error stack traces.

### Express Application

- Uses `compression` for response compression.
- Uses `morgan` for HTTP request logging.
- Serves static files and handles all incoming requests.

### Development Mode

- Uses `vite` for development server with middleware mode.
- Dynamically loads and handles requests using Vite's SSR capabilities.

### Production Mode

- Serves static assets with caching.
- Loads the server build from the specified path.

### Request Handling

- Uses `react-router` for handling requests and responses.
- Custom request handler created using `react-router-express`.

## Middleware Behavior

### Compression Middleware (`compression`)

- Compresses response bodies for all requests.
- Adds `Content-Encoding` headers and compresses the body.

### Morgan Middleware (`morgan`)

- Logs HTTP requests.
- Does not modify the `Request` or `Response` but logs details like method, URL, status, and response time.

### Static File Serving (`express.static`)

- Serves static files from the specified directories.
- Adds appropriate headers (e.g., `Cache-Control`) and serves files.

### Vite Dev Server Middleware (`viteDevServer.middlewares`)

- Handles requests in development mode.
- Attaches custom middlewares and acts as a handler function for custom HTTP servers.
- Provides features like HMR, module loading, and development-specific optimizations.

## Vite Dev Server in Development Mode

- A connect app instance that attaches custom middlewares to the dev server.
- Acts as a handler function for custom HTTP servers or as middleware in connect-style Node.js frameworks.
- Provides HMR, module loading, and development-specific optimizations.

## `viteDevServer.ssrLoadModule('./server/app.ts')` and `app.ts`

### `ssrLoadModule`

- Loads a given URL as an instantiated module for SSR.

### `app.ts`

- Sets up the Express application and request handler.
- Defines a request handler using `createRequestHandler` from `react-router-express`.
- Configures the request handler to load the server build and provide context values.

## Handling OS Signals for Graceful Shutdown

- Listens for termination signals (`SIGTERM` and `SIGINT`).
- Closes the server instance and cleans up resources when a signal is received.

## Additional Details

### Configuration

- Dynamically configures the port and host based on environment variables or default settings.

### Source Map Support

- Enhances error stack traces by retrieving source maps for better debugging.

### Request Handling

- Uses `react-router` to handle requests and responses, providing SSR capabilities.

## External Dependencies

- `compression`: Middleware for response compression.
- `effect`: Library for schema validation.
- `express`: Web framework for building the server.
- `get-port`: Utility to get an available port.
- `morgan`: HTTP request logger.
- `source-map-support`: Enhances error stack traces.
- `vite`: Development server and build tool.
- `react-router`: Library for handling routing and SSR.

# Unit Tests

## Objectives

- Validate the configuration schema and default values.
- Ensure the server starts correctly in both development and production modes.
- Verify middleware and request handling logic.
- Test static file serving and caching behavior.
- Validate error handling and logging.

## Test Cases

### Configuration Tests

- Validate default values for `NODE_ENV`, `PORT`, and `HOST`.
- Test schema validation for different configurations.

### Server Initialization Tests

- Ensure the server starts on the specified port and host.
- Verify the correct middleware is applied based on the environment.

### Middleware Tests

- Test `compression` middleware for response compression.
- Verify `morgan` logs HTTP requests correctly.

### Request Handling Tests

- Test request handling in development mode using Vite.
- Verify request handling in production mode with static file serving.

### Static File Serving Tests

- Ensure static assets are served with correct caching headers.
- Test serving of client-side assets from the build directory.

### Error Handling Tests

- Verify error handling and logging for different scenarios.
- Test source map support for enhanced error stack traces.
63 changes: 63 additions & 0 deletions apps/web/server/server.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { type MockInstance, afterAll, afterEach, beforeEach, describe, it, vi } from 'vitest'

describe('server setup', () => {
let consoleErrorSpy: MockInstance<(...args: unknown[]) => void>

beforeEach(() => {
vi.stubEnv('NODE_ENV', undefined)
consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
vi.spyOn(process, 'exit').mockImplementation((code?: number) => {
throw new Error(`process.exit(${code})`)
})
})

afterEach(() => {
vi.resetAllMocks()
vi.unstubAllEnvs()
vi.unstubAllGlobals()
})

afterAll(() => {
vi.restoreAllMocks()
})

describe('configuration tests', () => {
it.todo('should validate default values for NODE_ENV, PORT, and HOST')

it.todo('should test schema validation for different configurations')
})

describe('server Initialization Tests', () => {
it.todo('should ensure the server starts on the specified port and host')

it.todo('should verify the correct middleware is applied based on the environment')
})

describe('middleware Tests', () => {
it.todo('should test compression middleware for response compression')

it.todo('should verify morgan logs HTTP requests correctly')
})

describe('request Handling Tests', () => {
it.todo('should test request handling in development mode using Vite')

it.todo('should verify request handling in production mode with static file serving')
})

describe('static File Serving Tests', () => {
it.todo('should ensure static assets are served with correct caching headers')

it.todo('should test serving of client-side assets from the build directory')
})

describe('error Handling Tests', () => {
it.todo('should verify error handling and logging for different scenarios')

it.todo('should test source map support for enhanced error stack traces')
})

describe('graceful Shutdown Tests', () => {
it.todo('should handle OS signals to gracefully shut down the server')
})
})
Loading

0 comments on commit 71e8e07

Please sign in to comment.