4e433158cd
---ci--- project: atelier phase: 3 milestone: v0.4 status: complete phase_role: execution phase_tag: v0.3.3 requirements: covered: [ATELIER-102, ATELIER-103, ATELIER-104, ATELIER-105] partial: [] ---/ci---
114 lines
5.6 KiB
Markdown
114 lines
5.6 KiB
Markdown
# TypeScript Tooling — Derived Application
|
|
|
|
> Applies Atelier's domain principles to TypeScript tooling specifically.
|
|
> Derives from `domains/` docs; introduces no new P-rules (D-063).
|
|
> See `languages/typescript.md` for the language first-principles stub.
|
|
|
|
## tsc and tsconfig Discipline (DevOps P2 Automation, DevOps P1 Reproducibility)
|
|
|
|
- **`strict: true` is the floor, not the ceiling:** it enables `strictNullChecks`, `noImplicitAny`, `strictFunctionTypes`, and more. Disable sub-flags only with a justification comment.
|
|
- **`tsc --noEmit` in CI:** type-checking is a build gate; emission is the bundler's job. Separate the two so a type error fails CI even when the bundler would have succeeded.
|
|
- **`tsconfig` is per-project, not inherited verbatim:** a shared base (`extends`) encodes org defaults; each project overrides the deltas it needs. Avoids the "one monoreto-config-fits-all" trap.
|
|
- **`noUncheckedIndexedAccess` for safety:** `arr[i]` becomes `T | undefined`, forcing narrowing. Costs little, prevents a class of out-of-bounds deref bugs.
|
|
- **Applies `devops/P1` (reproducibility):** pinned `typescript` version in `package.json` and `lockfile` ensure every CI run type-checks against the same compiler.
|
|
|
|
```jsonc
|
|
// tsconfig.json — base
|
|
{
|
|
"compilerOptions": {
|
|
"strict": true,
|
|
"noUncheckedIndexedAccess": true,
|
|
"exactOptionalPropertyTypes": true,
|
|
"noEmit": true,
|
|
"moduleResolution": "bundler",
|
|
"isolatedModules": true
|
|
}
|
|
}
|
|
```
|
|
|
|
## ESLint and @typescript-eslint (DevOps P2 Automation, Documentation P9 Living Documents)
|
|
|
|
- **ESLint with `@typescript-eslint` strict ruleset:** `recommended-type-checked` enables rules that require the type checker (`no-floating-promises`, `no-misused-promises`).
|
|
- **Rules encode decisions, not taste:** every custom rule in the config has a one-line `// reason:` comment linking to the principle it enforces. This makes the config a living document (Documentation P9).
|
|
- **Format is Prettier's job; ESLint lints:** `eslint-config-prettier` disables conflicting format rules. Do not relitigate formatting in code review.
|
|
- **`no-floating-promises` enforces `concurrency/P8` (timeout discipline):** an un-awaited `Promise` is a fire-and-forget that swallows errors and timeouts. The rule forces `.catch()` or `await`.
|
|
|
|
```jsonc
|
|
// .eslintrc.json
|
|
{
|
|
"extends": [
|
|
"eslint:recommended",
|
|
"plugin:@typescript-eslint/recommended-type-checked",
|
|
"prettier"
|
|
],
|
|
"parserOptions": { "project": "./tsconfig.json" },
|
|
"rules": {
|
|
// reason: enforce Concurrency P8 — no un-awaited promises
|
|
"@typescript-eslint/no-floating-promises": "error",
|
|
// reason: enforce Data P7 — no `any` escaping the type checker
|
|
"@typescript-eslint/no-explicit-any": "error"
|
|
}
|
|
}
|
|
```
|
|
|
|
## Project References and ts-jest (DevOps P2 Automation, C6 Composability)
|
|
|
|
- **Project references for monorepos:** `composite: true` + `references` let `tsc --build` incrementally type-check only changed projects, and enforce the dependency graph at the type level.
|
|
- **`paths` aliases mirror the import structure:** `@app/*` → `src/*`. Configure once in `tsconfig`, mirror in the bundler and the test runner so all three agree.
|
|
- **`ts-jest` (or `vitest`) with `isolatedModules: true`:** each test file is type-checked in isolation, matching how the bundler transpiles. Catches the "passes in `tsc` but fails in the bundler" gap.
|
|
- **Applies `devops/P2`:** the build pipeline (tsc → lint → test → bundle) is automated; a developer never runs a manual sequence.
|
|
|
|
```jsonc
|
|
// tsconfig.references.json
|
|
{
|
|
"files": [],
|
|
"references": [
|
|
{ "path": "./packages/core" },
|
|
{ "path": "./packages/api" },
|
|
{ "path": "./packages/web" }
|
|
]
|
|
}
|
|
```
|
|
|
|
## Lockfile and Reproducible Install (DevOps P1 Reproducibility)
|
|
|
|
- **`npm ci` in CI, not `npm install`:** `ci` reads the lockfile exactly and fails on drift. `install` mutates the lockfile.
|
|
- **Lockfile committed for applications:** for libraries, commit `package-lock.json` for CI reproducibility even though consumers resolve their own tree.
|
|
- **No floating ranges in `package.json`:** `^` and `~` are CI's job to resolve; pin the resolved version in the lockfile. An unpinned `*` is a supply-chain attack surface.
|
|
|
|
```bash
|
|
# CI install step — deterministic
|
|
npm ci
|
|
# Type-check gate
|
|
npx tsc --noEmit
|
|
# Lint gate
|
|
npx eslint .
|
|
```
|
|
|
|
## Documentation in the Pipeline (Documentation P1 Documentation is Code, DevOps P9 Documentation in the Pipeline)
|
|
|
|
- **Type-checked JSDoc:** `typedoc` (or `TypeDoc`) generates API docs from `tsdoc` comments. The compiler enforces that `@param` names match real parameters.
|
|
- **`@example` blocks are compiled:** a `tsdoc` `@example` fenced block is type-checked as part of the doc build. Stale examples fail the pipeline (Documentation P1 — docs are code).
|
|
- **README badges reflect CI status:** the build/lint/test/type-check gates are the source of truth; badges surface them. Do not hand-edit status tables.
|
|
|
|
```typescript
|
|
/**
|
|
* Fetch a user by ID.
|
|
*
|
|
* @param id - a branded UserId (see ts-types.md).
|
|
* @throws {NotFoundError} if the user does not exist.
|
|
* @example
|
|
* ```ts
|
|
* const u = await getUser(userId('abc'));
|
|
* ```
|
|
*/
|
|
async function getUser(id: UserId): Promise<User> { /* ... */ }
|
|
```
|
|
|
|
## Cross-References
|
|
|
|
- `domains/devops/ci-cd.md` — the pipeline gates that host tsc/ESLint/ts-jest.
|
|
- `domains/devops/first-principles.md` — DevOps P1 Reproducibility, P2 Automation.
|
|
- `domains/documentation/first-principles.md` — Documentation P1 Documentation is Code.
|
|
- `languages/ts-types.md` — the type rules ESLint enforces reference this doc.
|
|
- `languages/ts-testing.md` — the test-runner config (`ts-jest`/`vitest`) detailed here. |