Config files reference
Every build, test and tooling config that ships with ShipVeryFast, and exactly what each one controls. These are the files at the repo root you rarely touch but want to understand before you change anything.
All paths below are relative to the repo root. Values are quoted verbatim from the source, if something here disagrees with the file on disk, trust the file.
tsconfig.json
The TypeScript compiler config. A few options are load-bearing for a Next.js App Router project:
| Option | Value | Why |
|---|---|---|
strict | true | Full strict-mode type checking across the codebase. |
noEmit | true | TypeScript only type-checks; Next.js / SWC does the actual transpilation. |
jsx | "preserve" | Leaves JSX untouched so next build can compile it. (Jest overrides this, see below.) |
moduleResolution | "bundler" | Matches how the bundler resolves imports. |
paths | { "@/*": ["./*"] } | The @/ alias resolves to the repo root, so @/libs/config means ./libs/config. |
plugins | [{ "name": "next" }] | The Next.js TypeScript plugin (typed routes, server/client checks). |
include covers **/*.ts, **/*.tsx and .next/types/**/*.ts; exclude drops node_modules and temp-disabled. The @/* alias is mirrored in jest.config.js so the same imports resolve in tests.
jest.config.js
The unit and integration test config. It uses the ts-jest preset with the jsdom test environment, loads jest.setup.ts via setupFilesAfterEnv, and has a 30-second testTimeout (generous enough for integration tests).
Module & asset resolution
moduleNameMapper wires up three things so imports resolve the same way they do at build time:
^@/(.*)$→<rootDir>/$1(the@/alias).- CSS/SCSS imports →
identity-obj-proxy(stubbed class names). - Image imports (
png/jpg/jpeg/gif/svg) →<rootDir>/__mocks__/fileMock.js.
The project tsconfig.json uses jsx: "preserve" (required by next build), but ts-jest needs real JS, so the transform block overrides it to jsx: "react-jsx" just for tests.
Coverage
Coverage is off by default (collectCoverage: false) and only runs when you ask for it (npm run test:coverage). collectCoverageFrom globs over app, components, libs and models, then excludes presentational shells, config files, middleware and thin SDK adapters. The thresholds are a ratchet: set to the current real coverage level (~10% global) so CI passes, and they can only be raised over time toward the 80% goal.
// jest.config.js, coverage thresholds (the ratchet)
coverageThreshold: {
global: {
branches: 10,
functions: 5,
lines: 10,
statements: 10
}
},
// A representative slice of collectCoverageFrom:
collectCoverageFrom: [
'app/**/*.{ts,tsx}',
'components/**/*.{ts,tsx}',
'libs/**/*.{ts,tsx}',
'models/**/*.{ts,tsx}',
'!**/*.d.ts',
'!**/*.config.{js,ts}',
'!**/middleware.ts', // complex to test
'!components/providers/**', // integration-tested
'!libs/ai/**', // thin SDK adapters
'!app/api/ai/**', // streams from the SDKs
'!**/tests/**'
]Reporters include text, lcov, html and cobertura; the HTML report lands in coverage/. clearMocks and restoreMocks are both on, so mocks reset between tests.
playwright.config.ts
The end-to-end test config. testDir is ./tests/e2e, the per-test timeout is 30s and assertion expect.timeout is 10s. Tests run fullyParallel; on CI they retry twice and use 3 workers, and forbidOnly fails the build if a stray test.only slips in.
Base URL, locale & artifacts
use.baseURL defaults to http://localhost:3000 (overridable with PLAYWRIGHT_BASE_URL). Browser locale is pinned to en-US and timezone to America/New_York for deterministic runs. Traces are captured on-first-retry, screenshots only-on-failure, and video retain-on-failure; everything writes to test-results/.
Browser projects
On CI it runs chromium only for speed; locally it runs the full matrix: Desktop Chromium, Desktop Firefox, Desktop Safari (webkit), plus Mobile Chrome (Pixel 5) and Mobile Safari (iPhone 12).
webServer
Playwright boots the app itself before running. On CI it serves the production build (npm run start, after the build step); locally it uses the dev server and reuses one if it's already running.
// playwright.config.ts, webServer
webServer: {
command: process.env.CI ? 'npm run start' : 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
timeout: 120 * 1000,
},docker-compose.yml
A single service, db-test, for spinning up a throwaway Postgres for integration tests. It maps host port 5433to the container's 5432 so it never clashes with a local Postgres on the default port.
# docker-compose.yml
version: '3.8'
services:
db-test:
image: postgres:15-alpine
restart: unless-stopped
ports:
- "5433:5432"
environment:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: shipveryfast_testBring it up and down with the bundled scripts, npm run db:test:up runs docker-compose up -d db-test and npm run db:test:down runs docker-compose down. The connection details are fixed: database shipveryfast_test, user/password test/test, on localhost:5433.
feature-flags.json
The JSON store that backs libs/featureFlags.ts. Each flag has an enabled boolean, a description and a dependencies array. The library reads this file from process.cwd() at runtime, and isFeatureEnabled(key) is the main entry point.
| Flag | Enabled | Depends on |
|---|---|---|
auth | true | None |
blog | true | None |
darkMode | true | None |
stripe | true | auth |
email | true | auth |
teams | false | auth |
Dependencies are enforced: validateDependencies()throws if an enabled flag's dependency is disabled, and enableFeature() / disableFeature() cascade the change to dependencies and dependents. See Feature flags for the runtime API.
next.config.js and the pre-commit hook
next.config.js
Configures image optimization and, in production, a full set of security headers. Images allow image/avif and image/webp, with domains and remotePatterns for Supabase, Stripe, GitHub user content and a few others. The headers() function returns no headers in development (so nothing blocks local resources) and the full hardened set in production, Strict-Transport-Security, X-Frame-Options: DENY, a Content-Security-Policy, COOP/COEP/CORP and more. See Security for the header rationale.
postcss.config.js
Minimal: it wires tailwindcss and autoprefixer into the PostCSS pipeline. That's the entire file.
The Husky pre-commit hook
Husky (v9) is installed via the prepare script (husky install) on npm install. The hook lives at .husky/pre-commit and runs lint plus the fast unit-test path before every commit:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# Run linting
npm run lint
# Run unit tests (quick feedback)
npm run test:unit
# Check if coverage meets minimum threshold
echo "Checking test coverage..."
npm run test:coverage:unit -- --passWithNoTests
echo "✅ Pre-commit checks passed!"Because test:coverage:unit reuses the Jest coverageThreshold ratchet, a commit will fail if coverage drops below the current floor, the same gate CI enforces.
