@vernikr/size-report 2.5.0 → 2.7.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 +186 -81
- package/package.json +2 -2
- package/src/check.js +1 -1
- package/src/data.js +21 -23
- package/src/doctor.js +4 -4
- package/src/history.js +3 -9
- package/src/init.js +2 -1
- package/src/locales.js +17 -11
- package/src/metrics.js +45 -63
- package/src/minify.js +2 -2
- package/src/modes.js +8 -8
- package/src/optional.js +2 -2
- package/src/page/app.css +33 -8
- package/src/page/app.js +139 -71
- package/src/page/build.js +166 -19
- package/src/page/panel.js +135 -30
- package/src/page/payload.js +168 -0
- package/src/page/state.js +88 -63
- package/src/page/table.js +280 -73
- package/src/page/work.js +55 -0
- package/src/parse.js +1 -1
- package/src/project.js +2 -2
- package/src/strip/guard.js +4 -3
- package/src/strip.js +4 -5
- package/src/table.css +44 -8
- package/src/tokens.js +5 -6
- package/templates/README.md +3 -3
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ shows.
|
|
|
10
10
|
|
|
11
11
|
## Status
|
|
12
12
|
|
|
13
|
-
**Release 2.
|
|
13
|
+
**Release 2.7.0 (2026-09-17).** The tool lives as a package of its own: the registry name is
|
|
14
14
|
`@vernikr/size-report` (published by tag from CI, with no secret). A project may keep no settings at
|
|
15
15
|
all: without a config file the tool derives them from the project itself and says so in one line,
|
|
16
16
|
and `--init` pins what was derived into a file. The report is **one file**, the self-contained page
|
|
@@ -19,13 +19,41 @@ is installed and on the first run. The version is in the manifest, and every rel
|
|
|
19
19
|
the journal — `worklog/` for today's entries, `worklog/archive/WORKLOG.md` for the earlier ones:
|
|
20
20
|
what changes in the numbers is measured rather than retold.
|
|
21
21
|
|
|
22
|
-
2.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
2.7.0 is about the page and what a reader does with it, and its figures are measured rather than retold.
|
|
23
|
+
**The report's address stays clean:** a switch writes the record into the browser's memory and nothing into the
|
|
24
|
+
address bar — the page no longer puts a `#size-report=…` tail on the report it was opened with — while a link
|
|
25
|
+
made by an earlier release is still read, and a foreign or broken one is still explained in words. **A test
|
|
26
|
+
is a category of its own**, right after Code: the files of `test`, `tests` and `fixtures` folders wherever
|
|
27
|
+
they stand, and every name carrying `.test` before its extension — on this repository's own history Tests
|
|
28
|
+
holds **47 of 317 columns**, and the checkbox of the documentation is called **Docs**. **The tree opens
|
|
29
|
+
folded**, and the unfolding is what the memory keeps, under a key of its own; inside a level a hidden name —
|
|
30
|
+
one beginning with a dot — stands after every visible one. **The drawing of a switch is measured, and it is one
|
|
31
|
+
task**: the click is counted and answered, a drawing short enough to be over before the browser could paint is
|
|
32
|
+
done on the click, and a longer one happens in the next task with a stripe over the top edge of the window. The
|
|
33
|
+
price of a switch is the browser's own relayout of the table — 234.5 ms of layout for a *single* cell of this
|
|
34
|
+
repository's report (249 rows, 317 columns, 238 thousand cells), 287 ms for 750, 539 ms for 6 000 — while the
|
|
35
|
+
class changes are about 2 µs a node; on this page the whole Table of Contents of the report (73 columns) is
|
|
36
|
+
867 ms blocked with a 742 ms task. A queue worked off in slices was written first and refused by measurement:
|
|
37
|
+
the same click in 37 slices paid that relayout 37 times — **169 layouts and 151.9 s of pure layout time against
|
|
38
|
+
1.04 s**, with the tab growing to gigabytes of repaint and the reader waiting four minutes for a switch that
|
|
39
|
+
costs him less than a second unsliced. Within one task nothing can repaint, so a bar that filled would be a bar
|
|
40
|
+
nobody could read: the stripe travels and says the work is going on (`probes/step-12-columns.mjs`, the records in
|
|
41
|
+
`worklog/0207-page-choice-bar.md`). The order of the columns did not change there: what the last commit touched
|
|
42
|
+
still stands first, the rest after it in the settings' order.
|
|
43
|
+
|
|
44
|
+
2.6.0 changes the numbers themselves, and they are measured rather than retold: on this repository's own
|
|
45
|
+
history the page this engine writes is **85 955 B against 1 899 370 B** written by 2.5.0 — 22.1 times
|
|
46
|
+
smaller, −95.5 % — because the data travels as one packed block (49 392 B, `base64+gzip`) instead of a list
|
|
47
|
+
of rows, the program is pasted without comments and indentation (28 007 B) and the styling is 6 172 B. The
|
|
48
|
+
page also stops rebuilding itself: a click on a filter rewrites the nodes in place and recomputes the
|
|
49
|
+
totals, the columns carry computed widths under a fixed layout, and two candidates (`content-visibility`,
|
|
50
|
+
`border-collapse: separate`) were measured dead here and left out, with the records in
|
|
51
|
+
`plans/2026-09-17-page-perf/`. **The contract loses fields:** `metrics[].accuracy` and the `approx` bit map
|
|
52
|
+
are gone and the split into exact and approximate numbers with them, so a reader of `--data` that still
|
|
53
|
+
asks for them gets nothing — while the two schema numbers (`1` for the contract, `2` for the packed block)
|
|
54
|
+
stay where they were. The checks grow with the work: 70 → **81** in the fast profile and 175 → **186** in
|
|
55
|
+
the full one. The note of 2.5.0 — the language the tool speaks — stands in the journal,
|
|
56
|
+
`worklog/0203-release-2.5.0.md`.
|
|
29
57
|
|
|
30
58
|
**Parity with the implementation the move started from is proven, not asserted.** The command is
|
|
31
59
|
`bin/size.js` and the package's entry point is `src/size-table.js` (a re-export only), with the
|
|
@@ -65,14 +93,55 @@ instruction below. The step with a key left it later, along with private access.
|
|
|
65
93
|
**The data contract and the page.** The engine hands over absolute values and the shape of the table
|
|
66
94
|
(`--data`), while deltas, totals, "now" and the filters are computed by the page — which is the
|
|
67
95
|
report itself (`size-report.html`): without that split the filters and "the total over the
|
|
68
|
-
selection" are impossible in principle. The contract
|
|
69
|
-
|
|
70
|
-
|
|
96
|
+
selection" are impossible in principle. The contract tells how each number was obtained (the metric's
|
|
97
|
+
`method`) and nothing else about it: the page shows what the engine said and judges no number — it keeps
|
|
98
|
+
no rule of counting of its own, and the split of numbers into exact and approximate was taken out of the
|
|
99
|
+
package. The page's panel is a
|
|
71
100
|
tree of files by folder, with a switch per folder for the whole subtree; a reader's choice survives
|
|
72
|
-
a revisit and
|
|
101
|
+
a revisit in the browser's memory, and the address keeps a clean tail — an address with a link of its
|
|
102
|
+
own is read and never written (release 2.7.0). The contract carries the **project
|
|
73
103
|
catalogue** too: every path git sees, so the page's tree is the project's tree, while numbers exist
|
|
74
104
|
only for the files that became columns (release 2.2.0).
|
|
75
105
|
|
|
106
|
+
**The page's block is the contract in sparse form.** The file carries the history as changes rather than
|
|
107
|
+
as a snapshot per commit — for every file the rows it appeared in (absolute numbers), moved in (deltas
|
|
108
|
+
against its own previous record) and disappeared in — with the texts in a dictionary and the rows' links
|
|
109
|
+
cut by the part they share. The page's own chapter unrolls it back into exactly the contract
|
|
110
|
+
(`src/page/payload.js`), so the calculation and the table know nothing of the sparse form and there is no
|
|
111
|
+
second way to count a row; `--data` still answers with the dense contract, and the block's `schema: 2` is
|
|
112
|
+
what refuses a record written for the previous form. On this repository the data block is
|
|
113
|
+
1 370 724 → 88 712 B, and its parse is 11.6 → 0.6 ms plus 3.3 ms of unrolling (`contract-data` holds the
|
|
114
|
+
round trip).
|
|
115
|
+
|
|
116
|
+
**The block travels packed, and that is the page's one asynchronous step.** It lies in the file gzipped and
|
|
117
|
+
base64 encoded — the tag says so (`data-pack="base64+gzip"`) — and the page unpacks it with the platform's own
|
|
118
|
+
`DecompressionStream`: no library travels in the page, nothing is fetched, and the whole artifact of this
|
|
119
|
+
repository goes 122 668 → 78 319 B (the block 88 786 → 42 856 B — 74 B more than step 05 measured, because the
|
|
120
|
+
report is itself a column of the report and its own size moved in between). The price is deliberate and twofold: the
|
|
121
|
+
block can no longer be read by eye or by `diff`, and the first drawing waits for a promise where it used to
|
|
122
|
+
happen during the parse. Everything after the first drawing is as synchronous as it was; a host that cannot
|
|
123
|
+
unpack is told in words rather than left with an empty table. The checks read the page in jsdom, which has no
|
|
124
|
+
such API, so the harness puts the platform's own implementations of it into the window — and that seam is
|
|
125
|
+
tested from both sides: the ordinary path with them put in, and the message in words without them.
|
|
126
|
+
|
|
127
|
+
**What the page carries is squeezed, and only what the page carries.** The program and the styling are pasted
|
|
128
|
+
with their comments and indentation out — the same stripping the `min` metric counts — so the artifact holds
|
|
129
|
+
code without ballast while `src/derived.js`, `src/page/*.js`, `src/table.css` and `src/page/app.css` stay the
|
|
130
|
+
ordinary files a person reads: the squeeze lives in the paste and nowhere else. It is 58 922 → 24 885 B of
|
|
131
|
+
program and 15 505 → 5 905 B of styling (the artifact 166 305 → 122 668 B), and the assembled program is
|
|
132
|
+
guarded at build time by the stripper's own `assertCompilable` — a squeeze that ate code stops the build
|
|
133
|
+
rather than the browser.
|
|
134
|
+
|
|
135
|
+
**Minification of what the page carries is decided, not defaulted: not taken — and measured.** esbuild would
|
|
136
|
+
take the pasted program 24 885 → 18 128 B and the styling 5 906 → 5 102 B, the artifact 122 668 → 115 107 B
|
|
137
|
+
(7 561 B), for 91 ms of every build. The price is not those bytes but the contract: esbuild is an **optional**
|
|
138
|
+
dependency and its absence is a different count rather than a refusal, while the artifact is rebuilt by the
|
|
139
|
+
post-commit hook on whatever machine made the commit — a builder that minifies when it can would build **a
|
|
140
|
+
different file** there, and the report would stop being a fixed point. Buying determinism instead would mean
|
|
141
|
+
a pinned version and a page that cannot be assembled at all without esbuild (`src/optional.js`), for 6 % of
|
|
142
|
+
the file. What the decision rests on — the bytes it would save, the fixed point it would cost, and what
|
|
143
|
+
reopens it — is written down beside the plan the step belongs to.
|
|
144
|
+
|
|
76
145
|
The tool grew out of one script in the consumer project [`safe-resets`](../figma/safe-resets) — the
|
|
77
146
|
metrics `raw` and "a simplification instead of minification", a static report in git; that path
|
|
78
147
|
does not exist in this repository, and it is not named anywhere without the project.
|
|
@@ -111,8 +180,7 @@ refusal site — the maps `SITES` and `PRINTED` hold the counts — and a case i
|
|
|
111
180
|
entry, so a new refusal cannot appear without a check. Refusals a run cannot reach are named
|
|
112
181
|
explicitly: four are guarded by a check of their own (the catalogue names the file and the phrases),
|
|
113
182
|
and one cannot be caught at all — "internal error" — which is said where it stands. What the
|
|
114
|
-
catalogue does not take on is said in words: wording beyond the listed phrases, and meaning,
|
|
115
|
-
"!" sign, which is a note (an approximation, a mixed commit, the automation switched off) rather
|
|
183
|
+
catalogue does not take on is said in words: wording beyond the listed phrases, and meaning, andthe "!" sign, which is a note (another count, a mixed commit, the automation switched off) rather
|
|
116
184
|
than a refusal, with exit code zero.
|
|
117
185
|
|
|
118
186
|
**And the advice in a refusal is executable — that is checked as well.** The truth about the cause is
|
|
@@ -149,10 +217,10 @@ commits and installs hooks; the reason for each expensive file is named line by
|
|
|
149
217
|
|
|
150
218
|
| Run | Command | Checks |
|
|
151
219
|
|---|---|---|
|
|
152
|
-
| Fast — every edit | `pnpm test` | **
|
|
153
|
-
| Full — release and CI | `pnpm test:all` | **
|
|
220
|
+
| Fast — every edit | `pnpm test` | **81 of 186** |
|
|
221
|
+
| Full — release and CI | `pnpm test:all` | **186** |
|
|
154
222
|
|
|
155
|
-
No check is lost or weakened: the full run starts all
|
|
223
|
+
No check is lost or weakened: the full run starts all 186 with the same files, the fast one takes part
|
|
156
224
|
of them. The default is the full run — a file becomes fast only explicitly and with a reason — so new
|
|
157
225
|
expensive work cannot quietly move into the fast one. Two declarations guard that:
|
|
158
226
|
`test/suites.test.js` (every file classified, and a reason for each) and the documentation guard
|
|
@@ -242,7 +310,9 @@ choice. The report itself is always in the catalogue, whether or not it is track
|
|
|
242
310
|
of the moment, and the page must not depend on it, or the first rebuild in a fresh clone would give
|
|
243
311
|
different bytes. A folder whose files are only partly in the report writes its count as a fraction
|
|
244
312
|
("2/5"), and everything outside the report stands after everything inside it — folders and leaves
|
|
245
|
-
alike — so that the report is seen at once in a list where half the rows do not switch
|
|
313
|
+
alike — so that the report is seen at once in a list where half the rows do not switch. The name of a level
|
|
314
|
+
holds a second rule: a hidden name — one beginning with a dot — stands after every visible one, the alphabet
|
|
315
|
+
deciding the rest, so the service files of a project do not stand in the reader's way
|
|
246
316
|
(`test/page-tree.test.js`).
|
|
247
317
|
|
|
248
318
|
**A folder is a switch like a file, and its sign is a decision of its own.** The checkbox of a folder
|
|
@@ -251,8 +321,10 @@ next to it. Neither a folder nor a category button keeps state of its own: both
|
|
|
251
321
|
checkboxes, so the tree, the buttons and the table cannot drift apart. The sign beside a folder answers
|
|
252
322
|
a different question — how much of the tree is visible, which is the onlooker's business rather than the
|
|
253
323
|
reader's choice — so it is remembered between visits in a record of its own, under a key of its own and
|
|
254
|
-
the same report passport, and it never goes into
|
|
255
|
-
|
|
324
|
+
the same report passport, and it never goes into a link. The tree opens **folded**: a project's tree is
|
|
325
|
+
longer than the window, and the reader looks at a short list first, so what the record keeps is the
|
|
326
|
+
unfolding rather than the folding — folding every folder back removes the record, just as turning the
|
|
327
|
+
checkboxes back on removes the choice.
|
|
256
328
|
|
|
257
329
|
**The list scrolls, and there is one scroll.** On a narrow window it is the file list that scrolls —
|
|
258
330
|
the panel grows with the page there — while on a wide one the whole panel does: otherwise the controls
|
|
@@ -261,9 +333,38 @@ its middle is out of reach.
|
|
|
261
333
|
|
|
262
334
|
**Folding is pure view, and it counts no numbers.** The subtree lies in the markup and a class on the
|
|
263
335
|
row hides it, so a click on the sign changes exactly the three things the reader sees — the class, the
|
|
264
|
-
sign and the note in the memory.
|
|
265
|
-
|
|
266
|
-
|
|
336
|
+
sign and the note in the memory. What guards this is that after folding the table is the same markup
|
|
337
|
+
rather than a rebuilt one (`test/page-tree.test.js`).
|
|
338
|
+
|
|
339
|
+
**A switch draws its columns in one task, with a stripe over the page while it is going on.** The class
|
|
340
|
+
changes are cheap — about 2 µs a node, so a category of this repository's report (73 columns, 55 042 nodes) is
|
|
341
|
+
120 ms of them — while the browser lays the whole table out again for any change of a column's visibility, and
|
|
342
|
+
that is where the seconds are (`probes/step-12-columns.mjs`). So `src/page/work.js` asks for the drawing once:
|
|
343
|
+
work short enough to be over before the browser could paint goes on the click itself, and a longer one goes to
|
|
344
|
+
the next task with the stripe over the top edge of the window. **The stripe carries no share, and that is the
|
|
345
|
+
measurement rather than a shortcut**: inside one task the browser cannot repaint, so a length would be a length
|
|
346
|
+
nobody could keep; what moves is an indeterminate highlight. A queue worked off between timeouts was written
|
|
347
|
+
first and refused by the same probe — 37 slices paid the table's relayout 37 times (2.5–4 s each, 169 layouts
|
|
348
|
+
against 1) — and the numbers stay out of all of it, because the totals are counted on the click itself, where
|
|
349
|
+
they cost arithmetic rather than nodes. Nothing is asked for a column that is already right either
|
|
350
|
+
(`appColumnStale`): a report opened with everything switched on has nothing to draw, and a record from the
|
|
351
|
+
memory or a link queues exactly the columns that differ from it. What guards the two halves is
|
|
352
|
+
`test/page-choice.test.js` — a long drawing that leaves the click's line at once, runs in the next task and
|
|
353
|
+
takes the stripe away, and a short one that is over before the click returns.
|
|
354
|
+
|
|
355
|
+
**A click shows and hides rather than builds.** The table is assembled once, with every column of every
|
|
356
|
+
file, and a switch afterwards changes only what is visible: a metric is one class on the table plus the
|
|
357
|
+
`colSpan` of the group headings, a file's column is a class per node of it, and a folder or a category is
|
|
358
|
+
the same for each file of its subtree. The table's own nodes stay the objects the first drawing made:
|
|
359
|
+
`test/page-view.test.js` counts what a click appends (a metric: nothing at all; a file, a folder or a
|
|
360
|
+
category: at most the cells of the totals, rows × metrics) and checks that the rows and the cells are
|
|
361
|
+
still the very same objects. What makes this possible is that the order of the columns depends on the
|
|
362
|
+
files rather than on the choice — the last commit's first, then the settings' order — so a hidden column
|
|
363
|
+
keeps its place and the visible ones do not move. The totals are the only numbers a choice changes, and
|
|
364
|
+
they are carried rather than recounted: a sum is linear, so a file switched off subtracts exactly its own
|
|
365
|
+
values, which costs its own rows instead of rows × files. That arithmetic is the step's one new piece,
|
|
366
|
+
and it is held against the engine's own `rowModel` cell by cell for a mixed choice, so that counting a
|
|
367
|
+
row stays in one place (`test/page-view.test.js`).
|
|
267
368
|
|
|
268
369
|
**The columns the last commit touched come first.** The report is rebuilt after every commit, and a
|
|
269
370
|
reader's first question is what that edit brought. The mark comes from the history rather than from the
|
|
@@ -290,17 +391,17 @@ numbers behind the layout is the contract rather than the markup: switching a fo
|
|
|
290
391
|
its columns and exactly its volume from the total (`test/contract.test.js`).
|
|
291
392
|
|
|
292
393
|
**A checkbox takes away neither the numbers' room nor the reader's place in the list.** The panel is
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
its focus
|
|
296
|
-
the panel from the start
|
|
394
|
+
built once and a switch writes only the fields it reached, so the reader's place is his still: there is
|
|
395
|
+
no rebuild that could lose the scroll of the panel or of the list, and the field under the keyboard keeps
|
|
396
|
+
its focus without being found again by hand (`test/page-tree.test.js`) — with a rebuild every switch with
|
|
397
|
+
`Tab` and `Space` would mean walking the panel from the start. The file list has no ceiling of its own in a wide window: the panel
|
|
297
398
|
scrolls, and the list does not push the table. The row of categories sticks to the top of the panel,
|
|
298
399
|
with the panel's own background (or passing rows of the list would read through it), and the panel's own
|
|
299
400
|
top padding lives on its first field, which travels away with it. File captions use the table's font size
|
|
300
401
|
(12.5px), and the legend under the tree is gone on purpose: below the list it pushed the numbers away,
|
|
301
402
|
while what it explained already stands next to the thing it explains — the sign of a number names the
|
|
302
|
-
colour of a delta,
|
|
303
|
-
|
|
403
|
+
colour of a delta, the way each number was counted stands under the metric switches, and the mark of a gap
|
|
404
|
+
lives in the cell's own text.
|
|
304
405
|
|
|
305
406
|
**The panel remembers the reader's choice.** The record lives in the browser's memory, tied to the
|
|
306
407
|
report's passport — the tool's name, the data schema, the artifact's path, the title and the column
|
|
@@ -314,32 +415,32 @@ neither the numbers nor the markup. The passport holds neither the tool's versio
|
|
|
314
415
|
history, and on purpose: updating the tool does not change what a column means, while a grown history is
|
|
315
416
|
the very history the reader comes back to.
|
|
316
417
|
|
|
317
|
-
**The
|
|
318
|
-
browser's memory
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
418
|
+
**The link is read and never written.** The page's address is the report's own: a choice lives in the
|
|
419
|
+
browser's memory, and the page puts nothing into the address bar — a report opened from disk keeps the
|
|
420
|
+
address it was opened with, and not one switch lands in the tab's title bar. What an earlier release sent
|
|
421
|
+
as a link is still read, though — the record in `#size-report=…`, in the same shape the memory holds it —
|
|
422
|
+
and such a link outranks the memory while it does not replace the reader's own until he changes something.
|
|
423
|
+
A foreign or broken address is not applied, and is not silent either: a line above the table names the
|
|
424
|
+
reason ("the link was made in another report" / "the choice in the address is unreadable"), the view stays
|
|
425
|
+
the reader's own, and the incoming address is not rewritten; names the report does not hold are reported
|
|
426
|
+
by count, skipped, and the rest is applied. The link works on an already open page as well: the browser
|
|
427
|
+
does not reload the document when the anchor changes, so the page reads the address itself, or a link
|
|
428
|
+
would only work in a new tab. The page makes no request to the network at all, and that is an assertion of
|
|
429
|
+
a check rather than a promise (`test/page-view.test.js`, `test/parity.test.js`).
|
|
328
430
|
|
|
329
431
|
**The `min` metric can count for real.** The way of counting is chosen in the settings:
|
|
330
432
|
`"minify": {"engine": "esbuild"}` minifies JS/TS/CSS for real through an optional dependency, while
|
|
331
433
|
`"engine": "strip"` is the earlier removal of comments and indentation. The default did not change,
|
|
332
434
|
because both frozen references were taken under it. Measured on the fixture: real minification is
|
|
333
435
|
smaller than stripping in **44 cells and never larger**; `src/code.js` **276 → 185 B**, `src/style.css`
|
|
334
|
-
**55 → 43 B**, and over the fixture's history **−1 372 B**. JSON is minified by parsing
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
answers come from one rule (`pointExact` in `src/metrics.js`), so they cannot diverge. With no minifier
|
|
436
|
+
**55 → 43 B**, and over the fixture's history **−1 372 B**. JSON is minified by parsing (parsing loses
|
|
437
|
+
only insignificant whitespace, and nobody would make it shorter), while the formats the minifier does not
|
|
438
|
+
take are named in the metric's caption **by extension**: `esbuild … (minify, rename); other formats
|
|
439
|
+
(.md .toml) lose comments and indentation`. The split of numbers into exact and approximate is gone from
|
|
440
|
+
the package: how a column was counted is told once, in the metric's method, and no cell carries a mark of
|
|
441
|
+
it any more. The list comes from the columns rather than from the name of the method (`otherCountFormats`
|
|
442
|
+
in `src/metrics.js`), so a report made only of formats the minifier takes says nothing about other
|
|
443
|
+
formats. With no minifier
|
|
343
444
|
(an installation without optional dependencies, a platform without it) the metric falls back to
|
|
344
445
|
stripping, the method says so in words and the run answers **code 4** rather than staying silent, while
|
|
345
446
|
the numbers are the same as the earlier way of counting — byte for byte with the reference. The derived
|
|
@@ -365,9 +466,7 @@ and it has nothing to count tokens with. Counting every family on every run woul
|
|
|
365
466
|
the reader may never ask about, so the choice of family and encoding lives where it costs time — in the
|
|
366
467
|
run's settings — while the page **names** it: the method of each metric stands under the switches as
|
|
367
468
|
text rather than only in a tooltip. Formats without text (a picture, a font, an archive) are named in
|
|
368
|
-
the metric's caption
|
|
369
|
-
marked by the same rule, and the caption takes the worst in the column — two answers have nothing to
|
|
370
|
-
diverge with. With no dictionary (an installation without optional dependencies, a platform without it)
|
|
469
|
+
the metric's caption by extension: their number goes by bytes rather than text. With no dictionary (an installation without optional dependencies, a platform without it)
|
|
371
470
|
the count is an estimate by length with the coefficient named in the method, and the run answers **code
|
|
372
471
|
4**; the other metrics stay what they were in a report without tokens, and that seam is checked in an
|
|
373
472
|
environment with no optional dependencies at all (`SIZE_REPORT_NO_OPTIONAL`). Counting tokens costs a
|
|
@@ -415,6 +514,7 @@ acceptance for each.
|
|
|
415
514
|
|
|
416
515
|
| File | Role |
|
|
417
516
|
|---|---|
|
|
517
|
+
| `plans/` | The plans of work on this repository: `plans/archive/` holds the earlier ones, `plans/2026-09-17-page-perf/` the plan of the page work — an index and one file per step of the report, each with why, what changes, acceptance and the risks (`plans/2026-09-17-page-perf/README.md`) |
|
|
418
518
|
| `plans/archive/PLAN.md` | **The main document of the move:** inventory, boundaries, invariants, architecture, the seven steps, acceptance, risks, open questions |
|
|
419
519
|
| `docs/requirements.md` | The customer's requirements: what and why |
|
|
420
520
|
| `docs/module-design.md` | The design of the extraction: how the module is put together |
|
|
@@ -427,6 +527,7 @@ acceptance for each.
|
|
|
427
527
|
| `tools/parity-freeze.js` | Takes the parity reference (`pnpm run parity`): with the frozen copy, at the project revision from the manifest — `--json`, the config, the artifact's hash, the tool's hash |
|
|
428
528
|
| `tools/make-fixture.js` | Assembles the synthetic fixture (`pnpm run fixture`): a deterministic history with traps plus the reference numbers |
|
|
429
529
|
| `tools/synthetic/` | The subjects of that assembly, one per matter: `repo.js` — how git is spoken to (pinned time, author, settings), `content.js` — what the files hold, `history.js` — which commits come of it, `note.js` — the fixture's note with the list of traps |
|
|
530
|
+
| `probes/` | The scripted measurements behind `plans/2026-09-17-page-perf/`: one file per step, run by hand against live Chrome at the debug port — the fixed layout, where `content-visibility` acts at all, and the two border models with their pixels (`probes/README.md`). Outside the sensors' paths on purpose: they measure the product rather than being part of it, and a suite cannot see layout, paint or a browser's own skipping |
|
|
430
531
|
| `tools/parity-live.js` | Compares the engine with the live project on a clone: the numbers and the self-contained report at the path the consumer's settings give (`pnpm run parity:live`) |
|
|
431
532
|
| `tools/pack-check.js` | Assembles the tarball and checks that everything works from it: all sources arrived, the numbers and the report as from the repository (`pnpm run pack:check`) |
|
|
432
533
|
| `tools/check-standards.js` | Checks that both references reproduce: a re-take goes nowhere and is compared with what is committed (our files byte for byte, the bundle by content), and that the live-history bundle carries `HEAD` (`pnpm run check:standards`) |
|
|
@@ -458,14 +559,16 @@ acceptance for each.
|
|
|
458
559
|
| `src/css.js` | Reading the styling from disk: which sets of styles exist and what role each has |
|
|
459
560
|
| `src/table.css` | The report's table: the geometry of a cell, the sticky header and commit column, the colour of deltas |
|
|
460
561
|
| `src/page/app.css` | The page's styling on top of the shared part: the panel with the file tree and its sticky row of categories (a column on the left on a wide screen, the page fitting the window), the empty states, a narrow window |
|
|
461
|
-
| `src/page/
|
|
562
|
+
| `src/page/payload.js` | The page's block in sparse form, and the one place that unrolls it back: the history as changes (a file's appearance, its moves, its disappearance) turned into the snapshots the calculation and the table already speak — a value that did not move is one object shared by the rows that hold it |
|
|
563
|
+
| `src/page/state.js` | The page's state: the report's data (the block unrolled by the payload chapter), the view of the checkboxes, the pointer "which path is which column", the unfolded folders, the record's passport, the browser's memory and the link that is read out of the address — a chapter of the page's program |
|
|
462
564
|
| `src/page/dom.js` | The page's nodes: the small helpers of markup (`appEl`, `appBox`) — one set for the panel and the table alike |
|
|
463
|
-
| `src/page/panel.js` | The panel of choices: the switches of metrics and files, the categories, the tree of the project's paths (files outside the report keep a checkbox off with a reason and stand after the rest; folders carry a
|
|
464
|
-
| `src/page/table.js` | The page's table: a cell, a commit's caption, the header
|
|
465
|
-
| `src/page/
|
|
466
|
-
| `src/page/
|
|
565
|
+
| `src/page/panel.js` | The panel of choices: the switches of metrics and files, the categories, the tree of the project's paths (files outside the report keep a checkbox off with a reason and stand after the rest, and a hidden name after every visible one; the tree opens folded and folders carry a sign that hides the subtree by a class rather than by a rebuild); built once, with the fields of the switches and of the folders and categories written where they stand |
|
|
566
|
+
| `src/page/table.js` | The page's table, built once: a cell, a commit's caption, the header, the empty states and the cache of the nodes of every column — markup over the shared calculation, with the totals carried rather than recounted, and the count answerable for how many nodes a column holds |
|
|
567
|
+
| `src/page/work.js` | The page's long drawing: the columns of a switch in one task, on the click when they are few and in the next task with an indeterminate stripe over the top edge of the window when they are many — one task because the browser's relayout of the table is the price, and it is paid once (this is where the measurement that refused the slices stands) |
|
|
568
|
+
| `src/page/app.js` | Assembling and starting the page: the first drawing, then a switch that counts the numbers on the click, hands the columns to the queue and writes the fields it reached without making a node; an anchor change; pasted into the assembled page |
|
|
569
|
+
| `src/page/build.js` | Assembling the page: data, styling and program in one file with no external references — the pasted text is **squeezed** on the way in (comments and indentation out, the same stripping the `min` metric counts) while the sources keep them, and the result is guarded by the stripper's own `assertCompilable` |
|
|
467
570
|
| `src/git.js` | The only border where git is called: the pinned settings, blobs by the batch, the history, the comparison with the working tree |
|
|
468
|
-
| `src/strip.js` | Removing ballast: which form goes to which file (extension, strategy) and
|
|
571
|
+
| `src/strip.js` | Removing ballast: which form goes to which file (extension, strategy) and which strategies are minification itself — the entry to the parsing of forms |
|
|
469
572
|
| `src/strip/js.js` | Removing comments and indentation in JS: a pass over the cases (a comment, a regexp, a string, a character) — through strings and templates as well |
|
|
470
573
|
| `src/strip/forms.js` | The forms of text with a removal of their own: markup, styles, the lines of a file and JSON |
|
|
471
574
|
| `src/strip/guard.js` | The stripper's guard: what was stripped has to compile — as a script in the process or as a module in a worker thread |
|
|
@@ -483,7 +586,7 @@ acceptance for each.
|
|
|
483
586
|
| `bin/postinstall.js` | Installing the hook after the package is added: it looks for the consumer project and stays silent when there is nowhere to install |
|
|
484
587
|
| `src/artifact.js` | The report on disk: the only place where it becomes a file (both `--write` and the hook use it); the report is a self-contained page |
|
|
485
588
|
| `src/journal.js` | The journal and links: which section a commit belongs to and where a description leads |
|
|
486
|
-
| `src/data.js` | The file categories and the contract with the page (`--data`): the numbers, the shape of the table and the catalogue of the project's paths |
|
|
589
|
+
| `src/data.js` | The file categories (a test by its path — a `test`, `tests` or `fixtures` folder, or `.test` before the extension — otherwise the extension, and the settings above both) and the contract with the page (`--data`): the numbers, the shape of the table and the catalogue of the project's paths |
|
|
487
590
|
| `src/config.js` | The consumer project's settings: the defaults, reading them, checking them |
|
|
488
591
|
| `src/project.js` | The settings derived from the project itself (its tree and history): columns, the journal, the exceptions, the catalogue of paths for the page's tree. Without a settings file it *is* the settings; `--init` pins it as a file |
|
|
489
592
|
| `src/locales.js`, `src/refusal.js`, `src/tool.js` | The report's texts; the exit codes and the help; the package's name and version |
|
|
@@ -494,7 +597,7 @@ acceptance for each.
|
|
|
494
597
|
| `test/api.test.js` | The package's public API: the list of names is frozen, and splitting the engine may not change it |
|
|
495
598
|
| `eslint.config.js` | The rules of formatting: the same as the consumer project's, plus a ban on gluing operators into one line (`pnpm run lint`, `pnpm run lint:strict`) |
|
|
496
599
|
| `tools/harness.js` | The harness of the checks: paths, clones of the fixture (including one shared per suite and one with CRLF), running the tool, reading refusals, hashes |
|
|
497
|
-
| `tools/page-harness.js` | The harness of the contract and page checks: the contract data, the assembled page, reading it in a real DOM, the panel's switches — one for
|
|
600
|
+
| `tools/page-harness.js` | The harness of the contract and page checks: the contract data, the assembled page, reading it in a real DOM, the panel's switches, the page's calculation and its decoder evaluated from their sources, the block unpacked, and the platform's unpacker put into jsdom (which has none) — one for six suites |
|
|
498
601
|
| `tools/suites.js` | The split of the suite: which files go into the fast run (with a reason for each) and why every dear one is in the full run |
|
|
499
602
|
| `tools/run-tests.js` | Running the suite (`pnpm test`, `pnpm test:all`, `pnpm run suites:measure`): each file's duration measured on its own, and the counts of checks adding up |
|
|
500
603
|
| `tools/docs-facts.js` | Reading facts out of the documentation — one layer for the four checks of the documentation guard: what a document names (paths, calls, section addresses) against what the repository holds |
|
|
@@ -508,11 +611,12 @@ acceptance for each.
|
|
|
508
611
|
| `test/cli.test.js`, `test/cli-paths.test.js` | The command line's refusals: the help, the settings, the exit codes — and where the tool writes |
|
|
509
612
|
| `test/refusals.test.js` | The refusals are executed: each one is called by a run, its exit code and its promised phrases are compared (with clones of their own for someone else's hook, a shallow history and a branch past the report), and **the advice runs** — the command answers with the promised code and no stack, while where "the refusal is gone" is declared the same call answers differently after it |
|
|
510
613
|
| `test/refusals-catalog.test.js` | The guard of the refusal catalogue: every refusal site in the sources has an entry, every entry declares its advice, and refusals handed to another check are really accepted by it (the named file and line are checked) |
|
|
511
|
-
| `test/contract-data.test.js` | The data contract: the numbers against the reference, the set of fields against the derived quantities, the
|
|
614
|
+
| `test/contract-data.test.js` | The data contract: the numbers against the reference, the set of fields against the derived quantities, the metric's method against the way the numbers were counted — and the round trip through the page's sparse block, which restores the contract whole and twice over the same bytes |
|
|
512
615
|
| `test/contract-derived.test.js` | The derived quantities against the artifact's numbers: a row's totals, a cell's delta and the delta of a total — on the code that lies in the tree |
|
|
513
|
-
| `test/page-view.test.js` | The assembled page: pasted with no copy of the calculation, self-contained, the empty states, the styling and the
|
|
514
|
-
| `test/page-tree.test.js` | The panel's file tree: folders by the project's paths, three states, the subtree, files and folders outside the report (a checkbox off, a place after the rest), folding without a rebuild and
|
|
515
|
-
| `test/page-choice.test.js` | The
|
|
616
|
+
| `test/page-view.test.js` | The assembled page: pasted with no copy of the calculation, self-contained, the empty states, the styling, the switches, a click that makes no table and the carried totals against the engine's own sums |
|
|
617
|
+
| `test/page-tree.test.js` | The panel's file tree: folders by the project's paths, three states, the subtree, files and folders outside the report (a checkbox off, a place after the rest), the hidden names at the end of a level, the tree that opens folded and the unfolding the memory keeps, folding without a rebuild and a scroll a click does not touch |
|
|
618
|
+
| `test/page-choice.test.js` | The reader's choice and the work a click starts: the memory and a revisit, someone else's report, a foreign and a broken record, an address that stays clean, a link read at opening and on an open page, and the drawing of a long switch in a task of its own behind the stripe |
|
|
619
|
+
| `test/page-cols.test.js` | The fixed layout the page carries: a column's width is counted from the model rather than measured in a laid-out cell, the clip keeps a caption inside its column, the sticky header and commit column keep their edges, and the table names its own width (`width: auto` would hand the layout back to the automatic algorithm) |
|
|
516
620
|
| `test/module.test.js` | A module under a `.js` extension: measured without touching the settings; the stripper's guard is alive (proved by mutation) and does not accuse the innocent |
|
|
517
621
|
| `test/guard.test.js` | Parsing a module: it goes through a thread, both paths give one verdict, the fallback works with the thread's file away, and hundreds of parses are cheaper than a launch |
|
|
518
622
|
| `test/runner.test.js` | Reading a process's output: chunks are glued as buffers rather than appended to a string — a multi-byte character at a chunk border does not turn into two replacement characters |
|
|
@@ -563,8 +667,9 @@ The seams between modules follow the borders of data: above sit the parts that r
|
|
|
563
667
|
(`git`, `strip`, `metrics`, `history`), below the parts that work on values already collected (`data`,
|
|
564
668
|
`derived`, `page`), while the settings, the texts and the refusal stand at the edges, because everyone
|
|
565
669
|
knows them and they know no one. Both reports are counted at build time: the page gets the sources of the
|
|
566
|
-
shared calculation and of its own program pasted in (`src/derived.js`, `src/page/*.js`)
|
|
567
|
-
from disk, with no server and no network. The rest is planned step by step in
|
|
670
|
+
shared calculation and of its own program pasted in (`src/derived.js`, `src/page/*.js`) and squeezed on the way
|
|
671
|
+
in, because it opens from disk, with no server and no network. The rest is planned step by step in
|
|
672
|
+
`plans/archive/PLAN.md`.
|
|
568
673
|
|
|
569
674
|
## Wiring it into your project
|
|
570
675
|
|
|
@@ -591,7 +696,7 @@ The same release can be taken by a reference to the repository — installation
|
|
|
591
696
|
registry, but stays tied to a revision:
|
|
592
697
|
|
|
593
698
|
```bash
|
|
594
|
-
pnpm add -D github:vernikr/size-report#v2.
|
|
699
|
+
pnpm add -D github:vernikr/size-report#v2.7.0
|
|
595
700
|
```
|
|
596
701
|
|
|
597
702
|
With no network (or nothing to fetch from codeload) — the tarball: `pnpm pack` in the package clone, then
|
|
@@ -603,7 +708,7 @@ the branch moves on the installation fails with `Could not resolve <sha> to a co
|
|
|
603
708
|
observation rather than reasoning: the short pin `6530237` installed while `main` stood on it and stopped
|
|
604
709
|
working at the very next commit, while the same sha in full installed. A branch name (`#main`) and a tag
|
|
605
710
|
are both accepted, but a branch is a moving target and a tag is constant: this release stands on the tag
|
|
606
|
-
`v2.
|
|
711
|
+
`v2.7.0`, which is also the one in the example (forty characters work as well, but they have to be copied
|
|
607
712
|
out of the history by eye).
|
|
608
713
|
|
|
609
714
|
The revision in the example is a part of the claim rather than decoration: what is described below is
|
|
@@ -674,7 +779,7 @@ known to a person. What is edited most often:
|
|
|
674
779
|
| `columns` | the table's columns: `{label, paths: [...]}`; **a column is a file**: the list of paths is its renames (a revision takes whichever of them it holds), not several files at once; `label` is what a person will see |
|
|
675
780
|
| `metrics` | what a number is made of: `raw` (the size of the git object), `min` (the minified form — which one, `minify.engine` decides), `tok` (tokens), `gzip` |
|
|
676
781
|
| `tokens.family`, `tokens.encoding` | the dictionary for `tok`: the family (`openai`) and the encoding (`o200k_base` or `cl100k_base`) — the encoding changes the number, which is why it is both in the settings and in the metric's label |
|
|
677
|
-
| `minify.engine` | what counts `min`: `strip` (comments and indentation,
|
|
782
|
+
| `minify.engine` | what counts `min`: `strip` (comments and indentation — a simplification, and the method names it as one) or `esbuild` (real compression; a format the minifier does not take counts as stripping, and the method says so) |
|
|
678
783
|
| `output` | the report file (in the derived profile `docs/size-report.html`; the directory is created by the writer). The path enters the report's passport — the key of the saved choice — so a changed path means a fresh choice |
|
|
679
784
|
| `journal` | where to look for the journal sections the rows refer to |
|
|
680
785
|
| `links.commitUrl` | the commit link template, for example `https://github.com/org/repo/commit/{sha}`; derived from the `origin` address for GitHub and GitLab (for other hosts — empty rather than a guess) |
|
|
@@ -732,11 +837,11 @@ commit".
|
|
|
732
837
|
|
|
733
838
|
`size doctor` gathers all the diagnostics into one answer: the environment and its influence on the numbers
|
|
734
839
|
(the machine's settings do not influence them — the engine pins them at the call's border), the state of
|
|
735
|
-
the optional dependencies and what it means for
|
|
840
|
+
the optional dependencies and what it means for the count, the validity of the settings and the completeness
|
|
736
841
|
of the coverage. It answers with the same pieces as the other commands: the coverage block is exactly the
|
|
737
842
|
answer of `size check` rather than a second calculation. The exit code is the first by importance rather
|
|
738
843
|
than "something was found": `2` the settings are unreadable (there is nothing else to read), `3` the
|
|
739
|
-
history is cut short, `1` the coverage is incomplete, `4` a
|
|
844
|
+
history is cut short, `1` the coverage is incomplete, `4` a sensor counted another way, `0` nothing to do. A
|
|
740
845
|
sensor the settings are silent about is named unneeded rather than missing, and it is not loaded: the
|
|
741
846
|
dictionary weighs megabytes, and there is nothing to pay with for an answer the numbers never needed.
|
|
742
847
|
|
|
@@ -768,7 +873,7 @@ put together from are the templates above.
|
|
|
768
873
|
| 1 | the table diverged from the history (or an edit on disk is not committed); for `size check` — a path of the history is neither tracked nor excluded | `pnpm run sizes` and commit the table; for `check` — add the path as a column or to `skip` |
|
|
769
874
|
| 2 | something in the call or in the project — the causes are quoted as the tool prints them: **command line** (unknown flag, flag without a value, repeated flag, two modes at once, extra word, command and mode, unknown command, incompatible flag, no JSON answer, two answers at once, no commit); **settings and the project** (no settings file, settings not parsed, settings invalid, git missing, not a git repository, config already exists); **history** (no such commit, ambiguous commit, commit outside the history); **hook** (foreign hook, foreign core.hooksPath, no way to invoke the tool); **measurement** (file is not JavaScript, minifier did not parse) | the refusal text names the reason and a ready command — and it is executable: `test/refusals.test.js` guards that |
|
|
770
875
|
| 3 | a shallow history (a clone with `--depth`) | a full clone: `git fetch --unshallow` |
|
|
771
|
-
| 4 | no sensor | `minify.engine: "esbuild"` with no minifier: the numbers are stripped rather than minified. The report is built, and its text carries the reason and the fix; if the table also diverges from the history, the code stays **1** (a mismatch outranks the
|
|
876
|
+
| 4 | no sensor | `minify.engine: "esbuild"` with no minifier: the numbers are stripped rather than minified. The report is built, and its text carries the reason and the fix; if the table also diverges from the history, the code stays **1** (a mismatch outranks the sensor note) while the note about the other count is printed next to it |
|
|
772
877
|
| 5 | an internal error | this is a defect of the tool: we are the ones who need the text — see "Traps worth testing the engine on" below |
|
|
773
878
|
|
|
774
879
|
The cell of code 2 quotes the tool rather than describing it: those are the names of the refusal registry
|
|
@@ -832,12 +937,12 @@ behaviour rather than as workarounds:
|
|
|
832
937
|
metric honestly falls back to stripping: the numbers are the same as `strip`, the label says so in words,
|
|
833
938
|
and a **build** (`--write`) returns **code 4** with a ready fix. A **check** answers in two parts in that
|
|
834
939
|
case, and it is named here because it is what CI advises: if the report on disk was built with the real
|
|
835
|
-
minifier while the run goes without it, the
|
|
836
|
-
agree with the history, so the check says as much (**code 1**), showing the diverged signature
|
|
837
|
-
**naming the other count right there** in a note with a ready fix. The verdict stays with the
|
|
838
|
-
code 4 would claim the difference is explained by the sensor, and nobody checked that — the
|
|
839
|
-
may also be an edit that went past the report (the same order as `size check` and `doctor`: a
|
|
840
|
-
outranks
|
|
940
|
+
minifier while the run goes without it, the numbers were counted another way — the numbers in the table
|
|
941
|
+
no longer agree with the history, so the check says as much (**code 1**), showing the diverged signature
|
|
942
|
+
row and **naming the other count right there** in a note with a ready fix. The verdict stays with the
|
|
943
|
+
divergence: code 4 would claim the difference is explained by the sensor, and nobody checked that — the
|
|
944
|
+
divergence may also be an edit that went past the report (the same order as `size check` and `doctor`: a
|
|
945
|
+
mismatch outranks the sensor note). The fix in both cases is `pnpm run sizes`; on this environment it returns
|
|
841
946
|
**code 4**. This can be checked without reinstalling by the `SIZE_REPORT_NO_OPTIONAL=1` environment — the
|
|
842
947
|
same way `test/minify.test.js` does it.
|
|
843
948
|
- **The module parse is one worker raised once per a run** (`REFACTOR.md` R-5.4): the fallback to
|
|
@@ -933,9 +1038,9 @@ rejected tools (knip, ast-grep, size-limit, gitleaks) are in `worklog/archive/WO
|
|
|
933
1038
|
excluded, untracked) and a ready fix. The commit is named by a revision (`HEAD`, a branch, a tag), by a
|
|
934
1039
|
full sha or by its beginning.
|
|
935
1040
|
- The data without the markup — the rows, the numbers, the totals — is `--json` (the earlier form, frozen
|
|
936
|
-
byte for byte by the parity reference) and `--data` (the
|
|
937
|
-
|
|
938
|
-
command does not exist yet.
|
|
1041
|
+
byte for byte by the parity reference) and `--data` (the contract: absolute values and the shape of the
|
|
1042
|
+
table, with nothing derived — whatever the page can count itself is not there; the page's own block is the
|
|
1043
|
+
same data in sparse form). A `size measure` command does not exist yet.
|
|
939
1044
|
- `--json` is a form of answer rather than a mode of its own, and it has one rule: exactly four calls have
|
|
940
1045
|
an answer. With no command it is the earlier form of the data (frozen by the parity reference), and for
|
|
941
1046
|
`check`, `explain` and `doctor` it is their answer. For a command with no answer, and next to a mode
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vernikr/size-report",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"author": "vernikr",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"report"
|
|
66
66
|
],
|
|
67
67
|
"devDependencies": {
|
|
68
|
-
"@vernikr/size-report": "2.
|
|
68
|
+
"@vernikr/size-report": "2.6.0",
|
|
69
69
|
"c8": "10",
|
|
70
70
|
"dependency-cruiser": "17",
|
|
71
71
|
"eslint": "^9.18.0",
|
package/src/check.js
CHANGED
|
@@ -68,7 +68,7 @@ function pathCoverage(cfg, commits) {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
/* The full answer: settings, history, paths, sensors — and the verdict. `ok` means only "nothing went past
|
|
71
|
-
* the report": a sensor counting
|
|
71
|
+
* the report": a sensor counting another way does not change the verdict (it has an exit code of its own,
|
|
72
72
|
* as in every other mode). */
|
|
73
73
|
export function coverage(cfg, root, configFile) {
|
|
74
74
|
assertFullHistory(root);
|
package/src/data.js
CHANGED
|
@@ -10,19 +10,35 @@ import { TOOL_PKG } from './tool.js';
|
|
|
10
10
|
* derived quantity at all. Everything the page counts itself begins where this module ends. */
|
|
11
11
|
|
|
12
12
|
/* A file's category is only for the page's quick on/off buttons for a group: it does not reach the
|
|
13
|
-
* numbers. The rule is
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* numbers. The rule is: a category set in the column's settings outranks everything, a test is
|
|
14
|
+
* recognised by its path rather than by its extension, and for the rest the extension gives the
|
|
15
|
+
* category while everything else counts as code. The data says where the category came from
|
|
16
|
+
* (`categoryBy`) — a manual decision is explainable, while a table of extensions is a guess by file
|
|
17
|
+
* name. */
|
|
16
18
|
export const CATEGORY_EXTS = {
|
|
17
19
|
docs: ['.md', '.markdown', '.rst', '.txt', '.adoc'],
|
|
18
20
|
chore: ['.json', '.yaml', '.yml', '.toml', '.ini', '.cfg', '.conf', '.lock', '.editorconfig'],
|
|
19
21
|
assets: ['.svg', '.png', '.jpg', '.jpeg', '.gif', '.webp', '.ico', '.woff', '.woff2', '.ttf', '.otf']
|
|
20
22
|
};
|
|
21
|
-
|
|
23
|
+
/* Tests stand next to code: they are files of the same kind (`*.test.js` is JavaScript too), and a reader
|
|
24
|
+
* switching code off wants to see what the tests weigh separately. */
|
|
25
|
+
export const CATEGORY_ORDER = ['code', 'tests', 'docs', 'chore', 'assets'];
|
|
26
|
+
|
|
27
|
+
/* What makes a file a test: the folder it lies in, wherever that folder is in the project, or a name
|
|
28
|
+
* carrying `.test` before its extension. Both are read off the path rather than off the extension, which is
|
|
29
|
+
* how a test is recognised in practice — a test may be written in any language the tool can measure. */
|
|
30
|
+
const TEST_DIRS = ['test', 'tests', 'fixtures'];
|
|
31
|
+
const TEST_NAME = /\.test\.[^./]+$/;
|
|
32
|
+
|
|
33
|
+
function isTest(p) {
|
|
34
|
+
return p.split('/').some((part) => TEST_DIRS.indexOf(part) >= 0) || TEST_NAME.test(path.basename(p));
|
|
35
|
+
}
|
|
22
36
|
|
|
23
37
|
export function categoryOf(col) {
|
|
24
38
|
if (col.category) return { key: col.category, by: 'config' };
|
|
25
|
-
const
|
|
39
|
+
const last = col.paths[col.paths.length - 1];
|
|
40
|
+
if (isTest(last)) return { key: 'tests', by: 'auto' };
|
|
41
|
+
const ext = path.extname(last).toLowerCase();
|
|
26
42
|
const known = CATEGORY_ORDER.find((key) => (CATEGORY_EXTS[key] || []).indexOf(ext) >= 0);
|
|
27
43
|
return { key: known === undefined ? 'code' : known, by: 'auto' };
|
|
28
44
|
}
|
|
@@ -75,28 +91,10 @@ export function reportData(cfg, root) {
|
|
|
75
91
|
* changed just now rather than the rest. This is a fact from the history rather than a derived quantity:
|
|
76
92
|
* it does not count anything, it says what the edit touched. */
|
|
77
93
|
last: last,
|
|
78
|
-
approx: approxMarks(rows, state, cfg),
|
|
79
94
|
skipped: dropped.map(skipLine)
|
|
80
95
|
};
|
|
81
96
|
}
|
|
82
97
|
|
|
83
|
-
/* Marks of approximate cells — one record per metric: a string of marks over the row cells and one over the
|
|
84
|
-
* "now" row. '1' means the number came out of stripping or an estimate, '0' that it is exact. A metric with
|
|
85
|
-
* no mark at all does not appear in the report: all numbers exact — silence.
|
|
86
|
-
*
|
|
87
|
-
* The engine sets a mark where it counts the number, by the same rule as the metric's label. Hence the page
|
|
88
|
-
* derives nothing about paths and formats: it only shows what was said and keeps no second rule of accuracy. */
|
|
89
|
-
function approxMarks(rows, state, cfg) {
|
|
90
|
-
const out = {};
|
|
91
|
-
cfg.metrics.forEach((m) => {
|
|
92
|
-
const mark = (flags) => (flags !== null && flags[m] ? '1' : '0');
|
|
93
|
-
const inRows = rows.map((r) => r.approx.map(mark).join('')).join('');
|
|
94
|
-
const now = state.map((s) => mark(s === null ? null : s.approx)).join('');
|
|
95
|
-
if (inRows.indexOf('1') >= 0 || now.indexOf('1') >= 0) out[m] = { rows: inRows, now: now };
|
|
96
|
-
});
|
|
97
|
-
return out;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
98
|
/* The part of a row shared by both answers: the page's contract (`--data`) and the older `--json` form (frozen
|
|
101
99
|
* by the parity fixture). One place, because these two answers can drift apart exactly here, and **the order
|
|
102
100
|
* of the fields is the bytes of the answer**: the object holds them in the order the frozen form prints. */
|