@hublo/sentinel 1.5.0-alpha.9 → 1.5.0
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 +9 -7
- package/dist/bin/sentinel.js +61 -16
- package/dist/{chunk-KLEXJFJ6.js → chunk-43HQ6BVJ.js} +61 -15
- package/dist/{chunk-YQRDKWSH.js → chunk-IL5PPLXE.js} +11 -1
- package/dist/chunk-IL5PPLXE.js.map +1 -0
- package/dist/chunk-MOELAQPC.js +10365 -0
- package/dist/chunk-WBDBRGLV.js +214 -0
- package/dist/chunk-WBDBRGLV.js.map +1 -0
- package/dist/{chunk-AMCGMLPS.js → chunk-Y57KA4QS.js} +2 -2
- package/dist/index.d.ts +28 -1
- package/dist/index.js +4 -4
- package/dist/roles/build/nest/toolchain.js +68 -18
- package/dist/roles/build/nest/toolchain.js.map +1 -1
- package/dist/roles/static-analysis/config.d.ts +15 -1
- package/dist/roles/static-analysis/config.js +1 -1
- package/dist/roles/test/setup/jest-parity.js +2 -2
- package/dist/roles/test/setup/workspace-entry.js +2 -2
- package/dist/{validate-AWWMBSH5.js → validate-FRMENKHM.js} +2 -2
- package/docs/bundle-analysis-spec.md +272 -0
- package/docs/sql-verification-study.md +401 -0
- package/docs/static-analysis-adoption.md +285 -0
- package/docs/static-analysis-study.md +824 -18
- package/docs/typescript-adoption.md +1 -1
- package/docs/typescript-traces.md +27 -12
- package/docs/using-sentinel.md +22 -12
- package/docs/validating-a-change.md +185 -0
- package/lint/nest.json +98 -18
- package/lint/node.json +83 -3
- package/lint/react-lib.json +121 -31
- package/lint/react.json +101 -2
- package/package.json +27 -6
- package/plugins/hublo/no-palette-common.js +76 -0
- package/plugins/hublo/no-regex-lookbehind.js +70 -0
- package/plugins/hublo/require-reload-document.js +70 -0
- package/plugins/hublo.js +20 -145
- package/plugins/sonar-cognitive.js +108 -0
- package/dist/chunk-EDFIVLUA.js +0 -100
- package/dist/chunk-EDFIVLUA.js.map +0 -1
- package/dist/chunk-OZZICSSZ.js +0 -6469
- package/dist/chunk-YQRDKWSH.js.map +0 -1
- /package/dist/{chunk-AMCGMLPS.js.map → chunk-Y57KA4QS.js.map} +0 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# Static analysis adoption cheat sheet
|
|
2
|
+
|
|
3
|
+
A quick reference for adopting the sentinel static analysis role on a module, and for reading what it prints. The reasoning behind every number, and the measurements that fixed the thresholds, are in [`static-analysis-study.md`](static-analysis-study.md); this page is what you need to run it.
|
|
4
|
+
|
|
5
|
+
**It measures, it never refuses.** Nothing here fails a build or blocks a merge. A figure answers "is this module drifting", which is a question about a trend, and a check that blocks a merge on a trend blocks the wrong thing. Refusing a single unit is the linter's job and oxlint already does it.
|
|
6
|
+
|
|
7
|
+
## Adopt a module in two steps
|
|
8
|
+
|
|
9
|
+
Run from the **module's own directory**. You do not need to install sentinel first: use `npx --yes` for a one-off.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# 1. write the module's config + the pinned devDependency
|
|
13
|
+
cd apps/.../<module>
|
|
14
|
+
npx --yes @hublo/sentinel@<version> --init --static-analysis
|
|
15
|
+
|
|
16
|
+
# 2. fetch what step 1 declared
|
|
17
|
+
pnpm install
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`--init` writes `static-analysis.config.mjs`, adds the three scripts below to the module's `package.json` along with its nx target, pins `@hublo/sentinel` into its own `devDependencies`, and ignores the report directory in the repository's root `.gitignore`. It prints what it did:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
wrote static-analysis.config.mjs
|
|
24
|
+
wrote package.json
|
|
25
|
+
added the three `static-analysis` scripts and static-analysis.config.mjs; declare
|
|
26
|
+
this module's entry points there, everything else comes from the role
|
|
27
|
+
ignored static-analysis-report/ in the root .gitignore
|
|
28
|
+
run `pnpm install` to apply the workspace changes
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
⚠️ **If your module has a `project.json` and no `package.json`, `--init` creates one.** Measured on
|
|
32
|
+
this repository, **49 of 243** nx projects are in that case, and none of them has adopted any
|
|
33
|
+
sentinel role yet, which is the same fact seen from the other side. The consequence is worth
|
|
34
|
+
knowing before you read the diff: the pnpm workspace globs are `apps/**` and `libs/**`, so a new
|
|
35
|
+
`package.json` there makes the directory a workspace package and the lockfile gains an importer for
|
|
36
|
+
it. Verified: `pnpm install` stays green, and nx keeps reading the project, now from two files.
|
|
37
|
+
|
|
38
|
+
This is not specific to this role. `--init --format` on the same module prints `create
|
|
39
|
+
package.json` too: it is the shape of every sentinel adoption, because a role declares its nx
|
|
40
|
+
target in `package.json` and nowhere else.
|
|
41
|
+
|
|
42
|
+
**Nothing else is required.** The config stub is written with an empty `entry` on purpose: for most modules the figures are already right without declaring anything, and the stub exists so you can correct the one case where they are not (see [Declaring entry points](#declaring-entry-points)).
|
|
43
|
+
|
|
44
|
+
## Verbs at a glance
|
|
45
|
+
|
|
46
|
+
| Verb | Who / when | Runs the tools? | What you get |
|
|
47
|
+
| ----------- | ------------------ | ----------------- | --------------------------------------------------------------------- |
|
|
48
|
+
| `--run` | dev, everyday | **yes** | the figures, in one of four shapes. Never exits non-zero on a finding |
|
|
49
|
+
| `--inspect` | dev, everyday | no (reads config) | the resolved settings, and whether this module has adopted |
|
|
50
|
+
| `--init` | dev, once to adopt | writes files | the config stub, the three scripts, the pinned dep |
|
|
51
|
+
| `--migrate` | planned | not available yet | reserved for changing an already-initialized setup |
|
|
52
|
+
|
|
53
|
+
**Location** (any verb): from a module dir → that module; from the root → `--module <name>`, `--ci` (affected), or every module. `--init` writes, so it targets one module only.
|
|
54
|
+
|
|
55
|
+
⚠️ **From the workspace root, `pnpm exec sentinel` does not resolve.** sentinel is a devDependency of each adopting MODULE, not of the root, so the root's `.bin` has no `sentinel` and you get `Command "sentinel" not found`. Three things do work:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pnpm --filter <module> exec sentinel --run --static-analysis # the module becomes the cwd
|
|
59
|
+
pnpm --filter <module> run static-analysis # its own script, same thing
|
|
60
|
+
npx @hublo/sentinel --run --static-analysis --module <module> # when nothing is installed
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`--module` is for the third form. With the first two the cwd is already the module, so it is redundant.
|
|
64
|
+
|
|
65
|
+
## Everyday use
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pnpm run static-analysis # the summary, in your terminal. Writes nothing
|
|
69
|
+
pnpm run static-analysis:json # static-analysis-report/static-analysis.json, for a machine
|
|
70
|
+
pnpm run static-analysis:html # static-analysis-report/static-analysis.html, for a reader
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Or without the scripts:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
sentinel --run --static-analysis # same as the first script
|
|
77
|
+
sentinel --run --static-analysis --output html # same as the third
|
|
78
|
+
sentinel --run --static-analysis --output markdown # static-analysis.md, the pull-request recap
|
|
79
|
+
sentinel --inspect --static-analysis # the settings, without running anything
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Which shape to use.** `console` is the one you run in a loop: it writes nothing and skips the expensive drawing. `json` is for CI and for a script. `html` is the one you open and send: it carries every place with **the code behind it** and an explorable import graph. `markdown` is six lines for a pull-request comment, and it has no npm script on purpose: nobody runs it by hand.
|
|
83
|
+
|
|
84
|
+
⚠️ The `html` shape pays for a graph layout that the other three skip, 5 seconds on the largest module measured against 14 ms for the rest of the graph pass. That is fine for a page somebody opens and wrong for a commit hook. `html` and `json` also open the source files behind the places they list, 201 of them on the largest front measured; `console` and `markdown` print no code and open none.
|
|
85
|
+
|
|
86
|
+
## Reading the report
|
|
87
|
+
|
|
88
|
+
The console summary of a nest microservice, in full:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
sentinel static-analysis: contract
|
|
92
|
+
Duplicated lines 3.95 %
|
|
93
|
+
Clones 61 entries
|
|
94
|
+
Duplicated lines (tests) 10.93 %
|
|
95
|
+
Clones (tests) 196 entries
|
|
96
|
+
Entry points 46 of 656 files
|
|
97
|
+
Unreachable lines 1.07 %
|
|
98
|
+
Unreachable files 5 files
|
|
99
|
+
Unused exports 1 exports
|
|
100
|
+
Unused symbols 1 symbols
|
|
101
|
+
Unused imports 0 imports
|
|
102
|
+
Complexity, worst function 27 max per function
|
|
103
|
+
Complexity 11-20, moderate risk 18 of 1386 functions
|
|
104
|
+
Complexity 21-50, high risk 1 of 1386 functions
|
|
105
|
+
Imports between files 1135 between 495 files
|
|
106
|
+
Density 0.0046 of the possible edges
|
|
107
|
+
Import cycles 3 groups, 8 files
|
|
108
|
+
Communities 12 of 495 files
|
|
109
|
+
Modularity (Q) 0.753
|
|
110
|
+
Files the gauge read 495 files the gauge read
|
|
111
|
+
Files with a complex function 18 files over 10, of 495
|
|
112
|
+
Files with deep nesting 4 files over 3, of 495
|
|
113
|
+
Long files 43 files over 150, of 495
|
|
114
|
+
Files with a long function 5 files over 109, of 495
|
|
115
|
+
Files with a long function body 47 files over 10, of 495
|
|
116
|
+
Files with a wide signature 33 files over 3, of 495
|
|
117
|
+
Files with several classes 16 files over 1, of 495
|
|
118
|
+
Files hard to follow 23 files over 7, of 495
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Four groups, and they answer different questions:
|
|
122
|
+
|
|
123
|
+
| | what it answers |
|
|
124
|
+
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
125
|
+
| duplication | how much of this module repeats itself, product and tests counted apart |
|
|
126
|
+
| dead code | what nothing reaches: files, exports, symbols, imports |
|
|
127
|
+
| complexity | two measures, on purpose: how hard the hardest functions are to TEST (McCabe) and how hard they are to FOLLOW (cognitive). They disagree on 60% of the files either one flags |
|
|
128
|
+
| structure and size | how the files depend on each other, and which ones are outliers |
|
|
129
|
+
|
|
130
|
+
**Every row carries its denominator**, because none of them compares between modules without one: three breaches over 50 files and three over 3 000 are not the same module.
|
|
131
|
+
|
|
132
|
+
**An absent row means one of two things, and the notes tell you which.** When a pass could not produce a figure, the report says so in its own line under the figures rather than printing `0.00%`, because "no dead code" and "the walk had no entry point" are different facts. When a COUNT is zero, the row is dropped silently and on purpose: the complexity bands and the nine size indicators would otherwise print a column of zeroes on a healthy module, which makes the normal case the loudest part of the report.
|
|
133
|
+
|
|
134
|
+
So: a row missing **with** a note under the figures was not measured; a row missing **without** one was measured and found nothing.
|
|
135
|
+
|
|
136
|
+
### Three rows people misread
|
|
137
|
+
|
|
138
|
+
**`Unreachable files` is not a delete list.** It means nothing in this module imports them. A file another module's spec imports is alive, and it has its own row, `Used only by tests`. For a library the question cannot even be answered inside the module, so the role leaves it (see below).
|
|
139
|
+
|
|
140
|
+
**`Import cycles` reports GROUPS, not paths.** A group of six files can hold a dozen distinct circles, and breaking one does not necessarily break the group. The group is the unit precisely for that reason.
|
|
141
|
+
|
|
142
|
+
**`Density` must not be compared between modules of different sizes.** Its denominator grows with the square of the file count, so a module gets a lower density by growing rather than by improving. Measured: `libs/nest/auth` 0.0216 at 101 files, `apps/front/host-admin` 0.0005 at 4 380. It is good for one module watched over time.
|
|
143
|
+
|
|
144
|
+
### Where the size thresholds come from
|
|
145
|
+
|
|
146
|
+
The nine size rows are measured with oxlint at thresholds this role fixes once for every module, so the same question gets the same answer everywhere. Each one is the **strictest of three candidates**: published research, our own p90, and the rule's own default.
|
|
147
|
+
|
|
148
|
+
| row | threshold | what decided it |
|
|
149
|
+
| --------------------------------- | ------------: | ----------------------------------------------------------------------------------------- |
|
|
150
|
+
| `Files with a complex function` | 10 | McCabe 1976 says 10, our p90 says 11 |
|
|
151
|
+
| `Files with a long function` | 109 | our p90. IfSQ SP-1 says 150 and loses for being more permissive than our code |
|
|
152
|
+
| `Long files` | 150 | our p90 of 157, rounded down. No research sets a file's length |
|
|
153
|
+
| `Files with deep nesting` | 3 | IfSQ SP-2, and our p90 agrees |
|
|
154
|
+
| `Files with nested callbacks` | 3 | same nesting family |
|
|
155
|
+
| `Files with a long function body` | 10 statements | the rule's default, already the strictest candidate |
|
|
156
|
+
| `Files with a wide signature` | 3 parameters | beyond three the fix is an object or currying, so this is a design boundary |
|
|
157
|
+
| `Files with several classes` | 1 | the rule's floor |
|
|
158
|
+
| `Files hard to follow` | 7 | Sonar publishes 15 for cognitive complexity and says no formula supports it; our p90 is 7 |
|
|
159
|
+
|
|
160
|
+
⚠️ **These will disagree with your green lint, out loud.** The `nest` lint preset refuses `complexity` at 30 while this reports above 10, so a nest developer reads breaches beside a passing build. That is correct: the lint is the line your build refuses at, this is a gauge that answers the same question the same way in every module, including the ones that have not adopted lint at all. The report prints its thresholds under `measured with` so the two are never confused.
|
|
161
|
+
|
|
162
|
+
## Bundle analysis, for a front application
|
|
163
|
+
|
|
164
|
+
Six rows appear when the module is a front application **that has been built**. They answer how much
|
|
165
|
+
JavaScript a user downloads to open it, and what that costs twice over.
|
|
166
|
+
|
|
167
|
+
| row | what it is |
|
|
168
|
+
| ------------------------- | --------------------------------------------------------------------------------- |
|
|
169
|
+
| initial load, transfer | gzipped, what crosses the network, against a **170 KB** budget and the web median |
|
|
170
|
+
| initial load, to parse | decompressed, what the engine reads, against **717 KB** |
|
|
171
|
+
| initial load, files | how many files the entry pulls |
|
|
172
|
+
| heaviest single asset | the largest file, named, against **100 KB** per script |
|
|
173
|
+
| dynamic layer | what no entry references statically, as a pair of bounds over its group count |
|
|
174
|
+
| referenced by the entries | everything the entries reference, read as code-splitting health |
|
|
175
|
+
|
|
176
|
+
**Why two weights and never one.** Compression is a network lever with no effect on the engine:
|
|
177
|
+
"900 KB of decompressed JavaScript is still 900 KB to the parser and compiler, even though it may be
|
|
178
|
+
roughly 300 KB when compressed." Measured across these fronts the ratio runs from 3.30 to 3.70, so
|
|
179
|
+
neither figure can be read off the other.
|
|
180
|
+
|
|
181
|
+
**Why two references for the transfer weight.** The web median answers _are we normal_, the
|
|
182
|
+
engineering budget answers _are we good_, and they routinely disagree: every front here sits below
|
|
183
|
+
the median and above the budget. Both are true. The budget carries its conditions, and they travel
|
|
184
|
+
with it: getting interactive in under five seconds on a slow 3G connection on a median mobile
|
|
185
|
+
device.
|
|
186
|
+
|
|
187
|
+
Each figure's band is the word beside it: `ok` below 70% of its reference, `near` from 70% to 100%,
|
|
188
|
+
`exceed` above. And each row carries its own lever and its sources in the `html` report, because a
|
|
189
|
+
weight nobody can act on is a weight nobody acts on.
|
|
190
|
+
|
|
191
|
+
⚠️ **Three conditions are printed with the figures, and they decide what the figures mean**: how old
|
|
192
|
+
the build is, that the brand it was built for is not recorded anywhere in the output, and any path
|
|
193
|
+
the manifest announced that resolved to no file. A measurement on a stale build is silently wrong.
|
|
194
|
+
|
|
195
|
+
⚠️ **No build, no figures, and the report says so** rather than printing zeros. A module that has
|
|
196
|
+
never been built and a module weighing nothing are different facts. A component library or a
|
|
197
|
+
back-end service produces no entry manifest at all, so it will always read that line: that is the
|
|
198
|
+
perimeter, not a defect.
|
|
199
|
+
|
|
200
|
+
**You also get a recap on the pull request**, for the fronts it touched, with each band. It is
|
|
201
|
+
posted on every run rather than only on a breach, because a comment that appears only when
|
|
202
|
+
something is wrong is a comment nobody learns to read, and it updates the same comment instead of
|
|
203
|
+
adding a second one.
|
|
204
|
+
|
|
205
|
+
⛔ **It measures, it never refuses.** Nothing here fails a build, including that comment. Blocking
|
|
206
|
+
is a later choice, and the thing worth blocking on is the **delta** against the default branch
|
|
207
|
+
rather than the level: that needs CI to keep a baseline, so it comes with the baseline.
|
|
208
|
+
|
|
209
|
+
## The `html` report
|
|
210
|
+
|
|
211
|
+
Written to `static-analysis-report/`, two files:
|
|
212
|
+
|
|
213
|
+
- `static-analysis.html`, self-contained: no CDN, no library, no font, nothing fetched. Open it from disk, attach it to a ticket, serve it from a CI artifact.
|
|
214
|
+
- `static-analysis-graph.svg`, the import graph as a plain image, for when you want to paste a picture.
|
|
215
|
+
|
|
216
|
+
The page carries what the console summary leaves out, and it is laid out the way a scanner is: **one card per figure**, grouped into tabs by family, each carrying its own help and its own places. A place is a card too: the path with its line, the measure in its own unit, the rule it broke, and **the code**, collapsed.
|
|
217
|
+
|
|
218
|
+
The code is read from your source files for every place the page lists, not just for clones: a clone comes with its fragment, while a size or complexity finding is a file and a line, so the page opens the file. Which lines it shows depends on what the rule points at, which was measured rather than assumed: a rule naming a declaration shows ten lines from it, `max-depth` and `max-nested-callbacks` show the three lines above too because the nesting they report is there, and `max-lines` shows the file's HEAD because it reports at the file's last line. A dead export with no line is located by its name when exactly one line declares it, and shows nothing rather than a guess otherwise.
|
|
219
|
+
|
|
220
|
+
The **import graph** is explorable. Point at a dot for its path, which the browser shows under your cursor; click it to keep only that file and its neighbours, click the background to come back. The drawing renders with scripting off; the script only adds the filtering.
|
|
221
|
+
|
|
222
|
+
## The pull-request recap
|
|
223
|
+
|
|
224
|
+
`--output markdown` writes `static-analysis-report/static-analysis.md`: one table, one row per family, the headline figure and how many places that family holds. It is what a workflow posts as a comment, one section per affected module.
|
|
225
|
+
|
|
226
|
+
⛔ **It does not compare with `main`.** It says so on itself, because a number in a pull-request comment is read as a delta by default, and that reading is wrong in the dangerous direction: a module already at 6.81% looks exactly like one this branch pushed there. Comparing needs a baseline kept between runs, which is the next version's work.
|
|
227
|
+
|
|
228
|
+
⚠️ `static-analysis-report/` is generated, and **`--init` ignores it for you**: it writes one line into your repository's root `.gitignore`, unanchored so it matches at every depth, rather than one line in every adopting module's own file. It checks first, in any spelling, so adopting a second module adds nothing. You have nothing to do and nothing to commit beyond that line.
|
|
229
|
+
|
|
230
|
+
⛔ **The one case where it is not there**: a repository that runs the role WITHOUT ever adopting it, which is what a CI workflow measuring modules with `npx` does. `--init` never ran, so nothing wrote the rule, and every measured module leaves an untracked directory. If that is your situation, add the line yourself:
|
|
231
|
+
|
|
232
|
+
```gitignore
|
|
233
|
+
static-analysis-report/
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
⚠️ This paragraph said the opposite for one commit, on the strength of that very case: the line was missing from a repository where no module had adopted, and the conclusion drawn was that the documentation lied, without checking what writes it. `--init` does, in `src/core/workspace-prep.ts`. Verify yours with `git check-ignore -v <module>/static-analysis-report/`.
|
|
237
|
+
|
|
238
|
+
## Declaring entry points
|
|
239
|
+
|
|
240
|
+
`entry` names the files that **run although nothing imports them**. They are where reachability starts, and they are added to the patterns the role already ships rather than replacing them.
|
|
241
|
+
|
|
242
|
+
Most modules need nothing. Declare an entry point when the framework loads a file by convention the role has not met, or when code is loaded by name at run time:
|
|
243
|
+
|
|
244
|
+
```js
|
|
245
|
+
import { defineStaticAnalysis } from '@hublo/sentinel/static-analysis'
|
|
246
|
+
|
|
247
|
+
export default defineStaticAnalysis({
|
|
248
|
+
root: import.meta.dirname,
|
|
249
|
+
entry: ['src/emails/**'],
|
|
250
|
+
})
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**The symptom that tells you one is missing**: a large, tightly-grouped block of unreachable files under one directory. Measured on `libs/cloud/hermes`, 228 of its 231 findings sit under `src/emails`, which holds templates loaded by name rather than imported. That is one line of config, not 228 deletions.
|
|
254
|
+
|
|
255
|
+
## Two limits worth knowing before you read a number
|
|
256
|
+
|
|
257
|
+
**A single-file component format cannot be parsed.** `.svelte`, `.vue` and `.astro` files are not read, so on a module holding them the role reports the duplication, complexity and size figures and **omits the graph and the dead-code figures entirely**, with the share printed in their place:
|
|
258
|
+
|
|
259
|
+
```
|
|
260
|
+
(252 product file(s) in .svelte cannot be parsed, 53% of this module: no graph
|
|
261
|
+
and no dead-code figure is reported, because a file the role cannot open still
|
|
262
|
+
imports the files it imports)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Absent with its reason, rather than a figure that is wrong by half.
|
|
266
|
+
|
|
267
|
+
**A library's dead code is decided outside the module.** An application has an entry point; a library has none, so a module-local walk would report "the module does not import itself", which is true of every library. The role builds a workspace-wide index of who imports what, once, and caches it under `node_modules/.cache/sentinel/`. Measured on `libs/nest/auth`: **15** unreachable files with the index against **87** without it.
|
|
268
|
+
|
|
269
|
+
## Troubleshooting
|
|
270
|
+
|
|
271
|
+
| | |
|
|
272
|
+
| ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
273
|
+
| a wall of unreachable files under one directory | an undeclared entry point, see above |
|
|
274
|
+
| the graph and dead-code rows are missing | the module holds `.svelte`, `.vue` or `.astro`; the note above the figures says the share |
|
|
275
|
+
| `Entry points 0 of N` | nothing matched, so the walk had no start. Declare `entry` |
|
|
276
|
+
| the numbers differ from another module's | check `measured with`: a setting this module moved is marked `(module)`, and two modules measured differently cannot be compared |
|
|
277
|
+
| the run is slow | you are on `--output html`, which pays the graph layout. Use the plain script in a loop |
|
|
278
|
+
| `oxlint could not be run, so no size indicator is reported` | the binary did not resolve; the size rows are absent rather than zero |
|
|
279
|
+
|
|
280
|
+
## What the role will not do
|
|
281
|
+
|
|
282
|
+
- **It will not fail your build.** No threshold here refuses anything, by design, for as long as adoption is incomplete. The only non-zero exits are an unknown `--output` value and a tool that could not be run at all, never a finding.
|
|
283
|
+
- **It will not read your module's `.oxlintrc.json`.** The size gauge writes its own config to a temporary directory, so its scale is the same in every module. A gauge whose scale moves per module compares nothing.
|
|
284
|
+
- **It will not look outside the module**, except for the one measurement that cannot be answered inside it: a library's reachability.
|
|
285
|
+
- **It will not measure your tests.** Specs, fixtures, mocks, stories and generated files are out of every product figure. Duplication reports the test suite separately, on its own two rows, because that one is worth seeing and must not be blended into the product number.
|