Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cli/tests/architecture/catches-that-swallow.arch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import { readdirSync, statSync } from "node:fs";
import { join, relative } from "node:path";
import { describe, expect, it } from "vitest";
import { CLI_ROOT, read, sourceFiles } from "./helpers.js";
import { CLI_ROOT, canonicalPath, read, sourceFiles } from "./helpers.js";

/** File → how many empty catches it keeps, and why each is a deliberate best effort. */
const BASELINE: Readonly<Record<string, { readonly count: number; readonly reason: string }>> = {
Expand Down Expand Up @@ -78,7 +78,7 @@ function testFiles(): string[] {
const full = join(dir, entry);
if (statSync(full).isDirectory()) {
if (entry !== "fixtures" && entry !== "snapshots") walk(full);
} else if (entry.endsWith(".ts")) out.push(relative(CLI_ROOT, full));
} else if (entry.endsWith(".ts")) out.push(canonicalPath(relative(CLI_ROOT, full)));
}
};
walk(join(CLI_ROOT, "tests"));
Expand Down
4 changes: 2 additions & 2 deletions cli/tests/architecture/comments.arch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { readdirSync, statSync } from "node:fs";
import { join, relative } from "node:path";
import { describe, expect, it } from "vitest";
import { CLI_ROOT, read, sourceFiles } from "./helpers.js";
import { CLI_ROOT, canonicalPath, read, sourceFiles } from "./helpers.js";

const COMMENT_LINE = /^\s*(\/\/|\/\*|\*)/;
const DIRECTIVE = /biome-ignore|@ts-expect-error|eslint-disable/;
Expand All @@ -31,7 +31,7 @@ function testFiles(): string[] {
if (statSync(full).isDirectory()) {
if (entry === "fixtures" || entry === "snapshots") continue;
walk(full);
} else if (entry.endsWith(".ts")) out.push(relative(CLI_ROOT, full));
} else if (entry.endsWith(".ts")) out.push(canonicalPath(relative(CLI_ROOT, full)));
}
};
walk(join(CLI_ROOT, "tests"));
Expand Down
19 changes: 12 additions & 7 deletions cli/tests/architecture/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,24 @@
* enough for a pre-commit hook and cannot be broken by runtime wiring.
*/
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
import { dirname, join, normalize, relative, resolve } from "node:path";
import { join, posix, relative, resolve } from "node:path";

export const CLI_ROOT = resolve(import.meta.dirname, "..", "..");
export const SRC = join(CLI_ROOT, "src");

export const REPO_ROOT = resolve(CLI_ROOT, "..");

export function canonicalPath(path: string): string {
return path.replace(/\\/g, "/");
}

