Alpine.js Tools v1.0-v1.5: Three Bugs, Three Fixes
Alpine.js Tools has grown a lot since it launched in May. Most of that growth is new capability: diagnostics, Go to Definition, modifier completions, plugin directive support. Some of it is bugs, three of them, traced to specific lines of code and fixed fast in more than one case, a more honest picture of what building tooling for a framework with no compiler to lean on actually involves than a feature list alone is.
New capabilities since launch
The full, current feature set lives on the main extension page, kept up to date as things ship. The short version of what shipped, in order:
- v1.1.0: modifier completions,
$refs/$store/x-dataworkspace-aware completions, magic property hover. - v1.2.0: unknown-directive diagnostics with “did you mean” suggestions.
- v1.3.0: Quick Fix code actions for those diagnostics, Go to Definition for
Alpine.data()components, plugin directive snippets. - v1.4.0: Liquid and Jinja2 support, extending coverage to Shopify theme development and Python templating (Flask, Django, Ansible), added after rylanharper requested it.
- v1.5.0:
$persistmagic property support, matching the treatment already given to$el,$refs,$store, and the rest.
New language supported, three bugs found and fixed
Liquid is a templating language created by Shopify co-founder Tobias Lütke and released in 2006. It’s the language every Shopify theme is built on, deliberately sandboxed so merchants and theme developers can customize a storefront without touching the platform’s underlying code or data directly. It’s since been adopted well beyond Shopify too, Jekyll and GitHub Pages both build on it for the same reason, a template language safe to hand to people who aren’t the site’s own engineers.
v1.4.0 shipped Liquid support. It also quietly fixed two bugs nobody had reported yet.
Two languages that were silently never highlighted at all
Blade and Nunjucks were both listed as supported languages from early on, in activation events, providers, and snippets. Neither one actually got JavaScript syntax highlighting inside directive values, and nothing surfaced an error to say so.
The v1.4.0 changelog traces both to the same class of mistake: the injection grammar’s injectionSelector had text.blade.php and text.html.nunjucks listed, but VS Code decides whether to load a grammar package based on a separate field, injectTo, and that field was missing both entries. The grammar was configured correctly and simply never activated. This is the quietest kind of bug a tool like this can have, no warning, no error, no diagnostic, a feature that just doesn’t do anything in two entire languages until someone happens to notice the highlighting isn’t there.
A Tailwind class that looked like a directive
rylanharper was back with a second issue the very next day: Tailwind classes like translate-x-1/2 were getting flagged as unknown Alpine directives, anywhere they appeared, inside JavaScript expressions, CSS values, or object keys, not just as actual HTML attributes.
The actual cause, per the v1.4.1 changelog entry, was a directive-detection regex anchored with \b, a word boundary that fires at any transition between word and non-word characters, including the hyphens inside a class name like translate-x-1/2. The fix replaced that with a lookbehind requiring whitespace before x- and a lookahead requiring =, whitespace, >, or end-of-string after the directive name, conditions a real Alpine attribute always satisfies and a CSS class fragment never does.
A plugin directive Alpine itself renamed
watkit reported, inside a Laravel Blade template, that x-trap, the Alpine Focus plugin’s directive for trapping keyboard focus inside a modal, was throwing an unknown-directive warning despite being a real, current directive. watkit traced it directly to the specific source line responsible before filing.
The v1.5.0 changelog confirms the cause precisely: the diagnostics allow-list keyed the entry by the plugin’s package name, focus, instead of the directive it actually registers, x-trap. The Alpine Focus plugin docs confirm x-trap as current. Fixing the allow-list surfaced two more, related problems in the same release: x-trap was also missing from the HTML IntelliSense data entirely, so autocomplete never offered it even once the diagnostic was corrected, and x-morph and x-persist had been incorrectly treated as valid directives, when neither actually registers one, @alpinejs/morph exposes Alpine.morph() as a function call and @alpinejs/persist exposes the $persist magic property, not an x-* attribute at all. Both were suppressing a warning that should have fired for a genuine typo. All three were fixed in the same release.
The pattern underneath all three
The Tailwind collision and the Focus plugin rename were both surfaced by real projects and filed as GitHub issues, each fixed within a day. Blade and Nunjucks broke differently. That one was a configuration field left empty since the languages were first added, affecting every project using either from the start, not something a user reported, just something found later during development. What ties the three together isn’t how they were found, it’s why finding them required inference at all. A framework with no build step and no compiler to validate against means bugs like these surface through real usage or close review, not through a type system or a test suite catching them structurally.