alpine-collective / alpinejs-devtools

Chrome/Firefox DevTools extension for debugging Alpine.js applications.
MIT License
523 stars 18 forks source link

Track custom events being `$dispatch`-ed #48

Open HugoDF opened 4 years ago

HugoDF commented 4 years ago

This is again inspired by Vue.js Devtool's "events" tab, obviously Alpine.js doesn't use synthetic events or emit events to communicate between components.

Instead we should register a top-level listener for events being dispatched with $dispatch (which we can detect) eg. we can read @click="$dispatch('my-custom-event') which means we should listen to 'my-custom-event' at the top-level (using document.addEventListener) and then report their contents in a tab.

Any thoughts/ideas limitations you can think of @ryangjchandler @Te7a-Houdini ?

We probably want a new panel for this.

Update 1

With Alpine latest we can overwrite $dispatch and instrument it (see https://github.com/amaelftah/alpinejs-devtools/issues/48#issuecomment-732133163):

It turns out that it's possible to overwrite a magic property πŸ‘€ , so we can inject some code that will replace $dispatch and send debug info on call πŸ‘ https://codepen.io/hugodf/pen/xxOvqpg

Note on this: we should check that the suggested $dispatch function override works the same as regular Alpine one, one thing that might be different is which element dispatchEvent is being called on

Update 2

Overwriting $dispatch doesn't behave the same as Alpine implementation see https://codepen.io/hugodf/pen/GRjKgNO?editors=1111 the overriden $event.target is not correct (it's the root of the component, not the element from which $dispatch is called).

A way to get this to work is to monkey-patch dispatchEvent with something like this:

const _dispatchEvent = EventTarget.prototype.dispatchEvent;
EventTarget.prototype.dispatchEvent = function(...args) {
  // send a message to devtools
  // `this` is the element from which `$dispatch` is called
  // `args[0]` is the event name, `args[1]` is the options (including `options.detail`)
  console.log(this, args);

  return _dispatchEvent.apply(this, args);
}

This has the advantage of not being Alpine.js specific (eg. if custom events are sent from vanilla JS) + not locking us into a minimum Alpine version.

HugoDF commented 3 years ago

Here's the relevant code in Alpine, if we can hook into CustomEvent or el.dispatchEvent that could work

https://github.com/alpinejs/alpine/blob/3c3e8b801a7b00a982dd54290cf656f051e915d9/src/component.js#L369

ryangjchandler commented 3 years ago

When you say hook in, do you mean monkey-patching the prototype?

HugoDF commented 3 years ago

When you say hook in, do you mean monkey-patching the prototype?

Pretty much hahaha

Otherwise we would have to read the DOM and parse custom event names since I don't think you can just register a catch-all event handler for it.

Other ways we could go is a change in the Alpine side which detects devtools and integrates that way or maybe overwriting the $dispatch magic property somehow?

ryangjchandler commented 3 years ago

When you say hook in, do you mean monkey-patching the prototype?

Pretty much hahaha

Yeah, I think that could work. Would just be a case of extending EventTarget.dispatchEvent and checking that the element in question has a parent or itself with __x property?

ryangjchandler commented 3 years ago

I think we should do the page -> devtools communication refactor first.

HugoDF commented 3 years ago

@ryangjchandler Agreed re- the refactor.

It turns out that it's possible to overwrite a magic property πŸ‘€ , so we can inject some code that will replace $dispatch and send debug info on call πŸ‘

https://codepen.io/hugodf/pen/xxOvqpg

Other interesting note is that $dispatch basically doesn't need to be in core any more πŸ˜‚

ryangjchandler commented 3 years ago

@HugoDF Legendary, hadn't even thought about that.

KevinBatdorf commented 3 years ago

It turns out that it's possible to overwrite a magic property πŸ‘€

Oh nice! We can implement both $refs and $dispatch both now :D

HugoDF commented 3 years ago

It turns out that it's possible to overwrite a magic property πŸ‘€

Oh nice! We can implement both $refs and $dispatch both now :D

well see this update

Overwriting $dispatch doesn't behave the same as Alpine implementation see https://codepen.io/hugodf/pen/GRjKgNO?editors=1111 the overriden $event.target is not correct (it's the root of the component, not the element from which $dispatch is called).