Exclude AppleDouble companions from test discovery (#11) #14

Merged
Codex Bot merged 1 commits from fix/11-ignore-appledouble-inputs into main 2026-10-10 11:17:11 -07:00
6 changed files with 527 additions and 2 deletions
Showing only changes of commit 768a3275ea - Show all commits

No files matched your search

+14
View File
@@ -5765,3 +5765,17 @@ result is claimed. The source browser build stops at missing ignored Manastorm
assets; browser release/visual verification remains open. Native offline gear assets; browser release/visual verification remains open. Native offline gear
persistence, recovery presentation and authenticated RoM progression remain persistence, recovery presentation and authenticated RoM progression remain
under #124. See docs/GAME_GEAR_NAMESPACES.md and gear-namespace-validation.json. under #124. See docs/GAME_GEAR_NAMESPACES.md and gear-namespace-validation.json.
## AppleDouble input discovery — shared #11 (2026-10-10)
Vitest defaults now exclude ._-prefixed file/directory components while retaining
its built-in exclusions. A portable fresh-fixture proof reproduces three binary
sidecar transform failures in the unfiltered control; fixed defaults run both
ordinary sentinels, including ordinary._fragment.test.ts. TypeScript API/CLI
retain those two inputs. TypeScript 5.8 already skipped dot-prefixed roots, so
its explicit exclusions preserve observed behavior. Vite/Node config typecheck
passes. See docs/METADATA_DISCOVERY.md and metadata-discovery-validation.json.
This is source tooling; no native runtime install or new dungeon entry was
performed. No observed Cellblock Rush boss, GPU capture, full app build,
gameplay or balance certification is claimed. Existing metadata is retained.
+53
View File
@@ -0,0 +1,53 @@
# macOS metadata and source discovery
macOS can create AppleDouble companions whose path components start with `._`.
These files contain filesystem metadata and are not application source. On the
external-drive review checkout, the former Vitest include pattern selected
`src/avatar/._*.test.ts` files and attempted to transform their binary contents.
TypeScript 5.8 already skips the tested dot-prefixed paths with its existing
`src` include. Its new explicit exclusions document the same metadata policy
while preserving compiler options and ordinary input discovery.
Shared source issue [#11](https://git.whoagland.com/phenom/healer-man/issues/11)
owns the default discovery correction:
- `vite.config.ts` retains `configDefaults.exclude` and adds `**/._*` plus
`**/._*/**` to the test exclusions.
- `tsconfig.app.json` retains its compiler options and `include: ["src"]`.
Its exclusions retain the package-directory defaults and add the same
metadata patterns.
The two patterns cover companion files and files inside a companion directory,
including nested directories. A component must start with `._`; ordinary
underscore names and names containing those characters elsewhere remain source.
Existing metadata files are retained. This changes root input discovery;
TypeScript's normal handling of explicit imports still applies.
`tools/verify-appledouble-metadata-discovery.mjs` supplies a focused regression
proof through actual Vitest discovery/execution and TypeScript configuration
input parsing. It uses fresh task-owned fixtures to verify the rejection path
and two ordinary test sentinels to verify continued execution. It does not certify
full application typechecking, a browser production build, gameplay, balance,
character rendering or dungeon behavior.
Exact commands, input identities and observed results accompany the issue and
`metadata-discovery-validation.json`. Shared source issue
[#9](https://git.whoagland.com/phenom/healer-man/issues/9) still owns its separate
browser build/live visual acceptance gap. Native checkpoint
[#292](https://git.whoagland.com/Codex/healer-man-godot/issues/292) is already
installed and merged. This tooling change requires no native runtime rebuild:
the native writable source mirror is a subset without these browser configs.
Run the focused proof after installing the repository dependencies:
```sh
node tools/verify-appledouble-metadata-discovery.mjs --evidence-dir /absolute/fresh-path
```
Use a fresh absolute path outside the checkout. The helper keeps its fixtures
and reports; it refuses existing destinations, overlap and symlink paths. It
works from another current directory and uses the Node executable that launched
it. The final external-drive control records three binary metadata transform
failures; fixed Vitest defaults run both ordinary tests. TypeScript API and CLI
resolve the same two ordinary inputs, with and without the explicit metadata
filters. The Vite/Node configuration typecheck passes.
+41
View File
@@ -0,0 +1,41 @@
{
"schemaVersion": 1,
"date": "2026-10-10",
"issue": "https://git.whoagland.com/phenom/healer-man/issues/11",
"coordinator": "/root",
"session": "01a11cd3-fa7c-7510-ba8f-bc7a3d0310a6",
"sourceBase": "812162320d9bf041a1b635b553576962238e5138",
"files": {
"vite.config.ts": "c394d03344dd8cef5f5488e00e892e3a53b84d2374068688fdb1459114e5627c",
"tsconfig.app.json": "5cde2a8b2bd61121fbac7051ce403475ab487314b3970cadde8733ff5013292c",
"tools/verify-appledouble-metadata-discovery.mjs": "7ffc8b265d589c65e74122bd90062cc75413b52ffa4bb1daf737a22fcb128674"
},
"proof": {
"path": "diagnostics/metadata-validation/attempt-08/report.json",
"sha256": "fa33b3b0bbbd4ce1f6141319ebe2a4f8b465aa408c865a8ea259cc8a2ca6f30e",
"portableAlternateCwd": "/Volumes/backup1tb-2",
"toolVersions": {
"vitest": "3.2.7",
"vite": "7.3.6",
"typescript": "5.8.3"
},
"controlBinaryMetadataFailures": 3,
"candidateVitestOrdinaryTests": 2,
"candidateTypeScriptApiAndCliOrdinaryInputs": 2,
"metadataFileAndNestedDirectoryExcluded": true,
"ordinaryDotUnderscoreFragmentRetained": true,
"vitestDefaultsRetained": true,
"typescriptBaselineAlreadySkippedDotPaths": true
},
"nodeConfigTypecheck": {
"passed": true,
"receiptSha256": "c2dee7d831ab89628589267892058210f700f96ce69af6fe05f5b7ecaa33f29b"
},
"limits": [
"Root input discovery only; explicit imports use normal compiler rules",
"No full application typecheck/build, balance, gameplay or graphics certification",
"TypeScript explicit exclusions preserve observed TS5.8 behavior",
"Native input mirror lacks browser configs; no native runtime installation",
"Existing metadata files and other workers edits retained"
]
}
@@ -0,0 +1,405 @@
#!/usr/bin/env node
import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import { spawnSync } from "node:child_process";
import {
lstatSync,
mkdirSync,
readFileSync,
realpathSync,
writeFileSync,
} from "node:fs";
import { createRequire } from "node:module";
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
const PROJECT_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const METADATA_EXCLUDES = ["**/._*", "**/._*/**"];
const SENTINELS = ["src/metadata-discovery-sentinel.test.ts", "src/ordinary._fragment.test.ts"];
const SENTINEL = SENTINELS[0];
const METADATA_FILE = "src/._appledouble-file.test.ts";
const METADATA_NESTED_FILE = "src/._appledouble-directory/nested-metadata.test.ts";
function sha256(value) {
return createHash("sha256").update(value).digest("hex");
}
function sha256File(filePath) {
assertRegularNoSymlink(filePath);
return sha256(readFileSync(filePath));
}
function assertRegularNoSymlink(filePath) {
const stat = lstatSync(filePath);
assert.equal(stat.isSymbolicLink(), false, `symbolic link is not accepted: ${filePath}`);
assert.equal(stat.isFile(), true, `expected a regular file: ${filePath}`);
}
function assertNoSymlinkComponents(targetPath) {
const absolute = path.resolve(targetPath);
const parsed = path.parse(absolute);
let current = parsed.root;
for (const component of absolute.slice(parsed.root.length).split(path.sep).filter(Boolean)) {
current = path.join(current, component);
try {
const stat = lstatSync(current);
assert.equal(stat.isSymbolicLink(), false, `symbolic path component is not accepted: ${current}`);
} catch (error) {
if (error.code === "ENOENT") break;
throw error;
}
}
}
function hasPathPrefix(parent, candidate) {
const relative = path.relative(parent, candidate);
return relative === "" || (!relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative));
}
function canonicalizeProspectivePath(targetPath) {
let current = path.resolve(targetPath);
const suffix = [];
while (true) {
try {
const canonicalAncestor = realpathSync(current);
return path.resolve(canonicalAncestor, ...suffix.reverse());
} catch (error) {
if (error.code !== "ENOENT") throw error;
const parent = path.dirname(current);
assert.notEqual(parent, current, `no existing ancestor for evidence path: ${targetPath}`);
suffix.push(path.basename(current));
current = parent;
}
}
}
function assertNoAppleDoublePathComponents(targetPath) {
const absolute = path.resolve(targetPath);
const parsed = path.parse(absolute);
const components = absolute.slice(parsed.root.length).split(path.sep).filter(Boolean);
for (const component of components) {
assert.equal(component.startsWith("._"), false, `AppleDouble path component is not accepted: ${component}`);
}
}
function assertFreshEvidenceDirectory(targetPath) {
assert.equal(path.isAbsolute(targetPath), true, "--evidence-dir must be absolute");
const absolute = path.resolve(targetPath);
assert.equal(absolute, targetPath, "--evidence-dir must be normalized");
assertNoAppleDoublePathComponents(absolute);
assertNoSymlinkComponents(absolute);
const canonicalProjectRoot = realpathSync(PROJECT_ROOT);
const canonicalEvidence = canonicalizeProspectivePath(absolute);
assert.equal(hasPathPrefix(canonicalProjectRoot, canonicalEvidence) || hasPathPrefix(canonicalEvidence, canonicalProjectRoot), false,
"evidence directory must not overlap the checkout");
try {
lstatSync(absolute);
assert.fail(`evidence directory already exists; use a fresh path: ${absolute}`);
} catch (error) {
if (error.code !== "ENOENT") throw error;
}
mkdirSync(path.dirname(absolute), { recursive: true });
mkdirSync(absolute, { recursive: false });
return absolute;
}
function parseArgs(argv) {
const args = {};
for (let index = 0; index < argv.length; index += 1) {
const token = argv[index];
if (token !== "--evidence-dir" || !argv[index + 1]) {
throw new Error("usage: verify-appledouble-metadata-discovery.mjs --evidence-dir ABSOLUTE_FRESH_PATH");
}
if (args.evidenceDir) throw new Error("--evidence-dir may be provided once");
args.evidenceDir = argv[index + 1];
index += 1;
}
if (!args.evidenceDir || !path.isAbsolute(args.evidenceDir)) {
throw new Error("--evidence-dir must be an absolute path");
}
return args;
}
function runNode(args, cwd, stdoutPath, stderrPath) {
const result = spawnSync(process.execPath, args, {
cwd,
encoding: "utf8",
maxBuffer: 16 * 1024 * 1024,
env: { ...process.env, CI: "1" },
});
writeFileSync(stdoutPath, result.stdout ?? "", "utf8");
writeFileSync(stderrPath, result.stderr ?? "", "utf8");
if (result.error) throw result.error;
return {
argv: [process.execPath, ...args],
cwd,
exitCode: result.status,
signal: result.signal,
stdout: { path: stdoutPath, sha256: sha256File(stdoutPath) },
stderr: { path: stderrPath, sha256: sha256File(stderrPath) },
error: result.error?.message ?? null,
};
}
function readGitHead() {
const result = spawnSync("git", ["rev-parse", "HEAD"], {
cwd: PROJECT_ROOT,
encoding: "utf8",
});
return result.status === 0 ? result.stdout.trim() : null;
}
function generatedTest(importPath, name, body) {
return `import { expect, it } from ${JSON.stringify(importPath)};\n\nit(${JSON.stringify(name)}, () => {\n${body}\n});\n`;
}
function readVitestFiles(jsonPath, fixtureRoot) {
const report = JSON.parse(readFileSync(jsonPath, "utf8"));
const testResults = report.testResults;
assert.ok(Array.isArray(testResults), "Vitest JSON reporter must provide testResults");
return testResults.map((result) => path.relative(fixtureRoot, result.name).split(path.sep).join("/")).sort();
}
function writeJson(filePath, value) {
writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
}
let evidenceRootForFailure = null;
async function main() {
const { evidenceDir } = parseArgs(process.argv.slice(2));
const absoluteEvidence = assertFreshEvidenceDirectory(evidenceDir);
evidenceRootForFailure = absoluteEvidence;
const reportPath = path.join(absoluteEvidence, "report.json");
const R = createRequire(path.join(PROJECT_ROOT, "package.json"));
const vitestRunner = R.resolve("vitest/vitest.mjs");
const vitestEntry = R.resolve("vitest/dist/index.js");
const vitestConfigEntry = R.resolve("vitest/dist/config.js");
const viteEntry = R.resolve("vite");
const tsEntry = R.resolve("typescript");
const vitestPackage = JSON.parse(readFileSync(path.join(PROJECT_ROOT, "node_modules", "vitest", "package.json"), "utf8"));
const vitePackage = JSON.parse(readFileSync(path.join(PROJECT_ROOT, "node_modules", "vite", "package.json"), "utf8"));
const tsPackage = JSON.parse(readFileSync(path.join(PROJECT_ROOT, "node_modules", "typescript", "package.json"), "utf8"));
const configInfo = {};
for (const name of ["vite.config.ts", "tsconfig.app.json"]) {
const actualPath = path.join(PROJECT_ROOT, name);
const actualHash = sha256File(actualPath);
configInfo[name] = { path: actualPath, sha256: actualHash };
}
const vite = await import(pathToFileURL(viteEntry).href);
const { configDefaults } = await import(pathToFileURL(vitestConfigEntry).href);
const loaded = await vite.loadConfigFromFile({ command: "serve", mode: "test" }, path.join(PROJECT_ROOT, "vite.config.ts"), PROJECT_ROOT);
assert.ok(loaded?.config, "Vite must load the actual project config");
const testConfig = loaded.config.test;
assert.deepEqual(testConfig.include, ["src/**/*.test.ts"], "candidate must preserve the normal Vitest include");
assert.ok(Array.isArray(testConfig.exclude), "project Vitest config must define exclude patterns");
for (const defaultExclude of configDefaults.exclude) {
assert.ok(testConfig.exclude.includes(defaultExclude), `Vitest built-in exclusion was dropped: ${defaultExclude}`);
}
for (const metadataExclude of METADATA_EXCLUDES) {
assert.ok(testConfig.exclude.includes(metadataExclude), `metadata exclusion is missing: ${metadataExclude}`);
}
const baselineTestConfig = {
...testConfig,
exclude: testConfig.exclude.filter((pattern) => !METADATA_EXCLUDES.includes(pattern)),
};
const fixtureRoot = path.join(absoluteEvidence, "fixture-project");
const srcRoot = path.join(fixtureRoot, "src");
mkdirSync(srcRoot, { recursive: true });
writeFileSync(path.join(srcRoot, "._appledouble-file.test.ts"),
generatedTest(vitestEntry, "AppleDouble file fixture is discoverable without the scoped exclusion", " expect(true).toBe(true);\n"), "utf8");
mkdirSync(path.join(srcRoot, "._appledouble-directory"), { recursive: true });
writeFileSync(path.join(srcRoot, "._appledouble-directory", "nested-metadata.test.ts"),
generatedTest(vitestEntry, "AppleDouble nested-directory fixture is discoverable without the scoped exclusion", " expect(true).toBe(true);\n"), "utf8");
writeFileSync(path.join(srcRoot, "metadata-discovery-sentinel.test.ts"),
generatedTest(vitestEntry, "ordinary source sentinel runs", " expect(2 + 2).toBe(4);\n"), "utf8");
writeFileSync(path.join(srcRoot, "ordinary._fragment.test.ts"),
generatedTest(vitestEntry, "ordinary filename containing dot underscore remains included", " expect(" +
"'ordinary._fragment.test.ts'.startsWith('._')).toBe(false);\n"), "utf8");
// This control is derived from the actual loaded config and removes only the two issue filters.
const baselineConfig = path.join(absoluteEvidence, "baseline-vitest.config.mjs");
writeFileSync(baselineConfig, `export default ${JSON.stringify({ test: baselineTestConfig }, null, 2)};\n`, "utf8");
const baselineJson = path.join(absoluteEvidence, "vitest-baseline-results.json");
const baseline = runNode([vitestRunner, "run", "--root", fixtureRoot, "--config", baselineConfig,
"--reporter=json", `--outputFile=${baselineJson}`, "--maxWorkers=1"], PROJECT_ROOT,
path.join(absoluteEvidence, "vitest-baseline.stdout.log"), path.join(absoluteEvidence, "vitest-baseline.stderr.log"));
const baselineFiles = readVitestFiles(baselineJson, fixtureRoot);
for (const sentinel of SENTINELS) {
assert.ok(baselineFiles.includes(sentinel), `baseline Vitest control must execute ordinary file: ${sentinel}`);
}
assert.ok(baselineFiles.includes(METADATA_FILE), "baseline control must discover the AppleDouble file fixture");
assert.ok(baselineFiles.includes(METADATA_NESTED_FILE), "baseline control must discover the nested AppleDouble fixture");
const baselineJsonResult = JSON.parse(readFileSync(baselineJson, "utf8"));
const baselineFailures = baselineJsonResult.testResults
.filter((result) => result.status === "failed")
.map((result) => ({
path: path.relative(fixtureRoot, result.name).split(path.sep).join("/"),
message: result.message,
}));
assert.ok(baseline.exitCode === 0 || baseline.exitCode === 1, "baseline must exit normally, not crash");
for (const failure of baselineFailures) {
assert.ok(path.posix.basename(failure.path).startsWith("._"),
`baseline may fail only while transforming an AppleDouble sidecar: ${failure.path}`);
assert.ok(failure.message.includes("Unexpected") && failure.message.includes("\\x00"),
`baseline AppleDouble transform failure should identify binary metadata: ${failure.path}`);
}
const candidateJson = path.join(absoluteEvidence, "vitest-candidate-results.json");
const candidate = runNode([vitestRunner, "run", "--root", fixtureRoot,
"--config", path.join(PROJECT_ROOT, "vite.config.ts"), "--mode", "test", "--reporter=json",
`--outputFile=${candidateJson}`, "--maxWorkers=1"], PROJECT_ROOT,
path.join(absoluteEvidence, "vitest-candidate.stdout.log"), path.join(absoluteEvidence, "vitest-candidate.stderr.log"));
assert.equal(candidate.exitCode, 0, "candidate Vitest discovery must pass");
const candidateFiles = readVitestFiles(candidateJson, fixtureRoot);
assert.deepEqual(candidateFiles, [...SENTINELS].sort(), "candidate discovery must keep ordinary names and exclude dot-prefix metadata");
const ts = R("typescript");
const tsconfigSource = path.join(PROJECT_ROOT, "tsconfig.app.json");
const tsconfigPath = path.join(fixtureRoot, "tsconfig.app.json");
const tsconfigBytes = readFileSync(tsconfigSource);
writeFileSync(tsconfigPath, tsconfigBytes);
const tsconfigJson = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
assert.equal(tsconfigJson.error, undefined, "TypeScript must read the copied app config");
const parsedTsconfig = ts.parseJsonConfigFileContent(tsconfigJson.config, ts.sys, fixtureRoot, undefined, tsconfigPath);
assert.deepEqual(parsedTsconfig.errors, [], "TypeScript config must parse without diagnostics");
assert.deepEqual(tsconfigJson.config.include, ["src"], "TypeScript include must retain normal src scope");
assert.deepEqual(tsconfigJson.config.compilerOptions.types, ["vite/client", "vitest/globals"],
"TypeScript configured test/browser globals must remain unchanged");
const tsFiles = parsedTsconfig.fileNames.map((filePath) => path.relative(fixtureRoot, filePath).split(path.sep).join("/")).sort();
assert.deepEqual(tsFiles, [...SENTINELS].sort(), "TypeScript must keep ordinary names and exclude dot-prefix metadata");
const expectedPackageExcludes = ["node_modules", "bower_components", "jspm_packages"];
for (const exclude of expectedPackageExcludes) {
assert.ok(tsconfigJson.config.exclude.includes(exclude), `TypeScript package exclusion was dropped: ${exclude}`);
}
for (const exclude of METADATA_EXCLUDES) {
assert.ok(tsconfigJson.config.exclude.includes(exclude), `TypeScript metadata exclusion is missing: ${exclude}`);
}
const tscPath = path.resolve(path.dirname(tsEntry), "../bin/tsc");
const baselineTsconfigPath = path.join(fixtureRoot, "tsconfig.app.without-appledouble-excludes.json");
const baselineTsconfigObject = JSON.parse(tsconfigBytes.toString("utf8"));
baselineTsconfigObject.exclude = baselineTsconfigObject.exclude.filter((pattern) => !METADATA_EXCLUDES.includes(pattern));
writeFileSync(baselineTsconfigPath, `${JSON.stringify(baselineTsconfigObject, null, 2)}\n`, "utf8");
const baselineTsconfigJson = ts.readConfigFile(baselineTsconfigPath, ts.sys.readFile);
assert.equal(baselineTsconfigJson.error, undefined, "TypeScript must read the filtered-control app config");
const baselineParsedTsconfig = ts.parseJsonConfigFileContent(
baselineTsconfigJson.config, ts.sys, fixtureRoot, undefined, baselineTsconfigPath,
);
assert.deepEqual(baselineParsedTsconfig.errors, [], "filtered-control TypeScript config must parse without diagnostics");
const baselineTsFiles = baselineParsedTsconfig.fileNames
.map((filePath) => path.relative(fixtureRoot, filePath).split(path.sep).join("/")).sort();
assert.deepEqual(baselineTsFiles, [...SENTINELS].sort(),
"TypeScript 5.8 should continue to skip dot paths even without the explicit filters");
const baselineTsc = runNode([tscPath, "--showConfig", "--project", baselineTsconfigPath], PROJECT_ROOT,
path.join(absoluteEvidence, "tsc-control-show-config.stdout.json"), path.join(absoluteEvidence, "tsc-control-show-config.stderr.log"));
assert.equal(baselineTsc.exitCode, 0, "the filtered-control TypeScript CLI --showConfig must accept the fixture config");
const baselineTscJson = JSON.parse(readFileSync(path.join(absoluteEvidence, "tsc-control-show-config.stdout.json"), "utf8"));
const baselineTscFiles = baselineTscJson.files
.map((filePath) => path.relative(fixtureRoot, path.resolve(fixtureRoot, filePath)).split(path.sep).join("/")).sort();
assert.deepEqual(baselineTscFiles, [...SENTINELS].sort());
assert.deepEqual(baselineTscFiles, baselineTsFiles,
"TypeScript compiler API and CLI must agree on filtered-control src inputs");
const tsc = runNode([tscPath, "--showConfig", "--project", tsconfigPath], PROJECT_ROOT,
path.join(absoluteEvidence, "tsc-show-config.stdout.json"), path.join(absoluteEvidence, "tsc-show-config.stderr.log"));
assert.equal(tsc.exitCode, 0, "the actual TypeScript CLI --showConfig must accept the fixture config");
const tscJson = JSON.parse(readFileSync(path.join(absoluteEvidence, "tsc-show-config.stdout.json"), "utf8"));
const tscFiles = tscJson.files.map((filePath) => path.relative(fixtureRoot, path.resolve(fixtureRoot, filePath)).split(path.sep).join("/")).sort();
assert.deepEqual(tscFiles, [...SENTINELS].sort(), "the TypeScript CLI must resolve the same two ordinary inputs");
const report = {
schemaVersion: 1,
passed: true,
issue: 11,
node: { executable: process.execPath, version: process.version },
invocationCwd: process.cwd(),
tool: { path: fileURLToPath(import.meta.url), sha256: sha256File(fileURLToPath(import.meta.url)) },
project: {
root: PROJECT_ROOT,
gitHead: readGitHead(),
configHashes: configInfo,
},
packageVersions: { vitest: vitestPackage.version, vite: vitePackage.version, typescript: tsPackage.version },
fixtures: {
root: fixtureRoot,
ordinarySentinel: { path: SENTINEL, sha256: sha256File(path.join(fixtureRoot, SENTINEL)) },
ordinaryDotUnderscoreFragment: {
path: SENTINELS[1],
sha256: sha256File(path.join(fixtureRoot, SENTINELS[1])),
reasonIncluded: "the basename contains ._ but does not begin with the AppleDouble component prefix ._",
},
metadataFile: { path: METADATA_FILE, sha256: sha256File(path.join(fixtureRoot, METADATA_FILE)) },
metadataNestedFile: { path: METADATA_NESTED_FILE, sha256: sha256File(path.join(fixtureRoot, METADATA_NESTED_FILE)) },
},
vitest: {
unfilteredControl: {
description: "actual loaded test config with only the two issue-owned excludes removed; all other settings and Vitest built-in defaults are retained",
configSha256: sha256File(baselineConfig),
removedExcludes: METADATA_EXCLUDES,
command: baseline,
jsonReport: { path: baselineJson, sha256: sha256File(baselineJson) },
exitCode: baseline.exitCode,
failures: baselineFailures,
discoveredFiles: baselineFiles,
ordinarySentinelsExecuted: SENTINELS.every((name) => baselineFiles.includes(name)),
metadataFixturesDiscovered: [METADATA_FILE, METADATA_NESTED_FILE].every((name) => baselineFiles.includes(name)),
completedNormally: baseline.exitCode === 0 || baseline.exitCode === 1,
},
candidate: {
command: candidate,
jsonReport: { path: candidateJson, sha256: sha256File(candidateJson) },
exitCode: candidate.exitCode,
discoveredFiles: candidateFiles,
expectedOrdinaryFiles: [...SENTINELS].sort(),
include: testConfig.include,
builtinExcludesPreserved: configDefaults.exclude.every((name) => testConfig.exclude.includes(name)),
metadataExcludes: METADATA_EXCLUDES,
},
},
typescript: {
compilerApi: {
parsedConfigPath: tsconfigPath,
parsedConfigSha256: sha256(tsconfigBytes),
diagnostics: parsedTsconfig.errors.length,
},
baseline: {
configPath: baselineTsconfigPath,
removedExcludes: METADATA_EXCLUDES,
parsedFiles: baselineTsFiles,
metadataFileIncludedBeforeExplicitExcludes: baselineTsFiles.includes(METADATA_FILE),
nestedMetadataIncludedBeforeExplicitExcludes: baselineTsFiles.includes(METADATA_NESTED_FILE),
note: "TypeScript 5.8 already skips dot-prefixed paths during include globbing; the candidate also retains explicit excludes for portable input hygiene.",
cliCommand: baselineTsc,
cliJsonSha256: sha256File(path.join(absoluteEvidence, "tsc-control-show-config.stdout.json")),
},
cliCommand: tsc,
cliJsonSha256: sha256File(path.join(absoluteEvidence, "tsc-show-config.stdout.json")),
parsedFiles: tsFiles,
cliParsedFiles: tscFiles,
include: tsconfigJson.config.include,
types: tsconfigJson.config.compilerOptions.types,
packageExcludesPreserved: expectedPackageExcludes.every((name) => tsconfigJson.config.exclude.includes(name)),
metadataExcludes: METADATA_EXCLUDES,
copiedConfigSha256: sha256(tsconfigBytes),
},
logs: ["vitest-baseline.stdout.log", "vitest-baseline.stderr.log", "vitest-candidate.stdout.log",
"vitest-candidate.stderr.log", "tsc-control-show-config.stderr.log", "tsc-show-config.stderr.log"].map((name) => ({
path: path.join(absoluteEvidence, name),
sha256: sha256File(path.join(absoluteEvidence, name)),
})),
};
writeJson(reportPath, report);
process.stdout.write(`${reportPath}\n`);
}
main().catch((error) => {
const safe = { passed: false, error: `${error.name}: ${error.message}` };
if (evidenceRootForFailure) {
try { writeJson(path.join(evidenceRootForFailure, "failure.json"), safe); } catch { /* preserve original failure */ }
}
process.stderr.write(`${safe.error}\n`);
process.exitCode = 1;
});
+8 -1
View File
@@ -29,5 +29,12 @@
"jsx": "react-jsx", "jsx": "react-jsx",
"types": ["vite/client", "vitest/globals"] "types": ["vite/client", "vitest/globals"]
}, },
"include": ["src"] "include": ["src"],
"exclude": [
"node_modules",
"bower_components",
"jspm_packages",
"**/._*",
"**/._*/**"
]
} }
+6 -1
View File
@@ -1,6 +1,6 @@
import { readFileSync } from "node:fs"; import { readFileSync } from "node:fs";
import path from "node:path"; import path from "node:path";
import { defineConfig } from "vitest/config"; import { configDefaults, defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react"; import react from "@vitejs/plugin-react";
// @ts-expect-error Shared Node HTTP middleware also serves production bundles. // @ts-expect-error Shared Node HTTP middleware also serves production bundles.
import { createBundledContentHandler } from "./server/bundled-content.mjs"; import { createBundledContentHandler } from "./server/bundled-content.mjs";
@@ -119,6 +119,11 @@ export default defineConfig(({ mode }) => {
test: { test: {
environment: "node", environment: "node",
include: ["src/**/*.test.ts"], include: ["src/**/*.test.ts"],
exclude: [
...configDefaults.exclude,
"**/._*",
"**/._*/**",
],
maxWorkers: 4, maxWorkers: 4,
}, },
}; };