@vernikr/size-report 2.6.0 → 2.8.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 +111 -45
- package/package.json +2 -2
- package/src/css.js +2 -2
- package/src/data.js +21 -5
- package/src/locales.js +2 -2
- package/src/page/app.css +7 -2
- package/src/page/app.js +38 -47
- package/src/page/build.js +5 -3
- package/src/page/panel.js +24 -10
- package/src/page/state.js +43 -73
- package/src/page/table.js +221 -274
- package/src/table.css +89 -57
package/src/page/panel.js
CHANGED
|
@@ -66,27 +66,41 @@ function appDirHead(name, here, sub) {
|
|
|
66
66
|
return head;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/* The order inside one level, and the two rules of it: a hidden name (a leading dot) stands after every visible one —
|
|
70
|
+
* a project's service files are not what a reader looks for first — and otherwise the alphabet decides. "Other things
|
|
71
|
+
* being equal" is the whole of it: what the report holds stands before what it does not, and that partition is made
|
|
72
|
+
* before the names are compared. The rule is a function of two strings, which is what lets it be checked on its own
|
|
73
|
+
* rather than through a tree of a fixture that has no hidden files in it. */
|
|
74
|
+
export function appName(a, b) {
|
|
75
|
+
const hidden = (name) => (name.charAt(0) === '.' ? 1 : 0);
|
|
76
|
+
if (hidden(a) !== hidden(b)) return hidden(a) - hidden(b);
|
|
77
|
+
return a < b ? -1 : (a > b ? 1 : 0);
|
|
78
|
+
}
|
|
79
|
+
|
|
69
80
|
/* The folder's sign is a click target of its own, separate from the checkbox: the checkbox answers for the numbers (it
|
|
70
81
|
* switches the subtree's files on), while the sign answers for how much of the tree is visible. One target for two
|
|
71
82
|
* different decisions would mean a folder can be folded only together with switching its files on. The sign is drawn
|
|
72
83
|
* as a span rather than a button and stands beside the label rather than inside it: a label is one click target, and a
|
|
73
84
|
* control nested in it would be reached as that same target.
|
|
74
85
|
*
|
|
86
|
+
* The tree opens folded — the sign of an untouched folder says so — and the reader's unfolding is what the memory
|
|
87
|
+
* keeps (`appFoldSet`).
|
|
88
|
+
*
|
|
75
89
|
* A click on the sign rebuilds nothing: the subtree lies in the markup and a class on the row hides it. A rebuild here
|
|
76
90
|
* would be honest work for nothing — it counts the whole table (every row by every column) and so pays for numbers
|
|
77
91
|
* folding does not change. That is why only the three things the reader sees change: the class, the sign and the note
|
|
78
92
|
* in the memory. */
|
|
79
93
|
function appFoldBox(name, path) {
|
|
80
|
-
const
|
|
81
|
-
const box = appEl('span', 'fold',
|
|
82
|
-
box.title = (
|
|
94
|
+
const open = appView.open[path] === true;
|
|
95
|
+
const box = appEl('span', 'fold', open ? '▾' : '▸');
|
|
96
|
+
box.title = (open ? appUi.foldClose : appUi.foldOpen).replace('{name}', name);
|
|
83
97
|
box.addEventListener('click', () => {
|
|
84
|
-
const now = !(appView.
|
|
98
|
+
const now = !(appView.open[path] === true);
|
|
85
99
|
appFoldSet(path, now);
|
|
86
100
|
const li = box.closest('li');
|
|
87
|
-
if (li !== null) li.classList.toggle('folded', now);
|
|
88
|
-
box.textContent = now ? '
|
|
89
|
-
box.title = (now ? appUi.
|
|
101
|
+
if (li !== null) li.classList.toggle('folded', !now);
|
|
102
|
+
box.textContent = now ? '▾' : '▸';
|
|
103
|
+
box.title = (now ? appUi.foldClose : appUi.foldOpen).replace('{name}', name);
|
|
90
104
|
});
|
|
91
105
|
return box;
|
|
92
106
|
}
|
|
@@ -98,14 +112,14 @@ function appLeaves(node) {
|
|
|
98
112
|
node.others.forEach((entry) => {
|
|
99
113
|
items.push({ name: entry.path.split('/').pop(), i: null, entry: entry });
|
|
100
114
|
});
|
|
101
|
-
return items.sort((a, b) => (a.name
|
|
115
|
+
return items.sort((a, b) => appName(a.name, b.name));
|
|
102
116
|
}
|
|
103
117
|
|
|
104
118
|
/* A folder row: the folding sign, the checkbox with the number of files and the subtree. A folded folder differs by
|
|
105
119
|
* its class alone — the markup stays the same. */
|
|
106
120
|
function appDir(name, sub, prefix) {
|
|
107
121
|
const here = prefix === '' ? name : prefix + '/' + name;
|
|
108
|
-
const folded = appView.
|
|
122
|
+
const folded = appView.open[here] !== true;
|
|
109
123
|
const li = appEl('li', folded ? 'folded' : null);
|
|
110
124
|
li.appendChild(appFoldBox(name, here));
|
|
111
125
|
li.appendChild(appDirHead(name, here, sub));
|
|
@@ -126,7 +140,7 @@ function appLeaf(leaf) {
|
|
|
126
140
|
* it does not distract from what is in the table, while it can still be found — in the same place where it was. */
|
|
127
141
|
function appTreeList(node, prefix) {
|
|
128
142
|
const list = appEl('ul', 'tree');
|
|
129
|
-
const dirs = [...node.dirs.keys()].sort()
|
|
143
|
+
const dirs = [...node.dirs.keys()].sort(appName)
|
|
130
144
|
.map((name) => ({ name: name, sub: node.dirs.get(name), inReport: appIndexes(node.dirs.get(name)).length > 0 }));
|
|
131
145
|
const leaves = appLeaves(node);
|
|
132
146
|
const inside = leaves.filter((leaf) => leaf.entry === null);
|
package/src/page/state.js
CHANGED
|
@@ -11,10 +11,10 @@ import { appDecode } from './payload.js';
|
|
|
11
11
|
* network: the styling arrives in the same file, and the cell markup follows the rules of the shared part of the styling
|
|
12
12
|
* (`clip`, a commit's caption).
|
|
13
13
|
*
|
|
14
|
-
* The panel remembers the reader's choice between visits
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* The panel remembers the reader's choice between visits ("the choice's memory" below): the record is tied to the
|
|
15
|
+
* report's passport and keeps only what is switched off, by name, so someone else's record is not applied while a
|
|
16
|
+
* vanished name simply means nothing. The record stays in the browser's memory and nowhere else: a report opened from
|
|
17
|
+
* disk keeps a clean address, and a link made in an earlier release is still read (`appLinkUse`).
|
|
18
18
|
*
|
|
19
19
|
* The page draws no conclusion about how a number was obtained: the method of each metric arrives in the data, and the
|
|
20
20
|
* page prints it. There is no second rule of counting here, and no vocabulary of precision either. */
|
|
@@ -47,7 +47,10 @@ let appFoldKey = null;
|
|
|
47
47
|
* on the data, and until the block is unpacked there is nothing to count it from. */
|
|
48
48
|
export function appBoot(text) {
|
|
49
49
|
appData = appDecode(JSON.parse(text));
|
|
50
|
-
|
|
50
|
+
/* The tree is folded as it opens: a project's tree is longer than the window, and the reader's first look is at a
|
|
51
|
+
* short list rather than at everything. What is remembered is the opposite — the folders the reader unfolded
|
|
52
|
+
* (`appFoldRead`). */
|
|
53
|
+
appView = { metrics: {}, files: [], open: {} };
|
|
51
54
|
appMetric = {};
|
|
52
55
|
appMeasured = {};
|
|
53
56
|
appData.metrics.forEach((m) => { appView.metrics[m.key] = true; appMetric[m.key] = m; });
|
|
@@ -57,14 +60,12 @@ export function appBoot(text) {
|
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
/* 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.
|
|
63
|
+
* as a link, and there is nothing to argue with it about. The page does not write it any more — it only reads what came
|
|
64
|
+
* with the address — and this paragraph is what such a link is read by. */
|
|
61
65
|
const APP_LINK = '#size-report=';
|
|
62
66
|
|
|
63
|
-
/*
|
|
64
|
-
*
|
|
65
|
-
* the message about the link has not faded yet. */
|
|
66
|
-
let appStartup = true;
|
|
67
|
-
let appForeign = false;
|
|
67
|
+
/* One circumstance of the first drawing, and it acts on it alone: the memory is not written while somebody else's link
|
|
68
|
+
* is open — what came in is not the reader's choice, and only his own action makes it his. */
|
|
68
69
|
let appTransient = false;
|
|
69
70
|
|
|
70
71
|
/* -------- the reader's memory of his choice -------- */
|
|
@@ -121,59 +122,27 @@ function appRecordOk(rec) {
|
|
|
121
122
|
return rec !== null && typeof rec === 'object' && rec.v === 1 && rec.passport === appPassport();
|
|
122
123
|
}
|
|
123
124
|
|
|
124
|
-
/* The
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
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. */
|
|
125
|
+
/* The record goes into the browser's memory and nowhere else: it is written on the click itself, which is what survives
|
|
126
|
+
* a closing, and the page's address keeps a clean tail — the report is a local page whose address is copied as it is,
|
|
127
|
+
* and a reader's choice belongs in the browser that made it rather than in the tab's title bar. What a link sent from an
|
|
128
|
+
* earlier release holds is still read (`appLinkUse`), and it is not written into the reader's memory: what came in is
|
|
129
|
+
* not his choice until he changes something. */
|
|
155
130
|
export function appWrite() {
|
|
131
|
+
if (appTransient) return;
|
|
156
132
|
const rec = appRecord();
|
|
157
|
-
const text = JSON.stringify(rec);
|
|
158
133
|
const empty = Object.keys(rec.metrics).length === 0 && Object.keys(rec.files).length === 0;
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
* and the markup do not depend on it. */
|
|
166
|
-
}
|
|
134
|
+
try {
|
|
135
|
+
if (empty) window.localStorage.removeItem(appKey);
|
|
136
|
+
else window.localStorage.setItem(appKey, JSON.stringify(rec));
|
|
137
|
+
} catch (_e) {
|
|
138
|
+
/* There is no memory (the browser grants this page none): the choice will not survive a closing, while the numbers
|
|
139
|
+
* and the markup do not depend on it. */
|
|
167
140
|
}
|
|
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. */
|
|
170
|
-
if (appStartup || appForeign) return;
|
|
171
|
-
appAddressLater(text);
|
|
172
141
|
}
|
|
173
142
|
|
|
174
143
|
/* 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
|
|
176
|
-
* than on top of someone else's. */
|
|
144
|
+
* rather than a missing value. A link carries the sender's whole choice, which is why it is applied to a clean view
|
|
145
|
+
* rather than on top of someone else's. */
|
|
177
146
|
function appAll() {
|
|
178
147
|
appData.metrics.forEach((m) => { appView.metrics[m.key] = true; });
|
|
179
148
|
appData.files.forEach((_f, i) => { appView.files[i] = true; });
|
|
@@ -273,14 +242,15 @@ export function appApply(rec) {
|
|
|
273
242
|
appData.files.forEach((_f, i) => { if (files[appFileAt(i)] === false) appView.files[i] = false; });
|
|
274
243
|
}
|
|
275
244
|
|
|
276
|
-
/* -------- the
|
|
277
|
-
|
|
245
|
+
/* -------- the unfolded tree -------- */
|
|
278
246
|
|
|
279
|
-
/*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
247
|
+
/* How much of the tree is visible is a memory of the same kind as the choice, but of a record of its own: it is about
|
|
248
|
+
* what the onlooker looks at rather than about which numbers are read, which is why it never goes into a link and never
|
|
249
|
+
* leaves the browser. The tree opens folded, so **the unfolded folders are what is kept** (`true`) — the default is the
|
|
250
|
+
* absence of the name, the same way "switched on" is the absence of a choice. A folder's name is its path ("src/page"),
|
|
251
|
+
* so a vanished name simply means nothing, and unfolding nothing is the state the page opens in: then the record is not
|
|
252
|
+
* kept at all rather than being kept empty. `appFoldKey` is set with the model (`appBoot`), for the reason the key
|
|
253
|
+
* itself is. */
|
|
284
254
|
export function appFoldRead() {
|
|
285
255
|
let text = null;
|
|
286
256
|
try {
|
|
@@ -296,19 +266,19 @@ export function appFoldRead() {
|
|
|
296
266
|
return;
|
|
297
267
|
}
|
|
298
268
|
if (!appRecordOk(rec)) return;
|
|
299
|
-
const
|
|
300
|
-
Object.keys(
|
|
269
|
+
const open = rec.open || {};
|
|
270
|
+
Object.keys(open).forEach((p) => { if (open[p] === true) appView.open[p] = true; });
|
|
301
271
|
}
|
|
302
272
|
|
|
303
|
-
export function appFoldSet(path,
|
|
304
|
-
if (
|
|
305
|
-
else delete appView.
|
|
306
|
-
const rec = { v: 1, passport: appPassport(),
|
|
273
|
+
export function appFoldSet(path, open) {
|
|
274
|
+
if (open) appView.open[path] = true;
|
|
275
|
+
else delete appView.open[path];
|
|
276
|
+
const rec = { v: 1, passport: appPassport(), open: Object.assign({}, appView.open) };
|
|
307
277
|
try {
|
|
308
|
-
if (Object.keys(rec.
|
|
278
|
+
if (Object.keys(rec.open).length === 0) window.localStorage.removeItem(appFoldKey);
|
|
309
279
|
else window.localStorage.setItem(appFoldKey, JSON.stringify(rec));
|
|
310
280
|
} catch (_e) {
|
|
311
|
-
/* There is no memory: what is
|
|
312
|
-
*
|
|
281
|
+
/* There is no memory: what is unfolded will not survive a closing, while the view does not depend on it — the tree
|
|
282
|
+
* is unfolded exactly the way the reader unfolded it just now. */
|
|
313
283
|
}
|
|
314
284
|
}
|