@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.
@@ -171,7 +171,8 @@ var ServerSettingsSchema = import_v4.z.object({
171
171
  useESLintConfig: import_v4.z.boolean().default(true),
172
172
  eslintConfigPath: import_v4.z.string().optional(),
173
173
  accessibilityPolicy: AccessibilityPolicySchema.default("wcag-aa"),
174
- exclude: import_v4.z.array(import_v4.z.string()).default([])
174
+ exclude: import_v4.z.array(import_v4.z.string()).default([]),
175
+ enableTypeScriptDiagnostics: import_v4.z.boolean().default(false)
175
176
  });
176
177
  var isBun = typeof globalThis.Bun !== "undefined";
177
178
  var CHAR_FOLD_BIT = 32;
@@ -8303,10 +8304,17 @@ function wireJSXHierarchy(graph) {
8303
8304
  }
8304
8305
  function findParentJSXNode(node) {
8305
8306
  let current = node.parent;
8307
+ let crossedFunctionBoundary = false;
8306
8308
  while (current) {
8307
8309
  if (import_typescript29.default.isJsxElement(current) || import_typescript29.default.isJsxFragment(current)) {
8308
8310
  return current;
8309
8311
  }
8312
+ if (import_typescript29.default.isArrowFunction(current) || import_typescript29.default.isFunctionExpression(current)) {
8313
+ crossedFunctionBoundary = true;
8314
+ }
8315
+ if (crossedFunctionBoundary && import_typescript29.default.isJsxAttribute(current)) {
8316
+ return null;
8317
+ }
8310
8318
  current = current.parent;
8311
8319
  }
8312
8320
  return null;
@@ -30181,7 +30189,8 @@ var layoutSignalNames = [
30181
30189
  "inset-block-start",
30182
30190
  "inset-block-end",
30183
30191
  "writing-mode",
30184
- "direction"
30192
+ "direction",
30193
+ "contain"
30185
30194
  ];
30186
30195
 
30187
30196
  // src/cross-file/layout/offset-baseline.ts
@@ -31814,6 +31823,7 @@ function findNodeModulesPackage(importerFile, packageName) {
31814
31823
  }
31815
31824
 
31816
31825
  // src/cross-file/layout/scope.ts
31826
+ var CSS_COLOCATED_EXTENSIONS = [".css"];
31817
31827
  function collectCSSScopeBySolidFile(solids, css, moduleResolver) {
31818
31828
  const resolver = moduleResolver ?? createLayoutModuleResolver(solids, css);
31819
31829
  const cssFilesByNormalizedPath = buildCSSFileIndex(css);
@@ -31824,6 +31834,20 @@ function collectCSSScopeBySolidFile(solids, css, moduleResolver) {
31824
31834
  const solid = solids[i];
31825
31835
  if (!solid) continue;
31826
31836
  const scope = /* @__PURE__ */ new Set();
31837
+ const colocatedCssPath = resolveColocatedCss(solid.file, cssFilesByNormalizedPath);
31838
+ if (colocatedCssPath !== null) {
31839
+ const colocatedScope = getOrCollectTransitiveScope(
31840
+ colocatedCssPath,
31841
+ resolver,
31842
+ cssFilesByNormalizedPath,
31843
+ transitiveScopeByEntryPath
31844
+ );
31845
+ for (let k = 0; k < colocatedScope.length; k++) {
31846
+ const cs = colocatedScope[k];
31847
+ if (!cs) continue;
31848
+ scope.add(cs);
31849
+ }
31850
+ }
31827
31851
  for (let j = 0; j < solid.imports.length; j++) {
31828
31852
  const imp = solid.imports[j];
31829
31853
  if (!imp) continue;
@@ -31866,6 +31890,18 @@ function collectCSSScopeBySolidFile(solids, css, moduleResolver) {
31866
31890
  }
31867
31891
  return out;
31868
31892
  }
31893
+ function resolveColocatedCss(solidFilePath, cssFilesByNormalizedPath) {
31894
+ const dotIndex = solidFilePath.lastIndexOf(".");
31895
+ if (dotIndex === -1) return null;
31896
+ const stem = solidFilePath.slice(0, dotIndex);
31897
+ for (let i = 0; i < CSS_COLOCATED_EXTENSIONS.length; i++) {
31898
+ const ext = CSS_COLOCATED_EXTENSIONS[i];
31899
+ if (!ext) continue;
31900
+ const candidate = canonicalPath(stem + ext);
31901
+ if (cssFilesByNormalizedPath.has(candidate)) return candidate;
31902
+ }
31903
+ return null;
31904
+ }
31869
31905
  function buildCSSFileIndex(css) {
31870
31906
  const out = /* @__PURE__ */ new Map();
31871
31907
  for (let i = 0; i < css.files.length; i++) {
@@ -33207,73 +33243,108 @@ function compoundGroupNeedsAttributes(groups) {
33207
33243
  }
33208
33244
  function selectorMatchesLayoutElement(matcher, node, perf, fileRootElements = null, logger = noopLogger) {
33209
33245
  const firstCompound = matcher.compoundsRightToLeft[0];
33210
- if (firstCompound === void 0) return false;
33211
- if (!matchesCompound(node, firstCompound)) return false;
33212
- if (matcher.compoundsRightToLeft.length === 1) return true;
33213
- return matchesChain(matcher, node, 0, perf, fileRootElements, logger);
33246
+ if (firstCompound === void 0) return "no-match";
33247
+ const subjectResult = matchesCompound(node, firstCompound);
33248
+ if (subjectResult === "no-match") return "no-match";
33249
+ if (matcher.compoundsRightToLeft.length === 1) return subjectResult;
33250
+ const chainResult = matchesChain(matcher, node, 0, perf, fileRootElements, logger);
33251
+ if (chainResult === "no-match") return "no-match";
33252
+ if (subjectResult === "conditional" || chainResult === "conditional") return "conditional";
33253
+ return "match";
33214
33254
  }
33215
33255
  function matchesChain(matcher, node, index, perf, fileRootElements, logger) {
33216
33256
  const combinator = matcher.combinatorsRightToLeft[index];
33217
- if (combinator === void 0) return false;
33257
+ if (combinator === void 0) return "no-match";
33218
33258
  const nextIndex = index + 1;
33219
33259
  const targetCompound = matcher.compoundsRightToLeft[nextIndex];
33220
- if (targetCompound === void 0) return false;
33260
+ if (targetCompound === void 0) return "no-match";
33221
33261
  const isFinal = nextIndex === matcher.compoundsRightToLeft.length - 1;
33222
33262
  if (combinator === "child") {
33223
33263
  const parent = node.parentElementNode;
33224
- if (parent === null) return false;
33264
+ if (parent === null) return "no-match";
33225
33265
  perf.ancestryChecks++;
33226
- if (!matchesCompound(parent, targetCompound)) return false;
33227
- if (isFinal) return true;
33228
- return matchesChain(matcher, parent, nextIndex, perf, fileRootElements, logger);
33266
+ const compoundResult = matchesCompound(parent, targetCompound);
33267
+ if (compoundResult === "no-match") return "no-match";
33268
+ if (isFinal) return compoundResult;
33269
+ const chainResult = matchesChain(matcher, parent, nextIndex, perf, fileRootElements, logger);
33270
+ return mergeMatchResults(compoundResult, chainResult);
33229
33271
  }
33230
33272
  if (combinator === "adjacent") {
33231
33273
  const sibling = node.previousSiblingNode;
33232
- if (sibling === null) return false;
33274
+ if (sibling === null) return "no-match";
33233
33275
  perf.ancestryChecks++;
33234
- if (!matchesCompound(sibling, targetCompound)) return false;
33235
- if (isFinal) return true;
33236
- return matchesChain(matcher, sibling, nextIndex, perf, fileRootElements, logger);
33276
+ const compoundResult = matchesCompound(sibling, targetCompound);
33277
+ if (compoundResult === "no-match") return "no-match";
33278
+ if (isFinal) return compoundResult;
33279
+ const chainResult = matchesChain(matcher, sibling, nextIndex, perf, fileRootElements, logger);
33280
+ return mergeMatchResults(compoundResult, chainResult);
33237
33281
  }
33238
33282
  if (combinator === "sibling") {
33239
33283
  let sibling = node.previousSiblingNode;
33240
33284
  while (sibling !== null) {
33241
33285
  perf.ancestryChecks++;
33242
- if (matchesCompound(sibling, targetCompound)) {
33243
- if (isFinal) return true;
33244
- if (matchesChain(matcher, sibling, nextIndex, perf, fileRootElements, logger)) return true;
33286
+ const compoundResult = matchesCompound(sibling, targetCompound);
33287
+ if (compoundResult !== "no-match") {
33288
+ if (isFinal) return compoundResult;
33289
+ const chainResult = matchesChain(matcher, sibling, nextIndex, perf, fileRootElements, logger);
33290
+ if (chainResult !== "no-match") return mergeMatchResults(compoundResult, chainResult);
33245
33291
  }
33246
33292
  sibling = sibling.previousSiblingNode;
33247
33293
  }
33248
- return false;
33294
+ return "no-match";
33249
33295
  }
33296
+ let bestResult = "no-match";
33250
33297
  let ancestor = node.parentElementNode;
33251
33298
  while (ancestor !== null) {
33252
33299
  perf.ancestryChecks++;
33253
- if (matchesCompound(ancestor, targetCompound)) {
33254
- if (isFinal) return true;
33255
- if (matchesChain(matcher, ancestor, nextIndex, perf, fileRootElements, logger)) return true;
33300
+ const compoundResult = matchesCompound(ancestor, targetCompound);
33301
+ if (compoundResult !== "no-match") {
33302
+ if (isFinal) return compoundResult;
33303
+ const chainResult = matchesChain(matcher, ancestor, nextIndex, perf, fileRootElements, logger);
33304
+ if (chainResult !== "no-match") {
33305
+ const merged = mergeMatchResults(compoundResult, chainResult);
33306
+ if (merged === "match") return "match";
33307
+ bestResult = merged;
33308
+ }
33256
33309
  }
33257
33310
  ancestor = ancestor.parentElementNode;
33258
33311
  }
33259
33312
  if (fileRootElements !== null) {
33313
+ if (logger.enabled) {
33314
+ const compoundDesc = describeCompound(targetCompound);
33315
+ logger.trace(`[selector-match] fallback: node=${node.key} tag=${node.tagName} checking ${fileRootElements.length} roots for compound=${compoundDesc}`);
33316
+ }
33260
33317
  for (let r = 0; r < fileRootElements.length; r++) {
33261
33318
  const root = fileRootElements[r];
33262
33319
  if (root === void 0) continue;
33263
33320
  if (root === node) continue;
33264
33321
  if (root.solidFile !== node.solidFile) continue;
33265
33322
  perf.ancestryChecks++;
33266
- if (matchesCompound(root, targetCompound)) {
33323
+ const compoundResult = matchesCompound(root, targetCompound);
33324
+ if (logger.enabled && compoundResult === "no-match") {
33325
+ logger.trace(`[selector-match] fallback MISS: root=${root.key} tag=${root.tagName} attrs=[${[...root.attributes.entries()].map(([k, v]) => `${k}=${v}`).join(",")}]`);
33326
+ }
33327
+ if (compoundResult !== "no-match") {
33267
33328
  if (logger.enabled) {
33268
33329
  const compoundDesc = describeCompound(targetCompound);
33269
33330
  logger.debug(`[selector-match] fallback HIT: node=${node.key} tag=${node.tagName} matched root=${root.key} tag=${root.tagName} compound=${compoundDesc} isFinal=${isFinal}`);
33270
33331
  }
33271
- if (isFinal) return true;
33272
- if (matchesChain(matcher, root, nextIndex, perf, fileRootElements, logger)) return true;
33332
+ if (isFinal) return compoundResult;
33333
+ const chainResult = matchesChain(matcher, root, nextIndex, perf, fileRootElements, logger);
33334
+ if (chainResult !== "no-match") {
33335
+ const merged = mergeMatchResults(compoundResult, chainResult);
33336
+ if (merged === "match") return "match";
33337
+ bestResult = merged;
33338
+ }
33273
33339
  }
33274
33340
  }
33275
33341
  }
33276
- return false;
33342
+ return bestResult;
33343
+ }
33344
+ function mergeMatchResults(a, b) {
33345
+ if (a === "no-match" || b === "no-match") return "no-match";
33346
+ if (a === "conditional" || b === "conditional") return "conditional";
33347
+ return "match";
33277
33348
  }
33278
33349
  function describeCompound(compound) {
33279
33350
  const parts = [];
@@ -33293,15 +33364,16 @@ function describeCompound(compound) {
33293
33364
  return parts.join("") || "*";
33294
33365
  }
33295
33366
  function matchesCompound(node, compound) {
33296
- if (compound.tagName !== null && node.tagName !== compound.tagName) return false;
33367
+ if (compound.tagName !== null && node.tagName !== compound.tagName) return "no-match";
33297
33368
  if (compound.idValue !== null) {
33298
33369
  const id = node.attributes.get("id");
33299
- if (id !== compound.idValue) return false;
33370
+ if (id !== compound.idValue) return "no-match";
33300
33371
  }
33301
- if (!matchesRequiredClasses(compound.classes, node.classTokenSet)) return false;
33302
- if (!matchesRequiredAttributes(compound.attributes, node.attributes)) return false;
33303
- if (!matchesPseudoConstraints(node, compound.pseudo)) return false;
33304
- return true;
33372
+ if (!matchesRequiredClasses(compound.classes, node.classTokenSet)) return "no-match";
33373
+ const attrResult = matchesRequiredAttributes(compound.attributes, node.attributes);
33374
+ if (attrResult === "no-match") return "no-match";
33375
+ if (!matchesPseudoConstraints(node, compound.pseudo)) return "no-match";
33376
+ return attrResult;
33305
33377
  }
33306
33378
  function matchesPseudoConstraints(node, pseudo) {
33307
33379
  if (pseudo.firstChild && node.siblingIndex !== 1) return false;
@@ -33326,7 +33398,7 @@ function matchesPseudoConstraints(node, pseudo) {
33326
33398
  for (let j = 0; j < group.length; j++) {
33327
33399
  const compound = group[j];
33328
33400
  if (compound === void 0) continue;
33329
- if (!matchesCompound(node, compound)) continue;
33401
+ if (matchesCompound(node, compound) === "no-match") continue;
33330
33402
  matched = true;
33331
33403
  break;
33332
33404
  }
@@ -33338,7 +33410,7 @@ function matchesPseudoConstraints(node, pseudo) {
33338
33410
  for (let j = 0; j < group.length; j++) {
33339
33411
  const compound = group[j];
33340
33412
  if (compound === void 0) continue;
33341
- if (!matchesCompound(node, compound)) continue;
33413
+ if (matchesCompound(node, compound) === "no-match") continue;
33342
33414
  return false;
33343
33415
  }
33344
33416
  }
@@ -33983,19 +34055,24 @@ function matchesRequiredClasses(required, actual) {
33983
34055
  return true;
33984
34056
  }
33985
34057
  function matchesRequiredAttributes(required, actual) {
33986
- if (required.length === 0) return true;
34058
+ if (required.length === 0) return "match";
34059
+ let hasConditional = false;
33987
34060
  for (let i = 0; i < required.length; i++) {
33988
34061
  const constraint = required[i];
33989
34062
  if (constraint === void 0) continue;
33990
- if (!actual.has(constraint.name)) return false;
34063
+ if (!actual.has(constraint.name)) return "no-match";
33991
34064
  if (constraint.operator === "exists") continue;
33992
34065
  const actualValue = actual.get(constraint.name);
33993
- if (actualValue === null || actualValue === void 0) return false;
33994
- if (constraint.value === null) return false;
34066
+ if (actualValue === void 0) return "no-match";
34067
+ if (actualValue === null) {
34068
+ hasConditional = true;
34069
+ continue;
34070
+ }
34071
+ if (constraint.value === null) return "no-match";
33995
34072
  if (matchesAttributeValue(actualValue, constraint)) continue;
33996
- return false;
34073
+ return "no-match";
33997
34074
  }
33998
- return true;
34075
+ return hasConditional ? "conditional" : "match";
33999
34076
  }
34000
34077
  function matchesAttributeValue(actualValue, constraint) {
34001
34078
  const expectedValue = constraint.value;
@@ -36361,6 +36438,11 @@ function compareCascadePositions(posA, posB) {
36361
36438
  }
36362
36439
 
36363
36440
  // src/cross-file/layout/cascade-builder.ts
36441
+ var DYNAMIC_ATTRIBUTE_GUARD = {
36442
+ kind: 1 /* Conditional */,
36443
+ conditions: [{ kind: "dynamic-attribute", query: null, key: "dynamic-attribute:*" }],
36444
+ key: "dynamic-attribute:*"
36445
+ };
36364
36446
  var SCROLLABLE_VALUES = /* @__PURE__ */ new Set(["auto", "scroll"]);
36365
36447
  var EMPTY_EXPANSION_RESULT = [];
36366
36448
  function collectMonitoredDeclarations(selector, layerOrder, guard) {
@@ -36435,14 +36517,21 @@ function appendMatchingEdgesFromSelectorIds(ctx, selectorIds, node, applies, app
36435
36517
  if (!selector) {
36436
36518
  throw new Error(`missing selector ${selectorId}`);
36437
36519
  }
36438
- if (!selectorMatchesLayoutElement(metadata.matcher, node, ctx.perf, fileRoots, ctx.logger)) continue;
36520
+ const matchResult = selectorMatchesLayoutElement(metadata.matcher, node, ctx.perf, fileRoots, ctx.logger);
36521
+ if (matchResult === "no-match") continue;
36439
36522
  const edge = {
36440
36523
  selectorId: selector.id,
36441
36524
  specificityScore: selector.specificityScore,
36442
- sourceOrder: selector.rule.sourceOrder
36525
+ sourceOrder: selector.rule.sourceOrder,
36526
+ conditionalMatch: matchResult === "conditional"
36443
36527
  };
36444
36528
  applies.push(edge);
36445
36529
  ctx.perf.matchEdgesCreated++;
36530
+ if (ctx.logger.enabled) {
36531
+ ctx.logger.trace(
36532
+ `[cascade] edge node=${node.key} selector=${selector.id} match=${matchResult} conditional=${edge.conditionalMatch} selector-raw=${selector.raw.slice(0, 80)}`
36533
+ );
36534
+ }
36446
36535
  const existing = appliesByElementNodeMutable.get(node);
36447
36536
  if (existing) {
36448
36537
  existing.push(edge);
@@ -36503,10 +36592,11 @@ function buildCascadeMapForElement(node, edges, monitoredDeclarationsBySelectorI
36503
36592
  const declaration = declarations[j];
36504
36593
  if (!declaration) continue;
36505
36594
  const property = declaration.property;
36595
+ const guardProvenance = edge.conditionalMatch && declaration.guardProvenance.kind === 0 /* Unconditional */ ? DYNAMIC_ATTRIBUTE_GUARD : declaration.guardProvenance;
36506
36596
  const newDeclaration = {
36507
36597
  value: declaration.value,
36508
36598
  source: 0 /* Selector */,
36509
- guardProvenance: declaration.guardProvenance
36599
+ guardProvenance
36510
36600
  };
36511
36601
  const existingPosition = positions.get(property);
36512
36602
  if (existingPosition === void 0) {
@@ -36620,7 +36710,7 @@ function buildConditionalDeltaIndex(elements, appliesByNode, monitoredDeclaratio
36620
36710
  };
36621
36711
  byProperty.set(property, bucket);
36622
36712
  }
36623
- if (declaration.guardProvenance.kind === 1 /* Conditional */) {
36713
+ if (declaration.guardProvenance.kind === 1 /* Conditional */ || currentEdge.conditionalMatch) {
36624
36714
  bucket.conditional.add(expandedEntry.value);
36625
36715
  continue;
36626
36716
  }
@@ -37112,6 +37202,23 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37112
37202
  const moduleResolver = createLayoutModuleResolver(solids, css);
37113
37203
  const componentHostResolver = createLayoutComponentHostResolver(solids, moduleResolver, logger);
37114
37204
  const cssScopeBySolidFile = collectCSSScopeBySolidFile(solids, css, moduleResolver);
37205
+ if (logger.enabled) {
37206
+ for (const [solidFile, scopePaths] of cssScopeBySolidFile) {
37207
+ if (scopePaths.length > 0) {
37208
+ let names = "";
37209
+ for (let k = 0; k < scopePaths.length; k++) {
37210
+ const p = scopePaths[k];
37211
+ if (!p) continue;
37212
+ if (names.length > 0) names += ", ";
37213
+ const slash = p.lastIndexOf("/");
37214
+ names += slash === -1 ? p : p.slice(slash + 1);
37215
+ }
37216
+ logger.trace(`[scope] ${solidFile} \u2192 ${scopePaths.length} CSS files: ${names}`);
37217
+ } else {
37218
+ logger.trace(`[scope] ${solidFile} \u2192 EMPTY (no CSS in scope)`);
37219
+ }
37220
+ }
37221
+ }
37115
37222
  const selectorIndexStartedAt = performance.now();
37116
37223
  const scopedSelectorsBySolidFile = buildScopedSelectorIndexBySolidFile(
37117
37224
  cssScopeBySolidFile,
@@ -37254,6 +37361,22 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37254
37361
  appliesByNode.set(node, edges);
37255
37362
  }
37256
37363
  perf.cascadeBuildMs = performance.now() - cascadeStartedAt;
37364
+ if (logger.enabled) {
37365
+ for (let i = 0; i < elements.length; i++) {
37366
+ const node = elements[i];
37367
+ if (!node) continue;
37368
+ const cascade = cascadeByElementNode.get(node);
37369
+ if (!cascade || cascade.size === 0) continue;
37370
+ const displayDecl = cascade.get("display");
37371
+ const flexDirDecl = cascade.get("flex-direction");
37372
+ if (!displayDecl && !flexDirDecl) continue;
37373
+ const displayGuard = displayDecl?.guardProvenance.kind === 1 /* Conditional */ ? "conditional" : "unconditional";
37374
+ const flexDirGuard = flexDirDecl?.guardProvenance.kind === 1 /* Conditional */ ? "conditional" : "unconditional";
37375
+ logger.trace(
37376
+ `[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(",")}]`
37377
+ );
37378
+ }
37379
+ }
37257
37380
  const snapshotByElementNode = buildSignalSnapshotIndex(elements, cascadeByElementNode, perf);
37258
37381
  const measurementNodeByRootKey = buildMeasurementNodeIndex(elements, childrenByParentNodeMutable, snapshotByElementNode);
37259
37382
  const factIndex = buildElementFactIndex(elements, snapshotByElementNode);
@@ -37271,7 +37394,7 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
37271
37394
  layoutOffsetSignals
37272
37395
  );
37273
37396
  const statefulRuleIndexes = buildStatefulRuleIndexes(css.rules);
37274
- const contextByParentNode = buildContextIndex(childrenByParentNodeMutable, snapshotByElementNode, perf);
37397
+ const contextByParentNode = buildContextIndex(childrenByParentNodeMutable, snapshotByElementNode, perf, logger);
37275
37398
  const cohortIndex = buildCohortIndex({
37276
37399
  childrenByParentNode: childrenByParentNodeMutable,
37277
37400
  contextByParentNode,
@@ -37691,16 +37814,27 @@ function computeFlowParticipationFact(snapshot) {
37691
37814
  hasUnconditionalOutOfFlow: signal.guard.kind === 0 /* Unconditional */ && outOfFlow
37692
37815
  };
37693
37816
  }
37694
- function buildContextIndex(childrenByParentNode, snapshotByElementNode, perf) {
37817
+ function buildContextIndex(childrenByParentNode, snapshotByElementNode, perf, logger) {
37695
37818
  const out = /* @__PURE__ */ new Map();
37819
+ const trace = logger.enabled;
37696
37820
  for (const [parent, children] of childrenByParentNode) {
37697
37821
  if (children.length < 2) continue;
37698
37822
  const snapshot = snapshotByElementNode.get(parent);
37699
37823
  if (!snapshot) {
37700
37824
  throw new Error(`missing parent snapshot for context classification ${parent.key}`);
37701
37825
  }
37702
- out.set(parent, createAlignmentContextForParent(parent, snapshot));
37826
+ const ctx = createAlignmentContextForParent(parent, snapshot);
37827
+ out.set(parent, ctx);
37703
37828
  perf.contextsClassified++;
37829
+ if (trace) {
37830
+ const displaySignal = snapshot.signals.get("display");
37831
+ const flexDirSignal = snapshot.signals.get("flex-direction");
37832
+ const displayDesc = displaySignal ? `${displaySignal.kind}:${displaySignal.kind === "known" ? displaySignal.normalized : "?"}(guard=${displaySignal.guard.kind === 1 /* Conditional */ ? "conditional" : "unconditional"})` : "absent";
37833
+ const flexDirDesc = flexDirSignal ? `${flexDirSignal.kind}:${flexDirSignal.kind === "known" ? flexDirSignal.normalized : "?"}(guard=${flexDirSignal.guard.kind === 1 /* Conditional */ ? "conditional" : "unconditional"})` : "absent";
37834
+ logger.trace(
37835
+ `[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}`
37836
+ );
37837
+ }
37704
37838
  }
37705
37839
  return out;
37706
37840
  }
@@ -39283,7 +39417,13 @@ var MIN_OFFSET_PX_THRESHOLD = 2;
39283
39417
  var siblingAlignmentDetector = {
39284
39418
  id: "sibling-alignment-outlier",
39285
39419
  collect: collectAlignmentCases,
39286
- evaluate(input) {
39420
+ evaluate(input, context) {
39421
+ if (context.logger.enabled) {
39422
+ const ctx = input.context;
39423
+ context.logger.trace(
39424
+ `[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}`
39425
+ );
39426
+ }
39287
39427
  const decision = evaluateAlignmentCase(input);
39288
39428
  if (decision.kind === "reject") {
39289
39429
  return {
@@ -40401,8 +40541,16 @@ function isExemptFromCLS(layout, solidFile, element, property) {
40401
40541
  if (POSITIONED_OFFSET_PROPERTIES.has(property) && flowFact.position !== null && flowFact.position !== "static") {
40402
40542
  return true;
40403
40543
  }
40544
+ if (hasLayoutContainment(node) || node.parentElementNode !== null && hasLayoutContainment(node.parentElementNode)) {
40545
+ return true;
40546
+ }
40404
40547
  return false;
40405
40548
  }
40549
+ function hasLayoutContainment(node) {
40550
+ const contain = node.inlineStyleValues.get("contain");
40551
+ if (contain === void 0) return false;
40552
+ return contain === "layout" || contain === "strict" || contain === "content" || contain.includes("layout");
40553
+ }
40406
40554
  function hasUnstableLayoutDelta(property, node) {
40407
40555
  const unwrapped = unwrapTypeWrapper(node);
40408
40556
  if (isStaticComparable(property, unwrapped)) return false;