export function sourceFiles(): string[] {
const out: string[] = [];
const walk = (dir: string): void => {
for (const entry of readdirSync(dir)) {
const full = join(dir, entry);
if (statSync(full).isDirectory()) walk(full);
else if (entry.endsWith(".ts")) out.push(relative(CLI_ROOT, full));
else if (entry.endsWith(".ts")) out.push(canonicalPath(relative(CLI_ROOT, full)));
}
};
walk(SRC);
Expand All @@ -37,7 +41,7 @@ export function pluginReadmes(): string[] {
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
if (!entry.isDirectory()) continue;
const readme = join(pluginsDir, entry.name, "README.md");
if (existsSync(readme)) found.push(join("plugins", entry.name, "README.md"));
if (existsSync(readme)) found.push(canonicalPath(join("plugins", entry.name, "README.md")));
}
if (found.length === 0) {
throw new Error("no plugin README found — the scope of this rule is stale");
Expand All @@ -53,11 +57,12 @@ export function pluginReadmes(): string[] {
*/
export const INTERNAL_IMPORT = /(?:from|import)\s*\(?\s*["'](\.[^"']+|@\/[^"']+)["']/g;

function resolveImportTarget(file: string, specifier: string): string {
export function resolveImportTarget(file: string, specifier: string): string {
const target = canonicalPath(specifier);
return (
specifier.startsWith("@/")
? `src/${specifier.slice(2)}`
: normalize(join(dirname(file), specifier))
target.startsWith("@/")
? `src/${target.slice(2)}`
: posix.join(posix.dirname(canonicalPath(file)), target)
).replace(/\.js$/, ".ts");
}

Expand Down
3 changes: 2 additions & 1 deletion cli/tests/architecture/no-shared-binary.arch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { readdirSync, readFileSync, statSync } from "node:fs";
import { join, relative, resolve } from "node:path";
import { describe, expect, it } from "vitest";
import { canonicalPath } from "./helpers.js";

const CLI_ROOT = resolve(import.meta.dirname, "..", "..");
const TESTS_ROOT = join(CLI_ROOT, "tests");
Expand All @@ -31,7 +32,7 @@ describe("no test resolves a path into the shared dist/ build output", () => {
it("every file under tests/ reads the e2e run's own binary, not dist/cli.js", () => {
const violations = testFiles()
.filter((file) => CWD_INTO_DIST.test(readFileSync(file, "utf8")))
.map((file) => relative(CLI_ROOT, file));
.map((file) => canonicalPath(relative(CLI_ROOT, file)));

expect(
violations,
Expand Down
47 changes: 47 additions & 0 deletions cli/tests/architecture/paths-select-source.arch.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { describe, expect, it } from "vitest";
import {
canonicalPath,
contextOf,
importersByFile,
matchesGlob,
pluginReadmes,
resolveImportTarget,
sourceFiles,
} from "./helpers.js";

describe("architecture paths select the same files on every platform", () => {
it.each([
["src\\contexts\\tools\\domain\\tool.ts", "tools", "src/contexts/**/*.ts"],
["src\\kernel\\errors.ts", "kernel", "src/kernel/**/*.ts"],
["src\\presentation\\commands\\setup.ts", "presentation", "src/presentation/commands/**"],
])("selects the explicit backslash path %s", (nativePath, context, glob) => {
const file = canonicalPath(nativePath);
expect(contextOf(file)).toBe(context);
expect(matchesGlob(glob, file)).toBe(true);
expect(matchesGlob("src/deleted-context/**", file)).toBe(false);
});

it.each([
["src\\kernel\\ports\\file-reader.ts", "../file.js"],
["src/kernel/ports/file-reader.ts", "..\\file.js"],
["src\\runtime\\caller.ts", "@/kernel/file.js"],
])("resolves %s importing %s to the canonical source key", (file, specifier) => {
const known = new Set(["src/kernel/file.ts"]);
expect(known.has(resolveImportTarget(file, specifier))).toBe(true);
});

it("returns canonical, nonempty source and plugin scopes", () => {
const files = sourceFiles();
const readmes = pluginReadmes();
expect(files.length).toBeGreaterThan(0);
expect(files.every((file) => file.startsWith("src/") && !file.includes("\\"))).toBe(true);
expect(readmes.length).toBeGreaterThan(0);
expect(readmes.every((file) => /^plugins\/[^/]+\/README\.md$/.test(file))).toBe(true);
});

it("keeps relative-import lookups connected to the source-file keys", () => {
expect(importersByFile().get("src/kernel/file.ts")).toContain(
"src/kernel/ports/file-reader.ts"
);
});
});
8 changes: 4 additions & 4 deletions cli/tests/architecture/ports-are-called.arch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
* nothing spells the name as a call, never that a real path reaches it.
*/
import { readdirSync, readFileSync, statSync } from "node:fs";
import { join, relative, sep } from "node:path";
import { join, relative } from "node:path";
import { describe, expect, it } from "vitest";
import { CLI_ROOT, expectRatchet, SRC, sourceFiles } from "./helpers.js";
import { CLI_ROOT, canonicalPath, expectRatchet, SRC, sourceFiles } from "./helpers.js";

/** "ports" must be its own path segment: a substring check also matches `supports/` and
* `reports/`. */
Expand All @@ -17,8 +17,8 @@ function portFiles(): string[] {
for (const entry of readdirSync(dir)) {
const full = join(dir, entry);
if (statSync(full).isDirectory()) walk(full);
else if (entry.endsWith(".ts") && /(^|\/)ports\//.test(full.split(sep).join("/"))) {
out.push(relative(CLI_ROOT, full));
else if (entry.endsWith(".ts") && /(^|\/)ports\//.test(canonicalPath(full))) {
out.push(canonicalPath(relative(CLI_ROOT, full)));
}
}
};
Expand Down
Loading