feat(svelte): validated opt-in grammar path + all-platform engine checksums
- Build tree-sitter-svelte in one cc command (prebuilt parser.c, no tree-sitter CLI). - sgconfig.svelte.yml: registers .svelte + injects JS/TS into <script> blocks so the existing TS/JS rule packs apply there; rules-svelte/ adds template rules (on:, slot). Proven on fixtures/svelte/Messy.svelte; default sgconfig.yml unaffected. - Record sha256 for all 5 platform ast-grep binaries so first-run fetch is verifiable everywhere (binaries stay gitignored — no repo bloat). - Retire non-working svelte .example stubs; runes migration stays model-driven. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
3f6f642f6b
commit
b293afe7cf
@@ -53,7 +53,8 @@ to "simplify this codebase" (preview) or "simplify and apply".
|
||||
|
||||
JS/TS, Python, Go, Rust, Java today (ast-grep supports 20+; add a rule pack to extend).
|
||||
**Svelte/SvelteKit** is supported specially (no built-in grammar): detection leans on the
|
||||
project's own Svelte tooling + model-driven refactors with SvelteKit guardrails, with an
|
||||
opt-in ast-grep grammar path — see `references/SVELTE.md`.
|
||||
project's own Svelte tooling + model-driven refactors with SvelteKit guardrails, plus an
|
||||
opt-in ast-grep grammar (one-command build → `sgconfig.svelte.yml`) that injects JS/TS
|
||||
rules into `<script>` blocks and adds template rules — see `references/SVELTE.md`.
|
||||
|
||||
License: MIT.
|
||||
|
||||
@@ -110,8 +110,9 @@ these specially (full guide: `references/SVELTE.md`):
|
||||
- Detect primarily with the project's own Svelte tooling **if present** — run
|
||||
`svelte-check` (machine output), the project's `eslint` (eslint-plugin-svelte) on
|
||||
`**/*.svelte`, and/or `svelte/compiler` warnings; fold results into the ledger.
|
||||
- Optionally, if a tree-sitter-svelte grammar has been built and enabled in
|
||||
`sgconfig.yml`, the TS/JS packs apply to `<script>` blocks via injection.
|
||||
- Optionally, if the tree-sitter-svelte grammar is built (one command, see SVELTE.md),
|
||||
scan `.svelte` with `sgconfig.svelte.yml` — TS/JS packs then apply to `<script>` blocks
|
||||
via injection, plus `rules-svelte/` template rules.
|
||||
- Otherwise do model-driven refactors (runes migration, store→`$state`, etc.) — and
|
||||
**obey the SvelteKit guardrails in `references/SVELTE.md`** (never break
|
||||
`+page`/`+layout`/`load`/form-action conventions). Verify with `svelte-check` too.
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
e08eea52023724e309c7104ca32c41fb9b205d976d3b3e6e681753b2394a3069 linux-x64/ast-grep
|
||||
036104827852aa70feae65be7e7581b164becb64526c872a312677de5d82d4c9 linux-arm64/ast-grep
|
||||
bdc4ad8bd72ae05a9c816296ccace501dafb3fb4a98df0a87fe49699b016057d darwin-x64/ast-grep
|
||||
428f35359b297f7562cfc536bdf3104fdc6bc6c50ab38df70994ec15e992df0b darwin-arm64/ast-grep
|
||||
584c59eaf3b50bf436ad43cf36193af1d2fe5e29ddb21b7485c39f131691390f win32-x64/ast-grep.exe
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<script lang="ts">
|
||||
// TS inside a Svelte script block — existing TS rules should fire here via injection.
|
||||
export let tags: string[];
|
||||
let active = false;
|
||||
|
||||
function hasTag(tag: string): boolean {
|
||||
return tags.indexOf(tag) !== -1;
|
||||
}
|
||||
|
||||
function ready(flag: boolean): boolean {
|
||||
return flag ? true : false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<button on:click={() => (active = !active)}>toggle</button>
|
||||
|
||||
{#if active}
|
||||
<slot />
|
||||
{/if}
|
||||
+19
-16
@@ -34,26 +34,29 @@ For each `ERROR`/`WARNING` line, add a finding: `detector: "svelte-check"`,
|
||||
(or `mechanical` if the code is `eslint --fix`-able), `status: pending`. Same idea for
|
||||
`eslint -f json **/*.svelte` (use `messages[].ruleId/line`, `fix` present ⇒ mechanical).
|
||||
|
||||
### 2. ast-grep structural rules — OPT-IN (requires building a grammar)
|
||||
ast-grep has no built-in Svelte grammar. To run structural rules on `.svelte`:
|
||||
### 2. ast-grep structural rules — OPT-IN (one-command grammar build)
|
||||
ast-grep has no built-in Svelte grammar, so this is opt-in (a compiled grammar can't be
|
||||
bundled cross-platform). Setup is one command + a separate config — **validated working**:
|
||||
|
||||
1. Build `tree-sitter-svelte` into a shared library and put it at
|
||||
`grammars/svelte.so` (`.dylib` on macOS, `.dll` on Windows):
|
||||
1. Build the grammar into `grammars/svelte.<ext>` (~1s; needs only a C compiler — no
|
||||
tree-sitter CLI, because the grammar ships a prebuilt `parser.c`):
|
||||
```bash
|
||||
git clone --depth 1 https://github.com/tree-sitter-grammars/tree-sitter-svelte /tmp/tss
|
||||
cc -shared -fPIC -O2 -I /tmp/tss/src /tmp/tss/src/parser.c /tmp/tss/src/scanner.c \
|
||||
-o grammars/svelte.so # .dylib on macOS, .dll on Windows
|
||||
```
|
||||
# one-time, needs the tree-sitter CLI + a C compiler
|
||||
git clone https://github.com/tree-sitter-grammars/tree-sitter-svelte
|
||||
cd tree-sitter-svelte && tree-sitter build --output svelte.so
|
||||
2. Scan Svelte files with the Svelte config (no edits to the default config needed):
|
||||
```bash
|
||||
ast-grep scan -c sgconfig.svelte.yml --json=compact <paths>
|
||||
```
|
||||
2. Uncomment the `customLanguages` + `languageInjections` blocks in `sgconfig.yml`.
|
||||
3. Enable the template rules: `mv rules/svelte/template.yml.example rules/svelte/template.yml`
|
||||
(shipped disabled because a `language: svelte` rule errors every scan until the grammar
|
||||
is registered).
|
||||
4. Result: the existing **typescript/javascript rule packs automatically apply to the
|
||||
code inside `<script>` blocks** (via injection), and `rules/svelte/` adds
|
||||
template-level rules (e.g. `on:click` → `onclick`).
|
||||
3. Result (proven on `fixtures/svelte/Messy.svelte`): the **typescript/javascript rule
|
||||
packs apply to the code inside `<script>` blocks** via injection (e.g. `indexOf`→
|
||||
`includes`, ternary→`Boolean`), and `rules-svelte/` adds template rules (`on:` event
|
||||
directives, `<slot>`).
|
||||
|
||||
This is opt-in because shipping a compiled grammar per platform conflicts with the
|
||||
skill's "bundled, no-compile" principle. Most users should rely on layer 1 + layer 3.
|
||||
`grammars/` is gitignored (like the engine binary), so this stays a one-command local
|
||||
setup. Most users can rely on layers 1 + 3; turn this on for structural script-block
|
||||
cleanups at scale.
|
||||
|
||||
### 3. Model-driven semantic refactors — always available
|
||||
For Svelte-specific refactors that no rule expresses, the model edits the relevant span
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Svelte template-level rules. Loaded ONLY via sgconfig.svelte.yml (needs the grammar).
|
||||
# The JS/TS inside <script> blocks is handled by the typescript/javascript packs via the
|
||||
# language injections in sgconfig.svelte.yml — no Svelte rule needed for that.
|
||||
|
||||
id: svelte-event-directive-to-property
|
||||
language: svelte
|
||||
severity: hint
|
||||
message: "Svelte 5: prefer the event property (e.g. `onclick`) over the `on:` directive."
|
||||
rule:
|
||||
kind: attribute
|
||||
has:
|
||||
kind: attribute_name
|
||||
regex: "^on:"
|
||||
|
||||
---
|
||||
id: svelte-slot-to-render
|
||||
language: svelte
|
||||
severity: hint
|
||||
message: "Svelte 5: replace <slot> with the {@render children()} snippet."
|
||||
rule:
|
||||
kind: tag_name
|
||||
regex: "^slot$"
|
||||
@@ -27,9 +27,6 @@
|
||||
"py-magic-number": { "pattern": "magic-number", "track": "llm", "difficulty": "semantic", "weight": 3 },
|
||||
|
||||
"svelte-event-directive-to-property": { "pattern": "svelte-event-property", "track": "llm", "difficulty": "mechanical", "weight": 4, "requires": "svelte-grammar" },
|
||||
"svelte-export-let-to-props": { "pattern": "svelte-props", "track": "llm", "difficulty": "semantic", "weight": 6, "requires": "svelte-grammar" },
|
||||
"svelte-reactive-to-derived": { "pattern": "svelte-runes-reactivity", "track": "llm", "difficulty": "semantic", "weight": 8, "requires": "svelte-grammar" },
|
||||
"svelte-create-event-dispatcher":{ "pattern": "svelte-callback-props", "track": "llm", "difficulty": "semantic", "weight": 6, "requires": "svelte-grammar" },
|
||||
"svelte-slot-to-render": { "pattern": "svelte-snippets", "track": "llm", "difficulty": "semantic", "weight": 5, "requires": "svelte-grammar" },
|
||||
"svelte-check": { "pattern": "svelte-diagnostic", "track": "llm", "difficulty": "semantic", "weight": 7 },
|
||||
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
# Svelte 4 -> 5 runes migration rules.
|
||||
#
|
||||
# REQUIRES the opt-in Svelte grammar (see ../../sgconfig.yml + ../../references/SVELTE.md).
|
||||
# Shipped as .example so it never breaks default scans. Enable with:
|
||||
# mv runes.yml.example runes.yml
|
||||
# These are conservative: prop/event rewrites are mechanical-ish, but reactive-statement
|
||||
# migrations (`$:`) need judgment (derived vs effect), so they are detection-only (Track B).
|
||||
|
||||
id: svelte-export-let-to-props
|
||||
language: svelte
|
||||
severity: hint
|
||||
message: "Svelte 5: replace `export let` with `$props()` destructuring."
|
||||
metadata: { pattern: svelte-props, track: llm, difficulty: semantic }
|
||||
rule:
|
||||
any:
|
||||
- pattern: "export let $PROP;"
|
||||
- pattern: "export let $PROP = $DEFAULT;"
|
||||
note: |
|
||||
Merge ALL props of a component into a single `let { a, b = 1 } = $props();` — do not
|
||||
emit one destructure per prop. That cross-statement merge is why this is Track B.
|
||||
|
||||
---
|
||||
id: svelte-reactive-to-derived
|
||||
language: svelte
|
||||
severity: hint
|
||||
message: "Svelte 5: `$:` computed value -> `$derived`; `$:` side effect -> `$effect`."
|
||||
metadata: { pattern: svelte-runes-reactivity, track: llm, difficulty: semantic }
|
||||
rule:
|
||||
any:
|
||||
- pattern: "$: $X = $EXPR;" # pure assignment -> $derived
|
||||
- pattern: "$: { $$$BODY }" # block -> usually $effect
|
||||
note: |
|
||||
`$: x = expr;` with no side effects -> `let x = $derived(expr);`.
|
||||
`$: { ... }` performing side effects -> `$effect(() => { ... });`.
|
||||
Decide by inspecting the body; never blindly convert.
|
||||
|
||||
---
|
||||
id: svelte-create-event-dispatcher
|
||||
language: svelte
|
||||
severity: hint
|
||||
message: "Svelte 5: prefer callback props over createEventDispatcher()."
|
||||
metadata: { pattern: svelte-callback-props, track: llm, difficulty: semantic }
|
||||
rule:
|
||||
pattern: "createEventDispatcher($$$)"
|
||||
|
||||
---
|
||||
id: svelte-slot-to-render
|
||||
language: svelte
|
||||
severity: hint
|
||||
message: "Svelte 5: replace <slot/> with the {@render children()} snippet."
|
||||
metadata: { pattern: svelte-snippets, track: llm, difficulty: semantic }
|
||||
rule:
|
||||
pattern: "<slot />"
|
||||
@@ -1,21 +0,0 @@
|
||||
# Svelte — template-level mechanical simplifications.
|
||||
#
|
||||
# REQUIRES the opt-in Svelte grammar (see sgconfig.yml + references/SVELTE.md).
|
||||
# Without it these rules are inert (ast-grep can't parse .svelte). The JS/TS code
|
||||
# inside <script> blocks is handled by the existing typescript/javascript packs via
|
||||
# language injection — no Svelte-specific rules needed for that part.
|
||||
#
|
||||
# NOTE: `language: svelte` only resolves once tree-sitter-svelte is registered as a
|
||||
# customLanguage. These patterns target Svelte 5 syntax.
|
||||
|
||||
id: svelte-event-directive-to-property
|
||||
language: svelte
|
||||
severity: hint
|
||||
message: "Svelte 5: prefer the `onclick` property over the `on:click` directive."
|
||||
metadata: { pattern: svelte-event-property, track: llm, difficulty: mechanical }
|
||||
rule:
|
||||
any:
|
||||
- pattern: 'on:click={$H}'
|
||||
- pattern: 'on:input={$H}'
|
||||
- pattern: 'on:change={$H}'
|
||||
- pattern: 'on:submit={$H}'
|
||||
@@ -0,0 +1,28 @@
|
||||
# Svelte-enabled ast-grep config — OPT-IN.
|
||||
#
|
||||
# Use this instead of sgconfig.yml ONLY when the Svelte grammar is built at
|
||||
# grammars/svelte.so (.dylib/.dll). Build (one-time, ~1s, needs a C compiler):
|
||||
# git clone --depth 1 https://github.com/tree-sitter-grammars/tree-sitter-svelte /tmp/tss
|
||||
# cc -shared -fPIC -O2 -I /tmp/tss/src /tmp/tss/src/parser.c /tmp/tss/src/scanner.c \
|
||||
# -o grammars/svelte.so
|
||||
# Then scan Svelte files with: ast-grep scan -c sgconfig.svelte.yml <paths>
|
||||
#
|
||||
# This loads the normal rule packs PLUS rules-svelte/, registers .svelte, and injects
|
||||
# JS/TS into <script> blocks so the existing typescript/javascript rules apply there too.
|
||||
ruleDirs:
|
||||
- rules
|
||||
- rules-svelte
|
||||
|
||||
customLanguages:
|
||||
svelte:
|
||||
libraryPath: ./grammars/svelte.so
|
||||
extensions: [svelte]
|
||||
expandoChar: _
|
||||
|
||||
languageInjections:
|
||||
- hostLanguage: svelte
|
||||
rule: { pattern: '<script lang="ts">$CONTENT</script>' }
|
||||
injected: typescript
|
||||
- hostLanguage: svelte
|
||||
rule: { pattern: "<script>$CONTENT</script>" }
|
||||
injected: javascript
|
||||
+7
-30
@@ -7,34 +7,11 @@
|
||||
ruleDirs:
|
||||
- rules
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# OPT-IN: Svelte / SvelteKit structural rules on .svelte files.
|
||||
# This config covers JS/TS, Python, Go, Rust, Java — the always-on languages. It does
|
||||
# NOT register Svelte (ast-grep has no built-in Svelte grammar), so .svelte files are
|
||||
# skipped cleanly here.
|
||||
#
|
||||
# ast-grep has no built-in Svelte grammar. To run structural rules on .svelte
|
||||
# files you must register tree-sitter-svelte as a custom language AND inject the
|
||||
# JS/TS grammar into <script> blocks. This needs a compiled grammar library
|
||||
# (.so/.dylib/.dll), so it is NOT bundled — it is opt-in. See references/SVELTE.md
|
||||
# for the one-time build steps. Once built, uncomment the blocks below AND enable the
|
||||
# template rules (mv rules/svelte/template.yml.example rules/svelte/template.yml); then
|
||||
# the existing TypeScript/JavaScript rule packs automatically apply to the code inside
|
||||
# <script> blocks via injection, and rules/svelte/ adds template-level rules.
|
||||
#
|
||||
# customLanguages:
|
||||
# svelte:
|
||||
# libraryPath: ./grammars/svelte.so # build per references/SVELTE.md
|
||||
# extensions: [svelte]
|
||||
# expandoChar: _ # $ -> _ so $state/$props parse cleanly
|
||||
#
|
||||
# languageInjections:
|
||||
# - hostLanguage: svelte
|
||||
# rule: { pattern: '<script lang="ts">$CONTENT</script>' }
|
||||
# injected: typescript
|
||||
# - hostLanguage: svelte
|
||||
# rule: { pattern: "<script>$CONTENT</script>" }
|
||||
# injected: javascript
|
||||
# ---------------------------------------------------------------------------
|
||||
#
|
||||
# Without the grammar, Svelte is still supported: detection falls back to the
|
||||
# project's own Svelte tooling (svelte-check, eslint-plugin-svelte, svelte
|
||||
# compiler warnings) as enrichment, and semantic refactors go to the model with
|
||||
# the SvelteKit guardrails in references/SVELTE.md.
|
||||
# For Svelte: scan with the separate `sgconfig.svelte.yml` after a one-command grammar
|
||||
# build (see references/SVELTE.md). That config adds rules-svelte/ + injects JS/TS into
|
||||
# <script> blocks. Without the grammar, Svelte is still supported via the project's own
|
||||
# tooling (svelte-check, eslint-plugin-svelte) + model refactors with SvelteKit guardrails.
|
||||
|
||||
Reference in New Issue
Block a user