chore(format): run prettier over src/ and scripts/
Formatting was configured but never enforced: `bun run format:check` reported 199 unformatted files and ran in no workflow and in no git hook, so .prettierrc (printWidth 100, trailing commas) described an intention rather than the tree. This is the one-time sweep that makes the check gateable. Whitespace and token-reflow only -- no behavioural change: `bun run check` reports 0 errors and all 1053 frontend tests pass before and after. Kept out of every other commit on purpose. A 199-file diff mixed with real changes is unreviewable, and the next commit turns format:check into a hard CI gate so this cannot silently accumulate again.
This commit is contained in:
@@ -140,9 +140,10 @@ describe("findDanglingIds", () => {
|
||||
});
|
||||
|
||||
it("deduplicates and sorts, so one typo is reported once", () => {
|
||||
expect(
|
||||
findDanglingIds(["DR-189", "DR-189", "UR-999", "DR-189"], defined)
|
||||
).toEqual(["DR-189", "UR-999"]);
|
||||
expect(findDanglingIds(["DR-189", "DR-189", "UR-999", "DR-189"], defined)).toEqual([
|
||||
"DR-189",
|
||||
"UR-999",
|
||||
]);
|
||||
});
|
||||
|
||||
it("ignores IDs whose prefix is not a known trace type", () => {
|
||||
@@ -158,7 +159,7 @@ describe("coverage threshold", () => {
|
||||
// passes, which is how the 50%-while-actually-86% slack went unnoticed.
|
||||
const workflow = fs.readFileSync(
|
||||
path.resolve(HERE, "../.gitea/workflows/traceability-check.yml"),
|
||||
"utf-8"
|
||||
"utf-8",
|
||||
);
|
||||
const match = workflow.match(/^\s*MIN_THRESHOLD=(\d+)\s*$/m);
|
||||
expect(match).not.toBeNull();
|
||||
@@ -307,9 +308,7 @@ describe("generated matrix file links", () => {
|
||||
|
||||
it("keeps the #Lnn line anchor on the href", () => {
|
||||
const link = formatMatrixFileLink("scripts/extract-traces.ts", 427);
|
||||
expect(link).toBe(
|
||||
"[`scripts/extract-traces.ts`](../scripts/extract-traces.ts#L427)"
|
||||
);
|
||||
expect(link).toBe("[`scripts/extract-traces.ts`](../scripts/extract-traces.ts#L427)");
|
||||
});
|
||||
|
||||
it("does not produce a bare repo-root href, which resolves to docs/<path>", () => {
|
||||
@@ -334,10 +333,7 @@ describe("live requirements.md", () => {
|
||||
// row. Worse, the pins never guarded the actual defect — a stale denominator
|
||||
// is caught by the sum-consistency check below, and the >100% ratio it
|
||||
// produced is covered directly by the computeCoverage tests, on fixtures.
|
||||
const md = fs.readFileSync(
|
||||
path.resolve(HERE, "../docs/requirements.md"),
|
||||
"utf-8"
|
||||
);
|
||||
const md = fs.readFileSync(path.resolve(HERE, "../docs/requirements.md"), "utf-8");
|
||||
const defined = countDefinedRequirements(md);
|
||||
|
||||
// The parser found real rows of every type: a section silently failing to
|
||||
|
||||
+11
-30
@@ -64,8 +64,7 @@ export const MIN_COVERAGE_PERCENT = 88;
|
||||
// `import.meta.dir` is a Bun extension and is undefined when this module is
|
||||
// imported by vitest (which runs it as an ordinary ESM module), so fall back to
|
||||
// import.meta.url — this file must stay importable for extract-traces.test.ts.
|
||||
const SCRIPT_DIR =
|
||||
import.meta.dir ?? path.dirname(new URL(import.meta.url).pathname);
|
||||
const SCRIPT_DIR = import.meta.dir ?? path.dirname(new URL(import.meta.url).pathname);
|
||||
const BASE_DIR = path.resolve(SCRIPT_DIR, "..");
|
||||
|
||||
const TRACES_PATTERN = /TRACES:\s*([^\n]+)/gi;
|
||||
@@ -283,8 +282,7 @@ export function countDefinedRequirements(markdown: string): DefinedRequirements
|
||||
else ids.add(id);
|
||||
}
|
||||
|
||||
const countOf = (type: string) =>
|
||||
[...ids].filter((id) => id.startsWith(`${type}-`)).length;
|
||||
const countOf = (type: string) => [...ids].filter((id) => id.startsWith(`${type}-`)).length;
|
||||
|
||||
return {
|
||||
UR: countOf("UR"),
|
||||
@@ -311,16 +309,13 @@ export function countDefinedRequirements(markdown: string): DefinedRequirements
|
||||
*
|
||||
* TRACES: | DR-093
|
||||
*/
|
||||
export function findDanglingIds(
|
||||
tracedIds: string[],
|
||||
defined: DefinedRequirements
|
||||
): string[] {
|
||||
export function findDanglingIds(tracedIds: string[], defined: DefinedRequirements): string[] {
|
||||
const KNOWN_TYPE = /^(UR|IR|DR|JA|UT|IT)-\d{3}$/;
|
||||
|
||||
const dangling = new Set(
|
||||
tracedIds
|
||||
.filter((id) => KNOWN_TYPE.test(id))
|
||||
.filter((id) => !defined.ids.has(id) && !defined.testIds.has(id))
|
||||
.filter((id) => !defined.ids.has(id) && !defined.testIds.has(id)),
|
||||
);
|
||||
|
||||
return [...dangling].sort();
|
||||
@@ -336,10 +331,7 @@ export function findDanglingIds(
|
||||
*
|
||||
* TRACES: | DR-093
|
||||
*/
|
||||
export function computeCoverage(
|
||||
tracedIds: string[],
|
||||
defined: DefinedRequirements
|
||||
): CoverageResult {
|
||||
export function computeCoverage(tracedIds: string[], defined: DefinedRequirements): CoverageResult {
|
||||
// Only the four *requirement* types participate in coverage. UT/IT are test
|
||||
// identifiers defined in §4 of requirements.md — a different taxonomy, and
|
||||
// flagging them as orphans would bury real typos in ~60 lines of noise.
|
||||
@@ -352,10 +344,7 @@ export function computeCoverage(
|
||||
return {
|
||||
covered: covered.length,
|
||||
total: defined.total,
|
||||
percent:
|
||||
defined.total === 0
|
||||
? 0
|
||||
: Math.round((covered.length / defined.total) * 100),
|
||||
percent: defined.total === 0 ? 0 : Math.round((covered.length / defined.total) * 100),
|
||||
orphaned,
|
||||
};
|
||||
}
|
||||
@@ -488,16 +477,14 @@ function reportCoverage(data: TracesData, minThreshold: number): number {
|
||||
|
||||
if (cov.orphaned.length > 0) {
|
||||
console.log("");
|
||||
console.log(
|
||||
`⚠️ Traced but not defined in requirements.md: ${cov.orphaned.join(", ")}`
|
||||
);
|
||||
console.log(`⚠️ Traced but not defined in requirements.md: ${cov.orphaned.join(", ")}`);
|
||||
console.log(" Fix the TRACES comment or add the requirement.");
|
||||
}
|
||||
|
||||
if (data.dangling && data.dangling.length > 0) {
|
||||
console.log("");
|
||||
console.log(
|
||||
`⚠️ Dangling IDs (incl. UT/IT): ${data.dangling.join(", ")} — run \`bun run traces:validate\`.`
|
||||
`⚠️ Dangling IDs (incl. UT/IT): ${data.dangling.join(", ")} — run \`bun run traces:validate\`.`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -538,9 +525,7 @@ function reportDangling(data: TracesData): number {
|
||||
console.log("❌ TRACES reference IDs that docs/requirements.md does not define:");
|
||||
console.log("");
|
||||
for (const id of dangling) {
|
||||
const files = [
|
||||
...new Set((data.requirements[id] ?? []).map((e) => e.file)),
|
||||
].sort();
|
||||
const files = [...new Set((data.requirements[id] ?? []).map((e) => e.file))].sort();
|
||||
console.log(` ${id}`);
|
||||
for (const file of files) console.log(` ${file}`);
|
||||
}
|
||||
@@ -554,9 +539,7 @@ function reportDangling(data: TracesData): number {
|
||||
// Main — guarded so this module stays importable from extract-traces.test.ts.
|
||||
if (import.meta.main) {
|
||||
const args = process.argv.slice(2);
|
||||
const format = args.includes("--format")
|
||||
? args[args.indexOf("--format") + 1]
|
||||
: "markdown";
|
||||
const format = args.includes("--format") ? args[args.indexOf("--format") + 1] : "markdown";
|
||||
|
||||
console.error("🔍 Extracting TRACES from codebase...");
|
||||
const data = extractTraces();
|
||||
@@ -583,7 +566,5 @@ if (import.meta.main) {
|
||||
console.log(generateMarkdown(data));
|
||||
}
|
||||
|
||||
console.error(
|
||||
`\n✅ Complete! Found ${data.totalTraces} TRACES across ${data.totalFiles} files`
|
||||
);
|
||||
console.error(`\n✅ Complete! Found ${data.totalTraces} TRACES across ${data.totalFiles} files`);
|
||||
}
|
||||
|
||||
@@ -55,9 +55,7 @@ function loadRequirementDescriptions(): Map<string, string> {
|
||||
}
|
||||
|
||||
function changedFiles(range: string): string[] {
|
||||
const cmd = range
|
||||
? `git diff --name-only ${range}`
|
||||
: "git ls-files"; // untagged repo: describe everything currently traced
|
||||
const cmd = range ? `git diff --name-only ${range}` : "git ls-files"; // untagged repo: describe everything currently traced
|
||||
return sh(cmd)
|
||||
.split("\n")
|
||||
.filter((f) => f && existsSync(f));
|
||||
|
||||
@@ -34,30 +34,47 @@ function seed(dir: string) {
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(dir, "package.json"),
|
||||
JSON.stringify({ name: "jellytau", version: "0.0.1", dependencies: { hls: "1.2.3" } }, null, 2)
|
||||
JSON.stringify({ name: "jellytau", version: "0.0.1", dependencies: { hls: "1.2.3" } }, null, 2),
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(dir, "src-tauri", "tauri.conf.json"),
|
||||
JSON.stringify({ productName: "jellytau", version: "0.0.1" }, null, 2)
|
||||
JSON.stringify({ productName: "jellytau", version: "0.0.1" }, null, 2),
|
||||
);
|
||||
// A dependency carrying its own `version =` is the trap: a greedy regex
|
||||
// rewrites it too and the build then resolves the wrong crate.
|
||||
fs.writeFileSync(
|
||||
path.join(dir, "src-tauri", "Cargo.toml"),
|
||||
['[package]', 'name = "jellytau"', 'version = "0.0.1"', '', '[dependencies]', 'serde = { version = "1.0.100" }', ''].join("\n")
|
||||
[
|
||||
"[package]",
|
||||
'name = "jellytau"',
|
||||
'version = "0.0.1"',
|
||||
"",
|
||||
"[dependencies]",
|
||||
'serde = { version = "1.0.100" }',
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(dir, "src-tauri", "Cargo.lock"),
|
||||
['[[package]]', 'name = "serde"', 'version = "1.0.100"', '', '[[package]]', 'name = "jellytau"', 'version = "0.0.1"', ''].join("\n")
|
||||
[
|
||||
"[[package]]",
|
||||
'name = "serde"',
|
||||
'version = "1.0.100"',
|
||||
"",
|
||||
"[[package]]",
|
||||
'name = "jellytau"',
|
||||
'version = "0.0.1"',
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(dir, "src-tauri", "gen", "android", "app", "tauri.properties"),
|
||||
"tauri.android.versionCode=1\n"
|
||||
"tauri.android.versionCode=1\n",
|
||||
);
|
||||
fs.mkdirSync(path.join(dir, "packaging", "arch"), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(dir, "packaging", "arch", "PKGBUILD"),
|
||||
['pkgname=jellytau', 'pkgver=0.0.1', 'pkgrel=3', 'pkgdesc="x"', ''].join("\n")
|
||||
["pkgname=jellytau", "pkgver=0.0.1", "pkgrel=3", 'pkgdesc="x"', ""].join("\n"),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -74,7 +91,7 @@ function read(rel: string): string {
|
||||
|
||||
function versionCode(): number {
|
||||
const m = read("src-tauri/gen/android/app/tauri.properties").match(
|
||||
/^tauri\.android\.versionCode=(\d+)$/m
|
||||
/^tauri\.android\.versionCode=(\d+)$/m,
|
||||
);
|
||||
return m ? Number(m[1]) : NaN;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import { readFileSync } from "fs";
|
||||
import { resolve } from "path";
|
||||
|
||||
const config = JSON.parse(
|
||||
readFileSync(resolve(__dirname, "../src-tauri/tauri.conf.json"), "utf-8")
|
||||
readFileSync(resolve(__dirname, "../src-tauri/tauri.conf.json"), "utf-8"),
|
||||
);
|
||||
|
||||
const security = config.app.security;
|
||||
|
||||
Reference in New Issue
Block a user