* Replace text that spans across multiple child elements. * * When DOM textContent is "do software stuff" but the JSX is * "do software stuff", no single JSXText node contains * the full substring. This function builds a concatenated text from all * children, finds the match, t
(parentNode: any, oldSub: string, newSub: string, source?: string, matchStartHint?: number)
| 1237 | * - Last affected text node: trim matched portion from start |
| 1238 | */ |
| 1239 | function replaceCrossElementText(parentNode: any, oldSub: string, newSub: string, source?: string, matchStartHint?: number): boolean { |
| 1240 | const children = parentNode.children; |
| 1241 | if (!children || children.length === 0) return false; |
| 1242 | const segments = buildImmediateTextSegments(parentNode, source); |
| 1243 | const concat = getSegmentsText(segments); |
| 1244 | |
| 1245 | // Find oldSub in the concatenated text |
| 1246 | const hintedMatch = matchStartHint != null && concat.slice(matchStartHint, matchStartHint + oldSub.length) === oldSub |
| 1247 | ? matchStartHint |
| 1248 | : -1; |
| 1249 | const matchStart = hintedMatch !== -1 ? hintedMatch : concat.indexOf(oldSub); |
| 1250 | if (matchStart === -1) return false; |
| 1251 | const matchEnd = matchStart + oldSub.length; |
| 1252 | |
| 1253 | let cursor = 0; |
| 1254 | let firstChildIndex = -1; |
| 1255 | let lastChildIndex = -1; |
| 1256 | let prefix = ""; |
| 1257 | let suffix = ""; |
| 1258 | |
| 1259 | for (const segment of segments) { |
| 1260 | const segmentStart = cursor; |
| 1261 | const segmentEnd = cursor + segment.text.length; |
| 1262 | cursor = segmentEnd; |
| 1263 | |
| 1264 | if (segmentEnd <= matchStart || segmentStart >= matchEnd) continue; |
| 1265 | |
| 1266 | if (segment.type === "boundary") { |
| 1267 | if (firstChildIndex === -1) firstChildIndex = segment.leftChildIndex; |
| 1268 | lastChildIndex = segment.rightChildIndex; |
| 1269 | continue; |
| 1270 | } |
| 1271 | |
| 1272 | if (firstChildIndex === -1) { |
| 1273 | firstChildIndex = segment.childIndex; |
| 1274 | prefix = segment.text.slice(0, Math.max(0, matchStart - segmentStart)); |
| 1275 | } |
| 1276 | |
| 1277 | lastChildIndex = segment.childIndex; |
| 1278 | suffix = segment.text.slice(Math.max(0, matchEnd - segmentStart)); |
| 1279 | } |
| 1280 | |
| 1281 | if (firstChildIndex === -1 || lastChildIndex === -1) return false; |
| 1282 | |
| 1283 | logger.debug(`[crossElement] match spans children[${firstChildIndex}..${lastChildIndex}], replacing "${oldSub.slice(0, 40)}" → "${newSub.slice(0, 40)}"`); |
| 1284 | return flattenRangeIntoText(parentNode, firstChildIndex, lastChildIndex, prefix + newSub + suffix); |
| 1285 | } |
| 1286 | |
| 1287 | /** |
| 1288 | * Find the differing substring between two strings. |
no test coverage detected