next
Version:
The React Framework
124 lines (123 loc) • 4.39 kB
JavaScript
/* @ts-check */ /**
* Style injection mechanism for Next.js devtools with shadow DOM support
* Handles caching of style elements when the nextjs-portal shadow root is not available
*/ // Global cache for style elements when shadow root is not available
;
if (typeof window !== 'undefined') {
window._nextjsDevtoolsStyleCache = window._nextjsDevtoolsStyleCache || {
pendingElements: [],
isObserving: false,
lastInsertedElement: null,
cachedShadowRoot: null
};
}
/**
* @returns {ShadowRoot | null}
*/ function getShadowRoot() {
const cache = window._nextjsDevtoolsStyleCache;
// Return cached shadow root if available
if (cache.cachedShadowRoot) {
return cache.cachedShadowRoot;
}
// Query the DOM and cache the result if found
const portal = document.querySelector('nextjs-portal');
const shadowRoot = (portal == null ? void 0 : portal.shadowRoot) || null;
if (shadowRoot) {
cache.cachedShadowRoot = shadowRoot;
}
return shadowRoot;
}
/**
* @param {HTMLElement} element
* @param {ShadowRoot} shadowRoot
*/ function insertElementIntoShadowRoot(element, shadowRoot) {
const cache = window._nextjsDevtoolsStyleCache;
if (!cache.lastInsertedElement) {
shadowRoot.insertBefore(element, shadowRoot.firstChild);
} else if (cache.lastInsertedElement.nextSibling) {
shadowRoot.insertBefore(element, cache.lastInsertedElement.nextSibling);
} else {
shadowRoot.appendChild(element);
}
cache.lastInsertedElement = element;
}
function flushCachedElements() {
const cache = window._nextjsDevtoolsStyleCache;
const shadowRoot = getShadowRoot();
if (!shadowRoot) {
return;
}
cache.pendingElements.forEach((element)=>{
insertElementIntoShadowRoot(element, shadowRoot);
});
cache.pendingElements = [];
}
function startObservingForPortal() {
const cache = window._nextjsDevtoolsStyleCache;
if (cache.isObserving) {
return;
}
cache.isObserving = true;
// First check if the portal already exists
const shadowRoot = getShadowRoot() // This will cache it if found
;
if (shadowRoot) {
flushCachedElements();
return;
}
// Set up MutationObserver to watch for the portal element
const observer = new MutationObserver((mutations)=>{
if (mutations.length === 0 || mutations[0].addedNodes.length === 0) {
return;
}
// Check if mutation is script[data-nextjs-dev-overlay] tag, which is the
// parent of the nextjs-portal element
const mutationNode = mutations[0].addedNodes[0];
let portalNode = null;
if (// app router: body > script[data-nextjs-dev-overlay] > nextjs-portal
mutationNode.tagName === 'SCRIPT' && mutationNode.getAttribute('data-nextjs-dev-overlay')) {
portalNode = mutationNode.firstChild;
} else if (// pages router: body > nextjs-portal
mutationNode.tagName === 'NEXTJS-PORTAL') {
portalNode = mutationNode;
}
if (!portalNode) {
return;
}
// Wait until shadow root is available
const checkShadowRoot = ()=>{
if (getShadowRoot()) {
flushCachedElements();
observer.disconnect();
cache.isObserving = false;
} else {
// Try again after a short delay
setTimeout(checkShadowRoot, 20);
}
};
checkShadowRoot();
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
/**
* @param {HTMLElement} element
*/ function insertAtTop(element) {
// Add special recognizable data prop to element
element.setAttribute('data-nextjs-dev-tool-style', 'true');
const shadowRoot = getShadowRoot();
if (shadowRoot) {
// Shadow root is available, insert directly
insertElementIntoShadowRoot(element, shadowRoot);
} else {
// Shadow root not available, cache the element
const cache = window._nextjsDevtoolsStyleCache;
cache.pendingElements.push(element);
// Start observing for the portal if not already observing
startObservingForPortal();
}
}
module.exports = insertAtTop;
//# sourceMappingURL=devtool-style-inject.js.map