@vernikr/size-report 2.8.7 → 2.8.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vernikr/size-report",
3
- "version": "2.8.7",
3
+ "version": "2.8.8",
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.6",
68
+ "@vernikr/size-report": "2.8.7",
69
69
  "c8": "10",
70
70
  "dependency-cruiser": "17",
71
71
  "eslint": "^9.18.0",
package/src/config.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import { execFileSync } from 'child_process';
4
- import { MAX_BUF, gitArgv, gitEnv } from './git.js';
3
+ import { gitRead } from './git.js';
5
4
  import { advicePath, cliCommand, invocation, refuseCause } from './refusal.js';
6
5
  import { LOCALES } from './locales.js';
7
6
  import { METRICS, MINIFY_ENGINES } from './metrics.js';
@@ -51,9 +50,7 @@ export function argValue(args, name) {
51
50
 
52
51
  export function gitRoot() {
53
52
  try {
54
- return execFileSync('git', gitArgv(['rev-parse', '--show-toplevel']), {
55
- encoding: 'utf8', maxBuffer: MAX_BUF, env: gitEnv()
56
- }).trim();
53
+ return gitRead(process.cwd(), ['rev-parse', '--show-toplevel']).trim();
57
54
  } catch (e) {
58
55
  // Two dead ends with different fixes — "git did not start" and "there is no repository here" —
59
56
  // are told apart by what git itself said rather than by a guess: ENOENT means the program was not
package/src/git.js CHANGED
@@ -36,10 +36,21 @@ export function gitEnv() {
36
36
  return Object.assign({}, process.env, { LC_ALL: 'C', LANG: 'C' });
37
37
  }
38
38
 
39
+ /* How git is started here: the options that make a read the same on any machine, written once. A caller
40
+ * adds what only it needs — `input` for a batch, `encoding: undefined` where the answer is the bytes of a
41
+ * blob rather than text, an environment on top of the boundary's own (the hook builds its commit in a
42
+ * separate index with it). */
43
+ function gitOptions(root, extra) {
44
+ return Object.assign({ cwd: root, encoding: 'utf8', maxBuffer: MAX_BUF, env: gitEnv() }, extra || {});
45
+ }
46
+
47
+ /* A read: stdout as text, and an exception where git did not answer. */
48
+ export function gitRead(root, args, extra) {
49
+ return execFileSync('git', gitArgv(args), gitOptions(root, extra));
50
+ }
51
+
39
52
  export function git(root, args) {
40
- return execFileSync('git', gitArgv(args), {
41
- cwd: root, encoding: 'utf8', maxBuffer: MAX_BUF, env: gitEnv()
42
- });
53
+ return gitRead(root, args);
43
54
  }
44
55
 
45
56
  /* The same read, but with the exit code: where a non-zero code is an expected answer rather than
@@ -48,10 +59,7 @@ export function git(root, args) {
48
59
  * environment — the hook builds the report commit in a separate index with it, leaving the real
49
60
  * one untouched (`src/hook.js`). */
50
61
  export function gitTry(root, args, env) {
51
- const res = spawnSync('git', gitArgv(args), {
52
- cwd: root, encoding: 'utf8', maxBuffer: MAX_BUF,
53
- env: Object.assign(gitEnv(), env || {})
54
- });
62
+ const res = spawnSync('git', gitArgv(args), gitOptions(root, { env: Object.assign(gitEnv(), env || {}) }));
55
63
  return { status: res.status, stdout: res.stdout || '', stderr: res.stderr || '' };
56
64
  }
57
65
 
@@ -66,15 +74,14 @@ export function gitTry(root, args, env) {
66
74
  const BLOB_CHUNK = 1000; // specs per batch: it bounds both stdin and memory
67
75
 
68
76
  function catFileCheck(root, specs) {
69
- const out = execFileSync('git', gitArgv(['cat-file', '--batch-check=%(objectname) %(objecttype) %(objectsize)']), {
70
- cwd: root, encoding: 'utf8', input: specs.join('\n') + '\n', maxBuffer: MAX_BUF, env: gitEnv()
71
- });
77
+ const out = gitRead(root, ['cat-file', '--batch-check=%(objectname) %(objecttype) %(objectsize)'],
78
+ { input: specs.join('\n') + '\n' });
72
79
  return out.split('\n');
73
80
  }
74
81
 
75
82
  function catFileBatch(root, shas) {
76
- const buf = execFileSync('git', gitArgv(['cat-file', '--batch']), {
77
- cwd: root, input: shas.join('\n') + '\n', maxBuffer: MAX_BUF, env: gitEnv()
83
+ const buf = gitRead(root, ['cat-file', '--batch'], {
84
+ input: shas.join('\n') + '\n', encoding: undefined
78
85
  });
79
86
  const out = new Map();
80
87
  let i = 0;
@@ -124,9 +131,7 @@ export function readBlobs(root, specs, needText) {
124
131
  * the output is split by NUL (`-z`), or paths with spaces would have to be unquoted. */
125
132
  export function headTree(root) {
126
133
  const out = new Map();
127
- const tree = execFileSync('git', gitArgv(['ls-tree', '-r', '-z', 'HEAD']), {
128
- cwd: root, encoding: 'utf8', maxBuffer: MAX_BUF, env: gitEnv()
129
- });
134
+ const tree = gitRead(root, ['ls-tree', '-r', '-z', 'HEAD']);
130
135
  tree.split('\u0000').forEach((rec) => {
131
136
  const tab = rec.indexOf('\t');
132
137
  if (tab < 0) return;
@@ -142,9 +147,8 @@ export function headTree(root) {
142
147
  * the answers are positional, as with `cat-file`. */
143
148
  export function diskHashes(root, paths) {
144
149
  const out = new Map();
145
- const lines = execFileSync('git', gitArgv(['hash-object', '--stdin-paths']), {
146
- cwd: root, encoding: 'utf8', input: paths.join('\n') + '\n', maxBuffer: MAX_BUF, env: gitEnv()
147
- }).split('\n');
150
+ const lines = gitRead(root, ['hash-object', '--stdin-paths'],
151
+ { input: paths.join('\n') + '\n' }).split('\n');
148
152
  paths.forEach((p, i) => { out.set(p, lines[i] === undefined ? '' : lines[i].trim()); });
149
153
  return out;
150
154
  }
@@ -155,9 +159,7 @@ export function diskHashes(root, paths) {
155
159
  * while "cleaning" would turn it back into LF — git warns about such files and puts exactly this
156
160
  * on disk. */
157
161
  export function diskForm(root, rev, p) {
158
- return execFileSync('git', gitArgv(['cat-file', '--filters', rev + ':' + p]), {
159
- cwd: root, maxBuffer: MAX_BUF, env: gitEnv()
160
- });
162
+ return gitRead(root, ['cat-file', '--filters', rev + ':' + p], { encoding: undefined });
161
163
  }
162
164
 
163
165
  // The content of a file in a revision, or null when the file is not there.
package/src/page/table.js CHANGED
@@ -332,9 +332,11 @@ export function appWindow(cache, redraw) {
332
332
  return;
333
333
  }
334
334
  appHead(cache, span);
335
- [...cache.rows.keys()].forEach((r) => {
336
- if (r >= span.r0 && r <= span.r1) { appCells(cache.rows.get(r), cache, span); return; }
337
- cache.rows.get(r).el.remove();
335
+ /* Walking the map itself rather than a copy of its keys: a step down the table does not allocate,
336
+ * and the entry the window keeps is taken where it is rather than looked up a second time. */
337
+ cache.rows.forEach((entry, r) => {
338
+ if (r >= span.r0 && r <= span.r1) { appCells(entry, cache, span); return; }
339
+ entry.el.remove();
338
340
  cache.rows.delete(r);
339
341
  });
340
342
  for (let r = span.r0; r <= span.r1; r++) {
package/src/project.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import { execFileSync } from 'child_process';
4
- import { MAX_BUF, git, gitArgv, gitEnv, gitTry, readHistory } from './git.js';
3
+ import { git, gitRead, gitTry, readHistory } from './git.js';
5
4
  import { cliCommand, invocation } from './refusal.js';
6
5
 
7
6
  /* Settings derived from the project itself: what to measure, where the journal is, where to write,
@@ -41,21 +40,24 @@ const JOURNALS = ['WORKLOG.md', 'CHANGELOG.md', 'CHANGES.md', 'HISTORY.md'];
41
40
  const MAX_BYTES = 512 * 1024;
42
41
 
43
42
  /* The index gives paths and sizes: `ls-files -s` yields the objects, and their sizes are asked for in
44
- * one batch (`cat-file --batch-check`) rather than by reading the content. */
43
+ * one batch (`cat-file --batch-check`) rather than by reading the content. One call per run: the tree
44
+ * is asked for once and the answer is handed to whoever needs it (`allPaths`, `projectConfig`). */
45
45
  function indexFiles(root) {
46
46
  const listed = git(root, ['ls-files', '-s']).split('\n').filter((l) => l !== '');
47
47
  const sizes = new Map();
48
48
  const shas = listed.map((l) => l.split(/\s+/)[1]);
49
49
  if (shas.length > 0) {
50
- const checked = execFileSync('git', gitArgv(['cat-file', '--batch-check=%(objectname)\t%(objectsize)']), {
51
- cwd: root, encoding: 'utf8', input: shas.join('\n') + '\n', maxBuffer: MAX_BUF, env: gitEnv()
52
- });
50
+ const checked = gitRead(root, ['cat-file', '--batch-check=%(objectname)\t%(objectsize)'],
51
+ { input: shas.join('\n') + '\n' });
53
52
  checked.split('\n').forEach((l) => {
54
53
  const [sha, size] = l.split('\t');
55
54
  sizes.set(sha, Number(size));
56
55
  });
57
56
  }
58
- return listed.map((line) => ({ p: line.split('\t')[1], size: sizes.get(line.split(/\s+/)[1]) || 0 }));
57
+ return listed.map((line) => {
58
+ const [meta, p] = line.split('\t');
59
+ return { p: p, size: sizes.get(meta.split(' ')[1]) || 0 };
60
+ });
59
61
  }
60
62
 
61
63
  /* The history is the union of the paths of every commit, read the same way coverage reads it
@@ -76,9 +78,10 @@ function historyPaths(root) {
76
78
  return seen;
77
79
  }
78
80
 
79
- // The tree and the history in one list: everything the history touched may become a column.
80
- function allPaths(root) {
81
- const files = indexFiles(root);
81
+ // The tree and the history in one list: everything the history touched may become a column. The index
82
+ // comes from outside rather than being read here, so that one run asks git for it once.
83
+ function allPaths(root, index) {
84
+ const files = index.slice();
82
85
  const known = new Set(files.map((f) => f.p));
83
86
  historyPaths(root).forEach((p) => {
84
87
  if (!known.has(p)) files.push({ p: p, size: 0 });
@@ -206,11 +209,12 @@ function columnsOf(files, journal) {
206
209
  export function projectConfig(root) {
207
210
  const output = outputOf();
208
211
  const journal = journalOf(root);
209
- const files = allPaths(root);
212
+ const index = indexFiles(root);
210
213
  /* Only what git tracks becomes a column: a file absent at HEAD has nothing to measure (it would be
211
214
  * empty in every row of the report). A path living only in the history is therefore an exception
212
215
  * rather than a column, while the list of paths stays complete either way. */
213
- const tracked = new Set(indexFiles(root).map((f) => f.p));
216
+ const tracked = new Set(index.map((f) => f.p));
217
+ const files = allPaths(root, index);
214
218
  const readable = files.filter((f) => tracked.has(f.p) && !generated(f.p, output) && f.size <= MAX_BYTES);
215
219
  const columns = columnsOf(readable, journal);
216
220
  const taken = new Set(columns.reduce((all, c) => all.concat(c.paths), []));
@@ -272,7 +276,7 @@ export function projectTree(root, output, measured) {
272
276
  * (`test/api.test.js` holds the list of names), and the shape of the answer is the same — columns,
273
277
  * the extensions the project knows, and how many paths there are in total. */
274
278
  export function sniffColumns(root) {
275
- const files = allPaths(root);
279
+ const files = allPaths(root, indexFiles(root));
276
280
  const exts = [...new Set(files.map((f) => path.extname(f.p).toLowerCase()))]
277
281
  .filter((e) => KNOWN_EXTS.indexOf(e) >= 0).sort();
278
282
  return { columns: projectConfig(root).columns, exts: exts, total: files.length };