mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 05:26:47 +00:00
2f54f04380
lint:size counted newlines, so a file could stay under its entry by joining lines, and the trip planner page already carried one JSX element on a single 3760 character line. A line now counts once per 120 characters it spans, the printWidth in .prettierrc, so formatted code measures what it shows and packed code measures what it holds. The check also covered only .ts and .tsx sources. Stylesheets now share the 1000 line limit, and tests get a limit of their own at 2000: they mirror the component they cover and carry fixtures, but the largest are past 5000 lines and growing with the god components. The baseline is measured again with the new rule and keyed by the path from client/, since it now spans src/ and tests/. Entries are higher in number than before because the metric changed, not because a file grew; every file is held at exactly what it holds today. New entries are the ten largest tests, index.css and studio.css, the generated countryShapes.ts (305 lines of 2600 characters each) and JourneyDetailPageEntryEditor.tsx, which was just under the old count. The scanning, baseline reading and lowering move to scripts/lib/ so the next ratchets share them. A baseline that is missing or misshapen, a directory that is not there and a scan that finds nothing now fail the run instead of reading as empty, and the command file always runs its check: there is no entry point guard left that could skip it.
79 lines
3.4 KiB
JavaScript
79 lines
3.4 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, 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.
|
|
*/
|
|
export const GROUPS = [
|
|
{ name: 'test', limit: 2000, accepts: (key) => /\.tsx?$/.test(key) && (key.startsWith('tests/') || 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']
|
|
|
|
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. 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 lowerable = Object.entries(baseline).filter(([key, n]) => (sizes[key] ?? 0) < n)
|
|
|
|
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.',
|
|
)
|
|
}
|
|
if (lowerable.length && !update) {
|
|
log(`${lowerable.length} file(s) are smaller than their baseline now: run with --update to lower it.`)
|
|
}
|
|
log(`size: ${Object.keys(sizes).length} file(s), ${Object.keys(baseline).length} past their limit held at their baseline`)
|
|
return grown.length ? 1 : 0
|
|
}
|