docs(P03): complete language-derived extension — v0.4
---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---
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user