mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
0344a86aec
The checker scripts were written without semicolons, against the semi: true in .prettierrc, so every one of them failed the formatting rule lint:format enforces on src/ and tests/. This is prettier --write over the scripts this branch added or rewrote, nothing else.
78 lines
2.9 KiB
JavaScript
78 lines
2.9 KiB
JavaScript
/*
|
|
* The comparison behind lint:offline (scripts/offline-lint.mjs).
|
|
*/
|
|
import { join } from 'node:path';
|
|
import { sourceGraph } from './imports.mjs';
|
|
import { fileList, lowerList, readBaseline, writeBaseline } from './ratchet.mjs';
|
|
|
|
/** The layers that render or drive a view, and so must not hold a network call. */
|
|
export const VIEW_LAYERS = ['components', 'mobile', 'pages', 'hooks'];
|
|
|
|
/**
|
|
* The view files that import from src/api/ for anything but types, each with
|
|
* the imports that do it. A type-only import is erased from the bundle and
|
|
* carries no request, so it does not count.
|
|
*/
|
|
export function bypasses(root) {
|
|
const found = {};
|
|
for (const file of sourceGraph(root)) {
|
|
if (!VIEW_LAYERS.includes(file.layer)) continue;
|
|
const direct = file.imports.filter((imp) => imp.layer === 'api' && !imp.typeOnly);
|
|
if (direct.length) found[file.key] = direct.map((imp) => `${imp.line}: '${imp.specifier}'`);
|
|
}
|
|
return found;
|
|
}
|
|
|
|
/**
|
|
* Runs the check against the list at baselinePath. A view file outside the
|
|
* list may not import src/api/. A listed file that no longer does fails the
|
|
* check as well until --update takes it out, so the list only shrinks and
|
|
* the next file at that path does not inherit the exemption. Returns the exit
|
|
* code.
|
|
*/
|
|
export function check({
|
|
root,
|
|
baselinePath = join(root, 'scripts/offline-baseline.json'),
|
|
update = false,
|
|
list = false,
|
|
log = console.log,
|
|
error = console.error,
|
|
}) {
|
|
const found = bypasses(root);
|
|
const offenders = Object.keys(found);
|
|
let baseline = readBaseline(baselinePath, fileList);
|
|
|
|
if (update) {
|
|
baseline = lowerList(baseline, offenders);
|
|
writeBaseline(baselinePath, baseline);
|
|
}
|
|
|
|
const allowed = new Set(baseline);
|
|
const fresh = offenders.filter((key) => !allowed.has(key));
|
|
const stale = baseline.filter((key) => !found[key]);
|
|
|
|
if (list) {
|
|
for (const [key, direct] of Object.entries(found)) {
|
|
log(`${allowed.has(key) ? 'listed' : 'new '} ${key}`);
|
|
for (const line of direct) log(` ${line}`);
|
|
}
|
|
}
|
|
for (const key of fresh) {
|
|
error(`FAIL ${key} imports src/api/ directly:`);
|
|
for (const line of found[key]) error(` ${line}`);
|
|
}
|
|
if (fresh.length) {
|
|
error(
|
|
'A view reads through a repo (src/repo/) or a store and writes through the mutation queue, ' +
|
|
'so it keeps working offline. A domain that is online-only on purpose still gets its calls ' +
|
|
'in a store or a repo, not in the view. See client/CLAUDE.md, "Data flow".'
|
|
);
|
|
}
|
|
for (const key of stale) {
|
|
error(`FAIL ${key} is listed in scripts/offline-baseline.json but no longer imports src/api/ (or is gone).`);
|
|
}
|
|
if (stale.length) error('Run npm run lint:offline -- --update to take it off the list.');
|
|
log(`offline: ${offenders.length} view file(s) import src/api/ directly, ${baseline.length} listed in the baseline`);
|
|
return fresh.length || stale.length ? 1 : 0;
|
|
}
|