fix(gate): knip sees the watch entry, the build must emit watch.html, and two hand copies are held
- The raw-source contract tests read files from disk (rawSources.testkit) instead of import.meta.glob, which knip counted as importing every src file — its unused-files lane could never name an orphan. knip now names src/watchMain.tsx as an entry (registered in docs/gates.md). - vite build fails when index.html or watch.html is not emitted. - The watch-entry import walk follows dynamic import() too. - audit_metrics.py's plan-code patterns are held equal to the guard's. - CrashBoundary is domain-free: it moves into components/ui.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { readSources } from "./rawSources.testkit";
|
||||
|
||||
// Every `useMutation` NAMES what failed (owner decision: the toast's title says what, the
|
||||
// line under it why). The app-wide reporter (lib/queryClient) reports a mutation that names nothing
|
||||
@@ -36,11 +37,7 @@ export function mutationCalls(source: string): string[] {
|
||||
}
|
||||
|
||||
function unnamed(): { hits: string[]; mutations: number } {
|
||||
const files = import.meta.glob("../**/*.{ts,tsx}", {
|
||||
query: "?raw",
|
||||
import: "default",
|
||||
eager: true,
|
||||
});
|
||||
const files = readSources(__dirname, "../**/*.{ts,tsx}");
|
||||
const hits: string[] = [];
|
||||
let mutations = 0;
|
||||
for (const [rel, source] of Object.entries(files)) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { readSources } from "./rawSources.testkit";
|
||||
|
||||
// Every push that opens a new view over the current one remembers the list item it was opened from
|
||||
// (`lib/history.rememberNavPosition`), so Back hands focus back to that item. A push that
|
||||
@@ -10,11 +11,10 @@ import { describe, expect, it } from "vitest";
|
||||
// The assertion pins the SET of offending sites and checks the scan found pushes at all: a guard whose
|
||||
// glob stops matching is green over nothing.
|
||||
|
||||
const files = import.meta.glob(["../**/*.ts", "../**/*.tsx", "!../**/*.test.*"], {
|
||||
query: "?raw",
|
||||
import: "default",
|
||||
eager: true,
|
||||
}) as Record<string, string>;
|
||||
const files = readSources(__dirname, ["../**/*.ts", "../**/*.tsx", "!../**/*.test.*"]) as Record<
|
||||
string,
|
||||
string
|
||||
>;
|
||||
|
||||
const EXEMPT: Record<string, string> = {
|
||||
"lib/history.ts#_ov": "the overlay stack: an overlay entry opens no view over the page",
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { readSources } from "./rawSources.testkit";
|
||||
|
||||
// Every infinite list's read-ahead waits while the list fetches ANYTHING and runs again when the fetch
|
||||
// settles. A read-ahead that only checks `isFetchingNextPage` calls during a background
|
||||
// refetch, joins it through NEXT_PAGE and loads no page: the grid stalls at its loaded end until the
|
||||
// reader scrolls the range. The jsdom virtualizer measures nothing, so the wiring is pinned in source.
|
||||
const files = import.meta.glob(["../components/**/*.tsx", "!../components/**/*.test.tsx"], {
|
||||
query: "?raw",
|
||||
import: "default",
|
||||
eager: true,
|
||||
});
|
||||
const files = readSources(__dirname, ["../components/**/*.tsx", "!../components/**/*.test.tsx"]);
|
||||
const source = (name: string) => {
|
||||
const hit = Object.entries(files).find(([path]) => path.endsWith(`/${name}`));
|
||||
if (!hit) throw new Error(`no ${name}`);
|
||||
|
||||
@@ -89,6 +89,20 @@ describe("plan codes", () => {
|
||||
expect(clean.filter(isPlanCode)).toEqual([]);
|
||||
});
|
||||
|
||||
it("are what the audit metric counts by (its copy of the patterns is held equal here)", () => {
|
||||
const py = readFileSync(new URL("scripts/audit_metrics.py", REPO), "utf8");
|
||||
// A pattern there is `NAME = re.compile(r"…" r"…", re.ASCII)`: its raw literals, joined.
|
||||
const pattern = (name: string) => {
|
||||
const start = py.indexOf(`${name} = re.compile(`);
|
||||
const call = py.slice(start, py.indexOf("re.ASCII", start));
|
||||
return [...call.matchAll(/r(["'])(.*?)\1/g)].map((m) => m[2]).join("");
|
||||
};
|
||||
// JavaScript's source escapes the slash a regex literal needs; Python's has none to escape.
|
||||
const js = (re: RegExp) => re.source.split(String.raw`\/`).join("/");
|
||||
expect(pattern("NOT_CODES")).toBe(js(NOT_CODES));
|
||||
expect(pattern("PLAN_CODE")).toBe(PLAN_CODES.map(js).join("|"));
|
||||
});
|
||||
|
||||
it("are not cited in the code", () => {
|
||||
const files = tracked();
|
||||
expect(files.length).toBeGreaterThan(1000); // a scan that reads nothing is green over nothing
|
||||
|
||||
@@ -2,6 +2,7 @@ import { readFileSync } from "node:fs";
|
||||
import { expect, it } from "vitest";
|
||||
|
||||
import { cardImage } from "./plexImage";
|
||||
import { readSources } from "./rawSources.testkit";
|
||||
|
||||
// A card's srcset names each Plex cut by its width, and the browser picks one by that width. The
|
||||
// widths are the image proxy's (backend `app/plex/imgcache.py` SIZES), with no wire format between
|
||||
@@ -51,14 +52,11 @@ it("names each backdrop cut by the width the proxy cuts it at", () => {
|
||||
// proxy's; the SET is pinned, so a new one fails here. Every <img>'s src counts, wherever it sits
|
||||
// among the attributes and whatever its variable is called: the first cut of this test read only a
|
||||
// leading `src={…thumb…}` and missed two of the three sites it was written for.
|
||||
const plexFiles = import.meta.glob(
|
||||
[
|
||||
"../components/{Plex,plex}*.tsx",
|
||||
"../components/{Home,PosterTile,UpNextCard}.tsx",
|
||||
"!../components/**/*.test.tsx",
|
||||
],
|
||||
{ query: "?raw", import: "default", eager: true },
|
||||
);
|
||||
const plexFiles = readSources(__dirname, [
|
||||
"../components/{Plex,plex}*.tsx",
|
||||
"../components/{Home,PosterTile,UpNextCard}.tsx",
|
||||
"!../components/**/*.test.tsx",
|
||||
]);
|
||||
const ALLOWED = new Set([
|
||||
"plexCards.tsx pic.src", // cardImage's own src (srcSet/sizes beside it): the card cut
|
||||
"plexCards.tsx c.thumb", // cast photos: the person-image route, not the image proxy
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { globSync, readFileSync } from "node:fs";
|
||||
import { matchesGlob, relative, resolve, sep } from "node:path";
|
||||
|
||||
// The source text a contract test scans, read from disk. Not `import.meta.glob(…, { query: "?raw" })`:
|
||||
// knip reads every such glob as an import of each file it matches, and two tests globbed all of src —
|
||||
// so its unused-files lane counted every source file as used and could never name an orphaned one
|
||||
// (the shared-link entry was "used" only by those globs).
|
||||
|
||||
/** Every file matching `patterns` (relative to `dir`, the calling test's directory; a leading "!"
|
||||
* excludes) with its text, keyed the way `import.meta.glob` keyed it: "./x.tsx", "../lib/y.ts". */
|
||||
export function readSources(dir: string, patterns: string | string[]): Record<string, string> {
|
||||
const list = typeof patterns === "string" ? [patterns] : patterns;
|
||||
const include = list.filter((p) => !p.startsWith("!"));
|
||||
const exclude = list.filter((p) => p.startsWith("!")).map((p) => key(p.slice(1)));
|
||||
const out: Record<string, string> = {};
|
||||
for (const pattern of include) {
|
||||
for (const rel of globSync(pattern, { cwd: dir })) {
|
||||
// Canonical, as Vite's: "../ui/Modal.tsx" from inside ui/ is "./Modal.tsx", matched once.
|
||||
const k = key(relative(dir, resolve(dir, rel)));
|
||||
// An exclusion names the file either way: "!../**/*.test.*" as the pattern spelled it
|
||||
// ("../lib/x.test.ts"), "!./**" by its canonical key ("./x.test.ts" from inside lib/).
|
||||
if (exclude.some((x) => matchesGlob(key(rel), x) || matchesGlob(k, x))) continue;
|
||||
out[k] = readFileSync(resolve(dir, rel), "utf8");
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** A path relative to the test's directory, as `import.meta.glob` spells it. */
|
||||
function key(path: string): string {
|
||||
const posix = path.split(sep).join("/");
|
||||
return posix.startsWith(".") ? posix : `./${posix}`;
|
||||
}
|
||||
Reference in New Issue
Block a user