@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/derived.js
CHANGED
|
@@ -1,29 +1,27 @@
|
|
|
1
|
-
/*
|
|
2
|
-
*
|
|
1
|
+
/* The report's derived quantities: totals, deltas, the content of a cell and a commit's caption come out of
|
|
2
|
+
* the absolute values.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* может). Поэтому у этого файла два требования, и оба обязательны:
|
|
4
|
+
* The only place where they are counted. Both outputs use this file: the page gets its text pasted into its
|
|
5
|
+
* single file (which may hold no external reference), while the terminal answers import it as an ordinary
|
|
6
|
+
* module. Hence two requirements of this file, both binding:
|
|
8
7
|
*
|
|
9
|
-
* 1.
|
|
10
|
-
* 2.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
8
|
+
* 1. No imports and no module state — or the text cannot be pasted in;
|
|
9
|
+
* 2. One `import` per line and exports as declarations (`export function`) rather than a list of names:
|
|
10
|
+
* module syntax is removed line by line when pasting, and an unparsed line must not slip into the page
|
|
11
|
+
* silently (`stripModules` / `pageScript`).
|
|
13
12
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* своего расчёта. */
|
|
13
|
+
* The two outputs can drift apart only here, so here is where it has to be guarded:
|
|
14
|
+
* `test/contract-derived.test.js` compares the page's numbers with the artifact's, while `test/page-view.test.js`
|
|
15
|
+
* watches that the page grows no calculation of its own. */
|
|
18
16
|
|
|
19
|
-
//
|
|
20
|
-
//
|
|
17
|
+
// Thousands split by thin spaces: toLocaleString depends on the Node build's ICU, while the report has to be
|
|
18
|
+
// byte-identical on any machine.
|
|
21
19
|
export function group(n) {
|
|
22
20
|
return String(n).replace(/\B(?=(\d{3})+(?!\d))/g, '\u2009');
|
|
23
21
|
}
|
|
24
22
|
|
|
25
|
-
/*
|
|
26
|
-
*
|
|
23
|
+
/* The total: the sum over the switched-on files. A switched-off file joins neither the table nor the sum —
|
|
24
|
+
* otherwise "total" would answer about something other than what is visible. */
|
|
27
25
|
export function totalsOf(values, metrics, on) {
|
|
28
26
|
const out = {};
|
|
29
27
|
metrics.forEach((m) => { out[m] = 0; });
|
|
@@ -34,17 +32,17 @@ export function totalsOf(values, metrics, on) {
|
|
|
34
32
|
return out;
|
|
35
33
|
}
|
|
36
34
|
|
|
37
|
-
/*
|
|
38
|
-
*
|
|
35
|
+
/* The delta to the previous commit. A file's appearance is a growth by its whole volume: otherwise the sum
|
|
36
|
+
* of a column's deltas would not add up to the current size. */
|
|
39
37
|
export function deltaOf(now, before) {
|
|
40
38
|
return before === null || before === undefined ? now : now - before;
|
|
41
39
|
}
|
|
42
40
|
|
|
43
|
-
/*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* `minus`
|
|
47
|
-
*
|
|
41
|
+
/* The content of a cell of a commit row: what it says and in which colour. Each output turns this into markup
|
|
42
|
+
* itself (an HTML string or a DOM node), while the rules are one. An empty cell means "no change", `—` that
|
|
43
|
+
* the file is absent in the revision.
|
|
44
|
+
* `minus` is passed in rather than chosen here: the page draws the typographic one, and the signature is part
|
|
45
|
+
* of the package's frozen API (`test/api.test.js` holds the list of names). */
|
|
48
46
|
export function cellParts(value, delta, minus) {
|
|
49
47
|
if (value === null) return { text: '—', dir: null, miss: true };
|
|
50
48
|
if (!delta) return { text: '', dir: null, miss: false };
|
|
@@ -55,14 +53,13 @@ export function cellParts(value, delta, minus) {
|
|
|
55
53
|
};
|
|
56
54
|
}
|
|
57
55
|
|
|
58
|
-
//
|
|
56
|
+
// A cell of the top row: the absolute size, with no delta.
|
|
59
57
|
export function valueParts(value) {
|
|
60
58
|
return value === null ? { text: '—', miss: true } : { text: group(value), miss: false };
|
|
61
59
|
}
|
|
62
60
|
|
|
63
|
-
/*
|
|
64
|
-
*
|
|
65
|
-
* и суммы считаются от одного выбора. */
|
|
61
|
+
/* A commit row: a "total volume" block and one block per switched-on file, each with a cell per metric. The
|
|
62
|
+
* choosing of the switched-on files happens here, so both the table and the totals come out of one choice. */
|
|
66
63
|
export function rowModel(values, prev, metrics, on) {
|
|
67
64
|
const total = totalsOf(values, metrics, on);
|
|
68
65
|
const prevTotal = prev === null ? null : totalsOf(prev, metrics, on);
|
|
@@ -85,8 +82,8 @@ export function rowModel(values, prev, metrics, on) {
|
|
|
85
82
|
return out;
|
|
86
83
|
}
|
|
87
84
|
|
|
88
|
-
/*
|
|
89
|
-
*
|
|
85
|
+
/* The top row is the absolute sizes at HEAD: an absolute number stands in the table once, and it is the one
|
|
86
|
+
* every delta below it adds up to. */
|
|
90
87
|
export function nowModel(values, metrics, on) {
|
|
91
88
|
const total = totalsOf(values, metrics, on);
|
|
92
89
|
const files = [];
|
|
@@ -97,10 +94,9 @@ export function nowModel(values, metrics, on) {
|
|
|
97
94
|
return { total: metrics.map((m) => total[m]), files: files };
|
|
98
95
|
}
|
|
99
96
|
|
|
100
|
-
/*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* здесь: два вывода не должны подписывать один коммит по-разному. */
|
|
97
|
+
/* A commit's caption in terms of data: what to show, how to caption it and where to lead. The link is counted
|
|
98
|
+
* by the engine's `rowHref` — the same place the page's contract takes it from, so both outputs lead there. The
|
|
99
|
+
* caption of the tooltip is here too: two outputs must not caption one commit differently. */
|
|
104
100
|
export function commitParts(row, showSha, href) {
|
|
105
101
|
const short = showSha ? row.sha.slice(0, 7) : '';
|
|
106
102
|
return {
|
package/src/doctor.js
CHANGED
|
@@ -8,26 +8,25 @@ import { derivedSummary } from './project.js';
|
|
|
8
8
|
import { minifier } from './minify.js';
|
|
9
9
|
import { tokenizer } from './tokens.js';
|
|
10
10
|
|
|
11
|
-
/*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
11
|
+
/* Diagnostics in a single answer (`size doctor`): does the machine stand behind the numbers, what counts
|
|
12
|
+
* the metrics here and now, are the settings usable, is everything from the history covered. It computes
|
|
13
|
+
* nothing of its own: coverage is the same answer `size check` gives (`coverage`), the environment is this
|
|
14
|
+
* machine's facts, and the dependencies are the very loaders the sensors use. There is no second calculation
|
|
15
|
+
* in the package.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* `
|
|
20
|
-
* `
|
|
21
|
-
*
|
|
22
|
-
* поэтому разойтись эти два ответа не могут.
|
|
17
|
+
* The rule of the answer: `ok` means "nothing to do", and every finding names its level. `action` means
|
|
18
|
+
* something has to be done (with a fix command where one exists); `note` is an observation — useful to know,
|
|
19
|
+
* nothing to do. One `verdictOf` at the end counts the exit code: the reading steps only name the kind of
|
|
20
|
+
* trouble (`troubles`), while both the order of the kinds and each of their codes come from one list,
|
|
21
|
+
* `WEIGHT`. A step has no code of its own, so those two answers cannot drift apart.
|
|
23
22
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
23
|
+
* What the answer does not do: it does not say whether the columns are chosen "correctly" (the project knows
|
|
24
|
+
* that), and it does not guess where there are no data — a missing answer is named in words (`coverage: null`
|
|
25
|
+
* and a finding carrying the reason).
|
|
27
26
|
*/
|
|
28
27
|
|
|
29
|
-
/*
|
|
30
|
-
*
|
|
28
|
+
/* The environment: what machine this is and what it says about the numbers. With no answer from git the
|
|
29
|
+
* answer is honestly incomplete (`git: null`) rather than invented. */
|
|
31
30
|
function environment(root) {
|
|
32
31
|
const env = {
|
|
33
32
|
node: process.version,
|
|
@@ -42,26 +41,23 @@ function environment(root) {
|
|
|
42
41
|
env.git = git(root, ['--version']).trim();
|
|
43
42
|
env.shallow = git(root, ['rev-parse', '--is-shallow-repository']).trim() === 'true';
|
|
44
43
|
} catch (_e) {
|
|
45
|
-
// git
|
|
44
|
+
// git did not answer — the finding will say so, rather than an invented value.
|
|
46
45
|
}
|
|
47
46
|
return env;
|
|
48
47
|
}
|
|
49
48
|
|
|
50
|
-
/*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* `tok` — оценкой.
|
|
49
|
+
/* Dependencies: what counts the metrics here and now. The very loaders the sensors use are asked
|
|
50
|
+
* (`minifier`, `tokenizer`), so the answer cannot drift from the number: without the minifier `min` counts
|
|
51
|
+
* by simplification, without the dictionary `tok` by estimate.
|
|
54
52
|
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
|
|
61
|
-
const UNREADABLE = 'неизвестно: настройки нечитаемы';
|
|
53
|
+
* Only what the project actually asked for is loaded: the dictionary weighs megabytes, and touching it for
|
|
54
|
+
* the sake of an "installed" line would mean paying for an answer the numbers never needed (the same rule as
|
|
55
|
+
* in the report: `test/tokens.test.js`). An unwanted sensor is named unneeded rather than unknown — it does
|
|
56
|
+
* not affect the count, and that is the answer; "unknown" stays for the case where the settings are unreadable
|
|
57
|
+
* and there is nobody to ask. */
|
|
58
|
+
const UNREADABLE = 'unknown: the settings cannot be read';
|
|
62
59
|
|
|
63
|
-
/*
|
|
64
|
-
* платим за него. */
|
|
60
|
+
/* Asked — ask the loader; not asked — say so in words and do not pay for it. */
|
|
65
61
|
function entry(asked, name, metric, load, note) {
|
|
66
62
|
if (asked !== true) return { name: name, metric: metric, present: null, note: note };
|
|
67
63
|
const { tool, version } = load();
|
|
@@ -73,20 +69,20 @@ function dependencies(cfg) {
|
|
|
73
69
|
const asksTokens = cfg === null ? null : cfg.metrics.indexOf('tok') >= 0;
|
|
74
70
|
return [
|
|
75
71
|
entry(asksMinify, 'esbuild', 'min', () => minifier(),
|
|
76
|
-
asksMinify === null ? UNREADABLE : '
|
|
72
|
+
asksMinify === null ? UNREADABLE : 'not asked for: "minify" counts by stripping'),
|
|
77
73
|
entry(asksTokens, 'gpt-tokenizer', 'tok', () => tokenizer(cfg.tokens),
|
|
78
|
-
asksTokens === null ? UNREADABLE : '
|
|
74
|
+
asksTokens === null ? UNREADABLE : 'not asked for: the metrics are ' + cfg.metrics.join(' '))
|
|
79
75
|
];
|
|
80
76
|
}
|
|
81
77
|
|
|
82
|
-
/*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
78
|
+
/* Settings: with unreadable ones the answer is honestly incomplete (there is nothing to count coverage with),
|
|
79
|
+
* and the cause is a finding rather than a refusal — diagnostics exist to name the cause and its fix, and the
|
|
80
|
+
* refusal's text carries both. A step only **names** the weight of a trouble (`troubles`); whether it outranks
|
|
81
|
+
* another is not its business — `verdictOf` decides.
|
|
86
82
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
83
|
+
* Settings derived from the project are an observation here rather than a trouble: the project works, but the
|
|
84
|
+
* numbers in its report depend on what the tool guessed about it — hence a `note` finding (it carries no exit
|
|
85
|
+
* code) and the `derived` field in the answer. */
|
|
90
86
|
function readConfig(root, configFile) {
|
|
91
87
|
try {
|
|
92
88
|
const cfg = loadConfig(configFile, root);
|
|
@@ -98,7 +94,7 @@ function readConfig(root, configFile) {
|
|
|
98
94
|
columns: cfg.columns.length, metrics: cfg.metrics
|
|
99
95
|
},
|
|
100
96
|
findings: derived
|
|
101
|
-
? [{ level: 'note', what: derivedSummary(cfg), fix: '
|
|
97
|
+
? [{ level: 'note', what: derivedSummary(cfg), fix: 'make them a file of their own: ' + cliCommand('--init') }]
|
|
102
98
|
: [],
|
|
103
99
|
troubles: []
|
|
104
100
|
};
|
|
@@ -113,41 +109,41 @@ function readConfig(root, configFile) {
|
|
|
113
109
|
}
|
|
114
110
|
}
|
|
115
111
|
|
|
116
|
-
/*
|
|
117
|
-
*
|
|
112
|
+
/* The hook: two findings, each with a fix of its own. They carry neither weight nor an exit code of their
|
|
113
|
+
* own — the report is built without the hook too, so a broken hook changes only the `ok` verdict. */
|
|
118
114
|
function hookFindings(hooks) {
|
|
119
115
|
const found = [];
|
|
120
116
|
if (!hooks.installed) return found;
|
|
121
117
|
if (hooks.enabled === false) {
|
|
122
118
|
found.push({
|
|
123
119
|
level: 'action',
|
|
124
|
-
what: '
|
|
125
|
-
fix: '
|
|
120
|
+
what: 'the hook is installed, but the automation is switched off by the setting hooks.enabled: the report is updated by hand',
|
|
121
|
+
fix: 'put "hooks": {"enabled": true} back in the settings file, or take the hook off: ' + cliCommand('uninstall-hook')
|
|
126
122
|
});
|
|
127
123
|
}
|
|
128
124
|
if (hooks.last !== null && HOOK_BAD.indexOf(hooks.last.result) >= 0) {
|
|
129
125
|
found.push({
|
|
130
126
|
level: 'action',
|
|
131
|
-
what: '
|
|
132
|
-
fix: '
|
|
127
|
+
what: 'hook: the last run did not rebuild the report — ' + hooks.last.why,
|
|
128
|
+
fix: 'fix what the reason complains about and rebuild the report: ' + cliCommand('--write')
|
|
133
129
|
});
|
|
134
130
|
}
|
|
135
131
|
return found;
|
|
136
132
|
}
|
|
137
133
|
|
|
138
|
-
/*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
134
|
+
/* Coverage is the same answer `size check` gives, plus the kind of trouble if there is one: an incomplete path
|
|
135
|
+
* or a sensor counting another way (the kinds weigh differently — `WEIGHT`), with incompleteness outranking,
|
|
136
|
+
* because without it there are no numbers at all.
|
|
137
|
+
* What becomes a finding and what does not: the report holds the whole coverage block (the same text as
|
|
138
|
+
* `size check`), so incompleteness is not retold a second time — it weighs. Sensors do get a finding:
|
|
139
|
+
* `size check` prints them as a `!` line, while here they are part of the answer. */
|
|
144
140
|
function readCoverage(cfg, root, configFile) {
|
|
145
141
|
if (cfg === null) {
|
|
146
142
|
return {
|
|
147
143
|
report: null,
|
|
148
144
|
findings: [{
|
|
149
145
|
level: 'note',
|
|
150
|
-
what: '
|
|
146
|
+
what: 'coverage was not counted: the settings cannot be read — fix them and ask again'
|
|
151
147
|
}],
|
|
152
148
|
troubles: []
|
|
153
149
|
};
|
|
@@ -161,10 +157,10 @@ function readCoverage(cfg, root, configFile) {
|
|
|
161
157
|
};
|
|
162
158
|
} catch (e) {
|
|
163
159
|
if (!(e instanceof Refusal)) throw e;
|
|
164
|
-
/*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
160
|
+
/* Two things refuse inside coverage, of different kinds: a truncated history gets a kind of its own
|
|
161
|
+
* (`assertFullHistory`), while an unparsed file counts as settings — the number is missing because of them,
|
|
162
|
+
* and its fix is in the settings too. The kind is picked by the refusal's code: those two codes are the
|
|
163
|
+
* whole choice. */
|
|
168
164
|
return {
|
|
169
165
|
report: null,
|
|
170
166
|
findings: [{ level: 'action', what: e.message }],
|
|
@@ -173,11 +169,11 @@ function readCoverage(cfg, root, configFile) {
|
|
|
173
169
|
}
|
|
174
170
|
}
|
|
175
171
|
|
|
176
|
-
/*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
172
|
+
/* The weight of troubles — the whole order of importance, and one for the entire module: the keys run by
|
|
173
|
+
* importance, the values are each kind's code. The exit code comes from the most important thing found rather
|
|
174
|
+
* than from the last one found: first what leaves nothing to count with (settings, history), then incomplete
|
|
175
|
+
* coverage, then the caveat about the count.
|
|
176
|
+
* The hook is not in this list: the report is built without it too (see `hookFindings`). */
|
|
181
177
|
const WEIGHT = {
|
|
182
178
|
config: EXIT.CONFIG,
|
|
183
179
|
history: EXIT.SHALLOW,
|
|
@@ -185,9 +181,9 @@ const WEIGHT = {
|
|
|
185
181
|
sensor: EXIT.SENSOR
|
|
186
182
|
};
|
|
187
183
|
|
|
188
|
-
/*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
184
|
+
/* The verdict — the one place where troubles turn into an exit code and into `ok`. "Nothing to do" means no
|
|
185
|
+
* action finding and counted complete coverage: with no coverage there is no verdict, because there is nothing
|
|
186
|
+
* left to count (see the note in `readCoverage`). */
|
|
191
187
|
function verdictOf(rep, troubles) {
|
|
192
188
|
const found = Object.keys(WEIGHT).filter((kind) => troubles.indexOf(kind) >= 0);
|
|
193
189
|
rep.exit = found.length === 0 ? EXIT.OK : WEIGHT[found[0]];
|
|
@@ -215,22 +211,22 @@ export function doctor(root, configFile) {
|
|
|
215
211
|
return verdictOf(rep, config.troubles.concat(cov.troubles));
|
|
216
212
|
}
|
|
217
213
|
|
|
218
|
-
/*
|
|
219
|
-
*
|
|
214
|
+
/* The outcome of the hook's last run in words: it tells a person what happened after a commit without looking
|
|
215
|
+
* into `.git`. */
|
|
220
216
|
const HOOK_RESULT = {
|
|
221
|
-
committed: '
|
|
222
|
-
rebuilt: '
|
|
223
|
-
unchanged: '
|
|
224
|
-
refused: '
|
|
225
|
-
failed: '
|
|
226
|
-
skipped: '
|
|
217
|
+
committed: 'the report was rebuilt and committed',
|
|
218
|
+
rebuilt: 'the report was rebuilt without a commit',
|
|
219
|
+
unchanged: 'there was nothing to change',
|
|
220
|
+
refused: 'a refusal',
|
|
221
|
+
failed: 'an error',
|
|
222
|
+
skipped: 'skipped'
|
|
227
223
|
};
|
|
228
|
-
//
|
|
224
|
+
// The outcomes that call for action: a refusal by the tool and its own error.
|
|
229
225
|
const HOOK_BAD = ['refused', 'failed'];
|
|
230
226
|
|
|
231
|
-
/*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
227
|
+
/* The state of the hook: whether it is installed, switched on by the settings, and how its last run ended.
|
|
228
|
+
* "Not installed" is not a finding: the automation is installed by an explicit command, and its absence is the
|
|
229
|
+
* project's decision rather than forgetfulness. */
|
|
234
230
|
function hooksReport(root, cfg) {
|
|
235
231
|
const status = hookStatus(root);
|
|
236
232
|
return {
|
|
@@ -242,40 +238,40 @@ function hooksReport(root, cfg) {
|
|
|
242
238
|
}
|
|
243
239
|
|
|
244
240
|
function hookLine(hooks) {
|
|
245
|
-
if (!hooks.installed) return '
|
|
241
|
+
if (!hooks.installed) return 'not installed (installed with the command ' + cliCommand('install-hook') + ')';
|
|
246
242
|
const last = hooks.last === null
|
|
247
|
-
? '
|
|
248
|
-
: '
|
|
243
|
+
? 'it has not run yet'
|
|
244
|
+
: 'the last run ' + hooks.last.at + ' — ' + (HOOK_RESULT[hooks.last.result] || hooks.last.result)
|
|
249
245
|
+ (hooks.last.commit ? ' (' + hooks.last.commit + ')' : '')
|
|
250
246
|
+ (hooks.last.why ? ': ' + hooks.last.why.split('\n')[0] : '');
|
|
251
|
-
return hooks.files.join(', ') + (hooks.enabled === false ? ' (
|
|
247
|
+
return hooks.files.join(', ') + (hooks.enabled === false ? ' (switched off by the settings)' : '') + '; ' + last;
|
|
252
248
|
}
|
|
253
249
|
|
|
254
|
-
/*
|
|
255
|
-
*
|
|
250
|
+
/* The text for a person. Coverage is printed by `coverageText` — the same one `size check` uses: two answers
|
|
251
|
+
* about one thing must not drift apart in wording. */
|
|
256
252
|
export function doctorText(rep) {
|
|
257
253
|
const env = rep.environment;
|
|
258
254
|
const lines = [];
|
|
259
|
-
lines.push((rep.ok ? '✓ ' : '✗ ') + rep.tool.name + ' ' + rep.tool.version + ':
|
|
260
|
-
lines.push('
|
|
261
|
-
+ (env.git === null ? 'git
|
|
262
|
-
+ (env.shallow === null ? '' : env.shallow ? ',
|
|
263
|
-
//
|
|
264
|
-
//
|
|
265
|
-
lines.push(' git
|
|
266
|
-
+ ' (
|
|
267
|
-
lines.push('
|
|
268
|
-
? (rep.config.derived ? '
|
|
269
|
-
+ ' — ' + rep.config.columns + '
|
|
270
|
-
: rep.config.file + ' —
|
|
271
|
-
lines.push('
|
|
272
|
-
+ (d.present === null ? ' — ' + d.note : d.present ? ' ' + d.version + '
|
|
255
|
+
lines.push((rep.ok ? '✓ ' : '✗ ') + rep.tool.name + ' ' + rep.tool.version + ': diagnostics for ' + env.root);
|
|
256
|
+
lines.push(' environment: Node ' + env.node + ', ' + env.platform + ', '
|
|
257
|
+
+ (env.git === null ? 'git is unavailable' : env.git)
|
|
258
|
+
+ (env.shallow === null ? '' : env.shallow ? ', the history is truncated' : ', the history is complete'));
|
|
259
|
+
// The pins are a mechanism rather than decoration: the engine sets them itself at the call boundary, so the
|
|
260
|
+
// machine's settings do not reach the numbers (guarded by `test/environment.test.js`).
|
|
261
|
+
lines.push(' git is read with the pins: ' + env.pins.join(', ') + '; locale ' + env.locale
|
|
262
|
+
+ ' (the settings of the machine do not reach the numbers)');
|
|
263
|
+
lines.push(' settings: ' + (rep.config.ok
|
|
264
|
+
? (rep.config.derived ? 'derived from the project (no file)' : rep.config.file)
|
|
265
|
+
+ ' — ' + rep.config.columns + ' columns, metrics ' + rep.config.metrics.join(' ')
|
|
266
|
+
: rep.config.file + ' — unreadable'));
|
|
267
|
+
lines.push(' dependencies: ' + rep.dependencies.map((d) => d.name
|
|
268
|
+
+ (d.present === null ? ' — ' + d.note : d.present ? ' ' + d.version + ' present' : ' absent')
|
|
273
269
|
+ ' (' + d.metric + ')').join(', '));
|
|
274
|
-
lines.push('
|
|
270
|
+
lines.push(' hook: ' + hookLine(rep.hooks));
|
|
275
271
|
if (rep.coverage) lines.push(coverageText(rep.coverage));
|
|
276
272
|
rep.findings.forEach((f) => {
|
|
277
273
|
lines.push((f.level === 'action' ? '✗ ' : '· ') + f.what);
|
|
278
|
-
if (f.fix) lines.push('
|
|
274
|
+
if (f.fix) lines.push(' fix: ' + f.fix);
|
|
279
275
|
});
|
|
280
276
|
return lines.join('\n');
|
|
281
277
|
}
|