Highlights server-side rendered Suspense boundaries.
- Create Chrome Devtools snippet with content of source file
- Run snippet
| let treeWalker = document.createTreeWalker(document, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT, null, null); | |
| let node = null; | |
| let boundariesNodesList = []; | |
| let boundariesStack = [] | |
| let currentBoundary = []; | |
| while ((node = treeWalker.nextNode()) !== null) { | |
| if (node.nodeType === Node.COMMENT_NODE) { | |
| if (isSuspenseBoundaryStart(node)) { | |
| boundariesStack.push(currentBoundary) | |
| currentBoundary = [node] | |
| } else if (isSuspenseBoundaryEnd(node)) { | |
| currentBoundary.push(node); | |
| boundariesNodesList.push(currentBoundary) | |
| currentBoundary = boundariesStack.pop() | |
| } | |
| } else { | |
| currentBoundary.push(node); | |
| } | |
| } | |
| const boundaries = boundariesNodesList.map(boundaryNodes => { | |
| const boundaryStart = boundaryNodes.at(0) | |
| const boundaryEnd = boundaryNodes.at(-1); | |
| const nodes = boundaryNodes.slice(1, -1); | |
| const boundaryChildren = [] | |
| let currentNode = null; | |
| boundaryNodes.forEach(node => { | |
| if (node.nodeType === Node.COMMENT_NODE) { | |
| } else if (currentNode === null) { | |
| currentNode = node; | |
| } else if (!currentNode.contains(node)) { | |
| boundaryChildren.push(currentNode) | |
| currentNode = node; | |
| } | |
| }) | |
| boundaryChildren.push(currentNode) | |
| const range = new Range(); | |
| range.setStartAfter(boundaryStart); | |
| range.setEndBefore(boundaryEnd); | |
| return { range, start: boundaryStart, end: boundaryEnd, children: boundaryChildren } | |
| }) | |
| console.log(boundaries) | |
| try { | |
| clearBoundaryHighlights() | |
| } catch (error) { | |
| console.error('Trouble clearing pre-existing boundaries:', error) | |
| } | |
| boundaries.forEach(highlightBoundary); | |
| function isSuspenseBoundaryStart(commentNode) { | |
| return commentNode.textContent === '$' | |
| } | |
| function isSuspenseBoundaryEnd(commentNode) { | |
| return commentNode.textContent === '/$' | |
| } | |
| function clearBoundaryHighlights() { | |
| document.querySelectorAll('.boundary-highlight').forEach(highlight => { | |
| highlight.remove(); | |
| }); | |
| } | |
| function highlightBoundary({ range }) { | |
| const { top, left, width, height} = range.getBoundingClientRect(); | |
| if (width === 0 && height === 0) { | |
| return; | |
| } | |
| const highlight = document.createElement('div'); | |
| highlight.className = 'boundary-highlight' | |
| highlight.style.position = 'absolute'; | |
| highlight.style.pointerEvents = 'none'; | |
| highlight.style.backgroundColor = 'transparent'; | |
| highlight.style.border = '2px solid red'; | |
| highlight.style.width = `${width}px`; | |
| highlight.style.height = `${height}px`; | |
| highlight.style.top = `${window.scrollY + top}px`; | |
| highlight.style.left = `${window.scrollX + left}px`; | |
| document.body.appendChild(highlight); | |
| console.log(highlight) | |
| } |