AlpineLanguage Server Protocol

broken
x-show="open &&"
alpinelsp
error  2:18  alpine/syntax
       Invalid expression: Unexpected token
fixed
x-show="open && visible"

broken
<div x-data="{ count: 0 }">
  <button @click="cont++">         ← :class="cont" as "cont"  ("cont" vs "count")</button>
</div>
alpinelsp
warning  2:19  alpine/unknown-variable
         "cont" is not defined in the enclosing Alpine scope
         (expected one of: count)
fixed
<button @click="count++">

alpinelsp understands
✓ nested scopes      <div x-data="{a}"><div x-data="{b}">… a + b
✓ loop vars          <template x-for="(item, i) in items">… item, i
✓ callback params    items.forEach(i => …)  - i is local
✓ string literals    :class="s === 'cont'"  - not a variable

broken
<span x-ref="panel">panel</span>
<button @click="$refs.pannel.style.color = 'red'">paint</button>
alpinelsp
warning  4:19  alpine/dangling-ref
         $refs.pannel does not match any x-ref="pannel" in this file

alpinelsp
warning  1:5  alpine/duplicate-ref
       Duplicate x-ref "panel"

broken
<template x-for="item items">      ← missing "in"</template>
alpinelsp
error  1:11  alpine/malformed-x-for
      x-for expects "item in items" (or "i of items")

alpinelsp
✓ loop var inside    <p x-text="item">        - clean
✗ loop var outside   <span x-text="item">     - flagged: not in scope here

broken
<div x-shwo="open">              ← typo: x-show
<input x-model.fancy="q">        ← not a modifier
<span x-text="$frob(n)">            ← not an Alpine magic
alpinelsp
warning  1:5   alpine/unknown-directive   Unknown Alpine directive "x-shwo"
warning  2:13  alpine/unknown-modifier    Unknown ".fancy" modifier on "x-model"
warning  3:12  alpine/unknown-magic       Unknown magic "$frob"

alpinelsp understands
✓ x-transition:enter="ease duration-300"     selector-form arg
✓ x-model.debounce.500ms="query"             timing args
✓ @keydown.ctrl.s.prevent="save()"           key chains
✓ @click.outside / .away / .window           event modifiers
✓ X-DATA / @CLICK                            HTML case-insensitive

autocomplete
<p x-text="cou|">              typing…

  ├─ count          from x-data
  ├─ user           from x-data
  ├─ $dispatch      magic
  └─ $nextTick      magic

<input x-model.|                  after dot…
  └─ lazy, number, debounce

x-text="$|                        after dollar…
  └─ $el, $refs, $store, $watch, $dispatch, $nextTick, $root, $data, $id, $event

hover docs
<div x-init="$watch('q', v => …)">   ← hover

  $watch(property, callback)
  Fires callback when a watched property changes.

install
$ npx alpinejs-lsp --check index.html         no install
$ npm install -g alpinejs-lsp                global
cli linter
$ npx alpinejs-lsp --check src/**/*.html

page.html:3:23  error    alpine/syntax           Invalid expression
page.html:2:19  warning  alpine/unknown-variable "cont" is not defined…
page.html:4:19  warning  alpine/dangling-ref     $refs.pannel…

$ echo $?                        ← exit 1: errors found, CI fails
1

opencode.json - any Alpine project
{
  "lsp": {
    "alpinejs": {
      "command": ["npx", "-y", "alpinejs-lsp"],
      "extensions": [".html"]
    }
  }
}