@vernikr/size-report 2.8.3 → 2.8.4
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 +23 -8
- package/package.json +2 -2
- package/src/config.js +1 -1
- package/src/hook.js +13 -12
- package/src/locales.js +0 -3
- package/src/metrics.js +1 -4
- package/src/page/table.js +3 -3
- package/src/project.js +1 -1
- package/src/strip/js.js +10 -10
- package/src/tokens.js +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ shows.
|
|
|
10
10
|
|
|
11
11
|
## Status
|
|
12
12
|
|
|
13
|
-
**Release 2.8.
|
|
13
|
+
**Release 2.8.4 (2026-09-18).** 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,28 @@ 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.8.4 is about the volume of the tree rather than of a report, and it is a sweep — no feature. **The six probes that
|
|
23
|
+
measured the page's steps are archived** (`probes/archive/`, 1 458 lines): the plan they served is settled, its figures
|
|
24
|
+
stand in `plans/2026-09-17-page-perf/` and in the worklog, so the instruments stay as the record of how the numbers were
|
|
25
|
+
taken, with every reference following them. **The open notes are closed**: the help's continuation lines are no longer
|
|
26
|
+
read as commands (the list falls from ten words to six), an advice without a flag is a command again, two assertions
|
|
27
|
+
that could not fail are gone (`test/check.test.js`, `test/gates-verify.test.js`), the last two Russian comments left
|
|
28
|
+
`src/` (the locale's strings stay — they are data), and `tools/run-tests.js` hands its extra flags to `node --test`
|
|
29
|
+
**before** the file, where node 22 reads them. **The copies below the sensors' thresholds went too**: one helper
|
|
30
|
+
spawns the frozen copy instead of two builders' own, one function sets the identity git refuses to commit without
|
|
31
|
+
(three sites had grown the loop), and a suite's temporary directory removes itself when the process ends — 25 suites
|
|
32
|
+
opened with the same pair of lines. Nine `export` keywords on names nothing outside their module reads left the
|
|
33
|
+
surface, and `TODO.md` fell from 96 lines to 31. The checks are at **87** in the fast profile and **192** in the full
|
|
34
|
+
one, none added and none removed; the code of the tree changed by −44 lines. The figures and the reasoning stand in
|
|
35
|
+
`worklog/0213-the-volume-swept-and-the-probes-archived.md`.
|
|
36
|
+
|
|
22
37
|
2.8.3 is about what a sideways scroll of the report costs, and its figures are measured rather than retold. **A step
|
|
23
38
|
sideways moves the window instead of building it:** a row is made once and lives through the move, keeping its node and
|
|
24
39
|
its numbers — the window's columns are the range of that row's numbers, so the columns that entered it are made and the
|
|
25
40
|
ones that left are dropped, the header's line of names and its line of metrics by the same rule. A switch of the
|
|
26
41
|
reader's own and the shell's resize are the one thing that cannot be a move — a column that is not built cannot be
|
|
27
42
|
shown — and they build the window from nothing as they did. **Measured on this repository's own report in live Chrome**
|
|
28
|
-
(`probes/step-12-window.mjs`, two rounds, 1440 × 900): the page's own handling of one step **10.4 → 0.6 ms** (worst
|
|
43
|
+
(`probes/archive/step-12-window.mjs`, two rounds, 1440 × 900): the page's own handling of one step **10.4 → 0.6 ms** (worst
|
|
29
44
|
19.2 → 3.6) and the layout the step forces **14.4 → 5.9 ms**; over a sweep of forty steps the browser's own accounting
|
|
30
45
|
gives layout **0.605 → 0.246 s**, style recalculation 0.169 → 0.056 s and task time **1.281 → 0.350 s**. The checks go
|
|
31
46
|
86 → **87** in the fast profile and 191 → **192** in the full one: `test/page-grid.test.js` holds the rule by node
|
|
@@ -104,7 +119,7 @@ class changes are about 2 µs a node; on this page the whole Table of Contents o
|
|
|
104
119
|
the same click in 37 slices paid that relayout 37 times — **169 layouts and 151.9 s of pure layout time against
|
|
105
120
|
1.04 s**, with the tab growing to gigabytes of repaint and the reader waiting four minutes for a switch that
|
|
106
121
|
costs him less than a second unsliced. Within one task nothing can repaint, so a bar that filled would be a bar
|
|
107
|
-
nobody could read: the stripe travels and says the work is going on (`probes/step-12-columns.mjs`, the records in
|
|
122
|
+
nobody could read: the stripe travels and says the work is going on (`probes/archive/step-12-columns.mjs`, the records in
|
|
108
123
|
`worklog/0207-page-choice-bar.md`). The order of the columns did not change there: what the last commit touched
|
|
109
124
|
still stands first, the rest after it in the settings' order.
|
|
110
125
|
|
|
@@ -409,7 +424,7 @@ left it and what entered it. The header sticks to the top of the shell and the c
|
|
|
409
424
|
number belongs to and the file it stands under are always in sight. The price of a table is what this step removed:
|
|
410
425
|
238 500 cells were 253 770 nodes in the document and 377 396 in the browser, **1.46 GB** of a fresh Chrome's memory at
|
|
411
426
|
rest, and any change of a column's visibility meant the browser laying the whole of it out again — 234.5 ms of layout for
|
|
412
|
-
a *single* cell of this repository's report, 539 ms for 6 000 (`probes/step-12-columns.mjs`). A window of the same
|
|
427
|
+
a *single* cell of this repository's report, 539 ms for 6 000 (`probes/archive/step-12-columns.mjs`). A window of the same
|
|
413
428
|
table is **2 460 nodes and 0.15 GB**, a hundredth of the nodes and a tenth of the memory, and the whole-table scroll
|
|
414
429
|
pass that took 36 s of task time over 60 steps takes **1.1 s over 251** (`worklog/0208-table-window.md`). **The columns
|
|
415
430
|
are one width and fixed, 70px each**: the numbers are short and of one kind, and a width that came out of the text is a
|
|
@@ -423,7 +438,7 @@ and in the script that counts the window's ordinals in them, and `test/page-grid
|
|
|
423
438
|
together. **A step sideways moves that window rather than building it:** the rows live through the move and only the
|
|
424
439
|
columns that entered are made, at 10.4 → **0.6 ms** for the page's own handling of the step, 14.4 → **5.9 ms** for the
|
|
425
440
|
layout it forces and 1.281 → **0.350 s** of task time over a sweep of forty steps
|
|
426
|
-
(`probes/step-12-window.mjs`).
|
|
441
|
+
(`probes/archive/step-12-window.mjs`).
|
|
427
442
|
|
|
428
443
|
**A click builds the window again, and that is cheap now.** A file switched off is simply not among the columns that
|
|
429
444
|
are built, and a metric switched off not among the metrics: there is nothing to hide and nothing to carry, and the
|
|
@@ -602,7 +617,7 @@ acceptance for each.
|
|
|
602
617
|
| `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 |
|
|
603
618
|
| `tools/make-fixture.js` | Assembles the synthetic fixture (`pnpm run fixture`): a deterministic history with traps plus the reference numbers |
|
|
604
619
|
| `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 |
|
|
605
|
-
| `probes/` | The scripted measurements behind `plans/2026-09-17-page-perf
|
|
620
|
+
| `probes/` | The archived scripted measurements behind `plans/2026-09-17-page-perf/` (`probes/archive/`, one file per step, run by hand against live Chrome at the debug port — the fixed layout, where `content-visibility` acts at all, the price of a switch and of a step sideways) and their index (`probes/README.md`). Kept as the record of how the plan's figures were taken; 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 |
|
|
606
621
|
| `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`) |
|
|
607
622
|
| `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`) |
|
|
608
623
|
| `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`) |
|
|
@@ -771,7 +786,7 @@ The same release can be taken by a reference to the repository — installation
|
|
|
771
786
|
registry, but stays tied to a revision:
|
|
772
787
|
|
|
773
788
|
```bash
|
|
774
|
-
pnpm add -D github:vernikr/size-report#v2.8.
|
|
789
|
+
pnpm add -D github:vernikr/size-report#v2.8.4
|
|
775
790
|
```
|
|
776
791
|
|
|
777
792
|
With no network (or nothing to fetch from codeload) — the tarball: `pnpm pack` in the package clone, then
|
|
@@ -783,7 +798,7 @@ the branch moves on the installation fails with `Could not resolve <sha> to a co
|
|
|
783
798
|
observation rather than reasoning: the short pin `6530237` installed while `main` stood on it and stopped
|
|
784
799
|
working at the very next commit, while the same sha in full installed. A branch name (`#main`) and a tag
|
|
785
800
|
are both accepted, but a branch is a moving target and a tag is constant: this release stands on the tag
|
|
786
|
-
`v2.8.
|
|
801
|
+
`v2.8.4`, which is also the one in the example (forty characters work as well, but they have to be copied
|
|
787
802
|
out of the history by eye).
|
|
788
803
|
|
|
789
804
|
The revision in the example is a part of the claim rather than decoration: what is described below is
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vernikr/size-report",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.4",
|
|
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.8.
|
|
68
|
+
"@vernikr/size-report": "2.8.3",
|
|
69
69
|
"c8": "10",
|
|
70
70
|
"dependency-cruiser": "17",
|
|
71
71
|
"eslint": "^9.18.0",
|
package/src/config.js
CHANGED
|
@@ -76,7 +76,7 @@ export function gitRoot() {
|
|
|
76
76
|
* request for that very file, so its absence stays a refusal (otherwise a typo in the path would
|
|
77
77
|
* silently yield someone else's settings). `path` is given in words rather than as a path: there is no
|
|
78
78
|
* file, and "edit <path>" would lead the reader to something the project does not have. */
|
|
79
|
-
|
|
79
|
+
function derivedConfig(root) {
|
|
80
80
|
const cfg = derivedProfile(root);
|
|
81
81
|
cfg.path = 'derived from the project';
|
|
82
82
|
cfg.derived = true;
|
package/src/hook.js
CHANGED
|
@@ -2,10 +2,9 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import { EXIT, Refusal, cliCommand, refuseCause } from './refusal.js';
|
|
5
|
-
import { installSpec } from './tool.js';
|
|
5
|
+
import { TOOL_PKG, installSpec } from './tool.js';
|
|
6
6
|
import { git, gitTry } from './git.js';
|
|
7
7
|
import { loadConfig } from './config.js';
|
|
8
|
-
import { TOOL_PKG } from './tool.js';
|
|
9
8
|
import { rebuild } from './artifact.js';
|
|
10
9
|
|
|
11
10
|
/* The `post-commit` and `post-merge` hooks: after every commit and merge the report rebuilds itself and, when it is tracked
|
|
@@ -120,6 +119,16 @@ function script(entry) {
|
|
|
120
119
|
+ 'exec node ' + entry.quoted + ' hook-run\n';
|
|
121
120
|
}
|
|
122
121
|
|
|
122
|
+
/* Writing the hook files, executable: the named command and the silent install write the same bytes, since a difference
|
|
123
|
+
* between them would surface only after somebody's commit. */
|
|
124
|
+
function installFiles(hooks, entry, files) {
|
|
125
|
+
fs.mkdirSync(hooks.dir, { recursive: true });
|
|
126
|
+
files.forEach((file) => {
|
|
127
|
+
fs.writeFileSync(file, script(entry));
|
|
128
|
+
fs.chmodSync(file, 0o755);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
123
132
|
/* The hook's state for `size doctor`: whether it is installed and how its last run ended. It counts nothing and commits
|
|
124
133
|
* to nothing. */
|
|
125
134
|
export function hookStatus(root) {
|
|
@@ -180,11 +189,7 @@ export function installHook(root, cfg) {
|
|
|
180
189
|
' to remove it: ' + cliCommand('uninstall-hook')
|
|
181
190
|
] };
|
|
182
191
|
}
|
|
183
|
-
|
|
184
|
-
files.forEach((file) => {
|
|
185
|
-
fs.writeFileSync(file, script(entry));
|
|
186
|
-
fs.chmodSync(file, 0o755);
|
|
187
|
-
});
|
|
192
|
+
installFiles(hooks, entry, files);
|
|
188
193
|
|
|
189
194
|
const lines = [
|
|
190
195
|
'✓ hook: ' + rels.join(', '),
|
|
@@ -219,11 +224,7 @@ export function autoInstall(root, cfg) {
|
|
|
219
224
|
const files = HOOKS.map((name) => path.join(hooks.dir, name));
|
|
220
225
|
if (files.some((f) => fs.existsSync(f) && !isOurs(f))) return null;
|
|
221
226
|
if (files.every(isOurs)) return null;
|
|
222
|
-
|
|
223
|
-
files.forEach((file) => {
|
|
224
|
-
fs.writeFileSync(file, script(entry));
|
|
225
|
-
fs.chmodSync(file, 0o755);
|
|
226
|
-
});
|
|
227
|
+
installFiles(hooks, entry, files);
|
|
227
228
|
return files.map((f) => path.relative(root, f));
|
|
228
229
|
} catch (_e) {
|
|
229
230
|
/* Not a git repository, no rights on `.git`, a foreign format — all of it means one thing: there will be no automation
|
package/src/locales.js
CHANGED
|
@@ -48,9 +48,6 @@ export const LOCALES = {
|
|
|
48
48
|
+ 'что выбрано в этом браузере.',
|
|
49
49
|
linkExtra: 'Часть ссылки пропущена: в этом отчёте нет {n} названных имён. '
|
|
50
50
|
+ 'Остальное применено.',
|
|
51
|
-
/* Данные отчёта лежат в файле сжатыми, и распаковывает их браузер. Текст называет причину
|
|
52
|
-
* (браузера нет в списке тех, что умеют `DecompressionStream`) и что с этим делать — пустая
|
|
53
|
-
* таблица не сказала бы читателю ничего. */
|
|
54
51
|
unpack: 'Данные отчёта сжаты, а браузер не умеет их распаковать: в нём нет DecompressionStream. '
|
|
55
52
|
+ 'Обновите браузер — таблицу показывать не из чего.',
|
|
56
53
|
note: 'Абсолютные размеры стоят один раз — в строке «{now}». Строк: {rows}: коммиты без '
|
package/src/metrics.js
CHANGED
|
@@ -58,9 +58,6 @@ const STYLES = {
|
|
|
58
58
|
}
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
-
/* Метрика объявляет, нужен ли ей текст блоба: `needsText: false` вместе с
|
|
62
|
-
* `fromSize: true` означает «хватит размера объекта», и тогда содержимое не
|
|
63
|
-
* читается вовсе (`git cat-file --batch` не вызывается). */
|
|
64
61
|
export const METRICS = {
|
|
65
62
|
raw: {
|
|
66
63
|
label: 'raw',
|
|
@@ -208,7 +205,7 @@ function esbuildLoader(file, cfg) {
|
|
|
208
205
|
/* The engine actually in force: the requested one may be unavailable, in which case the metric
|
|
209
206
|
* counts another way — and that is announced (`sensorGaps`), or a different count would travel
|
|
210
207
|
* into CI as the requested one. */
|
|
211
|
-
|
|
208
|
+
function minEngine(cfg) {
|
|
212
209
|
if (cfg.minify.engine !== 'esbuild') return 'strip';
|
|
213
210
|
return minifier().tool === null ? 'strip' : 'esbuild';
|
|
214
211
|
}
|
package/src/page/table.js
CHANGED
|
@@ -8,8 +8,8 @@ import { appData, appUi, appView } from './state.js';
|
|
|
8
8
|
*
|
|
9
9
|
* **Why not a `<table>`.** Measured on this repository's own report (238 500 cells = 370 758 nodes, a table 71 712 ×
|
|
10
10
|
* 5 982 px): about 1.4 GB of a browser's memory, of which roughly half the nodes and half the painted area, and a
|
|
11
|
-
* browser's relayout of it costs close to a second on any switch (`probes/step-12-columns.mjs`). `content-visibility:
|
|
12
|
-
* auto`, the cheap way out, is ignored on a table row by Chrome 153 (`probes/step-10-tables.mjs`), so the answer is
|
|
11
|
+
* browser's relayout of it costs close to a second on any switch (`probes/archive/step-12-columns.mjs`). `content-visibility:
|
|
12
|
+
* auto`, the cheap way out, is ignored on a table row by Chrome 153 (`probes/archive/step-10-tables.mjs`), so the answer is
|
|
13
13
|
* to build less rather than to promise the browser will skip it. A grid of `position: absolute` rows has no layout to
|
|
14
14
|
* be redone: a row is placed by its `top`, a column by the `left` of the group of cells that starts it, and the
|
|
15
15
|
* browser never measures a cell to decide a width — every column is `--col` wide (70px), which is what the numbers
|
|
@@ -36,7 +36,7 @@ import { appData, appUi, appView } from './state.js';
|
|
|
36
36
|
* numbers, so a step sideways makes the columns that entered and drops the ones that left (`appStrip`) — a cell per
|
|
37
37
|
* row, the header's two lines included. A row that left the window vertically is dropped and made again when the
|
|
38
38
|
* reader comes back to it, which is what makes a jump down the table cost the same as a step. Building the rows again
|
|
39
|
-
* on every sideways scroll — the first version of this step — is what `probes/step-12-window.mjs` measures against the
|
|
39
|
+
* on every sideways scroll — the first version of this step — is what `probes/archive/step-12-window.mjs` measures against the
|
|
40
40
|
* splice (`worklog/0211-sideways-step.md`).
|
|
41
41
|
*
|
|
42
42
|
* The choice is applied by building the window again: the columns of a switched-off file are simply not among the
|
package/src/project.js
CHANGED
|
@@ -204,7 +204,7 @@ function columnsOf(files, journal) {
|
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
export function projectConfig(root) {
|
|
207
|
-
const output = outputOf(
|
|
207
|
+
const output = outputOf();
|
|
208
208
|
const journal = journalOf(root);
|
|
209
209
|
const files = allPaths(root);
|
|
210
210
|
/* Only what git tracks becomes a column: a file absent at HEAD has nothing to measure (it would be
|
package/src/strip/js.js
CHANGED
|
@@ -59,16 +59,21 @@ function regexAllowed(last, word) {
|
|
|
59
59
|
return '([{,;:=!&|?+-*%~^<>'.indexOf(last) !== -1;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
/* A token taken whole: it goes into the output and the pass state is advanced over it — the token itself, not the word
|
|
63
|
+
* before it, decides where a regex may stand next, and the word being read ends with it. */
|
|
64
|
+
function take(s, end, last) {
|
|
65
65
|
s.out += s.src.slice(s.i, end);
|
|
66
66
|
s.i = end;
|
|
67
|
-
s.last =
|
|
67
|
+
s.last = last;
|
|
68
68
|
s.word = '';
|
|
69
69
|
return true;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
function takeRegex(s) {
|
|
73
|
+
if (s.src[s.i] !== '/' || !regexAllowed(s.last, s.word)) return false;
|
|
74
|
+
return take(s, endOfRegex(s.src, s.i), '/');
|
|
75
|
+
}
|
|
76
|
+
|
|
72
77
|
function endOfRegex(src, start) {
|
|
73
78
|
let i = start + 1;
|
|
74
79
|
let inClass = false;
|
|
@@ -88,12 +93,7 @@ function endOfRegex(src, start) {
|
|
|
88
93
|
function takeString(s) {
|
|
89
94
|
const quote = s.src[s.i];
|
|
90
95
|
if (quote !== '"' && quote !== "'" && quote !== '`') return false;
|
|
91
|
-
|
|
92
|
-
s.out += s.src.slice(s.i, end);
|
|
93
|
-
s.i = end;
|
|
94
|
-
s.last = quote;
|
|
95
|
-
s.word = '';
|
|
96
|
-
return true;
|
|
96
|
+
return take(s, endOfString(s.src, s.i, quote), quote);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
function endOfString(src, start, quote) {
|
package/src/tokens.js
CHANGED
|
@@ -28,7 +28,7 @@ export const CHARS_PER_TOKEN = 3;
|
|
|
28
28
|
* and the tokenizer would split them into anything at all, giving a random number. The list
|
|
29
29
|
* exists so that the metric says this in words instead of passing such a count off as counted.
|
|
30
30
|
* SVG is deliberately not here: it is text, and its tokens are meaningful. */
|
|
31
|
-
|
|
31
|
+
const BINARY_EXTS = [
|
|
32
32
|
'.png', '.jpg', '.jpeg', '.gif', '.webp', '.ico', '.avif',
|
|
33
33
|
'.woff', '.woff2', '.ttf', '.otf', '.eot',
|
|
34
34
|
'.pdf', '.zip', '.gz', '.tar', '.mp4', '.mp3', '.mov'
|