@drskillissue/ganko 0.2.0 → 0.2.5

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.
@@ -14,7 +14,7 @@ import {
14
14
  rules3,
15
15
  runCrossFileRules,
16
16
  runPhases
17
- } from "./chunk-CAKVXEYV.js";
17
+ } from "./chunk-HIADOXXV.js";
18
18
  import "./chunk-EGRHWZRV.js";
19
19
 
20
20
  // src/eslint-adapter.ts
package/dist/index.cjs CHANGED
@@ -180,7 +180,8 @@ var ServerSettingsSchema = import_v4.z.object({
180
180
  useESLintConfig: import_v4.z.boolean().default(true),
181
181
  eslintConfigPath: import_v4.z.string().optional(),
182
182
  accessibilityPolicy: AccessibilityPolicySchema.default("wcag-aa"),
183
- exclude: import_v4.z.array(import_v4.z.string()).default([])
183
+ exclude: import_v4.z.array(import_v4.z.string()).default([]),
184
+ enableTypeScriptDiagnostics: import_v4.z.boolean().default(false)
184
185
  });
185
186
  var isBun = typeof globalThis.Bun !== "undefined";
186
187
  var CHAR_FOLD_BIT = 32;
@@ -3048,6 +3049,7 @@ var GraphCache = class {
3048
3049
  if (this.log.enabled) this.log.debug(
3049
3050
  `setCachedCrossFileResults: ${allDiagnostics.length} diags across ${byFile.size} files solidGen=${this.solidGeneration} cssGen=${this.cssGeneration}`
3050
3051
  );
3052
+ this.crossFileDiagnostics.clear();
3051
3053
  for (const [file, diagnostics] of byFile) {
3052
3054
  this.crossFileDiagnostics.set(file, diagnostics);
3053
3055
  }
@@ -8747,10 +8749,17 @@ function wireJSXHierarchy(graph) {
8747
8749
  }
8748
8750
  function findParentJSXNode(node) {
8749
8751
  let current = node.parent;
8752
+ let crossedFunctionBoundary = false;
8750
8753
  while (current) {
8751
8754
  if (import_typescript29.default.isJsxElement(current) || import_typescript29.default.isJsxFragment(current)) {
8752
8755
  return current;
8753
8756
  }
8757
+ if (import_typescript29.default.isArrowFunction(current) || import_typescript29.default.isFunctionExpression(current)) {
8758
+ crossedFunctionBoundary = true;
8759
+ }
8760
+ if (crossedFunctionBoundary && import_typescript29.default.isJsxAttribute(current)) {
8761
+ return null;
8762
+ }
8754
8763
  current = current.parent;
8755
8764
  }
8756
8765
  return null;
@@ -30904,7 +30913,8 @@ var layoutSignalNames = [
30904
30913
  "inset-block-start",
30905
30914
  "inset-block-end",
30906
30915
  "writing-mode",
30907
- "direction"
30916
+ "direction",
30917
+ "contain"
30908
30918
  ];
30909
30919
 
30910
30920
  // src/cross-file/layout/offset-baseline.ts
@@ -32537,6 +32547,7 @@ function findNodeModulesPackage(importerFile, packageName) {
32537
32547
  }
32538
32548
 
32539
32549
  // src/cross-file/layout/scope.ts
32550
+ var CSS_COLOCATED_EXTENSIONS = [".css"];
32540
32551
  function collectCSSScopeBySolidFile(solids, css, moduleResolver) {
32541
32552
  const resolver = moduleResolver ?? createLayoutModuleResolver(solids, css);
32542
32553
  const cssFilesByNormalizedPath = buildCSSFileIndex(css);
@@ -32547,6 +32558,20 @@ function collectCSSScopeBySolidFile(solids, css, moduleResolver) {
32547
32558
  const solid = solids[i];
32548
32559
  if (!solid) continue;
32549
32560
  const scope = /* @__PURE__ */ new Set();
32561
+ const colocatedCssPath = resolveColocatedCss(solid.file, cssFilesByNormalizedPath);
32562
+ if (colocatedCssPath !== null) {
32563
+ const colocatedScope = getOrCollectTransitiveScope(
32564
+ colocatedCssPath,
32565
+ resolver,
32566
+ cssFilesByNormalizedPath,
32567
+ transitiveScopeByEntryPath
32568
+ );
32569
+ for (let k = 0; k < colocatedScope.length; k++) {
32570
+ const cs = colocatedScope[k];
32571
+ if (!cs) continue;
32572
+ scope.add(cs);
32573
+ }
32574
+ }
32550
32575
  for (let j = 0; j < solid.imports.length; j++) {
32551
32576
  const imp = solid.imports[j];
32552
32577
  if (!imp) continue;
@@ -32589,6 +32614,18 @@ function collectCSSScopeBySolidFile(solids, css, moduleResolver) {
32589
32614
  }
32590
32615
  return out;
32591
32616
  }
32617
+ function resolveColocatedCss(solidFilePath, cssFilesByNormalizedPath) {
32618
+ const dotIndex = solidFilePath.lastIndexOf(".");
32619
+ if (dotIndex === -1) return null;
32620
+ const stem = solidFilePath.slice(0, dotIndex);
32621
+ for (let i = 0; i < CSS_COLOCATED_EXTENSIONS.length; i++) {
32622
+ const ext = CSS_COLOCATED_EXTENSIONS[i];
32623
+ if (!ext) continue;
32624
+ const candidate = canonicalPath(stem + ext);
32625
+ if (cssFilesByNormalizedPath.has(candidate)) return candidate;
32626
+ }
32627
+ return null;
32628
+ }
32592
32629
  function buildCSSFileIndex(css) {
32593
32630
  const out = /* @__PURE__ */ new Map();
32594
32631
  for (let i = 0; i < css.files.length; i++) {
@@ -33913,73 +33950,108 @@ function compoundGroupNeedsAttributes(groups) {
33913
33950
  }
33914
33951
  function selectorMatchesLayoutElement(matcher, node, perf, fileRootElements = null, logger = noopLogger) {
33915
33952
  const firstCompound = matcher.compoundsRightToLeft[0];
33916
- if (firstCompound === void 0) return false;
33917
- if (!matchesCompound(node, firstCompound)) return false;
33918
- if (matcher.compoundsRightToLeft.length === 1) return true;
33919
- return matchesChain(matcher, node, 0, perf, fileRootElements, logger);
33953
+ if (firstCompound === void 0) return "no-match";
33954
+ const subjectResult = matchesCompound(node, firstCompound);
33955
+ if (subjectResult === "no-match") return "no-match";
33956
+ if (matcher.compoundsRightToLeft.length === 1) return subjectResult;
33957
+ const chainResult = matchesChain(matcher, node, 0, perf, fileRootElements, logger);
33958
+ if (chainResult === "no-match") return "no-match";
33959
+ if (subjectResult === "conditional" || chainResult === "conditional") return "conditional";
33960
+ return "match";
33920
33961
  }
33921
33962
  function matchesChain(matcher, node, index, perf, fileRootElements, logger) {
33922
33963
  const combinator = matcher.combinatorsRightToLeft[index];
33923
- if (combinator === void 0) return false;
33964
+ if (combinator === void 0) return "no-match";
33924
33965
  const nextIndex = index + 1;
33925
33966
  const targetCompound = matcher.compoundsRightToLeft[nextIndex];
33926
- if (targetCompound === void 0) return false;
33967
+ if (targetCompound === void 0) return "no-match";
33927
33968
  const isFinal = nextIndex === matcher.compoundsRightToLeft.length - 1;
33928
33969
  if (combinator === "child") {
33929
33970
  const parent = node.parentElementNode;
33930
- if (parent === null) return false;
33971
+ if (parent === null) return "no-match";
33931
33972
  perf.ancestryChecks++;
33932
- if (!matchesCompound(parent, targetCompound)) return false;
33933
- if (isFinal) return true;
33934
- return matchesChain(matcher, parent, nextIndex, perf, fileRootElements, logger);
33973
+ const compoundResult = matchesCompound(parent, targetCompound);
33974
+ if (compoundResult === "no-match") return "no-match";
33975
+ if (isFinal) return compoundResult;
33976
+ const chainResult = matchesChain(matcher, parent, nextIndex, perf, fileRootElements, logger);
33977
+ return mergeMatchResults(compoundResult, chainResult);
33935
33978
  }
33936
33979
  if (combinator === "adjacent") {
33937
33980
  const sibling = node.previousSiblingNode;
33938
- if (sibling === null) return false;
33981
+ if (sibling === null) return "no-match";
33939
33982
  perf.ancestryChecks++;
33940
- if (!matchesCompound(sibling, targetCompound)) return false;
33941
- if (isFinal) return true;
33942
- return matchesChain(matcher, sibling, nextIndex, perf, fileRootElements, logger);
33983
+ const compoundResult = matchesCompound(sibling, targetCompound);
33984
+ if (compoundResult === "no-match") return "no-match";
33985
+ if (isFinal) return compoundResult;
33986
+ const chainResult = matchesChain(matcher, sibling, nextIndex, perf, fileRootElements, logger);
33987
+ return mergeMatchResults(compoundResult, chainResult);
33943
33988
  }
33944
33989
  if (combinator === "sibling") {
33945
33990
  let sibling = node.previousSiblingNode;
33946
33991
  while (sibling !== null) {
33947
33992
  perf.ancestryChecks++;
33948
- if (matchesCompound(sibling, targetCompound)) {
33949
- if (isFinal) return true;
33950
- if (matchesChain(matcher, sibling, nextIndex, perf, fileRootElements, logger)) return true;
33993
+ const compoundResult = matchesCompound(sibling, targetCompound);
33994
+ if (compoundResult !== "no-match") {
33995
+ if (isFinal) return compoundResult;
33996
+ const chainResult = matchesChain(matcher, sibling, nextIndex, perf, fileRootElements, logger);
33997
+ if (chainResult !== "no-match") return mergeMatchResults(compoundResult, chainResult);
33951
33998
  }
33952
33999
  sibling = sibling.previousSiblingNode;
33953
34000
  }
33954
- return false;
34001
+ return "no-match";
33955
34002
  }
34003
+ let bestResult = "no-match";
33956
34004
  let ancestor = node.parentElementNode;
33957
34005
  while (ancestor !== null) {
33958
34006
  perf.ancestryChecks++;
33959
- if (matchesCompound(ancestor, targetCompound)) {
33960
- if (isFinal) return true;
33961
- if (matchesChain(matcher, ancestor, nextIndex, perf, fileRootElements, logger)) return true;
34007
+ const compoundResult = matchesCompound(ancestor, targetCompound);
34008
+ if (compoundResult !== "no-match") {
34009
+ if (isFinal) return compoundResult;
34010
+ const chainResult = matchesChain(matcher, ancestor, nextIndex, perf, fileRootElements, logger);
34011
+ if (chainResult !== "no-match") {
34012
+ const merged = mergeMatchResults(compoundResult, chainResult);
34013
+ if (merged === "match") return "match";
34014
+ bestResult = merged;
34015
+ }
33962
34016
  }
33963
34017
  ancestor = ancestor.parentElementNode;
33964
34018
  }
33965
34019
  if (fileRootElements !== null) {
34020
+ if (logger.enabled) {
34021
+ const compoundDesc = describeCompound(targetCompound);
34022
+ logger.trace(`[selector-match] fallback: node=${node.key} tag=${node.tagName} checking ${fileRootElements.length} roots for compound=${compoundDesc}`);
34023
+ }
33966
34024
  for (let r = 0; r < fileRootElements.length; r++) {
33967
34025
  const root = fileRootElements[r];
33968
34026
  if (root === void 0) continue;
33969
34027
  if (root === node) continue;
33970
34028
  if (root.solidFile !== node.solidFile) continue;
33971
34029
  perf.ancestryChecks++;
33972
- if (matchesCompound(root, targetCompound)) {
34030
+ const compoundResult = matchesCompound(root, targetCompound);
34031
+ if (logger.enabled && compoundResult === "no-match") {
34032
+ logger.trace(`[selector-match] fallback MISS: root=${root.key} tag=${root.tagName} attrs=[${[...root.attributes.entries()].map(([k, v]) => `${k}=${v}`).join(",")}]`);
34033
+ }
34034
+ if (compoundResult !== "no-match") {
33973
34035
  if (logger.enabled) {
33974
34036
  const compoundDesc = describeCompound(targetCompound);
33975
34037
  logger.debug(`[selector-match] fallback HIT: node=${node.key} tag=${node.tagName} matched root=${root.key} tag=${root.tagName} compound=${compoundDesc} isFinal=${isFinal}`);
33976
34038
  }
33977
- if (isFinal) return true;
33978
- if (matchesChain(matcher, root, nextIndex, perf, fileRootElements, logger)) return true;
34039
+ if (isFinal) return compoundResult;
34040
+ const chainResult = matchesChain(matcher, root, nextIndex, perf, fileRootElements, logger);
34041
+ if (chainResult !== "no-match") {
34042
+ const merged = mergeMatchResults(compoundResult, chainResult);
34043
+ if (merged === "match") return "match";
34044
+ bestResult = merged;
34045
+ }
33979
34046
  }
33980
34047
  }
33981
34048
  }
33982
- return false;
34049
+ return bestResult;
34050
+ }
34051
+ function mergeMatchResults(a, b) {
34052
+ if (a === "no-match" || b === "no-match") return "no-match";
34053
+ if (a === "conditional" || b === "conditional") return "conditional";
34054
+ return "match";
33983
34055
  }
33984
34056
  function describeCompound(compound) {
33985
34057
  const parts = [];
@@ -33999,15 +34071,16 @@ function describeCompound(compound) {
33999
34071
  return parts.join("") || "*";
34000
34072
  }
34001
34073
  function matchesCompound(node, compound) {
34002
- if (compound.tagName !== null && node.tagName !== compound.tagName) return false;
34074
+ if (compound.tagName !== null && node.tagName !== compound.tagName) return "no-match";
34003
34075
  if (compound.idValue !== null) {
34004
34076
  const id = node.attributes.get("id");
34005
- if (id !== compound.idValue) return false;
34077
+ if (id !== compound.idValue) return "no-match";
34006
34078
  }
34007
- if (!matchesRequiredClasses(compound.classes, node.classTokenSet)) return false;
34008
- if (!matchesRequiredAttributes(compound.attributes, node.attributes)) return false;
34009
- if (!matchesPseudoConstraints(node, compound.pseudo)) return false;
34010
- return true;
34079
+ if (!matchesRequiredClasses(compound.classes, node.classTokenSet)) return "no-match";
34080
+ const attrResult = matchesRequiredAttributes(compound.attributes, node.attributes);
34081
+ if (attrResult === "no-match") return "no-match";
34082
+ if (!matchesPseudoConstraints(node, compound.pseudo)) return "no-match";
34083
+ return attrResult;
34011
34084
  }
34012
34085
  function matchesPseudoConstraints(node, pseudo) {
34013
34086
  if (pseudo.firstChild && node.siblingIndex !== 1) return false;
@@ -34032,7 +34105,7 @@ function matchesPseudoConstraints(node, pseudo) {
34032
34105
  for (let j = 0; j < group.length; j++) {
34033
34106
  const compound = group[j];
34034
34107
  if (compound === void 0) continue;
34035
- if (!matchesCompound(node, compound)) continue;
34108
+ if (matchesCompound(node, compound) === "no-match") continue;
34036
34109
  matched = true;
34037
34110
  break;
34038
34111
  }
@@ -34044,7 +34117,7 @@ function matchesPseudoConstraints(node, pseudo) {
34044
34117
  for (let j = 0; j < group.length; j++) {
34045
34118
  const compound = group[j];
34046
34119
  if (compound === void 0) continue;
34047
- if (!matchesCompound(node, compound)) continue;
34120
+ if (matchesCompound(node, compound) === "no-match") continue;
34048
34121
  return false;
34049
34122
  }
34050
34123
  }
@@ -34689,19 +34762,24 @@ function matchesRequiredClasses(required, actual) {
34689
34762
  return true;
34690
34763
  }
34691
34764
  function matchesRequiredAttributes(required, actual) {
34692
- if (required.length === 0) return true;
34765
+ if (required.length === 0) return "match";
34766
+ let hasConditional = false;
34693
34767
  for (let i = 0; i < required.length; i++) {
34694
34768
  const constraint = required[i];
34695
34769
  if (constraint === void 0) continue;
34696
- if (!actual.has(constraint.name)) return false;
34770
+ if (!actual.has(constraint.name)) return "no-match";
34697
34771
  if (constraint.operator === "exists") continue;
34698
34772
  const actualValue = actual.get(constraint.name);
34699
- if (actualValue === null || actualValue === void 0) return false;
34700
- if (constraint.value === null) return false;
34773
+ if (actualValue === void 0) return "no-match";
34774
+ if (actualValue === null) {
34775
+ hasConditional = true;
34776
+ continue;
34777
+ }
34778
+ if (constraint.value === null) return "no-match";
34701
34779
  if (matchesAttributeValue(actualValue, constraint)) continue;
34702
- return false;
34780
+ return "no-match";
34703
34781
  }
34704
- return true;
34782
+ return hasConditional ? "conditional" : "match";
34705
34783
  }
34706
34784
  function matchesAttributeValue(actualValue, constraint) {
34707
34785
  const expectedValue = constraint.value;
@@ -37067,6 +37145,11 @@ function compareCascadePositions(posA, posB) {
37067
37145
  }
37068
37146
 
37069
37147
  // src/cross-file/layout/cascade-builder.ts
37148
+ var DYNAMIC_ATTRIBUTE_GUARD = {
37149
+ kind: 1 /* Conditional */,
37150
+ conditions: [{ kind: "dynamic-attribute", query: null, key: "dynamic-attribute:*" }],
37151
+ key: "dynamic-attribute:*"
37152
+ };
37070
37153
  var SCROLLABLE_VALUES = /* @__PURE__ */ new Set(["auto", "scroll"]);
37071
37154
  var EMPTY_EXPANSION_RESULT = [];
37072
37155
  function collectMonitoredDeclarations(selector, layerOrder, guard) {
@@ -37141,14 +37224,21 @@ function appendMatchingEdgesFromSelectorIds(ctx, selectorIds, node, applies, app
37141
37224
  if (!selector) {
37142
37225
  throw new Error(`missing selector ${selectorId}`);
37143
37226
  }
37144
- if (!selectorMatchesLayoutElement(metadata.matcher, node, ctx.perf, fileRoots, ctx.logger)) continue;
37227
+ const matchResult = selectorMatchesLayoutElement(metadata.matcher, node, ctx.perf, fileRoots, ctx.logger);
37228
+ if (matchResult === "no-match") continue;
37145
37229
  const edge = {
37146
37230
  selectorId: selector.id,
37147
37231
  specificityScore: selector.specificityScore,
37148
- sourceOrder: selector.rule.sourceOrder
37232
+ sourceOrder: selector.rule.sourceOrder,
37233
+ conditionalMatch: matchResult === "conditional"
37149
37234
  };
37150
37235
  applies.push(edge);
37151
37236
  ctx.perf.matchEdgesCreated++;
37237
+ if (ctx.logger.enabled) {
37238
+ ctx.logger.trace(
37239
+ `[cascade] edge node=${node.key} selector=${selector.id} match=${matchResult} conditional=${edge.conditionalMatch} selector-raw=${selector.raw.slice(0, 80)}`
37240
+ );
37241
+ }
37152
37242
  const existing = appliesByElementNodeMutable.get(node);
37153
37243
  if (existing) {
37154
37244
  existing.push(edge);
@@ -37209,10 +37299,11 @@ function buildCascadeMapForElement(node, edges, monitoredDeclarationsBySelectorI
37209
37299
  const declaration = declarations[j];
37210
37300
  if (!declaration) continue;
37211
37301
  const property = declaration.property;
37302
+ const guardProvenance = edge.conditionalMatch && declaration.guardProvenance.kind === 0 /* Unconditional */ ? DYNAMIC_ATTRIBUTE_GUARD : declaration.guardProvenance;
37212
37303
  const newDeclaration = {
37213
37304
  value: declaration.value,
37214
37305
  source: 0 /* Selector */,
37215
- guardProvenance: declaration.guardProvenance
37306
+ guardProvenance
37216
37307
  };
37217
37308
  const existingPosition = positions.get(property);
37218
37309
  if (existingPosition === void 0) {
@@ -37326,7 +37417,7 @@ function buildConditionalDeltaIndex(elements, appliesByNode, monitoredDeclaratio
37326
37417
  };
37327
37418
  byProperty.set(property, bucket);
37328
37419
  }
37329
- if (declaration.guardProvenance.kind === 1 /* Conditional */) {
37420
+ if (declaration.guardProvenance.kind === 1 /* Conditional */ || currentEdge.conditionalMatch) {
37330
37421
  bucket.conditional.add(expandedEntry.value);
37331
37422
  continue;
37332
37423
  }
@@ -37818,6 +37909,23 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37818
37909
  const moduleResolver = createLayoutModuleResolver(solids, css);
37819
37910
  const componentHostResolver = createLayoutComponentHostResolver(solids, moduleResolver, logger);
37820
37911
  const cssScopeBySolidFile = collectCSSScopeBySolidFile(solids, css, moduleResolver);
37912
+ if (logger.enabled) {
37913
+ for (const [solidFile, scopePaths] of cssScopeBySolidFile) {
37914
+ if (scopePaths.length > 0) {
37915
+ let names = "";
37916
+ for (let k = 0; k < scopePaths.length; k++) {
37917
+ const p = scopePaths[k];
37918
+ if (!p) continue;
37919
+ if (names.length > 0) names += ", ";
37920
+ const slash = p.lastIndexOf("/");
37921
+ names += slash === -1 ? p : p.slice(slash + 1);
37922
+ }
37923
+ logger.trace(`[scope] ${solidFile} \u2192 ${scopePaths.length} CSS files: ${names}`);
37924
+ } else {
37925
+ logger.trace(`[scope] ${solidFile} \u2192 EMPTY (no CSS in scope)`);
37926
+ }
37927
+ }
37928
+ }
37821
37929
  const selectorIndexStartedAt = performance.now();
37822
37930
  const scopedSelectorsBySolidFile = buildScopedSelectorIndexBySolidFile(
37823
37931
  cssScopeBySolidFile,
@@ -37960,6 +38068,22 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37960
38068
  appliesByNode.set(node, edges);
37961
38069
  }
37962
38070
  perf.cascadeBuildMs = performance.now() - cascadeStartedAt;
38071
+ if (logger.enabled) {
38072
+ for (let i = 0; i < elements.length; i++) {
38073
+ const node = elements[i];
38074
+ if (!node) continue;
38075
+ const cascade = cascadeByElementNode.get(node);
38076
+ if (!cascade || cascade.size === 0) continue;
38077
+ const displayDecl = cascade.get("display");
38078
+ const flexDirDecl = cascade.get("flex-direction");
38079
+ if (!displayDecl && !flexDirDecl) continue;
38080
+ const displayGuard = displayDecl?.guardProvenance.kind === 1 /* Conditional */ ? "conditional" : "unconditional";
38081
+ const flexDirGuard = flexDirDecl?.guardProvenance.kind === 1 /* Conditional */ ? "conditional" : "unconditional";
38082
+ logger.trace(
38083
+ `[cascade] node=${node.key} tag=${node.tagName ?? "null"} display=${displayDecl ? `${displayDecl.value}(${displayGuard})` : "absent"} flex-direction=${flexDirDecl ? `${flexDirDecl.value}(${flexDirGuard})` : "absent"} edges=${(appliesByNode.get(node) ?? []).length} attrs=[${[...node.attributes.keys()].join(",")}]`
38084
+ );
38085
+ }
38086
+ }
37963
38087
  const snapshotByElementNode = buildSignalSnapshotIndex(elements, cascadeByElementNode, perf);
37964
38088
  const measurementNodeByRootKey = buildMeasurementNodeIndex(elements, childrenByParentNodeMutable, snapshotByElementNode);
37965
38089
  const factIndex = buildElementFactIndex(elements, snapshotByElementNode);
@@ -37977,7 +38101,7 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37977
38101
  layoutOffsetSignals
37978
38102
  );
37979
38103
  const statefulRuleIndexes = buildStatefulRuleIndexes(css.rules);
37980
- const contextByParentNode = buildContextIndex(childrenByParentNodeMutable, snapshotByElementNode, perf);
38104
+ const contextByParentNode = buildContextIndex(childrenByParentNodeMutable, snapshotByElementNode, perf, logger);
37981
38105
  const cohortIndex = buildCohortIndex({
37982
38106
  childrenByParentNode: childrenByParentNodeMutable,
37983
38107
  contextByParentNode,
@@ -38397,16 +38521,27 @@ function computeFlowParticipationFact(snapshot) {
38397
38521
  hasUnconditionalOutOfFlow: signal.guard.kind === 0 /* Unconditional */ && outOfFlow
38398
38522
  };
38399
38523
  }
38400
- function buildContextIndex(childrenByParentNode, snapshotByElementNode, perf) {
38524
+ function buildContextIndex(childrenByParentNode, snapshotByElementNode, perf, logger) {
38401
38525
  const out = /* @__PURE__ */ new Map();
38526
+ const trace = logger.enabled;
38402
38527
  for (const [parent, children] of childrenByParentNode) {
38403
38528
  if (children.length < 2) continue;
38404
38529
  const snapshot = snapshotByElementNode.get(parent);
38405
38530
  if (!snapshot) {
38406
38531
  throw new Error(`missing parent snapshot for context classification ${parent.key}`);
38407
38532
  }
38408
- out.set(parent, createAlignmentContextForParent(parent, snapshot));
38533
+ const ctx = createAlignmentContextForParent(parent, snapshot);
38534
+ out.set(parent, ctx);
38409
38535
  perf.contextsClassified++;
38536
+ if (trace) {
38537
+ const displaySignal = snapshot.signals.get("display");
38538
+ const flexDirSignal = snapshot.signals.get("flex-direction");
38539
+ const displayDesc = displaySignal ? `${displaySignal.kind}:${displaySignal.kind === "known" ? displaySignal.normalized : "?"}(guard=${displaySignal.guard.kind === 1 /* Conditional */ ? "conditional" : "unconditional"})` : "absent";
38540
+ const flexDirDesc = flexDirSignal ? `${flexDirSignal.kind}:${flexDirSignal.kind === "known" ? flexDirSignal.normalized : "?"}(guard=${flexDirSignal.guard.kind === 1 /* Conditional */ ? "conditional" : "unconditional"})` : "absent";
38541
+ logger.trace(
38542
+ `[context] parent=${parent.key} tag=${parent.tagName ?? "null"} children=${children.length} display=${displayDesc} flex-direction=${flexDirDesc} \u2192 kind=${ctx.kind} certainty=${ctx.certainty} crossAxisIsBlockAxis=${ctx.crossAxisIsBlockAxis} baseline=${ctx.baselineRelevance}`
38543
+ );
38544
+ }
38410
38545
  }
38411
38546
  return out;
38412
38547
  }
@@ -39894,7 +40029,13 @@ var MIN_OFFSET_PX_THRESHOLD = 2;
39894
40029
  var siblingAlignmentDetector = {
39895
40030
  id: "sibling-alignment-outlier",
39896
40031
  collect: collectAlignmentCases,
39897
- evaluate(input) {
40032
+ evaluate(input, context) {
40033
+ if (context.logger.enabled) {
40034
+ const ctx = input.context;
40035
+ context.logger.trace(
40036
+ `[sibling-alignment] evaluate subject=${input.subject.elementKey} tag=${input.subject.tag} parent=${ctx.parentElementKey} parentTag=${ctx.parentTag} context.kind=${ctx.kind} certainty=${ctx.certainty} display=${ctx.parentDisplay} alignItems=${ctx.parentAlignItems} crossAxisIsBlockAxis=${ctx.crossAxisIsBlockAxis} crossAxisCertainty=${ctx.crossAxisIsBlockAxisCertainty} baseline=${ctx.baselineRelevance}`
40037
+ );
40038
+ }
39898
40039
  const decision = evaluateAlignmentCase(input);
39899
40040
  if (decision.kind === "reject") {
39900
40041
  return {
@@ -41012,8 +41153,16 @@ function isExemptFromCLS(layout, solidFile, element, property) {
41012
41153
  if (POSITIONED_OFFSET_PROPERTIES.has(property) && flowFact.position !== null && flowFact.position !== "static") {
41013
41154
  return true;
41014
41155
  }
41156
+ if (hasLayoutContainment(node) || node.parentElementNode !== null && hasLayoutContainment(node.parentElementNode)) {
41157
+ return true;
41158
+ }
41015
41159
  return false;
41016
41160
  }
41161
+ function hasLayoutContainment(node) {
41162
+ const contain = node.inlineStyleValues.get("contain");
41163
+ if (contain === void 0) return false;
41164
+ return contain === "layout" || contain === "strict" || contain === "content" || contain.includes("layout");
41165
+ }
41017
41166
  function hasUnstableLayoutDelta(property, node) {
41018
41167
  const unwrapped = unwrapTypeWrapper(node);
41019
41168
  if (isStaticComparable(property, unwrapped)) return false;