@vernikr/size-report 2.4.0 → 2.6.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 +989 -1081
- package/bin/postinstall.js +17 -18
- package/bin/size.js +2 -2
- package/package.json +3 -4
- package/src/args.js +72 -72
- package/src/artifact.js +14 -14
- package/src/check.js +41 -42
- package/src/cli.js +26 -29
- package/src/config.js +87 -91
- package/src/css.js +14 -14
- package/src/data.js +26 -50
- package/src/derived.js +31 -35
- package/src/doctor.js +95 -99
- package/src/explain.js +46 -47
- package/src/git.js +66 -71
- package/src/history.js +74 -83
- package/src/hook.js +130 -149
- package/src/init.js +37 -37
- package/src/journal.js +17 -15
- package/src/locales.js +31 -22
- package/src/metrics.js +72 -89
- package/src/minify.js +28 -27
- package/src/modes.js +57 -60
- package/src/optional.js +13 -11
- package/src/page/app.css +76 -94
- package/src/page/app.js +124 -80
- package/src/page/build.js +193 -50
- package/src/page/dom.js +8 -9
- package/src/page/panel.js +157 -69
- package/src/page/payload.js +168 -0
- package/src/page/state.js +144 -104
- package/src/page/table.js +270 -86
- package/src/parse-worker.js +10 -10
- package/src/parse.js +43 -45
- package/src/project.js +100 -104
- package/src/refusal.js +75 -76
- package/src/size-table.js +41 -76
- package/src/strip/forms.js +5 -5
- package/src/strip/guard.js +28 -28
- package/src/strip/js.js +27 -27
- package/src/strip.js +17 -21
- package/src/table.css +54 -19
- package/src/tokens.js +27 -27
- package/src/tool.js +10 -11
- package/templates/README.md +71 -77
- package/templates/ci.yml +33 -33
- package/templates/size-report.config.json +3 -3
- package/CHANGELOG.md +0 -690
package/src/page/state.js
CHANGED
|
@@ -1,70 +1,83 @@
|
|
|
1
|
-
|
|
1
|
+
import { appDecode } from './payload.js';
|
|
2
|
+
|
|
3
|
+
/* The page's choice state: what is switched on, how it survives a closing and how it travels as a link.
|
|
2
4
|
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* же расчёт, что считает статическую таблицу, и не может дать других чисел.
|
|
5
|
+
* An ordinary source file rather than a string inside the engine: a linter sees it, and it is pasted into the assembled
|
|
6
|
+
* page (`pageScript` strips module syntax — the `import` lines and the `export` keywords of the chapters). The numbers
|
|
7
|
+
* come from the derived quantities, so switching a metric, a category or a file calls the same calculation the report's
|
|
8
|
+
* numbers come from and cannot yield different ones.
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* правила общей части оформления).
|
|
10
|
+
* The page is one file without external references, so there is no dynamic import here and nothing is loaded over the
|
|
11
|
+
* network: the styling arrives in the same file, and the cell markup follows the rules of the shared part of the styling
|
|
12
|
+
* (`clip`, a commit's caption).
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* The panel remembers the reader's choice between visits and can hand it over as a link ("the choice's memory" below):
|
|
15
|
+
* the record is tied to the report's passport and keeps only what is switched off, by name, so someone else's record is
|
|
16
|
+
* not applied while a vanished name simply means nothing. The same record goes into the address — which is what one
|
|
17
|
+
* sends to a colleague.
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* форматов страница такого вывода не делает — второго правила точности не будет. */
|
|
22
|
-
|
|
23
|
-
/* Импорт — одной строкой: модульный синтаксис снимается при вклейке построчно,
|
|
24
|
-
* и оставшаяся строка `import` попала бы в страницу (её ловит проверка). */
|
|
19
|
+
* The page draws no conclusion about how a number was obtained: the method of each metric arrives in the data, and the
|
|
20
|
+
* page prints it. There is no second rule of counting here, and no vocabulary of precision either. */
|
|
25
21
|
|
|
26
|
-
export const appData = JSON.parse(document.getElementById('data').textContent);
|
|
27
22
|
export const appUi = JSON.parse(document.getElementById('ui').textContent);
|
|
28
|
-
export const appView = { metrics: {}, files: [], folded: {} };
|
|
29
|
-
appData.metrics.forEach((m) => { appView.metrics[m.key] = true; });
|
|
30
|
-
appData.files.forEach(() => { appView.files.push(true); });
|
|
31
23
|
|
|
32
|
-
/*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
|
|
36
|
-
|
|
24
|
+
/* The model is not a constant any longer: the block in the file is **packed** (gzipped and base64 encoded,
|
|
25
|
+
* `appUnpack` of the payload chapter), so unpacking is asynchronous and the model is set once, by `appBoot`,
|
|
26
|
+
* before anything is drawn and before any chapter below reads it. The names, their shapes and their order are what
|
|
27
|
+
* they were; only their appearance moved — from the parse to that one call. */
|
|
28
|
+
export let appData = null;
|
|
29
|
+
export let appView = null;
|
|
30
|
+
export let appMetric = null;
|
|
31
|
+
export let appMeasured = null;
|
|
32
|
+
let appKey = null;
|
|
33
|
+
let appFoldKey = null;
|
|
37
34
|
|
|
38
|
-
/*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
/* One place that turns the unpacked block into what the chapters speak: the sparse form is unrolled by the payload
|
|
36
|
+
* chapter into the dense contract every number comes from — snapshots per commit, the texts themselves, "now" as
|
|
37
|
+
* the state at HEAD — and the view starts switched on whole.
|
|
38
|
+
*
|
|
39
|
+
* A metric's description by key stands here too: the way the number was obtained is text under the switches, so the
|
|
40
|
+
* page holds no second answer about "counted with what".
|
|
41
|
+
*
|
|
42
|
+
* A column by a file's path is the pointer that decides whether a leaf of the tree is a checkbox or a caption (the
|
|
43
|
+
* page's tree is the project's tree while the numbers belong to columns only); the name follows the same rule as the
|
|
44
|
+
* choice's record (`appFileAt`), so the tree and the reader's memory cannot drift apart.
|
|
45
|
+
*
|
|
46
|
+
* The key the reader's memory lives under is counted here as well, because it is the report's passport: it depends
|
|
47
|
+
* on the data, and until the block is unpacked there is nothing to count it from. */
|
|
48
|
+
export function appBoot(text) {
|
|
49
|
+
appData = appDecode(JSON.parse(text));
|
|
50
|
+
appView = { metrics: {}, files: [], folded: {} };
|
|
51
|
+
appMetric = {};
|
|
52
|
+
appMeasured = {};
|
|
53
|
+
appData.metrics.forEach((m) => { appView.metrics[m.key] = true; appMetric[m.key] = m; });
|
|
54
|
+
appData.files.forEach((_f, i) => { appView.files.push(true); appMeasured[appFileAt(i)] = i; });
|
|
55
|
+
appKey = 'size-report:' + appPassport();
|
|
56
|
+
appFoldKey = appKey + ':tree';
|
|
57
|
+
}
|
|
44
58
|
|
|
45
|
-
/*
|
|
46
|
-
*
|
|
59
|
+
/* A link is that same choice in the address, under a name of its own: someone else's anchor on the page does not count
|
|
60
|
+
* as a link, and there is nothing to argue with it about. */
|
|
47
61
|
const APP_LINK = '#size-report=';
|
|
48
62
|
|
|
49
|
-
/*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* ещё не гаснет. */
|
|
63
|
+
/* Three circumstances of the first drawing, which act on it alone: the address is not rewritten during it (it was sent to
|
|
64
|
+
* the reader rather than the other way), the memory is not touched (a link that came in is not the reader's choice), and
|
|
65
|
+
* the message about the link has not faded yet. */
|
|
53
66
|
let appStartup = true;
|
|
54
67
|
let appForeign = false;
|
|
55
68
|
let appTransient = false;
|
|
56
69
|
|
|
57
|
-
/* --------
|
|
70
|
+
/* -------- the reader's memory of his choice -------- */
|
|
58
71
|
|
|
59
|
-
/*
|
|
60
|
-
*
|
|
72
|
+
/* The name of a file for the record is its path at HEAD, or the last of the settings when the file is already gone from
|
|
73
|
+
* there: that is the name it is recognised by in the report. */
|
|
61
74
|
export function appFileAt(i) {
|
|
62
75
|
const f = appData.files[i];
|
|
63
76
|
return f.path === null ? f.paths[0] : f.path;
|
|
64
77
|
}
|
|
65
78
|
|
|
66
|
-
/*
|
|
67
|
-
*
|
|
79
|
+
/* The passport's fingerprint: an identifying mark of a record rather than protection against forgery, so 32 bits are
|
|
80
|
+
* enough (FNV-1a). */
|
|
68
81
|
function appHash(text) {
|
|
69
82
|
let h = 2166136261;
|
|
70
83
|
for (let i = 0; i < text.length; i++) {
|
|
@@ -74,22 +87,27 @@ function appHash(text) {
|
|
|
74
87
|
return (h >>> 0).toString(16);
|
|
75
88
|
}
|
|
76
89
|
|
|
77
|
-
/*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
90
|
+
/* The report's passport: the tool's name, the data schema, the artifact's path, the title and the column labels in the
|
|
91
|
+
* report's order. It is what tells one report from another — the record's key is chosen by it, so a choice made in
|
|
92
|
+
* someone else's report is not picked up. The package version and the top of the history are absent on purpose: this is
|
|
93
|
+
* the same report — updating the tool does not change what a column means, while a grown history is the very history the
|
|
94
|
+
* reader comes back to.
|
|
95
|
+
*
|
|
96
|
+
* Counted once per document: it is a constant of the report, which depends on nothing the reader can change, and every
|
|
97
|
+
* click asks for it (the key of the memory and the passport of the record). A second count would be a second answer
|
|
98
|
+
* waiting to happen, and the labels it reads do not change while the page is open. */
|
|
99
|
+
let appPassportValue = null;
|
|
83
100
|
function appPassport() {
|
|
84
|
-
|
|
85
|
-
appData.
|
|
101
|
+
if (appPassportValue === null) {
|
|
102
|
+
appPassportValue = appHash([appData.tool.name, appData.schema, appData.report.artifact,
|
|
103
|
+
appData.report.title, appData.files.map((f) => f.label).join('|')].join('\n'));
|
|
104
|
+
}
|
|
105
|
+
return appPassportValue;
|
|
86
106
|
}
|
|
87
|
-
const appKey = 'size-report:' + appPassport();
|
|
88
107
|
|
|
89
|
-
/*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* убирает запись, а не оставляет след, неотличимый от выбора. */
|
|
108
|
+
/* One record of the choice for everything: it goes both into the memory and into the address, so there are no two formats
|
|
109
|
+
* of one state. Only what is switched off is kept, by name: "switched on" and "no record" are the same state, which is
|
|
110
|
+
* why turning every checkbox back on removes the record instead of leaving a trace indistinguishable from a choice. */
|
|
93
111
|
function appRecord() {
|
|
94
112
|
const metrics = {};
|
|
95
113
|
const files = {};
|
|
@@ -98,47 +116,73 @@ function appRecord() {
|
|
|
98
116
|
return { v: 1, passport: appPassport(), metrics: metrics, files: files };
|
|
99
117
|
}
|
|
100
118
|
|
|
101
|
-
//
|
|
119
|
+
// Whether a record is ours and of the right format — one rule for the memory and the address alike.
|
|
102
120
|
function appRecordOk(rec) {
|
|
103
121
|
return rec !== null && typeof rec === 'object' && rec.v === 1 && rec.passport === appPassport();
|
|
104
122
|
}
|
|
105
123
|
|
|
124
|
+
/* The address is the link for a colleague, while the memory is the reader's own: the memory is written on the click
|
|
125
|
+
* itself — that is what survives a closing — and the address 200 ms after the last of a burst of switches, because a
|
|
126
|
+
* burst is one link rather than five history entries and five URL parses. The delay is short enough for a person and
|
|
127
|
+
* long enough to swallow a run of clicks; a timer that fires after the page is gone writes nothing useful, which is the
|
|
128
|
+
* price of not writing the address five times. */
|
|
129
|
+
const APP_ADDRESS_DELAY = 200;
|
|
130
|
+
let appAddressTimer = null;
|
|
131
|
+
|
|
132
|
+
/* An address that came in from outside wins over a write this page has not made yet: a click arms a write, a link
|
|
133
|
+
* arrives within the delay, and the choice left behind must not land on the address the reader was sent — a refused link
|
|
134
|
+
* arms nothing to replace it, so without this the page would rewrite someone else's address a fifth of a second later. */
|
|
135
|
+
export function appAddressDrop() {
|
|
136
|
+
if (appAddressTimer === null) return;
|
|
137
|
+
clearTimeout(appAddressTimer);
|
|
138
|
+
appAddressTimer = null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function appAddressLater(text) {
|
|
142
|
+
appAddressDrop();
|
|
143
|
+
appAddressTimer = setTimeout(() => {
|
|
144
|
+
appAddressTimer = null;
|
|
145
|
+
try {
|
|
146
|
+
window.history.replaceState(null, '', APP_LINK + encodeURIComponent(text));
|
|
147
|
+
} catch (_e) {
|
|
148
|
+
/* The browser grants no change of the address: the link is then taken from the browser's memory. */
|
|
149
|
+
}
|
|
150
|
+
}, APP_ADDRESS_DELAY);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/* One record for a click and two destinations: the same text goes into the memory and — a moment later — into the
|
|
154
|
+
* address, so the two cannot describe different choices. */
|
|
106
155
|
export function appWrite() {
|
|
107
156
|
const rec = appRecord();
|
|
157
|
+
const text = JSON.stringify(rec);
|
|
108
158
|
const empty = Object.keys(rec.metrics).length === 0 && Object.keys(rec.files).length === 0;
|
|
109
159
|
if (!appTransient) {
|
|
110
160
|
try {
|
|
111
161
|
if (empty) window.localStorage.removeItem(appKey);
|
|
112
|
-
else window.localStorage.setItem(appKey,
|
|
162
|
+
else window.localStorage.setItem(appKey, text);
|
|
113
163
|
} catch (_e) {
|
|
114
|
-
/*
|
|
115
|
-
*
|
|
164
|
+
/* There is no memory (the browser grants this page none): the choice will not survive a closing, while the numbers
|
|
165
|
+
* and the markup do not depend on it. */
|
|
116
166
|
}
|
|
117
167
|
}
|
|
118
|
-
/*
|
|
119
|
-
*
|
|
120
|
-
* его читателю ещё читать. */
|
|
168
|
+
/* But not during the first drawing and not when the link turned out to be someone else's: an address that came in is
|
|
169
|
+
* not ours, and the reader has yet to read it. */
|
|
121
170
|
if (appStartup || appForeign) return;
|
|
122
|
-
|
|
123
|
-
window.history.replaceState(null, '', APP_LINK + encodeURIComponent(JSON.stringify(rec)));
|
|
124
|
-
} catch (_e) {
|
|
125
|
-
/* Браузер не даёт менять адрес: ссылку тогда берут из памяти браузера. */
|
|
126
|
-
}
|
|
171
|
+
appAddressLater(text);
|
|
127
172
|
}
|
|
128
173
|
|
|
129
|
-
/*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
174
|
+
/* A reset to "everything on": the border between "this is no longer in the report" and "switched off" is the record
|
|
175
|
+
* rather than a missing value. A link carries the sender's whole choice, which is why it is applied to a clean view rather
|
|
176
|
+
* than on top of someone else's. */
|
|
132
177
|
function appAll() {
|
|
133
178
|
appData.metrics.forEach((m) => { appView.metrics[m.key] = true; });
|
|
134
179
|
appData.files.forEach((_f, i) => { appView.files[i] = true; });
|
|
135
180
|
}
|
|
136
181
|
|
|
137
|
-
/*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
* не спорить с обычными якорями страницы. */
|
|
182
|
+
/* What the address says. It answers either with a record of ours or with a refusal (`linkForeign` — a link of another
|
|
183
|
+
* report, `linkBroken` — nothing to read): someone else's or a broken choice is not applied, but it does not stay silent
|
|
184
|
+
* either — otherwise the reader would not understand why he sees something other than what was sent to him. An address
|
|
185
|
+
* without the link's name is no link at all: silence, so as not to argue with the page's ordinary anchors. */
|
|
142
186
|
function appLinkRead() {
|
|
143
187
|
const hash = window.location.hash || '';
|
|
144
188
|
if (hash.indexOf(APP_LINK) !== 0) return { rec: null, refused: null };
|
|
@@ -160,8 +204,8 @@ function appLinkRead() {
|
|
|
160
204
|
return { rec: rec, refused: null, extra: appUnknown(rec) };
|
|
161
205
|
}
|
|
162
206
|
|
|
163
|
-
/*
|
|
164
|
-
*
|
|
207
|
+
/* How many names in the link are unknown to this report: the reader has to be told about them — otherwise he would look
|
|
208
|
+
* in the table for something that was never there. */
|
|
165
209
|
function appUnknown(rec) {
|
|
166
210
|
const known = {};
|
|
167
211
|
const metricKeys = {};
|
|
@@ -173,19 +217,18 @@ function appUnknown(rec) {
|
|
|
173
217
|
return n;
|
|
174
218
|
}
|
|
175
219
|
|
|
176
|
-
/*
|
|
177
|
-
*
|
|
220
|
+
/* The message about the link fades after the reader's very first action: he has read it, and a permanent warning is noise
|
|
221
|
+
* on top of the numbers. */
|
|
178
222
|
export function appNotice(text) {
|
|
179
223
|
const el = document.getElementById('notice');
|
|
180
224
|
el.textContent = text;
|
|
181
225
|
el.hidden = text === '';
|
|
182
226
|
}
|
|
183
227
|
|
|
184
|
-
/*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
* чужим. Отказ объясняется словами — и не трогает ни вид, ни адрес. */
|
|
228
|
+
/* What its event does with the address — opening the page and an anchor change on an already open one (the browser does
|
|
229
|
+
* not reload the document then, it only moves the anchor, so without this reading a link would work in a new tab alone).
|
|
230
|
+
* A link of ours replaces the view whole: it holds the sender's entire choice rather than a difference from someone
|
|
231
|
+
* else's. A refusal is explained in words — and touches neither the view nor the address. */
|
|
189
232
|
export function appLinkUse() {
|
|
190
233
|
const link = appLinkRead();
|
|
191
234
|
if (link.rec !== null) {
|
|
@@ -201,9 +244,8 @@ export function appLinkUse() {
|
|
|
201
244
|
return 'none';
|
|
202
245
|
}
|
|
203
246
|
|
|
204
|
-
/*
|
|
205
|
-
*
|
|
206
|
-
* равносильна её отсутствию. */
|
|
247
|
+
/* Reading: only a record of ours — of our format version and our passport. Another report's record lies under another key,
|
|
248
|
+
* while a foreign, outdated or broken one amounts to its absence. */
|
|
207
249
|
export function appRead() {
|
|
208
250
|
let text = null;
|
|
209
251
|
try {
|
|
@@ -221,10 +263,9 @@ export function appRead() {
|
|
|
221
263
|
return appRecordOk(rec) ? rec : null;
|
|
222
264
|
}
|
|
223
265
|
|
|
224
|
-
/*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
* как их видит тот, кто открыл страницу впервые. */
|
|
266
|
+
/* Applying goes by name: a file is recognised by its path, a metric by its key. A name the report does not hold matches
|
|
267
|
+
* nothing (a column was pointed at another path, a metric was dropped from the settings), while files and metrics that
|
|
268
|
+
* appeared stay switched on — the way someone opening the page for the first time sees them. */
|
|
228
269
|
export function appApply(rec) {
|
|
229
270
|
const metrics = rec.metrics || {};
|
|
230
271
|
const files = rec.files || {};
|
|
@@ -232,15 +273,14 @@ export function appApply(rec) {
|
|
|
232
273
|
appData.files.forEach((_f, i) => { if (files[appFileAt(i)] === false) appView.files[i] = false; });
|
|
233
274
|
}
|
|
234
275
|
|
|
235
|
-
/* --------
|
|
276
|
+
/* -------- the folded tree -------- */
|
|
236
277
|
|
|
237
|
-
/* Сложенные папки — память того же рода, что выбор, но своей записи: она про то,
|
|
238
|
-
* сколько дерева видно, а не про то, какие числа читают. Поэтому в адрес она не
|
|
239
|
-
* идёт: ссылку отправляют ради чисел, а разложенное дерево — дело смотрящего. Как и
|
|
240
|
-
* у выбора, здесь помнится только сложенное (`true`), а имя папки — это путь
|
|
241
|
-
* («src/page»), поэтому исчезнувшее имя просто ничего не значит. */
|
|
242
|
-
const appFoldKey = appKey + ':tree';
|
|
243
278
|
|
|
279
|
+
/* Folded folders are a memory of the same kind as the choice, but of a record of their own: it is about how much of the
|
|
280
|
+
* tree is visible rather than about which numbers are read. Hence it does not go into the address: a link is sent for the
|
|
281
|
+
* sake of the numbers, while an unfolded tree is the onlooker's business. As with the choice, only what is folded is kept
|
|
282
|
+
* (`true`), and a folder's name is its path ("src/page"), so a vanished name simply means nothing. `appFoldKey` is
|
|
283
|
+
* set with the rest of the model (`appBoot`), for the reason the key itself is. */
|
|
244
284
|
export function appFoldRead() {
|
|
245
285
|
let text = null;
|
|
246
286
|
try {
|
|
@@ -268,7 +308,7 @@ export function appFoldSet(path, folded) {
|
|
|
268
308
|
if (Object.keys(rec.folded).length === 0) window.localStorage.removeItem(appFoldKey);
|
|
269
309
|
else window.localStorage.setItem(appFoldKey, JSON.stringify(rec));
|
|
270
310
|
} catch (_e) {
|
|
271
|
-
/*
|
|
272
|
-
*
|
|
311
|
+
/* There is no memory: what is folded will not survive a closing, while the view does not depend on it — the tree is
|
|
312
|
+
* folded exactly the way the reader folded it just now. */
|
|
273
313
|
}
|
|
274
314
|
}
|