@drskillissue/ganko 0.1.18 → 0.1.20

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-OYGFWDEL.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
@@ -2805,6 +2805,26 @@ var GraphCache = class {
2805
2805
  this.solidGeneration++;
2806
2806
  if (this.log.enabled) this.log.debug(`setSolidGraph: ${key} v=${version} (${this.solids.size} total) solidGen=${this.solidGeneration}`);
2807
2807
  }
2808
+ /**
2809
+ * Get a cached SolidGraph without building on miss.
2810
+ *
2811
+ * Returns the cached graph if the version matches, null otherwise.
2812
+ * Use when the caller has already confirmed the entry exists via
2813
+ * `hasSolidGraph` and wants to avoid allocating a builder closure.
2814
+ *
2815
+ * @param path Absolute file path
2816
+ * @param version Script version string from the TS project service
2817
+ */
2818
+ getCachedSolidGraph(path, version) {
2819
+ const key = canonicalPath(path);
2820
+ const cached = this.solids.get(key);
2821
+ if (cached !== void 0 && cached.version === version) {
2822
+ if (this.log.enabled) this.log.debug(`getCachedSolidGraph HIT: ${key} v=${version}`);
2823
+ return cached.graph;
2824
+ }
2825
+ if (this.log.enabled) this.log.debug(`getCachedSolidGraph MISS: ${key} v=${version}`);
2826
+ return null;
2827
+ }
2808
2828
  /**
2809
2829
  * Get or build a SolidGraph for a file path.
2810
2830
  *
@@ -29273,7 +29293,8 @@ var cssRequireReducedMotionOverride = defineCSSRule({
29273
29293
  for (let j = 0; j < resolved.length; j++) {
29274
29294
  const sel = resolved[j];
29275
29295
  if (!sel) continue;
29276
- reduced.add(`${normalizeSelector2(sel)}|${group}`);
29296
+ const key = `${normalizeSelector2(sel)}|${group}`;
29297
+ reduced.add(key);
29277
29298
  }
29278
29299
  }
29279
29300
  for (let i = 0; i < motionDecls.length; i++) {
@@ -29292,7 +29313,8 @@ var cssRequireReducedMotionOverride = defineCSSRule({
29292
29313
  for (let j = 0; j < resolved.length; j++) {
29293
29314
  const sel = resolved[j];
29294
29315
  if (!sel) continue;
29295
- if (reduced.has(`${normalizeSelector2(sel)}|${group}`)) {
29316
+ const key = `${normalizeSelector2(sel)}|${group}`;
29317
+ if (reduced.has(key)) {
29296
29318
  covered = true;
29297
29319
  break;
29298
29320
  }
@@ -30700,6 +30722,16 @@ function readKnownNormalizedWithGuard(snapshot, name) {
30700
30722
  if (!value2) return null;
30701
30723
  return value2.normalized;
30702
30724
  }
30725
+ function isLayoutHidden(node, snapshotByElementNode) {
30726
+ if (node.attributes.has("hidden")) return true;
30727
+ if (node.classTokenSet.has("hidden")) return true;
30728
+ const snapshot = snapshotByElementNode.get(node);
30729
+ if (snapshot) {
30730
+ const display = readKnownNormalized(snapshot, "display");
30731
+ if (display === "none") return true;
30732
+ }
30733
+ return false;
30734
+ }
30703
30735
  function hasEffectivePosition(snapshot) {
30704
30736
  const position = readKnownNormalized(snapshot, "position");
30705
30737
  if (position === null) return false;
@@ -30768,6 +30800,7 @@ function readStatefulBaseValueIndex(graph) {
30768
30800
  }
30769
30801
 
30770
30802
  // src/cross-file/layout/context-classification.ts
30803
+ var WHITESPACE_RE3 = /\s+/;
30771
30804
  var TABLE_SEMANTIC_TAGS = /* @__PURE__ */ new Set(["table", "thead", "tbody", "tfoot", "tr", "td", "th"]);
