Initial commit

This commit is contained in:
2025-06-21 23:09:28 -05:00
commit 5351a94626
23 changed files with 6121 additions and 0 deletions

308
README.md Normal file
View File

@@ -0,0 +1,308 @@
# Minimal Express.js Scaffolding
This project offers a streamlined and minimalistic scaffolding for Express.js applications. Unlike many generators that require extensive cleanup before you can begin, this boilerplate strikes a balance between simplicity and completeness. It provides just the essentials, allowing you to pull the repository, customize the project name, and start building your API immediately.
## Support the Project
If you find this project useful, consider supporting its development:
[![Donate using Liberapay][liberapay-logo]][liberapay-link]
[liberapay-logo]: https://liberapay.com/assets/widgets/donate.svg "Liberapay Logo"
[liberapay-link]: https://liberapay.com/sfiorini/donate
## Installation
To install the project dependencies, run the following command:
```bash
pnpm install
```
## Running the App
To run the application in development mode with live reloading, use:
```bash
pnpm dev
```
To build and run the application for production, use:
```bash
pnpm start
```
## Customization
To rename the project, modify the `"name"` field in the `package.json` file.
```json
{
"name": "your-project-name"
}
```
## Technologies Used
* **Express:** Fast, unopinionated, minimalist web framework for Node.js.
* **TypeScript:** Typed superset of JavaScript that compiles to plain JavaScript.
* **Jest:** A delightful JavaScript Testing Framework with a focus on simplicity.
* **Knip:** A tool to find unused files, dependencies, and exports in your JavaScript and TypeScript projects.
* **ESLint:** A pluggable and configurable linter tool for identifying and reporting on patterns in JavaScript.
* **Docker:** A platform for developing, shipping, and running applications in containers.
* **tsx:** A CLI to seamlessly execute TypeScript and ESM.
* **tsdown:** A tool for building TypeScript projects.
## Scripts
* `pnpm start`: Starts the production server after building the project.
* `pnpm dev`: Runs the application in development mode with live reloading.
* `pnpm build`: Builds the TypeScript project into JavaScript.
* `pnpm test`: Runs the test suite using Jest and generates a coverage report.
* `pnpm test:watch`: Runs the tests in watch mode, re-running them on file changes.
* `pnpm knip`: Finds unused files, dependencies, and exports.
* `pnpm lint`: Lints the codebase using ESLint and automatically fixes issues.
## Building This Scaffold From Scratch
Here is a detailed, step-by-step guide to creating this scaffold from the ground up.
### Requirements
* [Node.js](https://nodejs.org/)
* [pnpm](https://pnpm.io/)
### Step-by-Step Guide
1. **Initialize the project:**
```bash
pnpm init
```
2. **Install Express:**
```bash
pnpm install express
```
3. **Set up TypeScript:**
```bash
pnpm install --save-dev typescript @types/node @types/express
npx tsc --init
```
4. **Create the main application file:**
Create a file at `src/index.ts`.
5. **Install `tsx` for development and `tsdown` for building:**
```bash
pnpm install --save-dev tsx tsdown
```
6. **Configure `tsdown`:**
Create a `tsdown.config.ts` file:
```typescript
import { defineConfig } from 'tsdown';
export default defineConfig({
entry: 'src/index.ts',
format: ["esm"],
target: "ESNext",
platform: "node"
});
```
7. **Configure `tsconfig.json`:**
Modify your `tsconfig.json` to look like this:
```json
{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "baseUrl": "src",
    "paths": {              
      "@*": ["*"]
    },
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true
  }
}
```
8. **Set up Jest for testing:**
```bash
pnpm install --save-dev jest ts-jest @types/jest @types/supertest supertest
npx ts-jest config:init
```
This will create a `jest.config.js` file.
9. **Refactor the application for testability:**
* Move all your Express app definition logic from `src/index.ts` into a new `src/server.ts` file.
* The `src/index.ts` file should only contain the `server.listen()` part, which starts the server.
* Create a `tests` folder and mirror the structure of your `src` folder for your test files.
10. **Install Knip to keep the project clean:**
```bash
pnpm add -D knip
```
11. **Set up ESLint for code linting:**
```bash
pnpm add --save-dev eslint jiti @eslint/js typescript-eslint @stylistic/eslint-plugin eslint-plugin-n
```
Create an `eslint.config.ts` file with the following settings (modify as needed):
```typescript
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import stylistic from '@stylistic/eslint-plugin';
import nodePlugin from 'eslint-plugin-n';
export default tseslint.config(
eslint.configs.recommended,
nodePlugin.configs['flat/recommended-script'],
...tseslint.configs.strictTypeChecked,
...tseslint.configs.stylisticTypeChecked,
{
ignores: [
'**/node_modules/*',
'**/*.mjs',
'**/*.js',
],
},
{
languageOptions: {
parserOptions: {
project: './tsconfig.json',
warnOnUnsupportedTypeScriptVersion: false,
},
},
},
{
plugins: {
'@stylistic/js': stylistic,
'@stylistic/ts': stylistic,
},
},
{
files: ['**/*.ts'],
},
{
rules: {
'@typescript-eslint/explicit-member-accessibility': 'warn',
'@typescript-eslint/no-misused-promises': 0,
'@typescript-eslint/no-floating-promises': 0,
'@typescript-eslint/no-confusing-void-expression': 0,
'@typescript-eslint/no-unnecessary-condition': 0,
'@typescript-eslint/restrict-template-expressions': [
'error', { allowNumber: true },
],
'@typescript-eslint/restrict-plus-operands': [
'warn', { allowNumberAndString: true },
],
'@typescript-eslint/no-unused-vars': 'warn',
'@typescript-eslint/no-unsafe-enum-comparison': 0,
'@typescript-eslint/no-unnecessary-type-parameters': 0,
'@stylistic/js/no-extra-semi': 'warn',
'max-len': [
'warn',
{
'code': 80,
},
],
'@stylistic/ts/semi': ['warn', 'always'],
'@stylistic/ts/member-delimiter-style': ['warn', {
'multiline': {
'delimiter': 'comma',
'requireLast': true,
},
'singleline': {
'delimiter': 'comma',
'requireLast': false,
},
'overrides': {
'interface': {
'singleline': {
'delimiter': 'semi',
'requireLast': false,
},
'multiline': {
'delimiter': 'semi',
'requireLast': true,
},
},
},
}],
'@typescript-eslint/no-non-null-assertion': 0,
'@typescript-eslint/no-unused-expressions': 'warn',
'comma-dangle': ['warn', 'always-multiline'],
'no-console': 1,
'no-extra-boolean-cast': 0,
'indent': ['warn', 2],
'quotes': ['warn', 'single'],
'n/no-process-env': 1,
'n/no-missing-import': 0,
'n/no-unpublished-import': 0,
'prefer-const': 'warn',
},
},
);
```
12. **Set up Docker:**
Create a `Dockerfile` file:
```text
# Use the official Node.js image.
FROM node:22-slim
# Create and change to the app directory.
WORKDIR /usr/src/app
# Copy package.json and pnpm-lock.yaml
COPY package.json pnpm-lock.yaml ./
# Install pnpm
RUN npm install -g pnpm
# Install dependencies
RUN pnpm install
# Copy the rest of the application's source code.
COPY . .
# Build the project
RUN pnpm run build
# Expose the port the app runs on
EXPOSE 3000
# Serve the app
CMD [ "pnpm", "start" ]
```
Create a `docker-compose.yml` file:
```yaml
services:
exp-min:
build: .
container_name: "exp-min"
ports:
- "3000:3000"
```