mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
Convert native directory separators only at Git patch argument production. Exercise fresh, applied and invalid dependency patches in private copies, preserving protectNTFS, and select the regression for Windows CI. Replace the spelling-based mock inventory with one binding-aware Oxlint rule. Canonical test projects own isolation for both Vitest and lint; external temporary CLI probes avoid concurrent test discovery. Validated actual local Git and Oxlint execution, resolved-policy parity, CI selection and report tests. Native Windows verification remains with CI. Refs: #7050
117 lines
3.5 KiB
JavaScript
117 lines
3.5 KiB
JavaScript
import { relative, isAbsolute } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
import { findBinding } from "../bindings.mjs";
|
|
import { projectForTest } from "../../lib/test-projects.mjs";
|
|
|
|
const repository = fileURLToPath(new URL("../../../", import.meta.url));
|
|
const mockMethods = new Set(["mock", "doMock"]);
|
|
|
|
const unwrap = (node) =>
|
|
node?.type === "ChainExpression" ? node.expression : node;
|
|
|
|
const propertyName = (member) => {
|
|
if (!member?.computed && member?.property?.type === "Identifier")
|
|
return member.property.name;
|
|
if (member?.computed && member.property?.type === "Literal")
|
|
return member.property.value;
|
|
return undefined;
|
|
};
|
|
|
|
const importedFromVitest = (sourceCode, identifier) => {
|
|
const binding = findBinding(sourceCode, identifier);
|
|
const definition = binding?.defs[0];
|
|
if (definition?.type !== "ImportBinding") return undefined;
|
|
const declaration = definition.parent ?? definition.node.parent;
|
|
if (declaration?.source?.value !== "vitest") return undefined;
|
|
return definition.node;
|
|
};
|
|
|
|
const isViImport = (sourceCode, identifier) => {
|
|
const specifier = importedFromVitest(sourceCode, identifier);
|
|
return (
|
|
specifier?.type === "ImportSpecifier" &&
|
|
(specifier.imported.name ?? specifier.imported.value) === "vi"
|
|
);
|
|
};
|
|
|
|
const isVitestNamespace = (sourceCode, identifier) =>
|
|
importedFromVitest(sourceCode, identifier)?.type ===
|
|
"ImportNamespaceSpecifier";
|
|
|
|
const isVitestViMember = (sourceCode, node) => {
|
|
const member = unwrap(node);
|
|
if (
|
|
member?.type !== "MemberExpression" ||
|
|
propertyName(member) !== "vi" ||
|
|
member.object?.type !== "Identifier"
|
|
)
|
|
return false;
|
|
return isVitestNamespace(sourceCode, member.object);
|
|
};
|
|
|
|
const isModuleMockCall = (sourceCode, callee) => {
|
|
const method = unwrap(callee);
|
|
if (
|
|
method?.type !== "MemberExpression" ||
|
|
!mockMethods.has(propertyName(method))
|
|
)
|
|
return false;
|
|
const receiver = unwrap(method.object);
|
|
return (
|
|
(receiver?.type === "Identifier" && isViImport(sourceCode, receiver)) ||
|
|
isVitestViMember(sourceCode, receiver)
|
|
);
|
|
};
|
|
|
|
const isolationForFile = (filename, cwd) => {
|
|
if (typeof filename !== "string" || !filename.endsWith(".test.ts"))
|
|
return undefined;
|
|
const testPath = relative(cwd, filename).split("\\").join("/");
|
|
if (
|
|
testPath === "" ||
|
|
isAbsolute(testPath) ||
|
|
testPath === ".." ||
|
|
testPath.startsWith("../")
|
|
)
|
|
return undefined;
|
|
try {
|
|
return projectForTest(testPath).isolate !== false;
|
|
} catch (error) {
|
|
if (
|
|
error instanceof Error &&
|
|
error.message.startsWith("No test project owns ")
|
|
)
|
|
return undefined;
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
export default {
|
|
meta: {
|
|
type: "problem",
|
|
docs: {
|
|
description:
|
|
"Keep Vitest module mocks in projects with per-file module isolation.",
|
|
},
|
|
schema: [],
|
|
messages: {
|
|
shared:
|
|
"Vitest module mocks need an isolated test project; move the test to an isolated project or replace the module mock with a scoped spy or injected dependency.",
|
|
},
|
|
},
|
|
create(context) {
|
|
const filename = context.physicalFilename ?? context.filename;
|
|
const sourceCode = context.sourceCode;
|
|
const cwd =
|
|
context.cwd ?? context.getCwd?.() ?? sourceCode.getCwd?.() ?? repository;
|
|
if (isolationForFile(filename, cwd) !== false) return {};
|
|
return {
|
|
CallExpression(node) {
|
|
if (isModuleMockCall(sourceCode, node.callee))
|
|
context.report({ node, messageId: "shared" });
|
|
},
|
|
};
|
|
},
|
|
};
|