dev-resources.site
for different kinds of informations.
Using ast-grep with a vue project
Motivation
The amazing ast-grep tool does not support vue
and scss
by default but can be configured to do so.
ast-grep
is like a much more powerful version of ripgrep
or grep
when it comes to searching for patterns within code as it understands the structure of the code (via the library tree-sitter).
For example the following command:
ast-grep -p '<h2>$A</h2>'
Will show all <h2>
tags in your html
(and also vue
files after applying the config from this post). Then the following could change all <h2>
tags to <h3>
tags interactively (each change will be shown and then y
can be used to accept it or n
to reject it):
ast-grep -p '<h2>$A</h2>' -r '<h3>$A</h3>' -i
-U
can be used instead of -i
to apply the changes without asking or neither can be used to view all the changes as a patch.
ast-grep
also has some overlap with eslint
, rules can be stored within the project along with fixes and these can be tested/fixed using ast-grep scan
and ast-grep scan -U
.
Configuration
All of the following commands should be run from the root directory of your vue
project.
First create a "rules" directory and add it to git
. This directory must exist for the config to be accepted so for now just create an empty directory. Rules may also use utilities so that common config can be shared across rules, so we're going to create both directories with this config:
mkdir ast/{rules,utils}
touch ast/{rules,utils}/.keep-dir
git add ast/{rules,utils}/.keep-dir
Then create a file sgconfig.yml
:
ruleDirs:
- ast/rules
utilDirs:
- ast/utils
customLanguages:
vue:
libraryPath: .tree-sitter/vue.so
extensions: [vue]
expandoChar: $
scss:
libraryPath: .tree-sitter/scss.so
extensions: [scss]
expandoChar: $
languageInjections:
- hostLanguage: vue
rule:
pattern: <template>$CONTENT</template>
injected: html
- hostLanguage: vue
rule:
pattern: <script $$$ lang="ts" $$$>$CONTENT</script>
injected: typescript
- hostLanguage: vue
rule:
pattern: <style $$$ lang="scss" $$$>$CONTENT</style>
injected: scss
Now a script is needed to create the libraries that this ast-grep
configuration needs to understand vue
and scss
files. This could be created at scripts/init-ast-grep-config.sh
:
#!/usr/bin/env bash
outdir=$PWD/.tree-sitter/
mkdir $outdir
cd /tmp
git clone https://github.com/tree-sitter-grammars/tree-sitter-vue
cd tree-sitter-vue
pnpm dlx tree-sitter-cli build
cp vue.so $outdir/
cd ..
rm -rf tree-sitter-vue
git clone https://github.com/serenadeai/tree-sitter-scss
cd tree-sitter-scss
pnpm dlx tree-sitter-cli build
cp scss.so $outdir/
cd ..
rm -rf tree-sitter-scss
Then ensure this script is executable:
chmod a+x scripts/init-ast-grep-config.sh
This script stores files in the directory .tree-sitter
within the project, these libraries are binaries so they are unique to the platform on which they were compiled/installed so it's a good idea to add the .tree-sitter
directory to .gitignore
.
Next add a reference to this script to the scripts
section of package.json
:
{
"scripts": {
"init-ast-grep-config": "./scripts/init-ast-grep-config.sh"
}
}
Now document this in your readme.md
, commit everything to git
, and developers on the project can begin using ast-grep
on your vue
project after running pnpm run init-ast-grep-config
.
Add some rules
Now that ast-grep
is configured I'll show how easy it is to create a rule to enforce code practices. vue
allows defineEmits
to be written in a type safe way:
defineEmits<{
'update:value': [string]
}>
Or an unsafe way:
defineEmits(['update:value'])
If using typescript then there's not much reason to use the type unsafe version and this can be enforced with a five line rule in the project (to do the same with eslint
would involve a lot more work). Create the following yaml
file at ast/rules/type-unsafe-define-emits.yaml
:
id: type-unsafe-define-emits
language: typescript
rule:
pattern: defineEmits([$$$])
message: Use type safe version of defineEmits
Then run ast-grep scan
and it will raise an error is the type unsafe version of defineEmits
has been used.
Featured ones: