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---
5.6 KiB
5.6 KiB
TypeScript Tooling — Derived Application
Applies Atelier's domain principles to TypeScript tooling specifically. Derives from
domains/docs; introduces no new P-rules (D-063). Seelanguages/typescript.mdfor the language first-principles stub.
tsc and tsconfig Discipline (DevOps P2 Automation, DevOps P1 Reproducibility)
strict: trueis the floor, not the ceiling: it enablesstrictNullChecks,noImplicitAny,strictFunctionTypes, and more. Disable sub-flags only with a justification comment.tsc --noEmitin 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.tsconfigis 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.noUncheckedIndexedAccessfor safety:arr[i]becomesT | undefined, forcing narrowing. Costs little, prevents a class of out-of-bounds deref bugs.- Applies
devops/P1(reproducibility): pinnedtypescriptversion inpackage.jsonandlockfileensure every CI run type-checks against the same compiler.
// 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-eslintstrict ruleset:recommended-type-checkedenables 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-prettierdisables conflicting format rules. Do not relitigate formatting in code review. no-floating-promisesenforcesconcurrency/P8(timeout discipline): an un-awaitedPromiseis a fire-and-forget that swallows errors and timeouts. The rule forces.catch()orawait.
// .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+referenceslettsc --buildincrementally type-check only changed projects, and enforce the dependency graph at the type level. pathsaliases mirror the import structure:@app/*→src/*. Configure once intsconfig, mirror in the bundler and the test runner so all three agree.ts-jest(orvitest) withisolatedModules: true: each test file is type-checked in isolation, matching how the bundler transpiles. Catches the "passes intscbut fails in the bundler" gap.- Applies
devops/P2: the build pipeline (tsc → lint → test → bundle) is automated; a developer never runs a manual sequence.
// tsconfig.references.json
{
"files": [],
"references": [
{ "path": "./packages/core" },
{ "path": "./packages/api" },
{ "path": "./packages/web" }
]
}
Lockfile and Reproducible Install (DevOps P1 Reproducibility)
npm ciin CI, notnpm install:cireads the lockfile exactly and fails on drift.installmutates the lockfile.- Lockfile committed for applications: for libraries, commit
package-lock.jsonfor 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.
# 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(orTypeDoc) generates API docs fromtsdoccomments. The compiler enforces that@paramnames match real parameters. @exampleblocks are compiled: atsdoc@examplefenced 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.
/**
* 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.