mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 13:36:48 +00:00
bd267c67f8
lint:size, lint:layers, lint:rtl (both baselines), lint:skips and lint:warnings only failed a count that passed its entry. A count that fell below its entry was logged and nothing more, so a file that shrank could grow back to its old entry unseen, and the entry of a deleted file stayed behind for the next file at that path. lint:format and lint:offline already fail on such a stale entry, and the docs said these counts may only shrink. Each of them now fails on an entry that lowerCounts would lower or drop, naming its count or saying the file is gone, and tells to run --update, which still only lowers. staleCounts and reportStale in lib/ratchet.mjs hold the shared part. Every baseline matches the tree today, so none needed lowering. The client CLAUDE.md says so, and that CI lints through lint:warnings rather than lint:check.
109 lines
3.6 KiB
JavaScript
109 lines
3.6 KiB
JavaScript
/*
|
|
* The measuring and the comparison behind lint:size (scripts/size-lint.mjs).
|
|
*
|
|
* A file's size is its lines as an editor wraps them at WIDTH columns: a line
|
|
* up to WIDTH characters counts once, a longer one once per WIDTH characters
|
|
* it spans. Counting newlines alone let a file stay under its entry by joining
|
|
* lines, and the trip planner page already held a 3760 character one; WIDTH is
|
|
* the printWidth in .prettierrc, so formatted code counts what it shows.
|
|
*/
|
|
import { join } from 'node:path';
|
|
import {
|
|
countMap,
|
|
listFiles,
|
|
lowerCounts,
|
|
readBaseline,
|
|
readText,
|
|
reportStale,
|
|
staleCounts,
|
|
TEST_FILE,
|
|
toKey,
|
|
writeBaseline,
|
|
} from './ratchet.mjs';
|
|
|
|
export const WIDTH = 120;
|
|
|
|
/**
|
|
* The kinds of file the check covers, each with the size a file without a
|
|
* baseline entry may reach. Tests get more room: a spec mirrors the component
|
|
* it covers and holds fixtures, but past LIMIT it is a god file too. The
|
|
* Playwright specs and their fixtures under e2e/ count as tests.
|
|
*/
|
|
export const GROUPS = [
|
|
{
|
|
name: 'test',
|
|
limit: 2000,
|
|
accepts: (key) =>
|
|
/\.tsx?$/.test(key) && (key.startsWith('tests/') || key.startsWith('e2e/') || TEST_FILE.test(key)),
|
|
},
|
|
{
|
|
name: 'source',
|
|
limit: 1000,
|
|
accepts: (key) => key.startsWith('src/') && /\.tsx?$/.test(key) && !TEST_FILE.test(key),
|
|
},
|
|
{ name: 'stylesheet', limit: 1000, accepts: (key) => key.startsWith('src/') && key.endsWith('.css') },
|
|
];
|
|
|
|
export const DIRS = ['src', 'tests', 'e2e'];
|
|
|
|
export function groupOf(key) {
|
|
return GROUPS.find((group) => group.accepts(key)) ?? null;
|
|
}
|
|
|
|
/** The size of a text as wrapped at WIDTH; a final newline does not start another line. */
|
|
export function sizeOf(text) {
|
|
if (text === '') return 0;
|
|
const lines = text.replace(/\r\n?/g, '\n').split('\n');
|
|
if (text.endsWith('\n')) lines.pop();
|
|
return lines.reduce((sum, line) => sum + Math.max(1, Math.ceil(line.length / WIDTH)), 0);
|
|
}
|
|
|
|
/** Every covered file under root with its size, keyed by its path from root. */
|
|
export function measure(root) {
|
|
const sizes = {};
|
|
for (const path of listFiles(root, DIRS, (key) => groupOf(key) !== null))
|
|
sizes[toKey(root, path)] = sizeOf(readText(path));
|
|
return sizes;
|
|
}
|
|
|
|
const limitOf = (key) => groupOf(key)?.limit ?? 0;
|
|
|
|
/**
|
|
* Runs the check against the baseline at baselinePath. With update, the
|
|
* baseline is lowered first. An entry above the file's size now, or for a
|
|
* file that is gone, fails as well until --update lowers it. Returns the
|
|
* exit code.
|
|
*/
|
|
export function check({
|
|
root,
|
|
baselinePath = join(root, 'scripts/size-baseline.json'),
|
|
update = false,
|
|
log = console.log,
|
|
error = console.error,
|
|
}) {
|
|
const sizes = measure(root);
|
|
let baseline = readBaseline(baselinePath, countMap);
|
|
|
|
if (update) {
|
|
baseline = lowerCounts(baseline, sizes, limitOf);
|
|
writeBaseline(baselinePath, baseline);
|
|
}
|
|
|
|
const grown = Object.entries(sizes).filter(([key, n]) => n > Math.max(baseline[key] ?? 0, limitOf(key)));
|
|
const stale = staleCounts(baseline, sizes, limitOf);
|
|
|
|
for (const [key, n] of grown) {
|
|
const entry = baseline[key];
|
|
error(
|
|
`FAIL ${key}: ${n} lines (wrapped at ${WIDTH} columns), ` +
|
|
(entry ? `its baseline is ${entry}. ` : `the limit for a ${groupOf(key).name} file is ${limitOf(key)}. `) +
|
|
'Move a concern into a module of its own instead of growing the file or packing its lines.'
|
|
);
|
|
}
|
|
reportStale(stale, { file: 'size-baseline.json', command: 'lint:size', root, error });
|
|
log(
|
|
`size: ${Object.keys(sizes).length} file(s), ${Object.keys(baseline).length} past their limit held at their baseline`
|
|
);
|
|
return grown.length || stale.length ? 1 : 0;
|
|
}
|