Fix x-ignore being added after using Alpine.morph #47
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
When using Alpine.morph (used by Livewire so it's quite a common case for the community) async-alpine re-adds an x-ignore attribute on the component node (which gets picked by the Alpine mutation observer and re-adds the internal _x_ignore property).
This happens because morph reprocesses all Alpine directives on a detached HTML subtree containing the html to morph to and, when everything is reinitialised, instead of replacing the html completely, it only patches nodes/attributes where needed so it doesn't lose hidden metadata (datastacks, internal x* props, html state for input fields, etc) and doesn't trigger the mutation observer when not needed.
Because of the nature of the plugin which is split in sync handler and async handler, x-ignore gets re-added before the morphing but the async handler is delayed by being a promise (even if everything is already downloaded) so the morph script doesn't know it has to wait, patches the live html and adds the attribute.
When the async handler removes x-ignore (on a detached version of the html), it's too late.
Before Alpine 3.14.4, this behaviour wasn't actually noticed because there was a bug in Alpine itself that would allow directives to run even if one of the parent elements had x-ignore but now that it's fixed, this behaviour breaks async-alpine in Livewire so this PR aims at fixing it.
Because the data is already downloaded, there's no need to rerun the directive on every Livewire refresh (the datastack will and should not change) so we use Alpine.skipDuringClone to prevent that. This function is already used by Alpine in a bunch of use cases such as x-data, x-if, x-for to prevent duplicate listeners or duplicate datastacks.
Note, if the original element didn't have x-load and it gets morphed to a component having x-load, it's still working correctly (it will get skipped by morph but picked up by the mutation observer looking for new html attributes so this edge use case is covered as well).
Feel free to Move the tests in a more suitable location if needed, thanks.
Closes #48