@drskillissue/ganko 0.1.19 → 0.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,13 +7,14 @@ import {
7
7
  canonicalPath,
8
8
  extensionsToGlobs,
9
9
  matchesExtension,
10
+ noopLogger,
10
11
  resolveTailwindValidatorSync,
11
12
  rules,
12
13
  rules2,
13
14
  rules3,
14
15
  runCrossFileRules,
15
16
  runPhases
16
- } from "./chunk-LGHIMW4P.js";
17
+ } from "./chunk-PX2XCAZW.js";
17
18
  import "./chunk-EGRHWZRV.js";
18
19
 
19
20
  // src/eslint-adapter.ts
@@ -243,7 +244,8 @@ function buildCrossContext(context) {
243
244
  return {
244
245
  solids: [solidGraph],
245
246
  css: cssGraph,
246
- layout: buildLayoutGraph([solidGraph], cssGraph)
247
+ layout: buildLayoutGraph([solidGraph], cssGraph),
248
+ logger: noopLogger
247
249
  };
248
250
  }
249
251
  var { eslintRules: eslintRules3 } = createBatchPluginAdapter(
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/eslint-adapter.ts","../src/solid/eslint-plugin.ts","../src/css/eslint-plugin.ts","../src/cross-file/eslint-plugin.ts","../src/eslint-plugin.ts"],"sourcesContent":["/**\n * Shared ESLint Adapter Utilities\n *\n * Common infrastructure for bridging ganko's rule engine into\n * ESLint's plugin format. Used by each plugin's eslint-plugin.ts.\n */\nimport type { TSESLint } from \"@typescript-eslint/utils\"\nimport type { Diagnostic, Fix, FixOperation, Suggestion } from \"./diagnostic\"\nimport type { BaseRule, Emit } from \"./graph\"\nimport type { SolidInput } from \"./solid/input\"\n\nexport type RuleModule = TSESLint.RuleModule<string>\nexport type RuleContext = TSESLint.RuleContext<string, readonly unknown[]>\n\n/**\n * Build a SolidInput from an ESLint rule context.\n */\nexport function buildSolidInputFromContext(context: RuleContext): SolidInput {\n const sourceCode = context.sourceCode\n return {\n file: context.filename,\n sourceCode,\n parserServices: sourceCode.parserServices ?? null,\n checker: null,\n }\n}\n\n/**\n * Passthrough message ID used by all rules.\n *\n * ganko resolves message templates at emit time, so the ESLint\n * adapter uses a single passthrough template that receives the\n * pre-resolved message via data.\n */\nexport const MSG_ID = \"_msg\" as const\nexport const MSG_TEMPLATE = { [MSG_ID]: \"{{msg}}\" }\n\n/**\n * Convert a ganko FixOperation to an ESLint RuleFix.\n */\nfunction applyFix(fixer: TSESLint.RuleFixer, op: FixOperation): TSESLint.RuleFix {\n return fixer.replaceTextRange([op.range[0], op.range[1]], op.text)\n}\n\n/**\n * Convert a ganko Fix to an ESLint fix function.\n */\nexport function toESLintFix(fix: Fix): (fixer: TSESLint.RuleFixer) => TSESLint.RuleFix | TSESLint.RuleFix[] {\n return (fixer) => {\n const first = fix[0]\n if (fix.length === 1 && first) return applyFix(fixer, first)\n const fixes: TSESLint.RuleFix[] = []\n for (let i = 0; i < fix.length; i++) {\n const op = fix[i]\n if (!op) continue\n fixes.push(applyFix(fixer, op))\n }\n return fixes\n }\n}\n\n/**\n * Convert ganko suggestions to ESLint suggestion descriptors.\n */\nexport function toESLintSuggestions(\n suggestions: readonly Suggestion[],\n): TSESLint.SuggestionReportDescriptor<string>[] {\n const result: TSESLint.SuggestionReportDescriptor<string>[] = []\n for (let i = 0; i < suggestions.length; i++) {\n const s = suggestions[i]\n if (!s) continue\n result.push({\n messageId: MSG_ID,\n data: { msg: s.message },\n fix: toESLintFix(s.fix),\n })\n }\n return result\n}\n\n/**\n * Create an ESLint RuleModule from a ganko rule.\n *\n * Works for any rule+graph pair where the graph is obtained from a\n * context-keyed getter (SolidRule+SolidGraph, CSSRule+CSSGraph).\n */\nexport function createRuleModule<G>(\n rule: BaseRule<G>,\n getGraph: (context: RuleContext) => G,\n): RuleModule {\n const meta: TSESLint.RuleMetaData<string> = {\n type: \"problem\",\n docs: {\n description: rule.meta.description,\n },\n messages: MSG_TEMPLATE,\n schema: [],\n }\n if (rule.meta.fixable) meta.fixable = \"code\" as const\n return {\n meta,\n defaultOptions: [],\n create(context) {\n return {\n Program() {\n const graph = getGraph(context)\n const diagnostics: Diagnostic[] = []\n const emit: Emit = (d) => diagnostics.push(d)\n\n rule.check(graph, emit)\n\n for (let i = 0; i < diagnostics.length; i++) {\n const diag = diagnostics[i]\n if (!diag) continue\n reportDiagnostic(context, diag)\n }\n },\n }\n },\n }\n}\n\n/**\n * Create a cached ESLint plugin adapter from rules and a graph builder.\n *\n * Handles the SourceCode-keyed WeakMap cache and the rules-to-modules\n * loop that is identical across Solid and CSS eslint-plugin files.\n */\nexport function createCachedPluginAdapter<G>(\n rules: readonly BaseRule<G>[],\n buildGraph: (context: RuleContext) => G,\n): { eslintRules: Record<string, RuleModule> } {\n const cache = new WeakMap<TSESLint.SourceCode, G>()\n\n function getGraph(context: RuleContext): G {\n const sourceCode = context.sourceCode\n const cached = cache.get(sourceCode)\n if (cached) return cached\n const graph = buildGraph(context)\n cache.set(sourceCode, graph)\n return graph\n }\n\n const eslintRules: Record<string, RuleModule> = {}\n for (let i = 0; i < rules.length; i++) {\n const r = rules[i]\n if (!r) continue\n eslintRules[r.id] = createRuleModule(r, getGraph)\n }\n\n return { eslintRules }\n}\n\n/**\n * Create an ESLint plugin adapter for rules that run in batch (all rules share\n * one analysis pass). The runAll function receives the context built from a\n * single ESLint RuleContext and an emit callback, runs analysis once, and\n * emits diagnostics keyed by rule ID.\n *\n * Used by cross-file rules where graph construction is expensive and shared.\n */\nexport function createBatchPluginAdapter<G>(\n rules: readonly BaseRule<G>[],\n buildContext: (context: RuleContext) => G,\n runAll: (graph: G, emit: Emit) => void,\n): { eslintRules: Record<string, RuleModule> } {\n const cache = new WeakMap<TSESLint.SourceCode, ReadonlyMap<string, readonly Diagnostic[]>>()\n\n function getResults(context: RuleContext): ReadonlyMap<string, readonly Diagnostic[]> {\n const sourceCode = context.sourceCode\n const cached = cache.get(sourceCode)\n if (cached) return cached\n\n const graph = buildContext(context)\n const byRule = new Map<string, Diagnostic[]>()\n const emit: Emit = (d) => {\n const list = byRule.get(d.rule)\n if (list) { list.push(d) }\n else { byRule.set(d.rule, [d]) }\n }\n runAll(graph, emit)\n cache.set(sourceCode, byRule)\n return byRule\n }\n\n function createBatchRuleModule(\n rule: BaseRule<G>,\n getResults: (context: RuleContext) => ReadonlyMap<string, readonly Diagnostic[]>,\n ): RuleModule {\n const meta: TSESLint.RuleMetaData<string> = {\n type: \"problem\",\n docs: { description: rule.meta.description },\n messages: MSG_TEMPLATE,\n schema: [],\n }\n if (rule.meta.fixable) meta.fixable = \"code\" as const\n return {\n meta,\n defaultOptions: [],\n create(context) {\n return {\n Program() {\n const results = getResults(context)\n const diagnostics = results.get(rule.id) ?? []\n for (let j = 0; j < diagnostics.length; j++) {\n const diag = diagnostics[j]\n if (!diag) continue\n reportDiagnostic(context, diag)\n }\n },\n }\n },\n }\n }\n\n const eslintRules: Record<string, RuleModule> = {}\n for (let i = 0; i < rules.length; i++) {\n const rule = rules[i]\n if (!rule) continue\n eslintRules[rule.id] = createBatchRuleModule(rule, getResults)\n }\n return { eslintRules }\n}\n\n/**\n * Report a ganko Diagnostic through ESLint's context.report().\n */\nexport function reportDiagnostic(context: RuleContext, d: Diagnostic): void {\n const data = { msg: d.message }\n\n if (d.fix) {\n if (d.suggest && d.suggest.length > 0) {\n context.report({\n messageId: MSG_ID,\n data,\n loc: d.loc,\n fix: toESLintFix(d.fix),\n suggest: toESLintSuggestions(d.suggest),\n })\n } else {\n context.report({\n messageId: MSG_ID,\n data,\n loc: d.loc,\n fix: toESLintFix(d.fix),\n })\n }\n } else if (d.suggest && d.suggest.length > 0) {\n context.report({\n messageId: MSG_ID,\n data,\n loc: d.loc,\n suggest: toESLintSuggestions(d.suggest),\n })\n } else {\n context.report({\n messageId: MSG_ID,\n data,\n loc: d.loc,\n })\n }\n}\n","/**\n * Solid ESLint Plugin Adapter\n *\n * Bridges ganko's Solid rules into ESLint's plugin format.\n * The SolidGraph is built once per file and cached via WeakMap on\n * SourceCode (unique per file per lint run).\n */\nimport { SolidGraph } from \"./impl\"\nimport { runPhases } from \"./phases\"\nimport { rules } from \"./rules\"\nimport { createCachedPluginAdapter, buildSolidInputFromContext } from \"../eslint-adapter\"\n\n/** All Solid rules as ESLint RuleModules, keyed by rule ID. */\nexport const { eslintRules } = createCachedPluginAdapter(rules, (context) => {\n const input = buildSolidInputFromContext(context)\n const graph = new SolidGraph(input)\n runPhases(graph, input)\n return graph\n})\n\n/** Solid rules array for config generation. */\nexport { rules }\n","/**\n * CSS ESLint Plugin Adapter\n *\n * Bridges ganko's CSS rules into ESLint's plugin format.\n * The CSSGraph is built once per file and cached via WeakMap on SourceCode.\n */\nimport type { CSSInput } from \"./input\"\nimport { buildCSSGraph } from \"./plugin\"\nimport { rules } from \"./rules\"\nimport { createCachedPluginAdapter } from \"../eslint-adapter\"\n\n/** All CSS rules as ESLint RuleModules, keyed by rule ID. */\nexport const { eslintRules } = createCachedPluginAdapter(rules, (context) => {\n const input: CSSInput = {\n files: [{ path: context.filename, content: context.sourceCode.getText() }],\n }\n return buildCSSGraph(input)\n})\n\n/** CSS rules array for config generation. */\nexport { rules }\n","/**\n * Cross-File ESLint Plugin Adapter\n *\n * Bridges ganko's cross-file rules into ESLint's plugin format.\n *\n * Cross-file rules require both SolidGraph and CSSGraph. In ESLint's\n * per-file model, these rules run on Solid files (.tsx/.jsx/.ts) and\n * resolve CSS files from static import declarations.\n *\n * Uses createBatchPluginAdapter: all cross-file rules share one analysis\n * pass per SourceCode instance, avoiding redundant graph construction.\n */\nimport { CSS_EXTENSIONS, canonicalPath, matchesExtension } from \"@drskillissue/ganko-shared\"\nimport { createBatchPluginAdapter, buildSolidInputFromContext } from \"../eslint-adapter\"\nimport type { RuleContext } from \"../eslint-adapter\"\nimport type { CrossRuleContext } from \"./rule\"\nimport { SolidGraph } from \"../solid/impl\"\nimport { runPhases as runSolidPhases } from \"../solid/phases\"\nimport type { CSSInput } from \"../css/input\"\nimport { buildCSSGraph } from \"../css/plugin\"\nimport { buildLayoutGraph } from \"./layout\"\nimport { runCrossFileRules } from \"./plugin\"\nimport { resolveTailwindValidatorSync } from \"../css/tailwind\"\nimport { rules } from \"./rules\"\nimport { existsSync, readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\n\nfunction findImportedCSS(graph: SolidGraph): readonly { path: string; content: string }[] {\n const out: { path: string; content: string }[] = []\n const seen = new Set<string>()\n const baseDir = dirname(graph.file)\n\n for (let i = 0; i < graph.imports.length; i++) {\n const imp = graph.imports[i]\n if (!imp) continue\n const source = imp.source\n if (!matchesExtension(source, CSS_EXTENSIONS)) continue\n const filePath = canonicalPath(resolve(baseDir, source))\n if (!existsSync(filePath)) continue\n if (seen.has(filePath)) continue\n seen.add(filePath)\n out.push({ path: filePath, content: readFileSync(filePath, \"utf-8\") })\n }\n\n return out\n}\n\nfunction buildCrossContext(context: RuleContext): CrossRuleContext {\n const input = buildSolidInputFromContext(context)\n const solidGraph = new SolidGraph(input)\n runSolidPhases(solidGraph, input)\n\n const cssFiles = findImportedCSS(solidGraph)\n const tailwind = cssFiles.length > 0 ? resolveTailwindValidatorSync(cssFiles) : null\n const resolved = tailwind ?? undefined\n const cssInput: { -readonly [K in keyof CSSInput]: CSSInput[K] } = { files: cssFiles }\n if (resolved !== undefined) cssInput.tailwind = resolved\n const cssGraph = buildCSSGraph(cssInput)\n return {\n solids: [solidGraph],\n css: cssGraph,\n layout: buildLayoutGraph([solidGraph], cssGraph),\n }\n}\n\nexport const { eslintRules } = createBatchPluginAdapter(\n rules,\n buildCrossContext,\n runCrossFileRules,\n)\n\nexport { rules }\n","/**\n * ESLint Plugin\n *\n * Aggregates all ganko rule engines (Solid, CSS, cross-file) into\n * a single ESLint plugin. Each plugin directory owns its own ESLint\n * adapter; this module merges their rules and builds configs.\n *\n * @example\n * ```js\n * // eslint.config.mjs\n * import solid from \"@drskillissue/ganko/eslint-plugin\"\n *\n * export default [\n * ...solid.configs.recommended,\n * ]\n * ```\n */\nimport type { TSESLint } from \"@typescript-eslint/utils\"\nimport type { RuleModule } from \"./eslint-adapter\"\nimport { eslintRules as solidRules, rules as solidRuleList } from \"./solid/eslint-plugin\"\nimport { eslintRules as cssRules, rules as cssRuleList } from \"./css/eslint-plugin\"\nimport { eslintRules as crossFileRules, rules as crossFileRuleList } from \"./cross-file/eslint-plugin\"\nimport { SOLID_EXTENSIONS, CSS_EXTENSIONS, extensionsToGlobs } from \"@drskillissue/ganko-shared\"\n\n/** Merge all rule modules into a single record. */\nconst allRules: Record<string, RuleModule> = {\n ...solidRules,\n ...cssRules,\n ...crossFileRules,\n}\n\ninterface SolidLintPlugin {\n readonly meta: { readonly name: string; readonly version: string }\n readonly rules: Record<string, RuleModule>\n readonly configs: Record<string, TSESLint.FlatConfig.ConfigArray>\n}\n\nconst configs: Record<string, TSESLint.FlatConfig.ConfigArray> = {}\n\nconst plugin: SolidLintPlugin = {\n meta: {\n name: \"eslint-plugin-ganko\",\n version: \"0.1.0\",\n },\n rules: allRules,\n configs,\n}\n\nfunction buildRuleConfig(\n ruleList: readonly { readonly id: string; readonly severity: string }[],\n): Partial<Record<string, TSESLint.SharedConfig.RuleEntry>> {\n const out: Partial<Record<string, TSESLint.SharedConfig.RuleEntry>> = {}\n for (let i = 0; i < ruleList.length; i++) {\n const r = ruleList[i]\n if (!r) continue\n out[`solid/${r.id}`] = r.severity === \"off\" ? \"off\" : r.severity === \"warn\" ? \"warn\" : \"error\"\n }\n return out\n}\n\nconst solidOnlyRules = buildRuleConfig(solidRuleList)\nconst cssOnlyRules = buildRuleConfig(cssRuleList)\nconst crossFileOnlyRules = buildRuleConfig(crossFileRuleList)\n\nconst tsFiles = extensionsToGlobs(SOLID_EXTENSIONS)\nconst cssFiles = extensionsToGlobs(CSS_EXTENSIONS)\n\nplugin.configs[\"recommended\"] = [\n {\n plugins: { solid: plugin },\n files: tsFiles,\n rules: solidOnlyRules,\n },\n {\n plugins: { solid: plugin },\n files: cssFiles,\n rules: cssOnlyRules,\n },\n {\n plugins: { solid: plugin },\n files: [...tsFiles, ...cssFiles],\n rules: crossFileOnlyRules,\n },\n]\n\nexport default plugin\n"],"mappings":"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","names":["rules","eslintRules","getResults","eslintRules","rules","cssFiles","eslintRules","rules","eslintRules","rules"]}
1
+ {"version":3,"sources":["../src/eslint-adapter.ts","../src/solid/eslint-plugin.ts","../src/css/eslint-plugin.ts","../src/cross-file/eslint-plugin.ts","../src/eslint-plugin.ts"],"sourcesContent":["/**\n * Shared ESLint Adapter Utilities\n *\n * Common infrastructure for bridging ganko's rule engine into\n * ESLint's plugin format. Used by each plugin's eslint-plugin.ts.\n */\nimport type { TSESLint } from \"@typescript-eslint/utils\"\nimport type { Diagnostic, Fix, FixOperation, Suggestion } from \"./diagnostic\"\nimport type { BaseRule, Emit } from \"./graph\"\nimport type { SolidInput } from \"./solid/input\"\n\nexport type RuleModule = TSESLint.RuleModule<string>\nexport type RuleContext = TSESLint.RuleContext<string, readonly unknown[]>\n\n/**\n * Build a SolidInput from an ESLint rule context.\n */\nexport function buildSolidInputFromContext(context: RuleContext): SolidInput {\n const sourceCode = context.sourceCode\n return {\n file: context.filename,\n sourceCode,\n parserServices: sourceCode.parserServices ?? null,\n checker: null,\n }\n}\n\n/**\n * Passthrough message ID used by all rules.\n *\n * ganko resolves message templates at emit time, so the ESLint\n * adapter uses a single passthrough template that receives the\n * pre-resolved message via data.\n */\nexport const MSG_ID = \"_msg\" as const\nexport const MSG_TEMPLATE = { [MSG_ID]: \"{{msg}}\" }\n\n/**\n * Convert a ganko FixOperation to an ESLint RuleFix.\n */\nfunction applyFix(fixer: TSESLint.RuleFixer, op: FixOperation): TSESLint.RuleFix {\n return fixer.replaceTextRange([op.range[0], op.range[1]], op.text)\n}\n\n/**\n * Convert a ganko Fix to an ESLint fix function.\n */\nexport function toESLintFix(fix: Fix): (fixer: TSESLint.RuleFixer) => TSESLint.RuleFix | TSESLint.RuleFix[] {\n return (fixer) => {\n const first = fix[0]\n if (fix.length === 1 && first) return applyFix(fixer, first)\n const fixes: TSESLint.RuleFix[] = []\n for (let i = 0; i < fix.length; i++) {\n const op = fix[i]\n if (!op) continue\n fixes.push(applyFix(fixer, op))\n }\n return fixes\n }\n}\n\n/**\n * Convert ganko suggestions to ESLint suggestion descriptors.\n */\nexport function toESLintSuggestions(\n suggestions: readonly Suggestion[],\n): TSESLint.SuggestionReportDescriptor<string>[] {\n const result: TSESLint.SuggestionReportDescriptor<string>[] = []\n for (let i = 0; i < suggestions.length; i++) {\n const s = suggestions[i]\n if (!s) continue\n result.push({\n messageId: MSG_ID,\n data: { msg: s.message },\n fix: toESLintFix(s.fix),\n })\n }\n return result\n}\n\n/**\n * Create an ESLint RuleModule from a ganko rule.\n *\n * Works for any rule+graph pair where the graph is obtained from a\n * context-keyed getter (SolidRule+SolidGraph, CSSRule+CSSGraph).\n */\nexport function createRuleModule<G>(\n rule: BaseRule<G>,\n getGraph: (context: RuleContext) => G,\n): RuleModule {\n const meta: TSESLint.RuleMetaData<string> = {\n type: \"problem\",\n docs: {\n description: rule.meta.description,\n },\n messages: MSG_TEMPLATE,\n schema: [],\n }\n if (rule.meta.fixable) meta.fixable = \"code\" as const\n return {\n meta,\n defaultOptions: [],\n create(context) {\n return {\n Program() {\n const graph = getGraph(context)\n const diagnostics: Diagnostic[] = []\n const emit: Emit = (d) => diagnostics.push(d)\n\n rule.check(graph, emit)\n\n for (let i = 0; i < diagnostics.length; i++) {\n const diag = diagnostics[i]\n if (!diag) continue\n reportDiagnostic(context, diag)\n }\n },\n }\n },\n }\n}\n\n/**\n * Create a cached ESLint plugin adapter from rules and a graph builder.\n *\n * Handles the SourceCode-keyed WeakMap cache and the rules-to-modules\n * loop that is identical across Solid and CSS eslint-plugin files.\n */\nexport function createCachedPluginAdapter<G>(\n rules: readonly BaseRule<G>[],\n buildGraph: (context: RuleContext) => G,\n): { eslintRules: Record<string, RuleModule> } {\n const cache = new WeakMap<TSESLint.SourceCode, G>()\n\n function getGraph(context: RuleContext): G {\n const sourceCode = context.sourceCode\n const cached = cache.get(sourceCode)\n if (cached) return cached\n const graph = buildGraph(context)\n cache.set(sourceCode, graph)\n return graph\n }\n\n const eslintRules: Record<string, RuleModule> = {}\n for (let i = 0; i < rules.length; i++) {\n const r = rules[i]\n if (!r) continue\n eslintRules[r.id] = createRuleModule(r, getGraph)\n }\n\n return { eslintRules }\n}\n\n/**\n * Create an ESLint plugin adapter for rules that run in batch (all rules share\n * one analysis pass). The runAll function receives the context built from a\n * single ESLint RuleContext and an emit callback, runs analysis once, and\n * emits diagnostics keyed by rule ID.\n *\n * Used by cross-file rules where graph construction is expensive and shared.\n */\nexport function createBatchPluginAdapter<G>(\n rules: readonly BaseRule<G>[],\n buildContext: (context: RuleContext) => G,\n runAll: (graph: G, emit: Emit) => void,\n): { eslintRules: Record<string, RuleModule> } {\n const cache = new WeakMap<TSESLint.SourceCode, ReadonlyMap<string, readonly Diagnostic[]>>()\n\n function getResults(context: RuleContext): ReadonlyMap<string, readonly Diagnostic[]> {\n const sourceCode = context.sourceCode\n const cached = cache.get(sourceCode)\n if (cached) return cached\n\n const graph = buildContext(context)\n const byRule = new Map<string, Diagnostic[]>()\n const emit: Emit = (d) => {\n const list = byRule.get(d.rule)\n if (list) { list.push(d) }\n else { byRule.set(d.rule, [d]) }\n }\n runAll(graph, emit)\n cache.set(sourceCode, byRule)\n return byRule\n }\n\n function createBatchRuleModule(\n rule: BaseRule<G>,\n getResults: (context: RuleContext) => ReadonlyMap<string, readonly Diagnostic[]>,\n ): RuleModule {\n const meta: TSESLint.RuleMetaData<string> = {\n type: \"problem\",\n docs: { description: rule.meta.description },\n messages: MSG_TEMPLATE,\n schema: [],\n }\n if (rule.meta.fixable) meta.fixable = \"code\" as const\n return {\n meta,\n defaultOptions: [],\n create(context) {\n return {\n Program() {\n const results = getResults(context)\n const diagnostics = results.get(rule.id) ?? []\n for (let j = 0; j < diagnostics.length; j++) {\n const diag = diagnostics[j]\n if (!diag) continue\n reportDiagnostic(context, diag)\n }\n },\n }\n },\n }\n }\n\n const eslintRules: Record<string, RuleModule> = {}\n for (let i = 0; i < rules.length; i++) {\n const rule = rules[i]\n if (!rule) continue\n eslintRules[rule.id] = createBatchRuleModule(rule, getResults)\n }\n return { eslintRules }\n}\n\n/**\n * Report a ganko Diagnostic through ESLint's context.report().\n */\nexport function reportDiagnostic(context: RuleContext, d: Diagnostic): void {\n const data = { msg: d.message }\n\n if (d.fix) {\n if (d.suggest && d.suggest.length > 0) {\n context.report({\n messageId: MSG_ID,\n data,\n loc: d.loc,\n fix: toESLintFix(d.fix),\n suggest: toESLintSuggestions(d.suggest),\n })\n } else {\n context.report({\n messageId: MSG_ID,\n data,\n loc: d.loc,\n fix: toESLintFix(d.fix),\n })\n }\n } else if (d.suggest && d.suggest.length > 0) {\n context.report({\n messageId: MSG_ID,\n data,\n loc: d.loc,\n suggest: toESLintSuggestions(d.suggest),\n })\n } else {\n context.report({\n messageId: MSG_ID,\n data,\n loc: d.loc,\n })\n }\n}\n","/**\n * Solid ESLint Plugin Adapter\n *\n * Bridges ganko's Solid rules into ESLint's plugin format.\n * The SolidGraph is built once per file and cached via WeakMap on\n * SourceCode (unique per file per lint run).\n */\nimport { SolidGraph } from \"./impl\"\nimport { runPhases } from \"./phases\"\nimport { rules } from \"./rules\"\nimport { createCachedPluginAdapter, buildSolidInputFromContext } from \"../eslint-adapter\"\n\n/** All Solid rules as ESLint RuleModules, keyed by rule ID. */\nexport const { eslintRules } = createCachedPluginAdapter(rules, (context) => {\n const input = buildSolidInputFromContext(context)\n const graph = new SolidGraph(input)\n runPhases(graph, input)\n return graph\n})\n\n/** Solid rules array for config generation. */\nexport { rules }\n","/**\n * CSS ESLint Plugin Adapter\n *\n * Bridges ganko's CSS rules into ESLint's plugin format.\n * The CSSGraph is built once per file and cached via WeakMap on SourceCode.\n */\nimport type { CSSInput } from \"./input\"\nimport { buildCSSGraph } from \"./plugin\"\nimport { rules } from \"./rules\"\nimport { createCachedPluginAdapter } from \"../eslint-adapter\"\n\n/** All CSS rules as ESLint RuleModules, keyed by rule ID. */\nexport const { eslintRules } = createCachedPluginAdapter(rules, (context) => {\n const input: CSSInput = {\n files: [{ path: context.filename, content: context.sourceCode.getText() }],\n }\n return buildCSSGraph(input)\n})\n\n/** CSS rules array for config generation. */\nexport { rules }\n","/**\n * Cross-File ESLint Plugin Adapter\n *\n * Bridges ganko's cross-file rules into ESLint's plugin format.\n *\n * Cross-file rules require both SolidGraph and CSSGraph. In ESLint's\n * per-file model, these rules run on Solid files (.tsx/.jsx/.ts) and\n * resolve CSS files from static import declarations.\n *\n * Uses createBatchPluginAdapter: all cross-file rules share one analysis\n * pass per SourceCode instance, avoiding redundant graph construction.\n */\nimport { CSS_EXTENSIONS, canonicalPath, matchesExtension, noopLogger } from \"@drskillissue/ganko-shared\"\nimport { createBatchPluginAdapter, buildSolidInputFromContext } from \"../eslint-adapter\"\nimport type { RuleContext } from \"../eslint-adapter\"\nimport type { CrossRuleContext } from \"./rule\"\nimport { SolidGraph } from \"../solid/impl\"\nimport { runPhases as runSolidPhases } from \"../solid/phases\"\nimport type { CSSInput } from \"../css/input\"\nimport { buildCSSGraph } from \"../css/plugin\"\nimport { buildLayoutGraph } from \"./layout\"\nimport { runCrossFileRules } from \"./plugin\"\nimport { resolveTailwindValidatorSync } from \"../css/tailwind\"\nimport { rules } from \"./rules\"\nimport { existsSync, readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\n\nfunction findImportedCSS(graph: SolidGraph): readonly { path: string; content: string }[] {\n const out: { path: string; content: string }[] = []\n const seen = new Set<string>()\n const baseDir = dirname(graph.file)\n\n for (let i = 0; i < graph.imports.length; i++) {\n const imp = graph.imports[i]\n if (!imp) continue\n const source = imp.source\n if (!matchesExtension(source, CSS_EXTENSIONS)) continue\n const filePath = canonicalPath(resolve(baseDir, source))\n if (!existsSync(filePath)) continue\n if (seen.has(filePath)) continue\n seen.add(filePath)\n out.push({ path: filePath, content: readFileSync(filePath, \"utf-8\") })\n }\n\n return out\n}\n\nfunction buildCrossContext(context: RuleContext): CrossRuleContext {\n const input = buildSolidInputFromContext(context)\n const solidGraph = new SolidGraph(input)\n runSolidPhases(solidGraph, input)\n\n const cssFiles = findImportedCSS(solidGraph)\n const tailwind = cssFiles.length > 0 ? resolveTailwindValidatorSync(cssFiles) : null\n const resolved = tailwind ?? undefined\n const cssInput: { -readonly [K in keyof CSSInput]: CSSInput[K] } = { files: cssFiles }\n if (resolved !== undefined) cssInput.tailwind = resolved\n const cssGraph = buildCSSGraph(cssInput)\n return {\n solids: [solidGraph],\n css: cssGraph,\n layout: buildLayoutGraph([solidGraph], cssGraph),\n logger: noopLogger,\n }\n}\n\nexport const { eslintRules } = createBatchPluginAdapter(\n rules,\n buildCrossContext,\n runCrossFileRules,\n)\n\nexport { rules }\n","/**\n * ESLint Plugin\n *\n * Aggregates all ganko rule engines (Solid, CSS, cross-file) into\n * a single ESLint plugin. Each plugin directory owns its own ESLint\n * adapter; this module merges their rules and builds configs.\n *\n * @example\n * ```js\n * // eslint.config.mjs\n * import solid from \"@drskillissue/ganko/eslint-plugin\"\n *\n * export default [\n * ...solid.configs.recommended,\n * ]\n * ```\n */\nimport type { TSESLint } from \"@typescript-eslint/utils\"\nimport type { RuleModule } from \"./eslint-adapter\"\nimport { eslintRules as solidRules, rules as solidRuleList } from \"./solid/eslint-plugin\"\nimport { eslintRules as cssRules, rules as cssRuleList } from \"./css/eslint-plugin\"\nimport { eslintRules as crossFileRules, rules as crossFileRuleList } from \"./cross-file/eslint-plugin\"\nimport { SOLID_EXTENSIONS, CSS_EXTENSIONS, extensionsToGlobs } from \"@drskillissue/ganko-shared\"\n\n/** Merge all rule modules into a single record. */\nconst allRules: Record<string, RuleModule> = {\n ...solidRules,\n ...cssRules,\n ...crossFileRules,\n}\n\ninterface SolidLintPlugin {\n readonly meta: { readonly name: string; readonly version: string }\n readonly rules: Record<string, RuleModule>\n readonly configs: Record<string, TSESLint.FlatConfig.ConfigArray>\n}\n\nconst configs: Record<string, TSESLint.FlatConfig.ConfigArray> = {}\n\nconst plugin: SolidLintPlugin = {\n meta: {\n name: \"eslint-plugin-ganko\",\n version: \"0.1.0\",\n },\n rules: allRules,\n configs,\n}\n\nfunction buildRuleConfig(\n ruleList: readonly { readonly id: string; readonly severity: string }[],\n): Partial<Record<string, TSESLint.SharedConfig.RuleEntry>> {\n const out: Partial<Record<string, TSESLint.SharedConfig.RuleEntry>> = {}\n for (let i = 0; i < ruleList.length; i++) {\n const r = ruleList[i]\n if (!r) continue\n out[`solid/${r.id}`] = r.severity === \"off\" ? \"off\" : r.severity === \"warn\" ? \"warn\" : \"error\"\n }\n return out\n}\n\nconst solidOnlyRules = buildRuleConfig(solidRuleList)\nconst cssOnlyRules = buildRuleConfig(cssRuleList)\nconst crossFileOnlyRules = buildRuleConfig(crossFileRuleList)\n\nconst tsFiles = extensionsToGlobs(SOLID_EXTENSIONS)\nconst cssFiles = extensionsToGlobs(CSS_EXTENSIONS)\n\nplugin.configs[\"recommended\"] = [\n {\n plugins: { solid: plugin },\n files: tsFiles,\n rules: solidOnlyRules,\n },\n {\n plugins: { solid: plugin },\n files: cssFiles,\n rules: cssOnlyRules,\n },\n {\n plugins: { solid: plugin },\n files: [...tsFiles, ...cssFiles],\n rules: crossFileOnlyRules,\n },\n]\n\nexport default plugin\n"],"mappings":"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","names":["rules","eslintRules","getResults","eslintRules","rules","cssFiles","eslintRules","rules","eslintRules","rules"]}
package/dist/index.cjs CHANGED
@@ -30800,6 +30800,7 @@ function readStatefulBaseValueIndex(graph) {
30800
30800
  }