30772
30805
  var TABLE_DISPLAY_VALUES = /* @__PURE__ */ new Set([
30773
30806
  "table",
@@ -30806,6 +30839,7 @@ function createAlignmentContextForParent(parent, snapshot) {
30806
30839
  const classified = classifyKind(evidence);
30807
30840
  const contextCertainty = combineCertainty(classified.certainty, axis.certainty);
30808
30841
  const certainty = combineCertainty(contextCertainty, inlineDirection.certainty);
30842
+ const baselineRelevance = computeBaselineRelevance(classified.kind, parentAlignItems, parentPlaceItems);
30809
30843
  const out = {
30810
30844
  kind: classified.kind,
30811
30845
  certainty,
@@ -30821,6 +30855,7 @@ function createAlignmentContextForParent(parent, snapshot) {
30821
30855
  parentAlignItems,
30822
30856
  parentPlaceItems,
30823
30857
  hasPositionedOffset: positionedOffset.hasPositionedOffset,
30858
+ baselineRelevance,
30824
30859
  evidence
30825
30860
  };
30826
30861
  return out;
@@ -31041,6 +31076,63 @@ function combineCertainty(left, right) {
31041
31076
  if (left === "conditional" || right === "conditional") return "conditional";
31042
31077
  return "resolved";
31043
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
+ }
31044
31136
 
31045
31137
  // src/cross-file/layout/diagnostics.ts
31046
31138
  var FINDING_WEIGHT_BY_KIND = /* @__PURE__ */ new Map([
@@ -33687,7 +33779,7 @@ var UNCONDITIONAL_GUARD = {
33687
33779
  conditions: [],
33688
33780
  key: "always"
33689
33781
  };
33690
- var WHITESPACE_RE3 = /\s+/g;
33782
+ var WHITESPACE_RE4 = /\s+/g;
33691
33783
  function resolveRuleGuard(rule) {
33692
33784
  const conditions = collectRuleConditions(rule);
33693
33785
  if (conditions.length === 0) return UNCONDITIONAL_GUARD;
@@ -33746,7 +33838,7 @@ function buildCondition(kind, query) {
33746
33838
  }
33747
33839
  function normalizeQuery(query) {
33748
33840
  if (query === null) return null;
33749
- const normalized = query.trim().toLowerCase().replace(WHITESPACE_RE3, " ");
33841
+ const normalized = query.trim().toLowerCase().replace(WHITESPACE_RE4, " ");
33750
33842
  if (normalized.length === 0) return null;
33751
33843
  return normalized;
33752
33844
  }
@@ -34368,12 +34460,7 @@ function resolveInlineReplacedKindDivergence(subjectFingerprint, allFingerprints
34368
34460
  return alignmentStrengthCalibration.compositionMixedOutlierAmongReplacedStrength;
34369
34461
  }
34370
34462
  function hasSharedBaselineAlignment(context) {
34371
- if (context.kind === "inline-formatting" || context.kind === "table-cell") return true;
34372
- if (context.kind === "flex-cross-axis" || context.kind === "grid-cross-axis") {
34373
- const alignItems = context.parentAlignItems;
34374
- return alignItems === null || alignItems === "baseline";
34375
- }
34376
- return false;
34463
+ return context.baselineRelevance === "relevant";
34377
34464
  }
34378
34465
  function resolveMajorityClassification(allFingerprints) {
34379
34466
  const countByClassification = /* @__PURE__ */ new Map();
@@ -34527,6 +34614,7 @@ function estimateBlockOffsetWithDeclaredFromSources(axis, position, readNumeric)
34527
34614
  // src/cross-file/layout/cohort-index.ts
34528
34615
  function buildCohortIndex(input) {
34529
34616
  const statsByParentNode = /* @__PURE__ */ new Map();
34617
+ const verticalAlignConsensusByParent = /* @__PURE__ */ new Map();
34530
34618
  const profileBuffers = createCohortProfileBuffers();
34531
34619
  let conditionalSignals = 0;
34532
34620
  let totalSignals = 0;
@@ -34601,12 +34689,14 @@ function buildCohortIndex(input) {
34601
34689
  subjectsByElementKey,
34602
34690
  excludedElementKeys: cohortMetricsResult.excludedElementKeys
34603
34691
  });
34692
+ verticalAlignConsensusByParent.set(parent, resolveVerticalAlignConsensus(signalIndex.verticalAlign));
34604
34693
  conditionalSignals += counts.conditional;
34605
34694
  totalSignals += counts.total;
34606
34695
  if (!profile.unimodal) unimodalFalseCount++;
34607
34696
  }
34608
34697
  return {
34609
34698
  statsByParentNode,
34699
+ verticalAlignConsensusByParent,
34610
34700
  conditionalSignals,
34611
34701
  totalSignals,
34612
34702
  unimodalFalseCount,
@@ -34626,6 +34716,10 @@ function collectCohortMetrics(input) {
34626
34716
  const node = input.children[i];
34627
34717
  if (!node) continue;
34628
34718
  const childSnapshot = input.snapshotByElementNode.get(node);
34719
+ if (isLayoutHidden(node, input.snapshotByElementNode)) {
34720
+ excluded.add(node.key);
34721
+ continue;
34722
+ }
34629
34723
  if (childSnapshot && isUnconditionallyOutOfFlow(childSnapshot)) {
34630
34724
  excluded.add(node.key);
34631
34725
  continue;
@@ -35425,6 +35519,13 @@ function swap(values, left, right) {
35425
35519
  values[left] = rightValue;
35426
35520
  values[right] = leftValue;
35427
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
+ }
35428
35529
 
35429
35530
  // src/cross-file/layout/measurement-node.ts
35430
35531
  var EMPTY_NODE_LIST = [];
@@ -35512,6 +35613,7 @@ function resolveMeasurementCandidates(root, childrenByParentNode, snapshotByElem
35512
35613
  for (let i = 0; i < children.length; i++) {
35513
35614
  const child = children[i];
35514
35615
  if (!child) continue;
35616
+ if (isLayoutHidden(child, snapshotByElementNode)) continue;
35515
35617
  if (firstControlOrReplacedDescendant === null && (child.isControl || child.isReplaced)) {
35516
35618
  firstControlOrReplacedDescendant = child;
35517
35619
  }
@@ -37026,6 +37128,7 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37026
37128
  snapshotByElementNode,
37027
37129
  snapshotHotSignalsByElementKey: factIndex.snapshotHotSignalsByElementKey
37028
37130
  });
37131
+ finalizeTableCellBaselineRelevance(contextByParentNode, cohortIndex.verticalAlignConsensusByParent);
37029
37132
  perf.conditionalSignals = cohortIndex.conditionalSignals;
37030
37133
  perf.totalSignals = cohortIndex.totalSignals;
37031
37134
  perf.cohortUnimodalFalse = cohortIndex.unimodalFalseCount;
@@ -37366,6 +37469,12 @@ function collectAlignmentCases(context) {
37366
37469
  if (!subjectStats) {
37367
37470
  throw new Error(`missing subject cohort stats for ${measurementNode.key}`);
37368
37471
  }
37472
+ const effectiveAlignmentContext = resolveEffectiveAlignmentContext(
37473
+ alignmentContext,
37474
+ child,
37475
+ measurementNode,
37476
+ context.layout.contextByParentNode
37477
+ );
37369
37478
  const subjectDeclaredOffsetDeviation = computeDeviation(
37370
37479
  subjectStats.declaredOffset,
37371
37480
  subjectStats.baselineProfile.medianDeclaredOffsetPx
@@ -37381,7 +37490,7 @@ function collectAlignmentCases(context) {
37381
37490
  out.push(
37382
37491
  buildAlignmentCase(
37383
37492
  parent,
37384
- alignmentContext,
37493
+ effectiveAlignmentContext,
37385
37494
  cohortStats.profile,
37386
37495
  subjectStats.signals,
37387
37496
  subjectStats.identifiability,
@@ -37530,6 +37639,31 @@ function collectCohortContentCompositions(cohortStats, children, measurementNode
37530
37639
  }
37531
37640
  return out;
37532
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
+ }
37533
37667
  function compareAlignmentCaseOrder(left, right) {
37534
37668
  if (left.subject.solidFile < right.subject.solidFile) return -1;
37535
37669
  if (left.subject.solidFile > right.subject.solidFile) return 1;
@@ -37562,10 +37696,11 @@ function buildConsistencyEvidence(input) {
37562
37696
  input.cohortProfile.lineHeightDispersionPx,
37563
37697
  input.cohortProfile.medianLineHeightPx
37564
37698
  );
37565
- const baselineStrength = resolveBaselineStrength(input, lineHeight);
37566
- const contextStrength = resolveContextStrength(input, lineHeight);
37567
- const replacedStrength = resolveReplacedControlStrength(input, lineHeight);
37568
- 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);
37569
37704
  const contextCertaintyPenalty = resolveContextCertaintyPenalty(input);
37570
37705
  const provenance = input.cohortProvenance;
37571
37706
  const atoms = buildEvidenceAtoms(
@@ -37861,6 +37996,7 @@ function toNegativeContribution(strength, maxPenalty, valueKind) {
37861
37996
  max: 0
37862
37997
  };
37863
37998
  }
37999
+ var ZERO_STRENGTH = { strength: 0, kind: "exact" };
37864
38000
 
37865
38001
  // src/cross-file/layout/consistency-policy.ts
37866
38002
  function applyConsistencyPolicy(input) {
@@ -38080,6 +38216,7 @@ function runLayoutDetector(context, detector) {
38080
38216
  const cases = detector.collect(context);
38081
38217
  const startedAt = performance.now();
38082
38218
  const out = [];
38219
+ const log = context.logger;
38083
38220
  for (let i = 0; i < cases.length; i++) {
38084
38221
  const current = cases[i];
38085
38222
  if (!current) continue;
@@ -38092,10 +38229,20 @@ function runLayoutDetector(context, detector) {
38092
38229
  result.evidence.posteriorLower,
38093
38230
  result.evidence.posteriorUpper
38094
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
+ }
38095
38237
  out.push({ caseData: current, evidence: result.evidence });
38096
38238
  continue;
38097
38239
  }
38098
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
+ }
38099
38246
  if (result.reason === "low-evidence") {
38100
38247
  context.layout.perf.casesRejectedLowEvidence++;
38101
38248
  continue;
@@ -38782,33 +38929,73 @@ var cssLayoutSiblingAlignmentOutlier = defineCrossRule({
38782
38929
  category: "css-layout"
38783
38930
  },
38784
38931
  check(context, emit) {
38932
+ const log = context.logger;
38785
38933
  const detections = runLayoutDetector(context, siblingAlignmentDetector);
38786
38934
  const uniqueDetections = dedupeDetectionsBySubject(detections);
38935
+ if (log.enabled) {
38936
+ log.debug(
38937
+ `[sibling-alignment] raw=${detections.length} deduped=${uniqueDetections.length}`
38938
+ );
38939
+ }
38787
38940
  for (let i = 0; i < uniqueDetections.length; i++) {
38788
38941
  const detection = uniqueDetections[i];
38789
38942
  if (!detection) continue;
38790
- 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
+ }
38791
38956
  const estimatedOffset = detection.evidence.estimatedOffsetPx;
38792
- 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
+ }
38793
38965
  if (isInsideOutOfFlowAncestor(
38794
38966
  context.layout,
38795
38967
  detection.caseData.cohort.parentElementKey,
38796
38968
  detection.caseData.subject.solidFile
38797
- )) continue;
38969
+ )) {
38970
+ if (log.enabled) {
38971
+ log.debug(`${logPrefix} SKIP: out-of-flow ancestor`);
38972
+ }
38973
+ continue;
38974
+ }
38798
38975
  const subjectRef = readNodeRefById(
38799
38976
  context.layout,
38800
38977
  detection.caseData.subject.solidFile,
38801
38978
  detection.caseData.subject.elementId
38802
38979
  );
38803
- if (!subjectRef) continue;
38804
- const subject = detection.caseData.subject.tag ?? "element";
38805
- 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;
38806
38988
  const severity = formatFixed(detection.evidence.severity);
38807
38989
  const confidence = formatFixed(detection.evidence.confidence);
38808
38990
  const offset = detection.evidence.estimatedOffsetPx;
38809
38991
  const hasOffset = offset !== null && Math.abs(offset) > 0.25;
38810
38992
  const offsetClause = hasOffset ? `, estimated offset ${formatFixed(offset)}px` : "";
38811
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
+ }
38812
38999
  emit(
38813
39000
  createDiagnostic(
38814
39001
  subjectRef.solid.file,
@@ -40217,7 +40404,8 @@ var CrossFilePlugin = {
40217
40404
  const context = {
40218
40405
  solids: solidGraphs,
40219
40406
  css: cssGraph,
40220
- layout: buildLayoutGraph(solidGraphs, cssGraph)
40407
+ layout: buildLayoutGraph(solidGraphs, cssGraph),
40408
+ logger: noopLogger
40221
40409
  };
40222
40410
  runCrossFileRules(context, emit);
40223
40411
  }