diff --git a/packages/shell-chrome/src/backend.js b/packages/shell-chrome/src/backend.js index 83013a2f..896e49ea 100644 --- a/packages/shell-chrome/src/backend.js +++ b/packages/shell-chrome/src/backend.js @@ -26,172 +26,171 @@ function serializeDataProperty(value) { } } -class AlpineDevtoolsBackend { - constructor() { - this.components = [] - this.uuid = 1 - this.hoverElement = null - this.observer = null - this._stopMutationObserver = false - } - - runWithMutationPaused(cb) { - this._stopMutationObserver = true - cb() - setTimeout(() => { +function init() { + class AlpineDevtoolsBackend { + constructor() { + this.components = [] + this.uuid = 1 + this.hoverElement = null + this.observer = null this._stopMutationObserver = false - }, 10) - } + } - start() { - this.getAlpineVersion() - this.discoverComponents() + runWithMutationPaused(cb) { + this._stopMutationObserver = true + cb() + setTimeout(() => { + this._stopMutationObserver = false + }, 10) + } - // Watch on the body for injected components. This is lightweight - // as work is only done if there are components added/removed - this.observeNode(document.querySelector('body')) - } + start() { + this.getAlpineVersion() + this.discoverComponents() - shutdown() { - this.cleanupHoverElement() - this.disconnectObserver() - } + // Watch on the body for injected components. This is lightweight + // as work is only done if there are components added/removed + this.observeNode(document.querySelector('body')) + } - discoverComponents() { - const rootEls = document.querySelectorAll('[x-data]') - // Exit early if no components have been added or removed - const allComponentsInitialized = Object.values(rootEls).every((e) => e.__alpineDevtool) - if (this.components.length === rootEls.length && allComponentsInitialized) { - return false + shutdown() { + this.cleanupHoverElement() + this.disconnectObserver() } - this.components = [] + discoverComponents() { + const rootEls = document.querySelectorAll('[x-data]') + // Exit early if no components have been added or removed + const allComponentsInitialized = Object.values(rootEls).every((e) => e.__alpineDevtool) + if (this.components.length === rootEls.length && allComponentsInitialized) { + return false + } - rootEls.forEach((rootEl, index) => { - Alpine.initializeComponent(rootEl) + this.components = [] - if (!rootEl.__alpineDevtool) { - rootEl.__alpineDevtool = {} - } + rootEls.forEach((rootEl, index) => { + Alpine.initializeComponent(rootEl) - if (!rootEl.__alpineDevtool.id) { - rootEl.__alpineDevtool.id = this.uuid++ - window[`$x${rootEl.__alpineDevtool.id - 1}`] = rootEl.__x - } + if (!rootEl.__alpineDevtool) { + rootEl.__alpineDevtool = {} + } - let depth = 0 + if (!rootEl.__alpineDevtool.id) { + rootEl.__alpineDevtool.id = this.uuid++ + window[`$x${rootEl.__alpineDevtool.id - 1}`] = rootEl.__x + } - if (index != 0) { - rootEls.forEach((innerElement, innerIndex) => { - if (index == innerIndex) { - return false - } + let depth = 0 - if (innerElement.contains(rootEl)) { - depth = depth + 1 - } - }) - } + if (index != 0) { + rootEls.forEach((innerElement, innerIndex) => { + if (index == innerIndex) { + return false + } - const data = Object.entries(rootEl.__x.getUnobservedData()).reduce((acc, [key, value]) => { - acc[key] = serializeDataProperty(value) + if (innerElement.contains(rootEl)) { + depth = depth + 1 + } + }) + } - return acc - }, {}) + const data = Object.entries(rootEl.__x.getUnobservedData()).reduce((acc, [key, value]) => { + acc[key] = serializeDataProperty(value) - this.components.push({ - name: getComponentName(rootEl), - depth: depth, - data: data, - index: index, - id: rootEl.__alpineDevtool.id, + return acc + }, {}) + + this.components.push({ + name: getComponentName(rootEl), + depth: depth, + data: data, + index: index, + id: rootEl.__alpineDevtool.id, + }) }) - }) - - window.postMessage( - { - source: 'alpine-devtools-backend', - payload: { - // stringify to unfurl proxies - // there's no way to detect proxies but - // we need to get rid of them - // this avoids `DataCloneError: The object could not be cloned.` - // see https://github.com/Te7a-Houdini/alpinejs-devtools/issues/17 - components: JSON.stringify(this.components), - type: 'render-components', - }, - }, - '*', - ) - } - getAlpineVersion() { - window.postMessage( - { - source: 'alpine-devtools-backend', - payload: { - version: window.Alpine.version, - type: 'set-version', + window.postMessage( + { + source: 'alpine-devtools-backend', + payload: { + // stringify to unfurl proxies + // there's no way to detect proxies but + // we need to get rid of them + // this avoids `DataCloneError: The object could not be cloned.` + // see https://github.com/Te7a-Houdini/alpinejs-devtools/issues/17 + components: JSON.stringify(this.components), + type: 'render-components', + }, }, - }, - '*', - ) - } + '*', + ) + } - observeNode(node) { - const observerOptions = { - childList: true, - attributes: true, - subtree: true, + getAlpineVersion() { + window.postMessage( + { + source: 'alpine-devtools-backend', + payload: { + version: window.Alpine.version, + type: 'set-version', + }, + }, + '*', + ) } - this.observer = new MutationObserver((_mutations) => { - if (!this._stopMutationObserver) { - this.discoverComponents() + observeNode(node) { + const observerOptions = { + childList: true, + attributes: true, + subtree: true, } - }) - this.observer.observe(node, observerOptions) - } + this.observer = new MutationObserver((_mutations) => { + if (!this._stopMutationObserver) { + this.discoverComponents() + } + }) - disconnectObserver() { - if (this.observer) { - this.observer.disconnect() - this.observer = null + this.observer.observe(node, observerOptions) } - } - addHoverElement(target) { - this.cleanupHoverElement() - - let hoverElement = document.createElement('div') - let bounds = target.getBoundingClientRect() - - Object.assign(hoverElement.style, { - position: 'absolute', - top: `${bounds.top}px`, - left: `${bounds.left}px`, - width: `${bounds.width}px`, - height: `${bounds.height}px`, - backgroundColor: 'rgba(104, 182, 255, 0.35)', - borderRadius: '4px', - zIndex: 9999, - }) - hoverElement.dataset.testid = 'hover-element' - - this.hoverElement = hoverElement - document.body.appendChild(this.hoverElement) - } + disconnectObserver() { + if (this.observer) { + this.observer.disconnect() + this.observer = null + } + } - cleanupHoverElement() { - if (this.hoverElement) { - this.hoverElement.remove() - this.hoverElement = null + addHoverElement(target) { + this.cleanupHoverElement() + + let hoverElement = document.createElement('div') + let bounds = target.getBoundingClientRect() + + Object.assign(hoverElement.style, { + position: 'absolute', + top: `${bounds.top}px`, + left: `${bounds.left}px`, + width: `${bounds.width}px`, + height: `${bounds.height}px`, + backgroundColor: 'rgba(104, 182, 255, 0.35)', + borderRadius: '4px', + zIndex: 9999, + }) + hoverElement.dataset.testid = 'hover-element' + + this.hoverElement = hoverElement + document.body.appendChild(this.hoverElement) } - } -} -function init() { + cleanupHoverElement() { + if (this.hoverElement) { + this.hoverElement.remove() + this.hoverElement = null + } + } + } // using a function scope to avoid running into issues on re-injection const devtoolsBackend = new AlpineDevtoolsBackend() window.addEventListener('message', handshake)