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.
Simple Configuration
The steps in the next section can be used to setup ast-grep for vue manually or the following can be run:
pnpm add -D pusang-ina
ln -sf node_modules/pusang-ina/sgconfig.yml .
Manual 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.
Top comments (1)
Thanks for sharing! This is very helpful and cool!