( node: Node, availableWidth: number, availableHeight: number, widthMode: MeasureMode, heightMode: MeasureMode, ownerWidth: number, ownerHeight: number, performLayout: boolean, // When true, ignore style dimension on this axis — the flex container // has already determined the main size (flex-basis + grow/shrink result). forceWidth = false, forceHeight = false, )
| 1056 | } |
| 1057 | |
| 1058 | function layoutNode( |
| 1059 | node: Node, |
| 1060 | availableWidth: number, |
| 1061 | availableHeight: number, |
| 1062 | widthMode: MeasureMode, |
| 1063 | heightMode: MeasureMode, |
| 1064 | ownerWidth: number, |
| 1065 | ownerHeight: number, |
| 1066 | performLayout: boolean, |
| 1067 | // When true, ignore style dimension on this axis — the flex container |
| 1068 | // has already determined the main size (flex-basis + grow/shrink result). |
| 1069 | forceWidth = false, |
| 1070 | forceHeight = false, |
| 1071 | ): void { |
| 1072 | _yogaNodesVisited++ |
| 1073 | const style = node.style |
| 1074 | const layout = node.layout |
| 1075 | |
| 1076 | // Dirty-flag skip: clean subtree + matching inputs → layout object already |
| 1077 | // holds the answer. A cached layout result also satisfies a measure request |
| 1078 | // (positions are a superset of dimensions); the reverse does not hold. |
| 1079 | // Same-generation entries are fresh regardless of isDirty_ — they were |
| 1080 | // computed THIS calculateLayout, the subtree hasn't changed since. |
| 1081 | // Previous-generation entries need !isDirty_ (a dirty node's cache from |
| 1082 | // before the dirty is stale). |
| 1083 | // sameGen bypass only for MEASURE calls — a layout-pass cache hit would |
| 1084 | // skip the child-positioning recursion (STEP 5), leaving children at |
| 1085 | // stale positions. Measure calls only need w/h which the cache stores. |
| 1086 | const sameGen = node._cGen === _generation && !performLayout |
| 1087 | if (!node.isDirty_ || sameGen) { |
| 1088 | if ( |
| 1089 | !node.isDirty_ && |
| 1090 | node._hasL && |
| 1091 | node._lWM === widthMode && |
| 1092 | node._lHM === heightMode && |
| 1093 | node._lFW === forceWidth && |
| 1094 | node._lFH === forceHeight && |
| 1095 | sameFloat(node._lW, availableWidth) && |
| 1096 | sameFloat(node._lH, availableHeight) && |
| 1097 | sameFloat(node._lOW, ownerWidth) && |
| 1098 | sameFloat(node._lOH, ownerHeight) |
| 1099 | ) { |
| 1100 | _yogaCacheHits++ |
| 1101 | layout.width = node._lOutW |
| 1102 | layout.height = node._lOutH |
| 1103 | return |
| 1104 | } |
| 1105 | // Multi-entry cache: scan for matching inputs, restore cached w/h on hit. |
| 1106 | // Covers the scroll case where a dirty ancestor's measure→layout cascade |
| 1107 | // produces N>1 distinct input combos per clean child — the single _hasL |
| 1108 | // slot thrashed, forcing full subtree recursion. With 500-message |
| 1109 | // scrollbox and one dirty leaf, this took dirty-leaf relayout from |
| 1110 | // 76k layoutNode calls (21.7×nodes) to 4k (1.2×nodes), 6.86ms → 550µs. |
| 1111 | // Same-generation check covers fresh-mounted (dirty) nodes during |
| 1112 | // virtual scroll — the dirty chain invokes them ≥2^depth times, first |
| 1113 | // call writes cache, rest hit: 105k visits → ~10k for 1593-node tree. |
| 1114 | if (node._cN > 0 && (sameGen || !node.isDirty_)) { |
| 1115 | const cIn = node._cIn! |
no test coverage detected