Skip to content

Vue Devtools doesn't work with this wrapper (workaround inside) #96

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wbern opened this issue Jan 21, 2021 · 0 comments
Open

Vue Devtools doesn't work with this wrapper (workaround inside) #96

wbern opened this issue Jan 21, 2021 · 0 comments

Comments

@wbern
Copy link

wbern commented Jan 21, 2021

Vue Devtools doesn't work with this tool.

I'm sharing a workaround that works with both this wrapper and vue-custom-element, here.

  • Just set var myRootElementSelector = 'body > #root'; to the relevant place for you.
  • You can add this to your index.html, or remove the script tags and put it in your code.
<script>
    var extractVueComponentFromVueMountPoint = (c) => {
        if (c.constructor.name === 'Vue') {
            return c.$children[0];
        }
        return c;
    };

    var getVueInstance = (el) => el.__vue__;

    var isWebComponent = (el) => !!el.vueComponent;
    var isVueCustomElement = (el) => !!el.__vue_custom_element__;

    var walk = (treeNode) => {
        var traverse = (elements) => {
            elements.forEach((el) => {
                if (isVueCustomElement(el) || isWebComponent(el)) {
                    // we found a custom element,
                    // this _could_ be the best way to "ignore" the custom element layer.
                    el.__vue__ = isWebComponent(el) ?
                        el.vueComponent : el.__vue_custom_element__.$children[0].$children[0];

                    // shouldn't be necessary
                    // getVueInstance(el).$options.name =
                    //   "Custom Element: " +
                    //   el.tagName.toLowerCase().replace(/^[A-z]/, a => a.toUpperCase());
                }

                if (getVueInstance(el)) {
                    // we found a vue component, extract it,
                    // create a new tree node level,
                    // and finally keep searching from it
                    var childTreeNode = {
                        ref: extractVueComponentFromVueMountPoint(
                            getVueInstance(el),
                        ),
                        tagName: el.tagName.toLowerCase(),
                        children: [],
                    };
                    treeNode.children.push(childTreeNode);

                    walk(childTreeNode);
                } else if (el.children && el.children.length > 0) {
                    // keep looking downwards for vue components
                    traverse(el.children);
                }
            });
        };

        if (treeNode.ref.$el.children) {
            traverse(treeNode.ref.$el.children);
        }
    };

    var repair = (treeNode) => {
        // now we "restore" the $children reference across the components
        // so that Vue Devtools can find them properly. This is hacky as hell. :-)

        treeNode.children.forEach((childTreeNode) => {
            if (!treeNode.ref.$children.includes(childTreeNode.ref)) {
                treeNode.ref.$children.push(childTreeNode.ref);
            }

            // keep traversing downwards
            repair(childTreeNode);
        });
    };

    // initiates everything. can be run multiple times.
    var glueThePage = (rootSelector, forceVueDevtoolsRefresh = false) => {
        var root = extractVueComponentFromVueMountPoint(
            document.querySelector(rootSelector).__vue__,
        );

        var tree = {
            ref: extractVueComponentFromVueMountPoint(root),
            tagName: root.$el.tagName.toLowerCase(),
            children: [],
        };

        // find all the vue components, put in the "tree" variable
        walk(tree);

        // put up links between the vue components, across vue mount points.
        repair(tree);

        let hookIsAvailable = !!window.__VUE_DEVTOOLS_GLOBAL_HOOK__;
        let vueDevtoolsIsOnComponentsTab =
            hookIsAvailable &&
            window.__VUE_DEVTOOLS_GLOBAL_HOOK__.currentTab === 'components';

        if (vueDevtoolsIsOnComponentsTab || forceVueDevtoolsRefresh) {
            if (hookIsAvailable) {
                window.__VUE_DEVTOOLS_GLOBAL_HOOK__.emit('flush');
            } else {
                throw new Error(
                    'Cannot force a refresh in Vue Devtools as it does not seem to be available.',
                );
            }
        }
    };

    // this is the root element of where your first Vue mount point is
    // for storybook, it should be 'body > #root'
    var myRootElementSelector = 'body > #root';

    // lets us adapt to changes, and makes sure we don't do scanning unnecessarily early.
    window.addEventListener(
        'DOMContentLoaded',
        () => {
            // check if the root element is already available
            if (
                document.querySelector(myRootElementSelector) &&
                document.querySelector(myRootElementSelector).__vue__
            ) {
                glueThePage(myRootElementSelector);
            }
            setInterval(() => { glueThePage(myRootElementSelector) }, 500);
            // listen for future changes
            var subscriber = new MutationObserver((mutations, observer) => {
                mutations.forEach((mutation) =>
                    mutation.addedNodes.forEach((node) => {
                        let isRootElement =
                            document.querySelector(myRootElementSelector) ===
                            node;

                        let isVueInstance = !!node.__vue__;
                        let isCustomElement = isWebComponent(node) || isVueCustomElement(node);

                        if (isRootElement || isVueInstance || isCustomElement) {
                            glueThePage(myRootElementSelector);
                        }
                    }),
                );
            }).observe(document.body, { childList: true, subtree: true });
        },
        false,
    );
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant