CodeVetter · Repo Unpack · Contributor handoff
expressjs/express
This is a mature, style-conservative library where the diff bar is high and behaviour is pinned by a very large test suite. Read `lib/express.js`, `lib/application.js`, then the method you are changing in `lib/response.js` or `lib/request.js`; the test file name mirrors the API name, so `res.send` changes belong in `test/res.send.js`. The single biggest trap is that `test/acceptance/*` boots the real apps in `examples/`, so editing an example breaks tests in a different directory. Dependencies are installed fresh (no lockfile, no committed `node_modules`), so `npm install` is a required first step.
- READ FIRST, in order: `lib/express.js` (the factory and the export surface), `lib/application.js` (settings, `handle`, `use`, `render`), then the specific prototype file you are touching — `lib/response.js` or `lib/request.js`.
- SETUP: `node_modules/` is absent and `.npmrc` sets `package-lock=false`, so you must run `npm install` before any test run; there is no build step to run afterwards.
- TESTS TO RUN: `npm test` for the full mocha suite (`test/` + `test/acceptance/`), `npm run test-cov` for an HTML coverage report, and `npm run lint` before committing — CI runs lint as a separate required job.
- LINT RULES that bite: 2-space `indent` with `MemberExpression: off`, `eqeqeq` with `allow-null`, `eol-last`, `no-trailing-spaces`, and a `no-restricted-globals` rule banning the global `Buffer` — you must write `const { Buffer } = require('node:buffer')`.
- STYLE: `lib/` is ES5-flavoured — `var` declarations, `'use strict'` at the top of every file, JSDoc blocks with explicit `@public`/`@private` on every exported member. Match it rather than modernising opportunistically.
- NODE BUILTINS are always imported with the `node:` prefix (`node:http`, `node:path`, `node:fs`, `node:net`, `node:querystring`, `node:buffer`, `node:events`) — follow this in new code and in tests.
- TEST NAMING is a strict mirror of the API: `test/res.<method>.js`, `test/req.<getter>.js`, `test/app.<method>.js`. A new `res.foo` needs a new `test/res.foo.js`, not an addition to a catch-all file.
- TRAP — cross-directory coupling: the 18 files in `test/acceptance/` `require('../../examples/<name>')`, so an edit to an example app fails tests that do not mention it. Check `test/acceptance/` before touching `examples/`.
- TRAP — `test/exports.js` permanently mutates `express.application`, `express.request` and `express.response` for the remainder of the mocha process; new global-prototype tests can interact with it.
- TRAP — `--check-leaks` is enabled, so any implicit global created during a test (including in `lib/`) fails the run with a leak error rather than an assertion error.
- TRAP — the test env sets `NO_DEPRECATION=body-parser,express`, so `depd` warnings you add will be invisible locally; assert on behaviour, not on console output.
- SAFE EDIT BOUNDARY — routing, body parsing and static serving are NOT in this repo. Bugs in path matching, `req.params`, `req.body` or directory traversal belong to `router`, `body-parser`, `send` and `serve-static`.
- HIGH-CAUTION FILES: `lib/response.js` `res.send` (every response path), `lib/application.js` `app.handle` (every request), and `lib/utils.js` `compileTrust` (all proxy-derived security decisions). Changes here need matching test additions in `test/res.send.js`, `test/app.router.js` and `test/req.ip.js`.
- PUBLISHED SURFACE is limited by `package.json` `files` to `LICENSE`, `Readme.md`, `index.js`, `lib/` — a new runtime file must live under `lib/` to be shipped at all.
- CHANGELOG convention: `History.md` (127KB) is the maintained release log; commit subjects follow conventional-commit style with a PR reference, e.g. `fix(res.send): preserve ETag generation with Transfer-Encoding (#7459)`. (inferred)
- DEPENDENCY POLICY: dependabot is configured monthly for both npm and GitHub Actions and explicitly ignores all `semver-major` updates, so do not propose a major bump as part of a feature change.