30801
30801
 
30802
30802
  // src/cross-file/layout/context-classification.ts
30803
+ var WHITESPACE_RE3 = /\s+/;
30803
30804
  var TABLE_SEMANTIC_TAGS = /* @__PURE__ */ new Set(["table", "thead", "tbody", "tfoot", "tr", "td", "th"]);
30804
30805
  var TABLE_DISPLAY_VALUES = /* @__PURE__ */ new Set([
30805
30806
  "table",
@@ -30838,6 +30839,7 @@ function createAlignmentContextForParent(parent, snapshot) {
30838
30839
  const classified = classifyKind(evidence);
30839
30840
  const contextCertainty = combineCertainty(classified.certainty, axis.certainty);
30840
30841
  const certainty = combineCertainty(contextCertainty, inlineDirection.certainty);
30842
+ const baselineRelevance = computeBaselineRelevance(classified.kind, parentAlignItems, parentPlaceItems);
30841
30843
  const out = {
30842
30844
  kind: classified.kind,
30843
30845
  certainty,
@@ -30853,6 +30855,7 @@ function createAlignmentContextForParent(parent, snapshot) {
30853
30855
  parentAlignItems,
30854
30856
  parentPlaceItems,
30855
30857
  hasPositionedOffset: positionedOffset.hasPositionedOffset,
30858
+ baselineRelevance,
30856
30859
  evidence
30857
30860
  };
30858
30861
  return out;
@@ -31073,6 +31076,63 @@ function combineCertainty(left, right) {
31073
31076
  if (left === "conditional" || right === "conditional") return "conditional";
31074
31077
  return "resolved";
31075
31078
  }
31079
+ var FLEX_GRID_GEOMETRIC_ALIGN_ITEMS = /* @__PURE__ */ new Set([
31080
+ "center",
31081
+ "flex-start",
31082
+ "flex-end",
31083
+ "start",
31084
+ "end",
31085
+ "stretch",
31086
+ "self-start",
31087
+ "self-end",
31088
+ "normal"
31089
+ ]);
31090
+ function computeBaselineRelevance(kind, parentAlignItems, parentPlaceItems) {
31091
+ if (kind === "flex-cross-axis" || kind === "grid-cross-axis") {
31092
+ const effective = resolveEffectiveAlignItems(parentAlignItems, parentPlaceItems);
31093
+ if (effective === null) return "relevant";
31094
+ return FLEX_GRID_GEOMETRIC_ALIGN_ITEMS.has(effective) ? "irrelevant" : "relevant";
31095
+ }
31096
+ return "relevant";
31097
+ }
31098
+ function resolveEffectiveAlignItems(alignItems, placeItems) {
31099
+ if (alignItems !== null) return alignItems;
31100
+ if (placeItems === null) return null;
31101
+ const firstToken2 = placeItems.split(WHITESPACE_RE3)[0];
31102
+ return firstToken2 ?? null;
31103
+ }
31104
+ var TABLE_CELL_GEOMETRIC_VERTICAL_ALIGN = /* @__PURE__ */ new Set([
31105
+ "middle",
31106
+ "top",
31107
+ "bottom"
31108
+ ]);
31109
+ function finalizeTableCellBaselineRelevance(contextByParentNode, cohortVerticalAlignConsensus) {
31110
+ for (const [parent, consensusValue] of cohortVerticalAlignConsensus) {
31111
+ const context = contextByParentNode.get(parent);
31112
+ if (!context) continue;
31113
+ if (context.kind !== "table-cell") continue;
31114
+ if (consensusValue === null) continue;
31115
+ if (!TABLE_CELL_GEOMETRIC_VERTICAL_ALIGN.has(consensusValue)) continue;
31116
+ contextByParentNode.set(parent, {
31117
+ kind: context.kind,
31118
+ certainty: context.certainty,
31119
+ parentSolidFile: context.parentSolidFile,
31120
+ parentElementId: context.parentElementId,
31121
+ parentElementKey: context.parentElementKey,
31122
+ parentTag: context.parentTag,
31123
+ axis: context.axis,
31124
+ axisCertainty: context.axisCertainty,
31125
+ inlineDirection: context.inlineDirection,
31126
+ inlineDirectionCertainty: context.inlineDirectionCertainty,
31127
+ parentDisplay: context.parentDisplay,
31128
+ parentAlignItems: context.parentAlignItems,
31129
+ parentPlaceItems: context.parentPlaceItems,
31130
+ hasPositionedOffset: context.hasPositionedOffset,
31131
+ baselineRelevance: "irrelevant",
31132
+ evidence: context.evidence
31133
+ });
31134
+ }
31135
+ }
31076
31136
 
31077
31137
  // src/cross-file/layout/diagnostics.ts
31078
31138
  var FINDING_WEIGHT_BY_KIND = /* @__PURE__ */ new Map([
@@ -33719,7 +33779,7 @@ var UNCONDITIONAL_GUARD = {
33719
33779
  conditions: [],
33720
33780
  key: "always"
33721
33781
  };
33722
- var WHITESPACE_RE3 = /\s+/g;
33782
+ var WHITESPACE_RE4 = /\s+/g;
33723
33783
  function resolveRuleGuard(rule) {
33724
33784
  const conditions = collectRuleConditions(rule);
33725
33785
  if (conditions.length === 0) return UNCONDITIONAL_GUARD;
@@ -33778,7 +33838,7 @@ function buildCondition(kind, query) {
33778
33838
  }
33779
33839
  function normalizeQuery(query) {
33780
33840
  if (query === null) return null;
33781
- const normalized = query.trim().toLowerCase().replace(WHITESPACE_RE3, " ");
33841
+ const normalized = query.trim().toLowerCase().replace(WHITESPACE_RE4, " ");
33782
33842
  if (normalized.length === 0) return null;
33783
33843
  return normalized;
33784
33844
  }
@@ -34400,12 +34460,7 @@ function resolveInlineReplacedKindDivergence(subjectFingerprint, allFingerprints
34400
34460
  return alignmentStrengthCalibration.compositionMixedOutlierAmongReplacedStrength;
34401
34461
  }
34402
34462
  function hasSharedBaselineAlignment(context) {
34403
- if (context.kind === "inline-formatting" || context.kind === "table-cell") return true;
34404
- if (context.kind === "flex-cross-axis" || context.kind === "grid-cross-axis") {
34405
- const alignItems = context.parentAlignItems;
34406
- return alignItems === null || alignItems === "baseline";
34407
- }
34408
- return false;
34463
+ return context.baselineRelevance === "relevant";
34409
34464
  }
34410
34465
  function resolveMajorityClassification(allFingerprints) {
34411
34466
  const countByClassification = /* @__PURE__ */ new Map();
@@ -34559,6 +34614,7 @@ function estimateBlockOffsetWithDeclaredFromSources(axis, position, readNumeric)
34559
34614
  // src/cross-file/layout/cohort-index.ts
34560
34615
  function buildCohortIndex(input) {
34561
34616
  const statsByParentNode = /* @__PURE__ */ new Map();
34617
+ const verticalAlignConsensusByParent = /* @__PURE__ */ new Map();
34562
34618
  const profileBuffers = createCohortProfileBuffers();
34563
34619
  let conditionalSignals = 0;
34564
34620
  let totalSignals = 0;
@@ -34633,12 +34689,14 @@ function buildCohortIndex(input) {
34633
34689
  subjectsByElementKey,
34634
34690
  excludedElementKeys: cohortMetricsResult.excludedElementKeys
34635
34691
  });
34692
+ verticalAlignConsensusByParent.set(parent, resolveVerticalAlignConsensus(signalIndex.verticalAlign));
34636
34693
  conditionalSignals += counts.conditional;
34637
34694
  totalSignals += counts.total;
34638
34695
  if (!profile.unimodal) unimodalFalseCount++;
34639
34696
  }
34640
34697
  return {
34641
34698
  statsByParentNode,
34699
+ verticalAlignConsensusByParent,
34642
34700
  conditionalSignals,
34643
34701
  totalSignals,
34644
34702
  unimodalFalseCount,
@@ -35461,6 +35519,13 @@ function swap(values, left, right) {
35461
35519
  values[left] = rightValue;
35462
35520
  values[right] = leftValue;
35463
35521
  }
35522
+ function resolveVerticalAlignConsensus(aggregate) {
35523
+ if (aggregate.comparableCount === 0) return null;
35524
+ if (aggregate.countsByValue.size !== 1) return null;
35525
+ const firstEntry = aggregate.countsByValue.entries().next();
35526
+ if (firstEntry.done) return null;
35527
+ return firstEntry.value[0];
35528
+ }
35464
35529
 
35465
35530
  // src/cross-file/layout/measurement-node.ts
35466
35531
  var EMPTY_NODE_LIST = [];
@@ -37063,6 +37128,7 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37063
37128
  snapshotByElementNode,
37064
37129
  snapshotHotSignalsByElementKey: factIndex.snapshotHotSignalsByElementKey
37065
37130
  });
37131
+ finalizeTableCellBaselineRelevance(contextByParentNode, cohortIndex.verticalAlignConsensusByParent);
37066
37132
  perf.conditionalSignals = cohortIndex.conditionalSignals;
37067
37133
  perf.totalSignals = cohortIndex.totalSignals;
37068
37134
  perf.cohortUnimodalFalse = cohortIndex.unimodalFalseCount;
@@ -37403,6 +37469,12 @@ function collectAlignmentCases(context) {
37403
37469
  if (!subjectStats) {
37404
37470
  throw new Error(`missing subject cohort stats for ${measurementNode.key}`);
37405
37471
  }
37472
+ const effectiveAlignmentContext = resolveEffectiveAlignmentContext(
37473
+ alignmentContext,
37474
+ child,
37475
+ measurementNode,
37476
+ context.layout.contextByParentNode
37477
+ );
37406
37478
  const subjectDeclaredOffsetDeviation = computeDeviation(
37407
37479
  subjectStats.declaredOffset,
37408
37480
  subjectStats.baselineProfile.medianDeclaredOffsetPx
@@ -37418,7 +37490,7 @@ function collectAlignmentCases(context) {
37418
37490
  out.push(
37419
37491
  buildAlignmentCase(
37420
37492
  parent,
37421
- alignmentContext,
37493
+ effectiveAlignmentContext,
37422
37494
  cohortStats.profile,
37423
37495
  subjectStats.signals,
37424
37496
  subjectStats.identifiability,
@@ -37567,6 +37639,31 @@ function collectCohortContentCompositions(cohortStats, children, measurementNode
37567
37639
  }
37568
37640
  return out;
37569
37641
  }
37642
+ function resolveEffectiveAlignmentContext(parentContext, child, measurementNode, contextByParentNode) {
37643
+ if (child === measurementNode) return parentContext;
37644
+ if (parentContext.baselineRelevance === "irrelevant") return parentContext;
37645
+ const childContext = contextByParentNode.get(child);
37646
+ if (!childContext) return parentContext;
37647
+ if (childContext.baselineRelevance !== "irrelevant") return parentContext;
37648
+ return {
37649
+ kind: parentContext.kind,
37650
+ certainty: parentContext.certainty,
37651
+ parentSolidFile: parentContext.parentSolidFile,
37652
+ parentElementId: parentContext.parentElementId,
37653
+ parentElementKey: parentContext.parentElementKey,
37654
+ parentTag: parentContext.parentTag,
37655
+ axis: parentContext.axis,
37656
+ axisCertainty: parentContext.axisCertainty,
37657
+ inlineDirection: parentContext.inlineDirection,
37658
+ inlineDirectionCertainty: parentContext.inlineDirectionCertainty,
37659
+ parentDisplay: parentContext.parentDisplay,
37660
+ parentAlignItems: parentContext.parentAlignItems,
37661
+ parentPlaceItems: parentContext.parentPlaceItems,
37662
+ hasPositionedOffset: parentContext.hasPositionedOffset,
37663
+ baselineRelevance: "irrelevant",
37664
+ evidence: parentContext.evidence
37665
+ };
37666
+ }
37570
37667
  function compareAlignmentCaseOrder(left, right) {
37571
37668
  if (left.subject.solidFile < right.subject.solidFile) return -1;
37572
37669
  if (left.subject.solidFile > right.subject.solidFile) return 1;
@@ -37599,10 +37696,11 @@ function buildConsistencyEvidence(input) {
37599
37696
  input.cohortProfile.lineHeightDispersionPx,
37600
37697
  input.cohortProfile.medianLineHeightPx
37601
37698
  );
37602
- const baselineStrength = resolveBaselineStrength(input, lineHeight);
37603
- const contextStrength = resolveContextStrength(input, lineHeight);
37604
- const replacedStrength = resolveReplacedControlStrength(input, lineHeight);
37605
- const compositionStrength = resolveContentCompositionStrength(input);
37699
+ const baselinesIrrelevant = input.context.baselineRelevance === "irrelevant";
37700
+ const baselineStrength = baselinesIrrelevant ? ZERO_STRENGTH : resolveBaselineStrength(input, lineHeight);
37701
+ const contextStrength = baselinesIrrelevant ? ZERO_STRENGTH : resolveContextStrength(input, lineHeight);
37702
+ const replacedStrength = baselinesIrrelevant ? ZERO_STRENGTH : resolveReplacedControlStrength(input, lineHeight);
37703
+ const compositionStrength = baselinesIrrelevant ? ZERO_STRENGTH : resolveContentCompositionStrength(input);
37606
37704
  const contextCertaintyPenalty = resolveContextCertaintyPenalty(input);
37607
37705
  const provenance = input.cohortProvenance;
37608
37706
  const atoms = buildEvidenceAtoms(
@@ -37898,6 +37996,7 @@ function toNegativeContribution(strength, maxPenalty, valueKind) {
37898
37996
  max: 0
37899
37997
  };
37900
37998
  }
37999
+ var ZERO_STRENGTH = { strength: 0, kind: "exact" };
37901
38000
 
37902
38001
  // src/cross-file/layout/consistency-policy.ts
37903
38002
  function applyConsistencyPolicy(input) {
@@ -38117,6 +38216,7 @@ function runLayoutDetector(context, detector) {
38117
38216
  const cases = detector.collect(context);
38118
38217
  const startedAt = performance.now();
38119
38218
  const out = [];
38219
+ const log = context.logger;
38120
38220
  for (let i = 0; i < cases.length; i++) {
38121
38221
  const current = cases[i];
38122
38222
  if (!current) continue;
@@ -38129,10 +38229,20 @@ function runLayoutDetector(context, detector) {
38129
38229
  result.evidence.posteriorLower,
38130
38230
  result.evidence.posteriorUpper
38131
38231
  );
38232
+ if (log.enabled) {
38233
+ log.debug(
38234
+ `[${detector.id}] accept case=${i} severity=${result.evidence.severity.toFixed(2)} confidence=${result.evidence.confidence.toFixed(2)} posterior=[${result.evidence.posteriorLower.toFixed(3)},${result.evidence.posteriorUpper.toFixed(3)}] evidenceMass=${result.evidence.evidenceMass.toFixed(3)} context=${result.evidence.contextKind} offset=${result.evidence.estimatedOffsetPx?.toFixed(2) ?? "null"} topFactors=[${result.evidence.topFactors.join(",")}] causes=[${result.evidence.causes.join("; ")}]`
38235
+ );
38236
+ }
38132
38237
  out.push({ caseData: current, evidence: result.evidence });
38133
38238
  continue;
38134
38239
  }
38135
38240
  recordPolicyMetrics(context, result.evidenceMass, result.posteriorLower, result.posteriorUpper);
38241
+ if (log.enabled) {
38242
+ log.debug(
38243
+ `[${detector.id}] reject case=${i} reason=${result.reason} detail=${result.detail ?? "none"} posterior=[${result.posteriorLower.toFixed(3)},${result.posteriorUpper.toFixed(3)}] evidenceMass=${result.evidenceMass.toFixed(3)}`
38244
+ );
38245
+ }
38136
38246
  if (result.reason === "low-evidence") {
38137
38247
  context.layout.perf.casesRejectedLowEvidence++;
38138
38248
  continue;
@@ -38819,33 +38929,73 @@ var cssLayoutSiblingAlignmentOutlier = defineCrossRule({
38819
38929
  category: "css-layout"
38820
38930
  },
38821
38931
  check(context, emit) {
38932
+ const log = context.logger;
38822
38933
  const detections = runLayoutDetector(context, siblingAlignmentDetector);
38823
38934
  const uniqueDetections = dedupeDetectionsBySubject(detections);
38935
+ if (log.enabled) {
38936
+ log.debug(
38937
+ `[sibling-alignment] raw=${detections.length} deduped=${uniqueDetections.length}`
38938
+ );
38939
+ }
38824
38940
  for (let i = 0; i < uniqueDetections.length; i++) {
38825
38941
  const detection = uniqueDetections[i];
38826
38942
  if (!detection) continue;
38827
- if (detection.evidence.confidence < MIN_CONFIDENCE_THRESHOLD) continue;
38943
+ const subjectTag = detection.caseData.subject.tag ?? "element";
38944
+ const parentTag = detection.caseData.cohort.parentTag ?? "container";
38945
+ const subjectFile = detection.caseData.subject.solidFile;
38946
+ const subjectId = detection.caseData.subject.elementId;
38947
+ const logPrefix = `[sibling-alignment] <${subjectTag}> in <${parentTag}> (${subjectFile}#${subjectId})`;
38948
+ if (detection.evidence.confidence < MIN_CONFIDENCE_THRESHOLD) {
38949
+ if (log.enabled) {
38950
+ log.debug(
38951
+ `${logPrefix} SKIP: confidence=${detection.evidence.confidence.toFixed(2)} < threshold=${MIN_CONFIDENCE_THRESHOLD}`
38952
+ );
38953
+ }
38954
+ continue;
38955
+ }
38828
38956
  const estimatedOffset = detection.evidence.estimatedOffsetPx;
38829
- if (estimatedOffset !== null && Math.abs(estimatedOffset) < MIN_OFFSET_PX_THRESHOLD && !hasNonOffsetPrimaryEvidence(detection.evidence.topFactors)) continue;
38957
+ if (estimatedOffset !== null && Math.abs(estimatedOffset) < MIN_OFFSET_PX_THRESHOLD && !hasNonOffsetPrimaryEvidence(detection.evidence.topFactors)) {
38958
+ if (log.enabled) {
38959
+ log.debug(
38960
+ `${logPrefix} SKIP: offset=${estimatedOffset.toFixed(2)}px < ${MIN_OFFSET_PX_THRESHOLD}px (no non-offset primary evidence, topFactors=[${detection.evidence.topFactors.join(",")}])`
38961
+ );
38962
+ }
38963
+ continue;
38964
+ }
38830
38965
  if (isInsideOutOfFlowAncestor(
38831
38966
  context.layout,
38832
38967
  detection.caseData.cohort.parentElementKey,
38833
38968
  detection.caseData.subject.solidFile
38834
- )) continue;
38969
+ )) {
38970
+ if (log.enabled) {
38971
+ log.debug(`${logPrefix} SKIP: out-of-flow ancestor`);
38972
+ }
38973
+ continue;
38974
+ }
38835
38975
  const subjectRef = readNodeRefById(
38836
38976
  context.layout,
38837
38977
  detection.caseData.subject.solidFile,
38838
38978
  detection.caseData.subject.elementId
38839
38979
  );
38840
- if (!subjectRef) continue;
38841
- const subject = detection.caseData.subject.tag ?? "element";
38842
- const parent = detection.caseData.cohort.parentTag ?? "container";
38980
+ if (!subjectRef) {
38981
+ if (log.enabled) {
38982
+ log.debug(`${logPrefix} SKIP: no node ref`);
38983
+ }
38984
+ continue;
38985
+ }
38986
+ const subject = subjectTag;
38987
+ const parent = parentTag;
38843
38988
  const severity = formatFixed(detection.evidence.severity);
38844
38989
  const confidence = formatFixed(detection.evidence.confidence);
38845
38990
  const offset = detection.evidence.estimatedOffsetPx;
38846
38991
  const hasOffset = offset !== null && Math.abs(offset) > 0.25;
38847
38992
  const offsetClause = hasOffset ? `, estimated offset ${formatFixed(offset)}px` : "";
38848
38993
  const causes = detection.evidence.causes.length === 0 ? "alignment signals indicate an outlier" : detection.evidence.causes.join("; ");
38994
+ if (log.enabled) {
38995
+ log.debug(
38996
+ `${logPrefix} EMIT: severity=${severity} confidence=${confidence} offset=${offset?.toFixed(2) ?? "null"} posterior=[${detection.evidence.posteriorLower.toFixed(3)},${detection.evidence.posteriorUpper.toFixed(3)}] evidenceMass=${detection.evidence.evidenceMass.toFixed(3)} topFactors=[${detection.evidence.topFactors.join(",")}] causes=[${causes}]`
38997
+ );
38998
+ }
38849
38999
  emit(
38850
39000
  createDiagnostic(
38851
39001
  subjectRef.solid.file,
@@ -40254,7 +40404,8 @@ var CrossFilePlugin = {
40254
40404
  const context = {
40255
40405
  solids: solidGraphs,
40256
40406
  css: cssGraph,
40257
- layout: buildLayoutGraph(solidGraphs, cssGraph)
40407
+ layout: buildLayoutGraph(solidGraphs, cssGraph),
40408
+ logger: noopLogger
40258
40409
  };
40259
40410
  runCrossFileRules(context, emit);
40260
40411
  }