Files
TREK/scripts/ci/knip-ratchet.test.mjs
Maurice c83fa66c67 ci: hold unused files, exports and dependencies to a knip baseline that only shrinks
Nothing looked for dead code: no knip, depcheck or ts-prune, noUnusedLocals
off. knip.jsonc configures knip over the client, server and shared workspaces
with their real entry points (main.tsx, index.ts and bootstrap, the MikroORM
config, the forked plugin host and its SDK surface, migrations and seeders
found by glob, the shared build entries with one per locale, the scripts,
the tests and the Playwright launcher). lint:knip (scripts/ci/knip-ratchet.mjs)
counts its findings per category and file against scripts/ci/knip-baseline.json,
which only shrinks and is built on the client's ratchet helpers. Today's 427
findings are frozen, among them unused server dependencies (nodemon,
@types/uuid, eslint-plugin-prettier, @mikro-orm/cli), the unused root unrun,
and imports of packages no package.json lists (leaflet.markercluster, geojson).
False positives are fixed in knip.jsonc with the reason. knip is a root
devDependency; the check runs in server-quality, the job that installs every
workspace, and knip.jsonc joins the code area of the change classifier.
2026-10-10 21:33:29 +02:00

87 lines
3.4 KiB
JavaScript

import assert from 'node:assert/strict';
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { after, describe, it } from 'node:test';
import { RatchetError } from '../../client/scripts/lib/ratchet.mjs';
import { categoryCounts, check, tally } from './lib/knip.mjs';
const scratch = mkdtempSync(join(tmpdir(), 'knip-ratchet-'));
after(() => rmSync(scratch, { recursive: true, force: true }));
const REPORT = {
issues: [
{
file: 'client/src/a.ts',
exports: [{ name: 'unusedA' }, { name: 'unusedB' }],
types: [{ name: 'OldType' }],
files: [],
},
{ file: 'server/package.json', devDependencies: [{ name: 'nodemon' }] },
{ file: 'client/src/dead.ts', files: [{ name: 'client/src/dead.ts' }] },
{ file: 'client/src/toast.tsx', duplicates: [[{ name: 'useToast' }, { name: 'default' }]] },
],
};
let n = 0;
function run(baseline, report = REPORT, update = false) {
const path = join(scratch, `baseline-${n++}.json`);
writeFileSync(path, JSON.stringify(baseline));
const out = { log: [], error: [] };
const code = check({
root: scratch,
baselinePath: path,
update,
knip: () => report,
log: (l) => out.log.push(l),
error: (l) => out.error.push(l),
});
return { code, out, path };
}
const FULL = {
exports: { 'client/src/a.ts': 2 },
types: { 'client/src/a.ts': 1 },
devDependencies: { 'server/package.json': 1 },
files: { 'client/src/dead.ts': 1 },
duplicates: { 'client/src/toast.tsx': 1 },
};
describe('lint:knip', () => {
it('KNIP-001: counts each category per file with the names behind it', () => {
const { counts, names } = tally(REPORT);
assert.deepEqual(counts.exports, { 'client/src/a.ts': 2 });
assert.deepEqual(counts.files, { 'client/src/dead.ts': 1 });
assert.deepEqual(names.duplicates['client/src/toast.tsx'], ['useToast = default']);
assert.deepEqual(counts.dependencies, {});
});
it('KNIP-002: passes findings within the baseline', () => {
const { code, out } = run(FULL);
assert.equal(code, 0, out.error.join('\n'));
assert.match(out.log.join('\n'), /knip: 6 finding\(s\), baseline allows 6/);
});
it('KNIP-003: fails a new finding, by category, and names it', () => {
const { code, out } = run({ ...FULL, exports: { 'client/src/a.ts': 1 } });
assert.equal(code, 1);
assert.match(out.error.join('\n'), /FAIL {2}exports: client\/src\/a\.ts has 2, baseline 1: unusedA, unusedB/);
});
it('KNIP-004: fails an entry above its count until --update lowers it, and never raises one', () => {
const { code, out } = run({ ...FULL, types: { 'client/src/a.ts': 3, 'client/src/gone.ts': 1 } });
assert.equal(code, 1);
assert.match(out.error.join('\n'), /types: client\/src\/a\.ts is held at 3 in scripts\/ci\/knip-baseline\.json/);
const updated = run({ ...FULL, types: { 'client/src/a.ts': 3, 'client/src/gone.ts': 1 }, exports: {} }, REPORT, true);
const written = JSON.parse(readFileSync(updated.path, 'utf8'));
assert.deepEqual(written.types, { 'client/src/a.ts': 1 });
assert.equal(written.exports, undefined);
});
it('KNIP-005: a broken baseline or a report without issues stops the check', () => {
assert.match(categoryCounts({ nonsense: {} }), /not one of/);
assert.throws(() => run({ exports: { a: 0 } }), RatchetError);
assert.throws(() => run(FULL, { files: [] }), /no issues array/);
});
});