import type { FactIndex } from "../facts/index.js"; import { makeFinding } from "./finding.js"; import type { Finding } from "./types.js"; export const RULE_ID = "imports/preferred-path"; export const RULE_VERSION = "1"; export function ruleJsxPreferredImportPath(ix: FactIndex): Finding[] { const policies = ix.policy.jsxImportPathPreferred(); if (policies.length === 0) return []; const findings: Finding[] = []; const seen = new Set(); for (const usage of ix.byKind("usage_import")) { const matches = policies.filter((policy) => { if (policy.from !== usage.source) return false; if (policy.imported !== undefined && policy.imported !== usage.imported) return false; return true; }); for (const policy of matches) { const importedIdentity = policy.imported ?? "*"; const key = `${policy.id}\0${usage.file}\0${usage.source}\0${usage.location.line}\0${usage.location.column}\0${importedIdentity}`; if (seen.has(key)) continue; seen.add(key); findings.push( makeFinding({ ruleId: RULE_ID, ruleVersion: RULE_VERSION, severity: policy.severity, message: `Import from "${usage.source}" should use "${policy.to}".`, location: usage.location, evidence: ix.evidence([usage.id, policy.id]), fingerprintIdentity: { file: usage.file, from: policy.from, to: policy.to, imported: policy.imported, line: usage.location.line, column: usage.location.column, }, fix: { kind: "replaceImport", title: policy.because ?? `Replace import path with "${policy.to}"`, from: policy.from, to: policy.to, deterministic: true, }, attributes: { importPath: usage.source, suggestedImport: policy.to, imported: usage.imported, local: usage.local, rawValue: usage.source, }, }), ); } } return findings; }