@hublo/sentinel 1.3.0 → 1.4.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -4
- package/dist/bin/sentinel.d.ts +0 -1
- package/dist/bin/sentinel.js +22 -8
- package/dist/chunk-2XLX6PFR.js +132 -0
- package/dist/chunk-3TDUIKVQ.js +178 -0
- package/dist/{chunk-676GBPMS.js → chunk-4UIZJ3TR.js} +3399 -549
- package/dist/chunk-CPCUPK4J.js +70 -0
- package/dist/chunk-NX4GHIHF.js +25 -0
- package/dist/chunk-PWV3BMDA.js +15 -0
- package/dist/chunk-WLFE5RUU.js +264 -0
- package/dist/index.js +2 -1
- package/dist/roles/build/nest/toolchain.d.ts +4 -36
- package/dist/roles/build/nest/toolchain.js +10 -178
- package/dist/roles/build/nest/toolchain.js.map +1 -0
- package/dist/roles/build/toolchain.js.map +1 -0
- package/dist/roles/test/nest/toolchain.d.ts +29 -0
- package/dist/roles/test/nest/toolchain.js +289 -0
- package/dist/roles/test/nest/toolchain.js.map +1 -0
- package/dist/roles/test/react/toolchain.d.ts +62 -0
- package/dist/roles/test/react/toolchain.js +5 -0
- package/dist/roles/test/react/toolchain.js.map +1 -0
- package/dist/roles/test/setup/mock-extended.d.ts +46 -0
- package/dist/roles/test/setup/mock-extended.js +65 -0
- package/dist/roles/test/setup/mock-extended.js.map +1 -0
- package/dist/roles/test/setup/msw-lifecycle.d.ts +16 -0
- package/dist/roles/test/setup/msw-lifecycle.js +12 -0
- package/dist/roles/test/setup/msw-lifecycle.js.map +1 -0
- package/dist/roles/test/setup/msw-server.d.ts +3 -0
- package/dist/roles/test/setup/msw-server.js +10 -0
- package/dist/roles/test/setup/msw-server.js.map +1 -0
- package/dist/roles/test/setup/nest.d.ts +2 -0
- package/dist/roles/test/setup/nest.js +159 -0
- package/dist/roles/test/setup/nest.js.map +1 -0
- package/dist/roles/test/setup/workspace-entry.d.ts +2 -0
- package/dist/roles/test/setup/workspace-entry.js +8 -0
- package/dist/roles/test/setup/workspace-entry.js.map +1 -0
- package/dist/tsconfig-aliases-Ce6axdJ4.d.ts +36 -0
- package/docs/.gitkeep +0 -0
- package/docs/build-adoption.md +521 -0
- package/docs/format-adoption.md +321 -0
- package/docs/lint-adoption.md +290 -0
- package/docs/performance.md +49 -0
- package/docs/test-adoption.md +175 -0
- package/docs/typescript-adoption.md +184 -0
- package/docs/typescript-traces.md +798 -0
- package/docs/using-sentinel.md +180 -0
- package/docs/validating-a-change.md +101 -0
- package/package.json +32 -5
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Test adoption cheat sheet
|
|
2
|
+
|
|
3
|
+
Sentinel owns the test toolchain so a module can be tested on its own, and so the workspace can
|
|
4
|
+
drop Jest before Nx v24 removes `@nx/jest:jest`.
|
|
5
|
+
|
|
6
|
+
This page is written from measurement. Every number in it was taken on this monorepo, and the ones
|
|
7
|
+
that decided a design choice say which choice.
|
|
8
|
+
|
|
9
|
+
- [What the ground actually looks like](#what-the-ground-actually-looks-like)
|
|
10
|
+
- [The one line that makes a per-module migration possible](#the-one-line-that-makes-a-per-module-migration-possible)
|
|
11
|
+
- [What `--init --test` rewrites, and what it refuses](#what---init---test-rewrites-and-what-it-refuses)
|
|
12
|
+
- [Nest services](#nest-services)
|
|
13
|
+
- [The check that decides whether you are done](#the-check-that-decides-whether-you-are-done)
|
|
14
|
+
|
|
15
|
+
## What the ground actually looks like
|
|
16
|
+
|
|
17
|
+
| | |
|
|
18
|
+
| -------------------------------------------- | ---------------------------------------------- |
|
|
19
|
+
| test targets on `@nx/jest:jest` | **107**, the executor Nx v24 removes |
|
|
20
|
+
| on `@nx/vitest:test` | 3, all front, target named `test` |
|
|
21
|
+
| target `vitest`, inferred from an npm script | 2, both Svelte |
|
|
22
|
+
| jest configs | 108, of which **107 extend one shared preset** |
|
|
23
|
+
| environment | 102 `node`, 5 `jsdom` |
|
|
24
|
+
| test files | 6397, of which **3481 call `jest.*`** |
|
|
25
|
+
| files importing `jest-mock-extended` | **2491** |
|
|
26
|
+
|
|
27
|
+
Two consequences worth stating before anything else.
|
|
28
|
+
|
|
29
|
+
**The target is named `test` everywhere.** 107 modules already call it that, and so do the three
|
|
30
|
+
fronts already on Vitest. The two Svelte modules call it `vitest` only because that is the name of
|
|
31
|
+
an npm script, which nx turned into a target. Adopting under `test` means **CI never changes during
|
|
32
|
+
the migration**: a module on Jest and a module on Vitest both answer `nx affected -t test`.
|
|
33
|
+
|
|
34
|
+
**Tests are barely declared in `package.json` today.** Zero test targets in an `nx` block, four
|
|
35
|
+
`test` scripts in the whole repo. Where the build role usually rewired a script that existed, this
|
|
36
|
+
one creates it.
|
|
37
|
+
|
|
38
|
+
## Which targets are in scope, decided by one question
|
|
39
|
+
|
|
40
|
+
Not "which directory", because tests live wherever their authors put them. The question is whether
|
|
41
|
+
a target **breaks when Jest is removed**, and that is measurable:
|
|
42
|
+
|
|
43
|
+
| target | how many | what runs it | in scope |
|
|
44
|
+
| ------------------------------- | -------: | ------------------------------------------------ | -------- |
|
|
45
|
+
| `test` | 107 | `@nx/jest:jest` | **yes** |
|
|
46
|
+
| `test-integration` | 3 | jest, 14 files call the jest API | **yes** |
|
|
47
|
+
| `test-acceptance` and `-legacy` | 3 | `nx:run-commands`, 15 files call the jest API | **yes** |
|
|
48
|
+
| `test-prisma` | 1 | `@nx/jest:jest` with `jest.prisma.config.ts` | **yes** |
|
|
49
|
+
| `test-functional` | 1 | `@nx/jest:jest` with `jest.functional.config.ts` | **yes** |
|
|
50
|
+
| `stress-test` | 2 | `zx` scripts | no |
|
|
51
|
+
| `loadtests` | 2 | `k6` | no |
|
|
52
|
+
|
|
53
|
+
The last two never load Jest, so removing it changes nothing for them. Everything else does, which
|
|
54
|
+
also means the migration has more than one config per module to answer for: `jest.prisma.config.ts`
|
|
55
|
+
and `jest.functional.config.ts` are targets of their own.
|
|
56
|
+
|
|
57
|
+
## A module with no tests is cleaned, not configured
|
|
58
|
+
|
|
59
|
+
Six Nest services have **zero test files** and a green `test` target, because `passWithNoTests: true`
|
|
60
|
+
makes an empty run a success. Writing them a Vitest config would be furniture for a room nobody
|
|
61
|
+
enters.
|
|
62
|
+
|
|
63
|
+
What they need is the opposite: their Jest leftovers removed, so nothing breaks the day Jest leaves
|
|
64
|
+
the workspace. The Jest config file, the Jest dependencies, and nothing else. No new config, no new
|
|
65
|
+
script, no change to what they build or ship.
|
|
66
|
+
|
|
67
|
+
## The one line that makes a per-module migration possible
|
|
68
|
+
|
|
69
|
+
**104 files under `libs/` import `jest-mock-extended`.** They are shared test helpers, used by
|
|
70
|
+
modules on both runners. That package loads `@jest/globals`, which refuses to run outside Jest, so:
|
|
71
|
+
|
|
72
|
+
- migrating them breaks every module still on Jest
|
|
73
|
+
- leaving them breaks every module moved to Vitest
|
|
74
|
+
|
|
75
|
+
Old and new do **not** cohabit on shared helpers, and that alone would force a big-bang migration.
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
{ find: /^jest-mock-extended$/, replacement: 'vitest-mock-extended' }
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`vitest-mock-extended` is a fork of the Jest package and exports the same four names this repo uses
|
|
82
|
+
(`mockDeep`, `mock`, `DeepMockProxy`, `MockProxy`), so an unmigrated helper resolves to the Vitest
|
|
83
|
+
fork **inside an adopted module** and keeps resolving to the Jest one everywhere else. Measured on
|
|
84
|
+
`mission`: failing suites went from **159 to 10**.
|
|
85
|
+
|
|
86
|
+
A helper that uses the raw Jest API (`jest.fn()`) cannot be aliased, because that is a global rather
|
|
87
|
+
than an import. There is one such helper on the Nest side, consumed by 6 modules. Sentinel ships the
|
|
88
|
+
Vitest equivalent and an adopted module imports it from here, the same answer as the build role's
|
|
89
|
+
toolchain: the tool owns the shared piece, so the monorepo keeps one copy per runner and no
|
|
90
|
+
ordering constraint.
|
|
91
|
+
|
|
92
|
+
## What `--init --test` rewrites, and what it refuses
|
|
93
|
+
|
|
94
|
+
This role rewrites **test files**, not only configuration, and says so before it writes anything. A
|
|
95
|
+
config-only adoption would leave the module red, so it would be worth nothing.
|
|
96
|
+
|
|
97
|
+
| rule | files in this repo |
|
|
98
|
+
| ------------------------------------------------------------------------------------------------------------------- | ------------------ |
|
|
99
|
+
| `jest-mock-extended` → `vitest-mock-extended` | 2491 |
|
|
100
|
+
| `jest.*` → `vi.*` | 3481 |
|
|
101
|
+
| type references (`jest.Mock`, `jest.Mocked`, `jest.SpyInstance` → `MockInstance`, …) become an import from `vitest` | 744 occurrences |
|
|
102
|
+
| `jest.requireActual` → `await vi.importActual`, factory becomes `async` | 26 |
|
|
103
|
+
| `jest.requireMock` → the mocked module imported directly, since `vi.mock` makes a static import the mock | 16 |
|
|
104
|
+
| `jest.setTimeout(n)` → `vi.setConfig({ testTimeout: n })` | 4 |
|
|
105
|
+
| a mock factory returning a non-object → `{ default: … }`, which Vitest requires | 2 |
|
|
106
|
+
| `jest.isolateModules` → `vi.resetModules()` + `await import(...)` | 1 |
|
|
107
|
+
|
|
108
|
+
**Selection is on API USE, never on file name.** Test helpers carry the same API as specs:
|
|
109
|
+
`*.mock.ts`, `__mocks__/`, wrappers, fixtures. Ninety files in this repo are outside any spec glob.
|
|
110
|
+
|
|
111
|
+
**What it refuses is fenced off before any global rewrite.** The first version of this codemod
|
|
112
|
+
reported leaving two files alone and rewrote them anyway, producing `vi.requireMock`, which does not
|
|
113
|
+
exist. Announcing a refusal and performing the change is worse than either.
|
|
114
|
+
|
|
115
|
+
## Nest services
|
|
116
|
+
|
|
117
|
+
The wall is decorator metadata: Nest resolves a constructor's parameters from
|
|
118
|
+
`emitDecoratorMetadata`, and esbuild does not emit it. The build role's `decoratorMetadata` is a
|
|
119
|
+
**Vite plugin**, so Vitest loads it unchanged, with the two compile modes already copied from nx.
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
plugins: [decoratorMetadata({ root: here })],
|
|
123
|
+
resolve: { alias: [{ find: /^jest-mock-extended$/, replacement: 'vitest-mock-extended' },
|
|
124
|
+
...tsconfigAliases(workspaceRoot)] },
|
|
125
|
+
test: { globals: true, environment: 'node', isolate: true },
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**`isolate` stays `true`**, whatever the performance note in Vitest's docs suggests. Jest gives each
|
|
129
|
+
test file a fresh module registry and `isolate: true` reproduces that. Nest registers metadata as an
|
|
130
|
+
import SIDE EFFECT, a decorator writing into a catalog when its module loads, so a shared registry
|
|
131
|
+
would make results depend on the order files ran in. That is the same fact the build role records
|
|
132
|
+
next to `treeshake: { moduleSideEffects: true }`.
|
|
133
|
+
|
|
134
|
+
## The check that decides whether you are done
|
|
135
|
+
|
|
136
|
+
Not "the suite is green". **The suite gives the same result as before**, compared against a baseline
|
|
137
|
+
taken BEFORE the migration:
|
|
138
|
+
|
|
139
|
+
```console
|
|
140
|
+
$ nx run <module>:test # jest, and write the numbers down
|
|
141
|
+
$ sentinel --init --test
|
|
142
|
+
$ nx run <module>:test # vitest
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Take the baseline first, and compare the failing SETS rather than the counts. On `mission` the Jest
|
|
146
|
+
baseline was not green (10 suites failed on `setTimeout is not defined`), and all ten pass under
|
|
147
|
+
Vitest, so a count comparison would have read as a regression where there was an improvement.
|
|
148
|
+
|
|
149
|
+
A codemod touching thousands of files cannot be reviewed by hand. The suite against its baseline is
|
|
150
|
+
the review.
|
|
151
|
+
|
|
152
|
+
### And the typecheck, which the suite cannot stand in for
|
|
153
|
+
|
|
154
|
+
```console
|
|
155
|
+
$ nx run <module>:typecheck # BEFORE, and write that down too
|
|
156
|
+
$ sentinel --init --test
|
|
157
|
+
$ nx run <module>:typecheck # after
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
⚠️ **A green suite does not mean a green module.** The migration rewrites TYPES as well as calls,
|
|
161
|
+
and a wrong type is invisible to a run: the tests never evaluate it.
|
|
162
|
+
|
|
163
|
+
Measured on `apps/nest/microservices/agency`, by someone adopting the published package with no
|
|
164
|
+
knowledge of how it was built:
|
|
165
|
+
|
|
166
|
+
| | before | after |
|
|
167
|
+
| -------------------- | ------------ | ------------- |
|
|
168
|
+
| `--run --test` | 534 green | 534 green |
|
|
169
|
+
| `--run --typescript` | **0 errors** | **10 errors** |
|
|
170
|
+
|
|
171
|
+
All ten came from two wrong mappings, both since fixed. The point is not those ten. It is that a
|
|
172
|
+
module can be migrated, pass its whole suite, and carry a broken typecheck into main, where the
|
|
173
|
+
next CI run of a different target finds it.
|
|
174
|
+
|
|
175
|
+
Take both baselines. A migration is done when both come back the same.
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# TypeScript adoption cheat sheet
|
|
2
|
+
|
|
3
|
+
A quick reference for adopting the sentinel TypeScript preset on a module, and for the everyday `--run` / `--inspect` commands. For the full, live command-and-output traces, see [`typescript-traces.md`](typescript-traces.md).
|
|
4
|
+
|
|
5
|
+
## Adopt a module in two steps
|
|
6
|
+
|
|
7
|
+
Run from the **module's own directory**. You do not need to install sentinel to run it: use `npx --yes` for a one-off.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# 1. write the module's config + the pinned devDependency, AND the workspace prep it needs
|
|
11
|
+
cd apps/.../<module>
|
|
12
|
+
npx --yes @hublo/sentinel@<version> --init --typescript --preset <react|nest|node>
|
|
13
|
+
|
|
14
|
+
# 2. fetch what step 1 declared
|
|
15
|
+
pnpm install
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`--init` writes the module's `tsconfig` stub, its `typecheck` script, and pins `@hublo/sentinel@<version>` into the module's own `devDependencies` (so there is no manual `pnpm add`). It composes `extends: [<your base>, <preset>]` (keeping the monorepo's `paths` and structure), strips the options the preset owns (see below), and scaffolds a minimal `package.json` for a `project.json`-only module. Svelte is skipped (no preset yet). Always pass `--preset` in a monorepo: dependencies are hoisted, so detection can fall back to `node`.
|
|
19
|
+
|
|
20
|
+
**`--init` also applies the workspace prep the module needs**, the one place it reaches the workspace root, because "set this module up" is not honest if the workspace then cannot install or resolve it. It is defensive: each fix is applied only when the root's own config shows it is needed, and is a no-op otherwise.
|
|
21
|
+
|
|
22
|
+
- **release-age allow-list**: if the root uses pnpm's `minimumReleaseAgeExclude`, a freshly published sentinel is allow-listed so it installs.
|
|
23
|
+
- **i18next singleton override**: if the root declares a second TypeScript (via `@typescript/native`), i18next is pinned to one instance so a re-resolve does not fork react-i18next and break translation-singleton tests. The pin tracks the declared version and disappears once the migration removes the second TypeScript.
|
|
24
|
+
|
|
25
|
+
> **Renamed:** the option is `--preset`. `--flavour` was the original spelling and still
|
|
26
|
+
> works, so nothing you have already written breaks, but it prints a deprecation notice and
|
|
27
|
+
> will not be documented beyond this line.
|
|
28
|
+
|
|
29
|
+
## Verbs at a glance
|
|
30
|
+
|
|
31
|
+
| Verb | Who / when | Runs the tool? | What you get |
|
|
32
|
+
| ----------- | ------------------ | ----------------- | --------------------------------------------------------------------------- |
|
|
33
|
+
| `--run` | dev, everyday | **yes** (tsc) | executes the check, pass/fail. This is your `typecheck` script. |
|
|
34
|
+
| `--inspect` | dev, everyday | no (reads config) | the resolved config: preset, target, and which rules are deferred |
|
|
35
|
+
| `--init` | dev, once to adopt | writes files | the tsconfig stub, the `typecheck` script, the pinned dep, + workspace prep |
|
|
36
|
+
| `--migrate` | planned | not available yet | reserved for changing an already-initialized setup (later ticket) |
|
|
37
|
+
| `--report` | **deprecated** | — | use `--run --json`: same work, same envelope |
|
|
38
|
+
| `--status` | **deprecated** | — | use `--inspect`, which absorbed it |
|
|
39
|
+
|
|
40
|
+
**`--run` vs `--inspect`:** run _executes_ the check and gives you pass/fail plus the numbers (`--json` for error counts and structured diagnostics); inspect _reads_ the config and shows what applies, instantly and without running the tool, including adoption and conformity across modules.
|
|
41
|
+
|
|
42
|
+
> **Deprecated, still working:** `--report` and `--status` were separate verbs and folded into
|
|
43
|
+
> the two above, because they answered the same two questions in a different shape. Both still
|
|
44
|
+
> run and print a notice telling you what to use instead.
|
|
45
|
+
|
|
46
|
+
**Location** (any verb): from a module dir → that module; from the root → `--module <name>`, `--ci` (affected), or all modules. `--init` writes, so it targets one module only.
|
|
47
|
+
|
|
48
|
+
**Your tsconfig extends exactly one preset.** The options every stack shares are flattened into
|
|
49
|
+
each published preset at build time, so `@hublo/sentinel/tsconfig/react` is self-contained and
|
|
50
|
+
there is nothing to stack under it. The shared layer lives in the repo as
|
|
51
|
+
`src/roles/typescript/presets/shared.json`; it is ours and is never published on its own.
|
|
52
|
+
|
|
53
|
+
## Everyday use (developers)
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
sentinel --run --typescript # in a module dir: type-check it (your `typecheck` script)
|
|
57
|
+
sentinel --inspect --typescript # what config/preset applies here, and what's deferred
|
|
58
|
+
sentinel --init --typescript --preset nest # adopt this module (writes the stub)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## CI & reporting
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
sentinel --run --typescript --ci # from the root: check affected modules, non-zero exit on failure
|
|
65
|
+
sentinel --run --typescript --json # machine-readable metrics + diagnostics for a dashboard
|
|
66
|
+
sentinel --inspect --typescript # adoption + conformity coverage across the workspace
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Options
|
|
70
|
+
|
|
71
|
+
| Option | What it does |
|
|
72
|
+
| ----------------------- | ----------------------------------------------------------------------------------- |
|
|
73
|
+
| `--preset <name>` | override detection (`react` / `nest` / `svelte` / `node`); recommended for `--init` |
|
|
74
|
+
| `--module <name>` | from the root, scope to one module |
|
|
75
|
+
| `--ci` | from the root, only affected modules; non-zero exit on failure |
|
|
76
|
+
| `--json` | machine-readable envelope on stdout, for every verb; progress goes to stderr |
|
|
77
|
+
| `--max-diagnostics <n>` | cap the diagnostics embedded per module in `--run --json` (`0` = no cap) |
|
|
78
|
+
| `--dry-run` | preview what `--init` would write, changing nothing |
|
|
79
|
+
| `--runner <tool>` | override the default runner |
|
|
80
|
+
| `-- <tool options>` | `--run` only, one type named: everything after `--` goes to tsc itself (see below) |
|
|
81
|
+
|
|
82
|
+
## Measuring a deferred rule
|
|
83
|
+
|
|
84
|
+
`--inspect` tells you a rule is deferred. That is honest, and it is not the question you
|
|
85
|
+
actually have, which is how much debt is behind it. Everything after `--` is handed to tsc,
|
|
86
|
+
so you can ask directly:
|
|
87
|
+
|
|
88
|
+
```console
|
|
89
|
+
$ sentinel --run --typescript -- --noImplicitAny
|
|
90
|
+
sentinel: NOT the standard check — passing --noImplicitAny to the typescript tool.
|
|
91
|
+
src/…/bulk-update-profile-exclusions-request.dto.ts(77,16): error TS7006: Parameter 'item' implicitly has an 'any' type.
|
|
92
|
+
…
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Three things worth knowing:
|
|
96
|
+
|
|
97
|
+
- **The result is not your normal check**, and it says so on every run, plus `toolArgs` in
|
|
98
|
+
the `--json` envelope. These options can weaken a check as easily as strengthen it, and
|
|
99
|
+
`--run` is what CI calls, so never put one in a committed `typecheck` script.
|
|
100
|
+
- **Emit is forced off.** This asks a question about the code, it does not build, so it will
|
|
101
|
+
not touch your build output or `.tsbuildinfo`.
|
|
102
|
+
- **It checks the projects your `tsconfig.json` references**, not the solution file itself,
|
|
103
|
+
which holds no files. Build mode cannot serve this at all: it refuses compiler options
|
|
104
|
+
outright (`error TS5094: Compiler option '--noImplicitAny' may not be used with '--build'`).
|
|
105
|
+
|
|
106
|
+
## Reading the numbers
|
|
107
|
+
|
|
108
|
+
A plain `--run` streams tsc's own output, so you read the errors themselves, and the summary
|
|
109
|
+
row is just the pass/fail mark. The counts are in the machine envelope:
|
|
110
|
+
|
|
111
|
+
```console
|
|
112
|
+
$ sentinel --run --typescript --json --max-diagnostics 1
|
|
113
|
+
{
|
|
114
|
+
"schemaVersion": 2,
|
|
115
|
+
"results": [
|
|
116
|
+
{
|
|
117
|
+
"project": "network",
|
|
118
|
+
"target": "typescript",
|
|
119
|
+
"flavour": "node",
|
|
120
|
+
"ok": false,
|
|
121
|
+
"status": "failed",
|
|
122
|
+
"errors": 9,
|
|
123
|
+
"implicitAny": "deferred",
|
|
124
|
+
"diagnostics": [
|
|
125
|
+
{ "file": "src/…/contractual-information.pg.repository.ts", "line": 133, "col": 21,
|
|
126
|
+
"code": "TS2339", "message": "Property 'closedAt' does not exist on type …" }
|
|
127
|
+
],
|
|
128
|
+
"diagnosticsTruncated": true
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
- `errors` is the authoritative type-error count; `diagnostics` carries each one
|
|
135
|
+
(`{file, line, col, code, message}`), capped by `--max-diagnostics`, with
|
|
136
|
+
`diagnosticsTruncated` saying when the cap dropped some, so nothing is hidden silently.
|
|
137
|
+
- `implicitAny: "deferred"` means the rule is off in phase 1 (non-breaking adoption),
|
|
138
|
+
reported honestly instead of a misleading `0`. To find out what it is hiding, see
|
|
139
|
+
[Measuring a deferred rule](#measuring-a-deferred-rule).
|
|
140
|
+
- `flavour` is the stack (`node` here). The key kept its old name on purpose: `preset`
|
|
141
|
+
already means the preset PATH in this shape, and the migration status page reads
|
|
142
|
+
`flavour`.
|
|
143
|
+
|
|
144
|
+
## Troubleshooting
|
|
145
|
+
|
|
146
|
+
| Symptom | Cause and fix |
|
|
147
|
+
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
148
|
+
| `target(s) not available yet: --test` | only wired types run; `--help` lists what is available now vs planned |
|
|
149
|
+
| `preset is ambiguous, detected "react" ... also found sails` | a mixed stack; pass `--preset` to be explicit |
|
|
150
|
+
| `sentinel requires Node >= 20.12` | run sentinel with a modern Node via `fnm`/`nvm`; the project's own Node is untouched |
|
|
151
|
+
| fresh version will not install (`minimumReleaseAge`) | `--init` allow-lists sentinel automatically when the root uses the gate; if you adopted by hand, add the package to `minimumReleaseAgeExclude` (or `--config.minimumReleaseAge=0`) until it ages |
|
|
152
|
+
| a translation-singleton test goes red after adopting a front module | `--init` handles this automatically when the root runs more than one TypeScript: adopting re-resolves pnpm and can duplicate a package with an optional, type-only `typescript` peer (e.g. i18next); `--init` pins that peer so it collapses to one instance |
|
|
153
|
+
|
|
154
|
+
## Phase 1 is non-breaking
|
|
155
|
+
|
|
156
|
+
Adoption is a lateral move: `noImplicitAny` / `noUnusedLocals` / `noUnusedParameters` are deferred (a warning says so; `--inspect` lists them). Enabling them centrally in a later wave is the migration.
|
|
157
|
+
|
|
158
|
+
## What `--init` strips, and what it leaves alone
|
|
159
|
+
|
|
160
|
+
Your tsconfig ends up extending **two** things: your workspace base, then the sentinel preset. That is what decides which of your `compilerOptions` survive.
|
|
161
|
+
|
|
162
|
+
| Your option | What happens | Why |
|
|
163
|
+
| ----------------------------------------------------------------------------- | ---------------------- | ------------------------------------------------------------- |
|
|
164
|
+
| the preset sets it (`strict`, `jsx`, `module`, …) | **stripped** | the preset's value governs, which is the point of adopting it |
|
|
165
|
+
| on the allowlist (`paths`, `baseUrl`, `rootDir`, `outDir`, `tsBuildInfoFile`) | kept, silently | genuinely project-specific, no preset can know them |
|
|
166
|
+
| the preset does **not** set it | **kept, and reported** | see below |
|
|
167
|
+
|
|
168
|
+
That last row exists because of a real breakage. `libs/nest/swagger-typescript-types` had:
|
|
169
|
+
|
|
170
|
+
```jsonc
|
|
171
|
+
{ "extends": "../../../tsconfig.base.json", "compilerOptions": { "verbatimModuleSyntax": false } } // deliberately opting OUT
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The workspace base sets `verbatimModuleSyntax: true`; the sentinel preset does not set it at all. Removing that line therefore did not hand the decision to the preset, it handed it back to the base: the option switched on and nine files stopped compiling, while `--init` reported success. Across the Hublo monorepo, **205 tsconfigs** set an option this way, mostly `importHelpers: false`.
|
|
175
|
+
|
|
176
|
+
So an option the preset has no opinion on is **yours**, not drift. `--init` keeps it and says so:
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
KEPT this module's own compilerOptions (verbatimModuleSyntax): the preset does not set them,
|
|
180
|
+
so they are yours rather than drift. Removing them would not hand the decision to the preset,
|
|
181
|
+
it would hand it to the workspace tsconfig this module extends
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
It is not reported as drift by `--inspect` either, so a module that legitimately overrides the base does not read as non-conformant forever.
|