Merge branch 'release/v1.45.0' into feat/v145-issues

This commit is contained in:
Marc-André Bombeck committed 2026-10-22 11:00:45 +02:00
commit 08675a4f13
41 files changed
+437 -239

No files matched your search

@@ -14,10 +14,9 @@
* those.
*/
import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { walkSourceFiles } from "./helpers/source-files";
import { readSourceText, walkSourceFiles } from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
@@ -30,7 +29,7 @@ function sourceFiles(): string[] {
}
function read(rel: string): string {
return readFileSync(join(SRC, rel), "utf8");
return readSourceText(join(SRC, rel));
}
function filesMatching(re: RegExp): string[] {
@@ -26,14 +26,17 @@
* another module would slip it; `AI_ROUTE_PATTERNS` lists the literal shapes
* the tree uses today.
*/
import { readFileSync } from "node:fs";
import path from "node:path";
import { describe, expect, it } from "vitest";
import type { AiCapabilityKey } from "@/lib/ai/capabilities/types";
import { stripComments, walkSourceFiles } from "./helpers/source-files";
import {
readSourceText,
stripComments,
walkSourceFiles,
} from "./helpers/source-files";
const SRC = path.resolve(__dirname, "..");
@@ -64,7 +67,7 @@ const FEATURE_FLAG_SWEEP = NON_TEST_FILES.filter(
);
const read = (rel: string) =>
stripComments(readFileSync(path.join(SRC, rel), "utf8"));
stripComments(readSourceText(path.join(SRC, rel)));
/** Request literals of routes that call a model or serve model text. */
const AI_ROUTE_PATTERNS: readonly RegExp[] = [
@@ -271,10 +271,17 @@ function delegateName(model: string): string {
* route outright: the guard stayed green on all twelve checks, because the
* cycle file was answering a question about a different table.
*/
const parentsCache = new Map<
string,
Array<{ field: string; parent: string }>
>();
function relationParents(model: string): Array<{
field: string;
parent: string;
}> {
const cached = parentsCache.get(model);
if (cached) return cached;
const found: Array<{ field: string; parent: string }> = [];
for (const [parent, body] of modelBodies()) {
for (const line of body.split("\n")) {
@@ -284,6 +291,7 @@ function relationParents(model: string): Array<{
}
}
}
parentsCache.set(model, found);
return found;
}
@@ -295,6 +303,8 @@ const WRITE_RELATION_OPS = /\{\s*create\b/;
/** Does `source` call a matching operation on `model`'s own Prisma delegate? */
function callsDelegate(source: string, model: string, ops: RegExp): boolean {
const delegate = delegateName(model);
// The expression below names the delegate; a file without it cannot match.
if (!source.includes(delegate)) return false;
for (const match of source.matchAll(
new RegExp(`\\.${delegate}\\s*\\.\\s*(\\w+)`, "g"),
)) {
@@ -304,7 +314,11 @@ function callsDelegate(source: string, model: string, ops: RegExp): boolean {
}
/** Every model a relation field of this name points at, across the schema. */
const targetsCache = new Map<string, Set<string>>();
function targetsOfRelationField(field: string): Set<string> {
const cached = targetsCache.get(field);
if (cached) return cached;
const targets = new Set<string>();
for (const [, body] of modelBodies()) {
for (const line of body.split("\n")) {
@@ -312,6 +326,7 @@ function targetsOfRelationField(field: string): Set<string> {
if (match && match[1] === field) targets.add(match[2]);
}
}
targetsCache.set(field, targets);
return targets;
}
@@ -337,6 +352,7 @@ function touches(
const source = flat(file);
if (callsDelegate(source, model, delegateOps)) return true;
return relationParents(model).some(({ field, parent }) => {
if (!source.includes(field)) return false;
if (
!new RegExp(`\\b${field}\\s*:\\s*(?:${relationOps.source})`).test(
source,
@@ -13,10 +13,9 @@
* for that review.
*/
import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { walkSourceFiles } from "./helpers/source-files";
import { readSourceText, walkSourceFiles } from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
@@ -33,7 +32,7 @@ function sourceFiles(): string[] {
}
function read(rel: string): string {
return readFileSync(join(SRC, rel), "utf8");
return readSourceText(join(SRC, rel));
}
function filesMatching(re: RegExp): string[] {
@@ -28,10 +28,9 @@
* outside `app/api/`); a server-side `take` is not a fetch.
*/
import { describe, expect, it } from "vitest";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { walkSourceFiles } from "./helpers/source-files";
import { readSourceText, walkSourceFiles } from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
@@ -178,7 +177,7 @@ function findLimitHits(file: string, source: string): Hit[] {
function allHits(): Hit[] {
return clientFiles().flatMap((file) =>
findLimitHits(file, readFileSync(join(SRC, file), "utf8")),
findLimitHits(file, readSourceText(join(SRC, file))),
);
}
+4 -3
View File
@@ -20,9 +20,10 @@
* loaded chunk may still carry zod.
*/
import { describe, expect, it } from "vitest";
import { existsSync, readFileSync } from "node:fs";
import { existsSync } from "node:fs";
import { dirname, join, normalize } from "node:path";
import ts from "typescript";
import { readSourceText } from "./helpers/source-files";
const ROOT = join(__dirname, "..", "..");
@@ -90,7 +91,7 @@ function clientZodImports(entry: string): { file: string; path: string[] }[] {
const [file, inClient] = stack.pop()!;
const prior = seen.get(file);
if (prior === true || (prior === false && !inClient)) continue;
const source = readFileSync(file, "utf8");
const source = readSourceText(file);
const client = inClient || isClient(source);
seen.set(file, client);
walked += 1;
@@ -141,7 +142,7 @@ describe("client routes without the schema library", () => {
// The schema module is zod by definition; walked as a client entry it
// must be reported, or the walk above proves nothing.
const probe = join(ROOT, "src/lib/validations/inbound-documents.ts");
const source = readFileSync(probe, "utf8");
const source = readSourceText(probe);
expect(staticImports(source, probe).some((s) => s.startsWith("zod"))).toBe(
true,
);
@@ -33,13 +33,16 @@
* from a variable that happens to hold a literal is not seen; review catches
* that.
*/
import { readFileSync } from "node:fs";
import { join } from "node:path";
import ts from "typescript";
import { describe, expect, it } from "vitest";
import { stripComments, walkSourceFiles } from "./helpers/source-files";
import {
readSourceText,
stripComments,
walkSourceFiles,
} from "./helpers/source-files";
const ROOT = process.cwd();
@@ -67,7 +70,7 @@ function clientFiles(): ClientFile[] {
const rel = `${dir}/${file}`;
if (rel.startsWith("src/app/api/")) continue;
if (rel.includes("/__tests__/") || /\.test\.tsx?$/.test(rel)) continue;
const source = readFileSync(join(ROOT, rel), "utf8");
const source = readSourceText(join(ROOT, rel));
if (!isClientModule(rel, source)) continue;
out.push({ rel, source });
}
@@ -75,14 +78,23 @@ function clientFiles(): ClientFile[] {
return out;
}
const parsed = new Map<string, ts.SourceFile>();
/** One parse per file and source, however many rules read it. */
function parse(name: string, source: string): ts.SourceFile {
return ts.createSourceFile(
name,
source,
ts.ScriptTarget.Latest,
true,
name.endsWith("x") ? ts.ScriptKind.TSX : ts.ScriptKind.TS,
);
const key = `${name}\0${source}`;
let file = parsed.get(key);
if (file === undefined) {
file = ts.createSourceFile(
name,
source,
ts.ScriptTarget.Latest,
true,
name.endsWith("x") ? ts.ScriptKind.TSX : ts.ScriptKind.TS,
);
parsed.set(key, file);
}
return file;
}
/** The literal text a JSX attribute initialiser carries, if it is one. */
@@ -218,6 +230,11 @@ function resolverImports(name: string, source: string): string[] {
interface Rule {
name: string;
find: (name: string, source: string) => string[];
/**
* Text a file must contain for `find` to have anything to report, tested
* before any parse. Every shape the rule matches carries one of these.
*/
markers: readonly string[];
/** Sources that must produce at least one hit, proving the matcher. */
samples: Array<{ name: string; source: string }>;
/** File → exact hit count it may have, with the reason. */
@@ -228,6 +245,7 @@ const RULES: Rule[] = [
{
name: "a literal directionSentiment",
find: literalDirections,
markers: ["directionSentiment"],
samples: [
{
name: "sample.tsx",
@@ -249,6 +267,7 @@ const RULES: Rule[] = [
{
name: "a reference line at a literal",
find: literalLines,
markers: [...LINE_TAGS],
samples: [
{
name: "sample.tsx",
@@ -280,12 +299,14 @@ const RULES: Rule[] = [
{
name: "a polarity table",
find: polarityTables,
markers: ["up-good", "up-bad", "UP_GOOD_", "UP_BAD_", "Sentiment"],
samples: POLARITY_SAMPLES.map((source) => ({ name: "sample.ts", source })),
allow: {},
},
{
name: "a server resolver imported into the browser",
find: resolverImports,
markers: SERVER_RESOLVERS,
samples: [
{
name: "sample.ts",
@@ -299,12 +320,19 @@ const RULES: Rule[] = [
const FILES = clientFiles();
const ruleHits = new Map<Rule, Map<string, string[]>>();
/** Each rule's hits across the client tree, found once per run. */
function hitsFor(rule: Rule): Map<string, string[]> {
const out = new Map<string, string[]>();
let out = ruleHits.get(rule);
if (out !== undefined) return out;
out = new Map<string, string[]>();
for (const { rel, source } of FILES) {
if (!rule.markers.some((marker) => source.includes(marker))) continue;
const hits = rule.find(rel, source);
if (hits.length > 0) out.set(rel, hits);
}
ruleHits.set(rule, out);
return out;
}
@@ -321,6 +349,12 @@ describe("client threshold literal guard", () => {
rule.find(sample.name, sample.source).length,
`no hit on: ${sample.source}`,
).toBeGreaterThan(0);
// The pre-filter has to let every sample through, or it would hide
// the shape before the matcher saw it.
expect(
rule.markers.some((marker) => sample.source.includes(marker)),
`the markers skip: ${sample.source}`,
).toBe(true);
}
});
@@ -35,10 +35,9 @@
* allowlist. Both the walk and every matcher therefore assert a floor.
*/
import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { walkSourceFiles } from "./helpers/source-files";
import { readSourceText, walkSourceFiles } from "./helpers/source-files";
const ROOT = process.cwd();
const SRC = join(ROOT, "src");
@@ -151,7 +150,7 @@ function sourceFiles(): string[] {
}
function read(rel: string): string {
return readFileSync(join(SRC, rel), "utf8");
return readSourceText(join(SRC, rel));
}
/** Every environment variable shipped server code reads, however it reads it. */
@@ -175,7 +174,7 @@ function runtimeReads(): Set<string> {
* repeating it.
*/
function composeWhitelist(): Set<string> {
const compose = readFileSync(join(ROOT, "docker-compose.yml"), "utf8");
const compose = readSourceText(join(ROOT, "docker-compose.yml"));
const keys = new Set<string>();
for (const line of compose.split("\n")) {
const m = line.match(/^\s{2,}([A-Z][A-Z0-9_]*):\s/);
@@ -17,11 +17,14 @@
* inline under another name slips it. Comments are stripped first; the
* planted cases prove it fires.
*/
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { stripComments, walkSourceFiles } from "./helpers/source-files";
import {
readSourceText,
stripComments,
walkSourceFiles,
} from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
@@ -56,14 +59,8 @@ describe("one Pearson correlation", () => {
});
it("both homes still hold their function", () => {
const engine = readFileSync(
join(SRC, "lib/insights/correlations.ts"),
"utf8",
);
const wrapper = readFileSync(
join(SRC, "lib/analytics/correlations.ts"),
"utf8",
);
const engine = readSourceText(join(SRC, "lib/insights/correlations.ts"));
const wrapper = readSourceText(join(SRC, "lib/analytics/correlations.ts"));
expect(engine).toMatch(/export function pearson\s*\(/);
expect(wrapper).toMatch(
/export function significantPearsonCorrelation\s*\(/,
@@ -73,14 +70,12 @@ describe("one Pearson correlation", () => {
it("no other file defines a Pearson coefficient or calls the deleted one", () => {
// The homes match the definition matcher, so a matcher gone blind fails here.
const definers = sourceFiles().filter((file) =>
PEARSON_DEFINITION.test(
stripComments(readFileSync(join(SRC, file), "utf8")),
),
PEARSON_DEFINITION.test(stripComments(readSourceText(join(SRC, file)))),
);
expect(definers.length).toBeGreaterThanOrEqual(2);
const offenders = sourceFiles().filter((file) => {
const code = stripComments(readFileSync(join(SRC, file), "utf8"));
const code = stripComments(readSourceText(join(SRC, file)));
if (DELETED.test(code)) return true;
return !HOMES.has(file) && PEARSON_DEFINITION.test(code);
});
@@ -13,7 +13,7 @@
* nothing in the client tree may derive the verdict, AND the surfaces that
* show it must still be reading the published field.
*/
import { readFileSync, readdirSync, statSync } from "node:fs";
import { readdirSync, statSync } from "node:fs";
import { join, relative as relativeTo } from "node:path";
import { describe, expect, it } from "vitest";
@@ -21,8 +21,10 @@ import { describe, expect, it } from "vitest";
import {
accessPaths,
derivationFindings,
mayBeCycleContext,
parse,
} from "./helpers/cycle-verdict-derivation";
import { readSourceText } from "./helpers/source-files";
const ROOT = process.cwd();
@@ -61,7 +63,7 @@ function clientFiles(): string[] {
}
function parseFile(relative: string) {
return parse(relative, readFileSync(join(ROOT, relative), "utf8"));
return parse(relative, readSourceText(join(ROOT, relative)));
}
describe("the cycle verdict is decided in one place", () => {
@@ -70,9 +72,11 @@ describe("the cycle verdict is decided in one place", () => {
// A guard over an empty file list proves nothing.
expect(files.length).toBeGreaterThan(100);
const findings = files.flatMap((relative) =>
derivationFindings(parseFile(relative)),
);
const findings = files
.filter((relative) =>
mayBeCycleContext(readSourceText(join(ROOT, relative))),
)
.flatMap((relative) => derivationFindings(parseFile(relative)));
expect(
findings.map((f) => `${f.file}:${f.line} ${f.kind} — ${f.where}`),
@@ -83,7 +87,7 @@ describe("the cycle verdict is decided in one place", () => {
// The specific constant that was copied. Named here so its return is loud
// rather than merely structural.
const offenders = clientFiles().filter((relative) =>
readFileSync(join(ROOT, relative), "utf8").includes("OVERDUE_GRACE_DAYS"),
readSourceText(join(ROOT, relative)).includes("OVERDUE_GRACE_DAYS"),
);
expect(offenders).toEqual([]);
});
@@ -106,9 +110,8 @@ describe("the surfaces that show the verdict actually read it", () => {
// The BBT chart used to walk the calendar itself to find the cycle start,
// which was a second copy of the same walk. It now takes the resolved
// value as a prop, and the cycle page feeds it from the verdict.
const chart = readFileSync(
const chart = readSourceText(
join(ROOT, "src/components/cycle/bbt-chart.tsx"),
"utf8",
);
expect(chart).toContain("cycleStartDate");
@@ -119,10 +122,7 @@ describe("the surfaces that show the verdict actually read it", () => {
it("the calendar response type declares the verdict as always present", () => {
// An optional verdict is a plausible default that lets a client fall back
// to deriving its own.
const types = readFileSync(
join(ROOT, "src/components/cycle/types.ts"),
"utf8",
);
const types = readSourceText(join(ROOT, "src/components/cycle/types.ts"));
expect(types).toContain("verdict: CycleVerdict;");
expect(types).not.toContain("verdict?:");
});
@@ -32,10 +32,9 @@
* assertion. Both fixes were then restored and the guard re-ran green.
*/
import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { walkSourceFiles } from "./helpers/source-files";
import { readSourceText, walkSourceFiles } from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
@@ -91,7 +90,7 @@ function sourceFiles(): string[] {
}
function read(rel: string): string {
return readFileSync(join(SRC, rel), "utf8");
return readSourceText(join(SRC, rel));
}
function count(s: string, re: RegExp): number {
+2 -3
View File
@@ -43,10 +43,9 @@
* how `bearer-scope-enforcement-guard` stayed green over a file it never saw.
*/
import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { walkSourceFiles } from "./helpers/source-files";
import { readSourceText, walkSourceFiles } from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
@@ -69,7 +68,7 @@ function sourceFiles(): string[] {
}
function read(rel: string): string {
return readFileSync(join(SRC, rel), "utf8");
return readSourceText(join(SRC, rel));
}
function filesMatching(re: RegExp): string[] {
@@ -23,10 +23,9 @@
* are pinned to a named allowlist, so a new converted-value surface
* cannot quietly join them.
*/
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { walkSourceFiles } from "./helpers/source-files";
import { readSourceText, walkSourceFiles } from "./helpers/source-files";
import { describe, it, expect } from "vitest";
@@ -83,7 +82,7 @@ function sourceFiles(): string[] {
}
function read(rel: string): string {
return readFileSync(join(SRC, rel), "utf8");
return readSourceText(join(SRC, rel));
}
describe("display-transform rounding guard — the registry property", () => {
@@ -40,12 +40,11 @@
* and asserts the sweep goes red without it, so every entry is shown to be
* load-bearing, and the sweep is shown to find what it claims to find.
*/
import { readFileSync } from "node:fs";
import { join } from "node:path";
import ts from "typescript";
import { describe, expect, it } from "vitest";
import { walkSourceFiles } from "./helpers/source-files";
import { readSourceText, walkSourceFiles } from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
const SCOPE = "lib/jobs/";
@@ -257,9 +256,7 @@ const ALLOWED: Readonly<Record<string, { count: number; reason: string }>> = {
function sweep(): Finding[] {
return walkSourceFiles(join(SRC, SCOPE), { floor: 100 })
.filter((p) => !p.includes("__tests__") && !p.endsWith(".test.ts"))
.flatMap((p) =>
scan(`${SCOPE}${p}`, readFileSync(join(SRC, SCOPE, p), "utf8")),
);
.flatMap((p) => scan(`${SCOPE}${p}`, readSourceText(join(SRC, SCOPE, p))));
}
function unallowed(
@@ -22,10 +22,9 @@
* addition defeats them, and no test substitutes for that review.
*/
import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { walkSourceFiles } from "./helpers/source-files";
import { readSourceText, walkSourceFiles } from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
const VALIDATOR_MODULE = "@/lib/validations/external-id";
@@ -39,7 +38,7 @@ function sourceFiles(): string[] {
}
function read(rel: string): string {
return readFileSync(join(SRC, rel), "utf8");
return readSourceText(join(SRC, rel));
}
/**
@@ -377,9 +376,8 @@ describe("the intake idempotency key is never read across users", () => {
});
it("the schema keeps the key unique per user, not globally", () => {
const schema = readFileSync(
const schema = readSourceText(
join(process.cwd(), "prisma", "schema.prisma"),
"utf8",
);
expect(schema).toContain("@@unique([userId, idempotencyKey])");
expect(schema).not.toMatch(
@@ -51,7 +51,7 @@ const DAY_COUNT_NAME = /day.?of.?cycle|cycleday|cyclerun|runlength/i;
* calendar's types, its phase names, or the verdict's own fields, so nothing
* that could actually commit the defect escapes by renaming one identifier.
*/
const CYCLE_MARKERS = new Set([
export const CYCLE_MARKERS: ReadonlySet<string> = new Set([
"CalendarDay",
"CalendarResponse",
"CyclePhase",
@@ -247,6 +247,16 @@ function isSumLike(
return false;
}
/**
* Whether a file's text could speak the cycle's vocabulary at all: every
* marker the parse below looks for is also a substring of the text, so a
* file holding none of them needs no parse.
*/
export function mayBeCycleContext(text: string): boolean {
for (const marker of CYCLE_MARKERS) if (text.includes(marker)) return true;
return false;
}
/** Whether the file speaks the cycle's vocabulary at all. */
export function isCycleContext(file: ts.SourceFile): boolean {
let found = false;
+13 -5
View File
@@ -305,11 +305,19 @@ function suffixBaseMatches(base: string, ref: Reference): boolean {
export function isReferenced(leaf: string, ref: Reference): boolean {
if (ref.literalKeys.has(leaf)) return true;
for (const ck of ref.literalKeys) {
if (leaf.startsWith(`${ck}.`)) return true;
}
for (const base of ref.dottedBases) {
if (leaf === base || leaf.startsWith(`${base}.`)) return true;
// A literal key or a dotted base covers `leaf` when it IS one of the
// leaf's dotted ancestors (or, for a base, the leaf itself), so the leaf's
// own ancestors are looked up rather than every key in the sets scanned.
if (ref.dottedBases.has(leaf)) return true;
for (
let dot = leaf.indexOf(".");
dot !== -1;
dot = leaf.indexOf(".", dot + 1)
) {
const ancestor = leaf.slice(0, dot);
if (ref.literalKeys.has(ancestor) || ref.dottedBases.has(ancestor)) {
return true;
}
}
for (const pre of ref.concatPrefixes) {
if (leaf.startsWith(pre)) return true;
+105 -8
View File
@@ -51,17 +51,114 @@
* match. Every caller roots at `src/`, `e2e/` or a subtree of one.
*/
import { existsSync, readdirSync, readFileSync } from "node:fs";
import { join, sep } from "node:path";
import { join, relative, resolve, sep } from "node:path";
// ── The shared source index ────────────────────────────────────────────────
//
// The guards walk and read the same tree, and the guards project shares one
// module graph per worker (`isolate: false`), so every walk and every read
// below is memoised for the life of the worker: the tree is listed once per
// top-level folder and each file is read once, however many guards ask. A
// guard that walked `src/` and read every file again for each pattern it
// tried ran for fifteen seconds; the same questions over the index take a
// fraction of one.
//
// Only folders of the repository that do not change while tests run are
// indexed (`INDEXED_ROOTS`). A walk or a read anywhere else (a temporary
// fixture tree a test writes and rewrites) goes to the disk every time.
const INDEXED_ROOTS = ["src", "e2e", "prisma"].map((dir) =>
resolve(process.cwd(), dir),
);
function indexedRootOf(path: string): string | null {
const absolute = resolve(path);
return (
INDEXED_ROOTS.find(
(root) => absolute === root || absolute.startsWith(`${root}${sep}`),
) ?? null
);
}
const listingCache = new Map<string, string[]>();
/** Every entry under `root`, posix-separated, sorted; memoised when indexed. */
function listTree(root: string): string[] {
const absolute = resolve(root);
const anchor = indexedRootOf(absolute);
if (anchor === null) {
return readdirSync(absolute, { recursive: true })
.map((entry) => String(entry).split(sep).join("/"))
.sort();
}
let all = listingCache.get(anchor);
if (all === undefined) {
all = readdirSync(anchor, { recursive: true })
.map((entry) => String(entry).split(sep).join("/"))
.sort();
listingCache.set(anchor, all);
}
if (absolute === anchor) return all;
const prefix = `${relative(anchor, absolute).split(sep).join("/")}/`;
return all
.filter((rel) => rel.startsWith(prefix))
.map((rel) => rel.slice(prefix.length));
}
const textCache = new Map<string, string>();
/**
* A repository file's text, read once per worker when it lies in an indexed
* folder. Use it in place of `readFileSync(path, "utf8")` for source the
* guard reads and never writes.
*/
export function readSourceText(path: string): string {
const absolute = resolve(path);
if (indexedRootOf(absolute) === null) return readFileSync(absolute, "utf8");
let text = textCache.get(absolute);
if (text === undefined) {
text = readFileSync(absolute, "utf8");
textCache.set(absolute, text);
}
return text;
}
/**
* The files under `root` (as `walkSourceFiles` lists them) whose text holds
* at least one of `markers`, with that text. The substring test runs before
* any regular expression or parse the caller applies, which is where most of
* a sweep's time used to go. The floor applies to the files walked, as in
* `walkSourceFiles`; a caller that expects matches states its own floor over
* the result.
*/
export function sourcesContaining(
root: string,
markers: string | readonly string[],
options: {
floor: number;
extensions?: readonly string[];
include?: (rel: string) => boolean;
},
): Array<[rel: string, text: string]> {
const wanted = typeof markers === "string" ? [markers] : markers;
const out: Array<[string, string]> = [];
for (const rel of walkSourceFiles(root, options).filter(
options.include ?? (() => true),
)) {
const text = readSourceText(join(root, rel));
if (wanted.some((marker) => text.includes(marker))) out.push([rel, text]);
}
return out;
}
export function walkSourceFiles(
root: string,
options: { floor: number; extensions?: readonly string[] },
): string[] {
const extensions = options.extensions ?? [".ts", ".tsx"];
const files = readdirSync(root, { recursive: true })
.map((entry) => String(entry).split(sep).join("/"))
.filter((rel) => extensions.some((ext) => rel.endsWith(ext)))
.sort();
const files = listTree(root).filter((rel) =>
extensions.some((ext) => rel.endsWith(ext)),
);
if (files.length < options.floor) {
throw new Error(
@@ -216,7 +313,7 @@ export function scanSourceMatches(
const matches: SourceMatch[] = [];
for (const file of files) {
const raw = readFileSync(join(root, file), "utf8");
const raw = readSourceText(join(root, file));
const source = options.stripComments ? stripComments(raw) : raw;
for (const m of source.matchAll(pattern)) {
const line = source.slice(0, m.index).split("\n").length;
@@ -273,7 +370,7 @@ export function readSource(
`readSourceFolder and a floor.`,
);
}
const text = readFileSync(path, "utf8");
const text = readSourceText(path);
const lost = list.filter((anchor) => anchorCount(text, anchor) === 0);
if (lost.length > 0) {
throw new Error(
@@ -306,5 +403,5 @@ export function readSourceFolder(
extensions: options.extensions,
}).filter(options.include ?? (() => true));
requireFloor(`readSourceFolder(${relDir})`, files, options.floor);
return files.map((rel) => [rel, readFileSync(join(root, rel), "utf8")]);
return files.map((rel) => [rel, readSourceText(join(root, rel))]);
}
+12 -3
View File
@@ -26,10 +26,11 @@
* its keys report as orphans.
*/
import { describe, it, expect } from "vitest";
import { readFileSync, readdirSync, statSync } from "node:fs";
import { readdirSync, statSync } from "node:fs";
import { join } from "node:path";
import { collectReferences, isReferenced } from "./helpers/i18n-reference";
import { readSourceText } from "./helpers/source-files";
const ROOT = join(__dirname, "../..");
const SRC = join(ROOT, "src");
@@ -142,8 +143,16 @@ function isAllowlisted(leaf: string): boolean {
);
}
let scanned: ReturnType<typeof scanOnce> | null = null;
/** The bundle and the tree's references, worked out once per run. */
function scan() {
const en = JSON.parse(readFileSync(EN_BUNDLE_PATH, "utf8"));
scanned ??= scanOnce();
return scanned;
}
function scanOnce() {
const en = JSON.parse(readSourceText(EN_BUNDLE_PATH));
const leaves: string[] = [];
leafPaths(en, "", leaves);
@@ -152,7 +161,7 @@ function scan() {
const topNamespaces = new Set<string>(Object.keys(en));
const allText = walk(SRC)
.map((f) => readFileSync(f, "utf8"))
.map((f) => readSourceText(f))
.join("\n");
return { leaves, ...collectReferences(allText, allNodes, topNamespaces) };
@@ -19,12 +19,13 @@
* hand-listed: the expected set is read out of the route tree on every run, so
* the guard cannot fall behind the code it watches.
*/
import { readFileSync, readdirSync } from "node:fs";
import { readdirSync } from "node:fs";
import { join, relative, sep } from "node:path";
import { describe, expect, it } from "vitest";
import { buildOpenApiDocument } from "@/lib/openapi/registry";
import { readSourceText } from "./helpers/source-files";
const API_ROOT = join(process.cwd(), "src", "app", "api");
@@ -74,7 +75,7 @@ function idempotentOperationsFromSource(): Set<string> {
const pattern =
/export\s+const\s+(POST|PUT|PATCH|DELETE)\s*=\s*([\s\S]*?)(?=\n(?:export\s+const|export\s+async\s+function|$))/g;
for (const file of routeFiles(API_ROOT)) {
const source = readFileSync(file, "utf8");
const source = readSourceText(file);
if (!source.includes("withIdempotency")) continue;
for (const match of source.matchAll(pattern)) {
if (!/\bwithIdempotency\s*[(<]/.test(match[2])) continue;
@@ -12,12 +12,15 @@
* The readable name survives only as the snapshot builder's input field, which
* carries the OPENED text, never the column.
*/
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { stripComments, walkSourceFiles } from "./helpers/source-files";
import {
readSourceText,
stripComments,
walkSourceFiles,
} from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
@@ -45,9 +48,7 @@ function sourceFilesMatching(pattern: RegExp): string[] {
return walkSourceFiles(SRC, { floor: 2000 })
.filter((f) => !f.startsWith("generated/"))
.filter((f) => !f.includes("__tests__/") && !/\.test\.tsx?$/.test(f))
.filter((f) =>
pattern.test(stripComments(readFileSync(join(SRC, f), "utf8"))),
)
.filter((f) => pattern.test(stripComments(readSourceText(join(SRC, f)))))
.sort();
}
@@ -23,11 +23,14 @@
* Comments are stripped before matching, and the planted cases prove each
* matcher finds what it claims to.
*/
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { stripComments, walkSourceFiles } from "./helpers/source-files";
import {
readSourceText,
stripComments,
walkSourceFiles,
} from "./helpers/source-files";
/** The shared comment strip, plus SQL line comments inside template strings. */
function stripCode(text: string): string {
@@ -98,7 +101,7 @@ describe("latest-per-type reads", () => {
it("no production read keeps one row per type with DISTINCT ON", () => {
const offenders = productionFiles().filter((file) =>
LATEST_PER_TYPE.test(stripCode(readFileSync(join(SRC, file), "utf8"))),
LATEST_PER_TYPE.test(stripCode(readSourceText(join(SRC, file)))),
);
expect(offenders).toEqual([]);
});
@@ -129,9 +132,7 @@ describe("latest-per-type reads", () => {
it("no production read groups every row for the latest per type", () => {
const files = productionFiles();
const grouped = files.filter((file) =>
/\.measurement\s*\.\s*groupBy\s*\(/.test(
readFileSync(join(SRC, file), "utf8"),
),
/\.measurement\s*\.\s*groupBy\s*\(/.test(readSourceText(join(SRC, file))),
);
// The tree groups measurements in a dozen places; finding none means
// the walk read the wrong tree.
@@ -139,16 +140,16 @@ describe("latest-per-type reads", () => {
const offenders = files
.filter((file) => !(file in GROUPBY_ALLOWED))
.flatMap((file) =>
latestOnlyGroupBys(
stripComments(readFileSync(join(SRC, file), "utf8")),
).map((arg) => `${file}: ${arg.slice(0, 100)}`),
latestOnlyGroupBys(stripComments(readSourceText(join(SRC, file)))).map(
(arg) => `${file}: ${arg.slice(0, 100)}`,
),
);
expect(offenders).toEqual([]);
});
it("the summaries slice is built only behind its cache cell", () => {
const callers = productionFiles().filter((file) =>
SLICE_BUILDER_CALL.test(stripCode(readFileSync(join(SRC, file), "utf8"))),
SLICE_BUILDER_CALL.test(stripCode(readSourceText(join(SRC, file)))),
);
expect(callers).toEqual(["lib/analytics/summaries-slice.ts"]);
});
@@ -7,11 +7,10 @@
* implementation. Comments are stripped first; the planted cases prove the
* matcher fires.
*/
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { walkSourceFiles } from "./helpers/source-files";
import { readSourceText, walkSourceFiles } from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
@@ -52,25 +51,21 @@ describe("one least-squares line", () => {
const offenders = files.filter(
(file) =>
!(file in ALLOWED) &&
HAND_ROLLED_SLOPE.test(
stripComments(readFileSync(join(SRC, file), "utf8")),
),
HAND_ROLLED_SLOPE.test(stripComments(readSourceText(join(SRC, file)))),
);
expect(offenders).toEqual([]);
// The six readers now call the implementation: a sweep that finds fewer
// call sites read the wrong tree or lost one.
const callers = files.filter((file) =>
/\b(?:linearFit|fitFromSums|trendLineByTime)\s*\(/.test(
stripComments(readFileSync(join(SRC, file), "utf8")),
stripComments(readSourceText(join(SRC, file))),
),
);
expect(callers.length).toBeGreaterThanOrEqual(7);
// The implementation itself matches, so the sweep read real files.
expect(
HAND_ROLLED_SLOPE.test(
stripComments(
readFileSync(join(SRC, "lib/stats/linear-fit.ts"), "utf8"),
),
stripComments(readSourceText(join(SRC, "lib/stats/linear-fit.ts"))),
),
).toBe(true);
});
@@ -19,10 +19,9 @@
* test substitutes for that review.
*/
import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { walkSourceFiles } from "./helpers/source-files";
import { readSourceText, walkSourceFiles } from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
@@ -164,14 +163,25 @@ function sourceFiles(): string[] {
}
function read(rel: string): string {
return readFileSync(join(SRC, rel), "utf8");
return readSourceText(join(SRC, rel));
}
let writers: string[] | null = null;
/**
* Every file that writes a measurement, found once per run. A write names
* the `measurement` delegate or the bulk insert's `MeasurementRows`, so a
* file holding neither word is skipped before the expressions run.
*/
function discoveredWriters(): string[] {
return sourceFiles().filter((rel) => {
writers ??= sourceFiles().filter((rel) => {
const text = read(rel);
if (!text.includes("measurement") && !text.includes("MeasurementRows")) {
return false;
}
return MEASUREMENT_WRITE.test(text) || BULK_INSERT_CALL.test(text);
});
return writers;
}
describe("every measurement writer declares how it meets the plausibility domain", () => {
@@ -195,9 +205,10 @@ describe("every measurement writer declares how it meets the plausibility domain
});
it("raw SQL inserts measurements in the bulk-insert module and nowhere else", () => {
const raw = sourceFiles().filter((rel) =>
RAW_MEASUREMENT_INSERT.test(read(rel)),
);
const raw = sourceFiles().filter((rel) => {
const text = read(rel);
return /insert/i.test(text) && RAW_MEASUREMENT_INSERT.test(text);
});
expect(raw).toEqual([BULK_INSERT_MODULE]);
});
@@ -18,18 +18,17 @@
*
* Every check is also run against a source broken on purpose.
*/
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { walkSourceFiles } from "./helpers/source-files";
import { readSourceText, walkSourceFiles } from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
const MEMORY = "lib/ai/coach/memory";
function read(rel: string): string {
return readFileSync(join(SRC, rel), "utf8");
return readSourceText(join(SRC, rel));
}
function sourceFiles(): string[] {
@@ -17,11 +17,14 @@
* (`bucket_start`) is out of its reach by name. Comments are stripped first;
* the planted cases prove it fires. It is a tripwire, not a proof.
*/
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { stripComments, walkSourceFiles } from "./helpers/source-files";
import {
readSourceText,
stripComments,
walkSourceFiles,
} from "./helpers/source-files";
/** The shared comment strip, plus SQL line comments inside template strings. */
function stripCode(text: string): string {
@@ -59,8 +62,7 @@ describe("naive timestamp zone shifts", () => {
!file.includes("__tests__/") &&
!/\.test\.tsx?$/.test(file),
);
const code = (file: string) =>
stripCode(readFileSync(join(SRC, file), "utf8"));
const code = (file: string) => stripCode(readSourceText(join(SRC, file)));
// The reads that shift correctly, so a sweep that reads nothing fails.
const doubleShifts = files.filter((file) => DOUBLE_SHIFT.test(code(file)));
expect(doubleShifts.length).toBeGreaterThanOrEqual(5);
+2 -2
View File
@@ -1,9 +1,9 @@
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { walkSourceFiles } from "@/__tests__/helpers/source-files";
import { readSourceText } from "./helpers/source-files";
/**
* No raw control byte in a source file, apart from tab, newline and carriage
@@ -28,7 +28,7 @@ describe("source files carry no raw control bytes", () => {
expect(files.length).toBeGreaterThan(3000);
const offenders: string[] = [];
for (const file of files) {
const lines = readFileSync(join(src, file), "utf8").split("\n");
const lines = readSourceText(join(src, file)).split("\n");
lines.forEach((line, i) => {
if (CONTROL.test(line)) offenders.push(`${file}:${i + 1}`);
});
+3 -3
View File
@@ -26,10 +26,10 @@
* that stops matching fails instead of passing.
*/
import { describe, expect, it } from "vitest";
import { existsSync, readFileSync } from "node:fs";
import { existsSync } from "node:fs";
import { join } from "node:path";
import { walkSourceFiles } from "./helpers/source-files";
import { readSourceText, walkSourceFiles } from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
@@ -54,7 +54,7 @@ function productionFiles(): string[] {
}
function read(rel: string): string {
return readFileSync(join(process.cwd(), rel), "utf8");
return readSourceText(join(process.cwd(), rel));
}
describe("OAuth token lifecycle guard", () => {
@@ -20,10 +20,9 @@
* frozen so that adding one has to be a deliberate edit here.
*/
import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { walkSourceFiles } from "./helpers/source-files";
import { readSourceText, walkSourceFiles } from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
@@ -47,7 +46,7 @@ function sourceFiles(): string[] {
}
function read(rel: string): string {
return readFileSync(join(SRC, rel), "utf8");
return readSourceText(join(SRC, rel));
}
/**
@@ -14,15 +14,14 @@
* with every list.
*/
import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { walkSourceFiles } from "./helpers/source-files";
import { readSourceText, walkSourceFiles } from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
function read(rel: string): string {
return readFileSync(join(SRC, rel), "utf8");
return readSourceText(join(SRC, rel));
}
function sourceFiles(): string[] {
@@ -39,10 +39,9 @@
* shipped several of those.
*/
import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { walkSourceFiles } from "./helpers/source-files";
import { readSourceText, walkSourceFiles } from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
@@ -55,7 +54,7 @@ function sourceFiles(): string[] {
}
function read(rel: string): string {
return readFileSync(join(SRC, rel), "utf8");
return readSourceText(join(SRC, rel));
}
function filesMatching(re: RegExp): string[] {
@@ -19,10 +19,11 @@
* `RecurrenceContext` annotation is invisible here. Every medication path
* today goes through one of the annotated builders below.
*/
import { readdirSync, readFileSync } from "node:fs";
import { readdirSync } from "node:fs";
import { join, relative, resolve } from "node:path";
import { describe, expect, it } from "vitest";
import { readSourceText } from "./helpers/source-files";
const SRC = resolve(__dirname, "..");
const ROOT = resolve(SRC, "..");
@@ -73,7 +74,7 @@ interface Builder {
function builders(): Builder[] {
const out: Builder[] = [];
for (const file of sourceFiles(SRC)) {
const text = readFileSync(file, "utf8");
const text = readSourceText(file);
for (const m of text.matchAll(DECLARATION)) {
const open = m.index! + m[0].length - 1;
out.push({
+2 -3
View File
@@ -33,10 +33,9 @@
* matches nothing fails instead of passing.
*/
import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { walkSourceFiles } from "./helpers/source-files";
import { readSourceText, walkSourceFiles } from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
@@ -55,7 +54,7 @@ function appFiles(): string[] {
}
function read(rel: string): string {
return readFileSync(join(SRC, rel), "utf8");
return readSourceText(join(SRC, rel));
}
function appFilesMatching(re: RegExp): string[] {
+3 -2
View File
@@ -65,7 +65,7 @@
* different set of symbols and answers it from the AST, because a route
* explaining in a comment why it is NOT delegable is a thing worth writing.
*/
import { readFileSync, readdirSync } from "node:fs";
import { readdirSync } from "node:fs";
import { join, sep } from "node:path";
import ts from "typescript";
@@ -75,6 +75,7 @@ import { ENTIRE_RECORD, SHARE_DOMAINS } from "@/lib/sharing/scope";
import type { ShareScope } from "@/lib/sharing/scope";
import { ADMITTED_MUTATING_HANDLERS } from "../../tests/fixtures/v137/sharing-matrix";
import { readSourceText } from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
@@ -198,7 +199,7 @@ const textCache = new Map<string, string>();
function read(rel: string): string {
const cached = textCache.get(rel);
if (cached !== undefined) return cached;
const text = readFileSync(join(SRC, rel), "utf8");
const text = readSourceText(join(SRC, rel));
textCache.set(rel, text);
return text;
}
@@ -16,11 +16,14 @@
* `glucoseSD`) and is out of the matcher's reach by shape. Comments are
* stripped first; the planted case proves the matcher fires.
*/
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { stripComments, walkSourceFiles } from "./helpers/source-files";
import {
readSourceText,
stripComments,
walkSourceFiles,
} from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
@@ -51,7 +54,7 @@ function computesSpread(code: string): boolean {
}
function read(file: string): string {
return stripComments(readFileSync(join(SRC, file), "utf8"));
return stripComments(readSourceText(join(SRC, file)));
}
describe("one standard deviation", () => {
+4 -3
View File
@@ -13,9 +13,10 @@
* The few bare reads left are listed below with the reason they are safe:
* each feeds a `timestamptz` column or returns the instant to JavaScript.
*/
import { readdirSync, readFileSync, statSync } from "node:fs";
import { readdirSync, statSync } from "node:fs";
import { join, relative } from "node:path";
import { describe, expect, it } from "vitest";
import { readSourceText } from "./helpers/source-files";
const ROOT = join(__dirname, "..", "..");
const SRC = join(ROOT, "src");
@@ -69,7 +70,7 @@ function bareReads(): string[] {
const found: string[] = [];
for (const file of walk(SRC)) {
const rel = relative(ROOT, file);
readFileSync(file, "utf8")
readSourceText(file)
.split("\n")
.forEach((line, index) => {
if (/^\s*(\/\/|\*|\/\*|--)/.test(line)) return;
@@ -84,7 +85,7 @@ function bareReads(): string[] {
describe("raw SQL clock reads are anchored to UTC", () => {
it("finds the anchored form across the swept files", () => {
// The matcher must see something, or a broken walk would pass.
const passkey = readFileSync(join(SRC, "lib/auth/passkey.ts"), "utf8");
const passkey = readSourceText(join(SRC, "lib/auth/passkey.ts"));
expect(passkey.match(ANCHORED)?.length).toBeGreaterThanOrEqual(3);
expect(SQL_CLOCK.test("AND expires_at > NOW()")).toBe(true);
expect(SQL_CLOCK.test("updated_at = now()")).toBe(true);
@@ -23,10 +23,9 @@
* only that it has not changed without someone editing this file.
*/
import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { walkSourceFiles } from "./helpers/source-files";
import { readSourceText, walkSourceFiles } from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
@@ -39,7 +38,7 @@ function sourceFiles(): string[] {
}
function read(rel: string): string {
return readFileSync(join(SRC, rel), "utf8");
return readSourceText(join(SRC, rel));
}
/** Local names bound for `exportName` from any module matching `moduleRe`. */
+26 -8
View File
@@ -32,27 +32,33 @@
* with every list.
*/
import { describe, expect, it } from "vitest";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { walkSourceFiles } from "./helpers/source-files";
import { readSourceText, walkSourceFiles } from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
function read(rel: string): string {
return readFileSync(join(SRC, rel), "utf8");
return readSourceText(join(SRC, rel));
}
/**
* Source with comments removed, so a route that only DESCRIBES a gate in its
* doc comment is not counted, and a path quoted in a comment is not a call.
*/
const codeCache = new Map<string, string>();
function code(rel: string): string {
return read(rel)
.replace(/\/\*[\s\S]*?\*\//g, "")
.split("\n")
.map((line) => line.replace(/(^|[^:"'`\\])\/\/.*$/, "$1"))
.join("\n");
let stripped = codeCache.get(rel);
if (stripped === undefined) {
stripped = read(rel)
.replace(/\/\*[\s\S]*?\*\//g, "")
.split("\n")
.map((line) => line.replace(/(^|[^:"'`\\])\/\/.*$/, "$1"))
.join("\n");
codeCache.set(rel, stripped);
}
return stripped;
}
const METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"] as const;
@@ -252,7 +258,15 @@ function hookCallersWrap(
return callers > 0;
}
let gatedCalls: GatedCall[] | null = null;
/** Every web call of a gated route, found once per run. */
function gatedWebCalls(): GatedCall[] {
gatedCalls ??= findGatedWebCalls();
return gatedCalls;
}
function findGatedWebCalls(): GatedCall[] {
const calls: GatedCall[] = [];
const files = webFiles();
const sources = new Map(files.map((f) => [f, code(f)]));
@@ -260,8 +274,12 @@ function gatedWebCalls(): GatedCall[] {
if (GATED_ROUTES[route] !== "web") continue;
const [method, path] = route.split(" ");
const re = pathPattern(path);
// Every match spells the path's static head out literally, up to the
// first `[param]`; a file without it cannot match and is not searched.
const head = path.split("/[")[0];
for (const file of files) {
const src = sources.get(file) ?? "";
if (!src.includes(head)) continue;
for (const m of src.matchAll(re)) {
const index = m.index ?? 0;
// The path handed to a component as a prop (`endpoint="/api/…"`):
@@ -1,9 +1,10 @@
import { readFileSync } from "node:fs";
import { execFileSync } from "node:child_process";
import { resolve } from "node:path";
import { describe, expect, it } from "vitest";
import { walkSourceFiles } from "./helpers/source-files";
/**
* A formatter that renders an instant must say which calendar it renders in.
*
@@ -29,6 +30,54 @@ import { describe, expect, it } from "vitest";
const REPO_ROOT = resolve(__dirname, "../..");
/** A source file under `src/`, by its repo-relative path, and its text. */
interface SourceFile {
file: string;
source: string;
}
/** Whatever any scan below looks for, as plain substrings. */
const MARKERS = [
"Intl.DateTimeFormat",
".toLocaleDateString",
".toLocaleTimeString",
"getDateTimeFormat",
"fmt.date",
];
let candidates: SourceFile[] | null = null;
/**
* Every source file that mentions one of the markers, read once per run.
*
* Each scan used to start its own `grep -r` over the whole of `src/`,
* generated client included, five times per run; under a loaded unit suite
* that alone ran past the 5 s test timeout. The tree is walked once now, the
* generated Prisma client left out (it renders no dates, and no scan has ever
* matched in it), and each file is kept only when a plain substring test says
* a pattern could match, before any regular expression runs.
*/
function candidateFiles(): SourceFile[] {
candidates ??= walkSourceFiles(resolve(REPO_ROOT, "src"), { floor: 3000 })
.filter((rel) => !rel.startsWith("generated/"))
.map((rel) => ({
file: `src/${rel}`,
source: readFileSync(resolve(REPO_ROOT, "src", rel), "utf8"),
}))
.filter(({ source }) => MARKERS.some((marker) => source.includes(marker)));
return candidates;
}
/** The candidate files a pattern matches, test files left in. */
function filesMatching(pattern: RegExp, under = "src/"): SourceFile[] {
return candidateFiles().filter(
({ file, source }) => file.startsWith(under) && pattern.test(source),
);
}
const ANY_FORMATTER =
/new Intl\.DateTimeFormat|\.toLocale(Date|Time)String\(|getDateTimeFormat\(/;
/**
* Bare formatter sites that are correct as they stand. Keep this short: it is
* an inventory of exceptions, not a parking space. Every entry says why, and
@@ -97,26 +146,12 @@ function topLevelArgCount(args: string): number {
}
function bareFormatterSites(): Array<{ file: string; line: number }> {
const listed = execFileSync(
"grep",
[
"-rlE",
"new Intl\\.DateTimeFormat|\\.toLocale(Date|Time)String\\(|getDateTimeFormat\\(",
"src",
"--include=*.ts",
"--include=*.tsx",
],
{ cwd: REPO_ROOT, encoding: "utf8" },
)
.trim()
.split("\n")
.filter(Boolean)
const listed = filesMatching(ANY_FORMATTER)
// Tests pin instants on purpose and are not user-facing surfaces.
.filter((f) => !f.includes("__tests__"));
.filter(({ file }) => !file.includes("__tests__"));
const hits: Array<{ file: string; line: number }> = [];
for (const file of listed) {
const source = readFileSync(resolve(REPO_ROOT, file), "utf8");
for (const { file, source } of listed) {
for (const pattern of FORMATTER_PATTERNS) {
pattern.lastIndex = 0;
for (let m = pattern.exec(source); m !== null; m = pattern.exec(source)) {
@@ -158,21 +193,7 @@ describe("date formatters name the calendar they render in", () => {
it("finds formatter sites at all (the guard must not pass vacuously)", () => {
// If this drops to zero the grep has stopped matching and every assertion
// below became free.
const all = execFileSync(
"grep",
[
"-rlE",
"new Intl\\.DateTimeFormat|\\.toLocale(Date|Time)String\\(|getDateTimeFormat\\(",
"src",
"--include=*.ts",
"--include=*.tsx",
],
{ cwd: REPO_ROOT, encoding: "utf8" },
)
.trim()
.split("\n")
.filter(Boolean);
expect(all.length).toBeGreaterThan(5);
expect(filesMatching(ANY_FORMATTER).length).toBeGreaterThan(5);
});
it("has no unexplained formatter without an explicit timeZone", () => {
@@ -241,26 +262,17 @@ describe("date formatters name the calendar they render in", () => {
describe("components format dates through useFormatters", () => {
const CALL = /\bgetDateTimeFormat\s*\(/g;
let scanned: { offenders: string[]; calls: number } | null = null;
function scan() {
const files = execFileSync(
"grep",
[
"-rlE",
"getDateTimeFormat\\s*\\(",
"src/components",
"--include=*.ts",
"--include=*.tsx",
],
{ cwd: REPO_ROOT, encoding: "utf8" },
)
.trim()
.split("\n")
.filter(Boolean)
.filter((f) => !f.includes("__tests__"));
if (scanned) return scanned;
const files = filesMatching(
/getDateTimeFormat\s*\(/,
"src/components/",
).filter(({ file }) => !file.includes("__tests__"));
const offenders: string[] = [];
let calls = 0;
for (const file of files) {
const source = readFileSync(resolve(REPO_ROOT, file), "utf8");
for (const { file, source } of files) {
CALL.lastIndex = 0;
for (let m = CALL.exec(source); m; m = CALL.exec(source)) {
const lineStart = source.lastIndexOf("\n", m.index) + 1;
@@ -275,7 +287,8 @@ describe("components format dates through useFormatters", () => {
}
}
}
return { offenders, calls };
scanned = { offenders, calls };
return scanned;
}
it("still sees the cache's calls in components (the matcher matches)", () => {
@@ -317,20 +330,16 @@ describe("a stated date is formatted on its calendar date", () => {
const CALENDAR_CALL = /\bfmt\.dateShortSmartCalendar\s*\(/g;
const NOON_ANCHOR = /T12:00:00/;
let scanned: { offenders: string[]; calendarCalls: number } | null = null;
function scan() {
const files = execFileSync(
"grep",
["-rlE", "fmt\\.date", "src", "--include=*.ts", "--include=*.tsx"],
{ cwd: REPO_ROOT, encoding: "utf8" },
)
.trim()
.split("\n")
.filter(Boolean)
.filter((f) => !f.includes("__tests__"));
if (scanned) return scanned;
const files = filesMatching(/fmt\.date/).filter(
({ file }) => !file.includes("__tests__"),
);
const offenders: string[] = [];
let calendarCalls = 0;
for (const file of files) {
const source = readFileSync(resolve(REPO_ROOT, file), "utf8");
for (const { file, source } of files) {
calendarCalls += (source.match(CALENDAR_CALL) ?? []).length;
if (/const fmt = makeBucketLabelFormatters\(/.test(source)) continue;
ZONED_CALL.lastIndex = 0;
@@ -343,7 +352,8 @@ describe("a stated date is formatted on its calendar date", () => {
}
}
}
return { offenders, calendarCalls };
scanned = { offenders, calendarCalls };
return scanned;
}
it("finds the calendar formatter in use (the matcher still matches)", () => {
@@ -18,10 +18,9 @@
* settles is that there is only one of it.
*/
import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { walkSourceFiles } from "./helpers/source-files";
import { readSourceText, walkSourceFiles } from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
@@ -81,7 +80,7 @@ function sourceFiles(): string[] {
}
function read(rel: string): string {
return readFileSync(join(SRC, rel), "utf8");
return readSourceText(join(SRC, rel));
}
/** A line that declares `relativeDueKey`, ignoring comment prose. */
@@ -19,10 +19,9 @@
* that a new ingest cannot appear without someone editing this file.
*/
import { describe, it, expect } from "vitest";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { walkSourceFiles } from "./helpers/source-files";
import { readSourceText, walkSourceFiles } from "./helpers/source-files";
const SRC = join(process.cwd(), "src");
@@ -36,7 +35,7 @@ function sourceFiles(): string[] {
}
function read(rel: string): string {
return readFileSync(join(SRC, rel), "utf8");
return readSourceText(join(SRC, rel));
}
interface ProviderIngest {