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) | |
} |