mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-12 12:09:26 +00:00
129 lines
4.1 KiB
JavaScript
129 lines
4.1 KiB
JavaScript
|
|
/** TODO: This should probably get abstracted into an actual package. This is listens to the "lit-hydration-error" and then will add a button to show a dialog of the diff. */
|
||
|
|
(async () => {
|
||
|
|
const hostname = new URL(document.baseURI).hostname;
|
||
|
|
|
||
|
|
// Only diff on localhost. We dont need to show hydration errors on main site. Only locally.
|
||
|
|
if (hostname !== 'localhost') {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const { diffLines } = await import('https://cdn.jsdelivr.net/npm/diff@5.2.0/+esm');
|
||
|
|
const { getDiffableHTML } = await import(
|
||
|
|
'https://cdn.jsdelivr.net/npm/@open-wc/semantic-dom-diff@0.20.1/get-diffable-html.js/+esm'
|
||
|
|
);
|
||
|
|
|
||
|
|
function wrap(el, wrapper) {
|
||
|
|
el.parentNode.insertBefore(wrapper, el);
|
||
|
|
wrapper.appendChild(el);
|
||
|
|
}
|
||
|
|
|
||
|
|
function handleLitHydrationError(e) {
|
||
|
|
const element = e.target;
|
||
|
|
const scratch = document.createElement('div');
|
||
|
|
const node = element.cloneNode(true);
|
||
|
|
scratch.append(node);
|
||
|
|
document.body.append(scratch);
|
||
|
|
customElements.upgrade(node);
|
||
|
|
node.updateComplete.then(() => {
|
||
|
|
// Render styles.
|
||
|
|
const elementStyles = element.constructor.elementStyles;
|
||
|
|
const finalStyles = [];
|
||
|
|
if (elementStyles !== undefined && elementStyles.length > 0) {
|
||
|
|
for (const style of elementStyles) {
|
||
|
|
finalStyles.push(style.cssText);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
let innerHTML = scratch.firstElementChild?.shadowRoot.innerHTML;
|
||
|
|
|
||
|
|
if (finalStyles?.length) {
|
||
|
|
const styleTag = `<style>${finalStyles.join('\n')}</style>`;
|
||
|
|
innerHTML = styleTag + '\n' + innerHTML;
|
||
|
|
}
|
||
|
|
|
||
|
|
const clientHTML = getDiffableHTML(innerHTML);
|
||
|
|
const serverHTML = getDiffableHTML(element.shadowRoot?.innerHTML);
|
||
|
|
|
||
|
|
const diffDebugger = document.createElement('div');
|
||
|
|
diffDebugger.className = 'diff-debugger';
|
||
|
|
|
||
|
|
diffDebugger.innerHTML = `
|
||
|
|
<button class="diff-dialog-toggle">
|
||
|
|
Show Hydration Mismatch
|
||
|
|
</button>
|
||
|
|
<wa-dialog class="diff-dialog" with-header light-dismiss>
|
||
|
|
<div class="diff-grid">
|
||
|
|
<div>
|
||
|
|
<div>Server</div>
|
||
|
|
<pre class="diff-server"><code></code></pre>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<div>Client</div>
|
||
|
|
<pre class="diff-client"><code></code></pre>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<div>Diff</div>
|
||
|
|
<pre class="diff-viewer"><code></code></pre>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</wa-dialog>
|
||
|
|
`;
|
||
|
|
|
||
|
|
element.focus();
|
||
|
|
wrap(element, diffDebugger);
|
||
|
|
|
||
|
|
diffDebugger.querySelector('.diff-server > code').textContent = serverHTML;
|
||
|
|
diffDebugger.querySelector('.diff-client > code').textContent = clientHTML;
|
||
|
|
const diffViewer = diffDebugger.querySelector('.diff-viewer > code');
|
||
|
|
diffViewer.innerHTML = '';
|
||
|
|
diffViewer.appendChild(
|
||
|
|
createDiff({
|
||
|
|
serverHTML,
|
||
|
|
clientHTML
|
||
|
|
})
|
||
|
|
);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
function createDiff({ serverHTML, clientHTML }) {
|
||
|
|
const diff = diffLines(serverHTML, clientHTML, {
|
||
|
|
ignoreWhitespace: false,
|
||
|
|
newLineIsToken: true
|
||
|
|
});
|
||
|
|
const fragment = document.createDocumentFragment();
|
||
|
|
for (var i = 0; i < diff.length; i++) {
|
||
|
|
if (diff[i].added && diff[i + 1] && diff[i + 1].removed) {
|
||
|
|
var swap = diff[i];
|
||
|
|
diff[i] = diff[i + 1];
|
||
|
|
diff[i + 1] = swap;
|
||
|
|
}
|
||
|
|
|
||
|
|
var node;
|
||
|
|
if (diff[i].removed) {
|
||
|
|
node = document.createElement('del');
|
||
|
|
node.appendChild(document.createTextNode(diff[i].value));
|
||
|
|
} else if (diff[i].added) {
|
||
|
|
node = document.createElement('ins');
|
||
|
|
node.appendChild(document.createTextNode(diff[i].value));
|
||
|
|
} else {
|
||
|
|
node = document.createTextNode(diff[i].value);
|
||
|
|
}
|
||
|
|
fragment.appendChild(node);
|
||
|
|
}
|
||
|
|
|
||
|
|
return fragment;
|
||
|
|
}
|
||
|
|
|
||
|
|
function handleDialogToggle(e) {
|
||
|
|
const button = e.composedPath().find(el => {
|
||
|
|
return el.classList && el.classList.contains('diff-dialog-toggle');
|
||
|
|
});
|
||
|
|
|
||
|
|
if (button) {
|
||
|
|
button.parentElement.querySelector('.diff-dialog').open = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
document.addEventListener('lit-hydration-error', handleLitHydrationError);
|
||
|
|
document.addEventListener('click', handleDialogToggle);
|
||
|
|
})();
|