SaltyCrane Blog — Notes on JavaScript and web development

How to use ast-grep with GraphQL

ast-grep is a code search + replace tool that uses the abstract syntax tree (AST). It has language support for TypeScript, Python, and others, but it doesn't support GraphQL out of the box. However, there is documentation for supporting other languages: Custom Language Support.

These are my notes using that Custom Language Support documentation along with the Multi-Language Documents documentation to set up GraphQL support for ast-grep on macOS.

Prepare Tree-sitter Tool and Parser

  • Install tree-sitter CLI

    brew install tree-sitter
    

    (Alternatively, npm install -g tree-sitter-cli)

  • Test tree-sitter is installed

    tree-sitter --version
    
    tree-sitter 0.23.0
    
  • Use a working directory

    I'll use /tmp as my working directory, but a different directory can be used

    cd /tmp
    
  • Get the tree-sitter GraphQL grammar

    git clone https://github.com/bkegley/tree-sitter-graphql.git
    

    (This is the repo used by the neovim tree-sitter library.)

Compile the Parser as a Dynamic Library

  • Compile the GraphQL parser as a dynamic library

    cd tree-sitter-graphql
    export TREE_SITTER_LIBDIR=/tmp
    tree-sitter test
    

    Note: I got the following error when I ran this. If anyone knows what is going on, please let me know.

    Error in query file "formatter.scm"
    
    Caused by:
        Query error at 5:31. Impossible pattern:
        (input_object_type_definition (input_value_definition) @field_definition)
    

    However, it still generated the graphql.dylib file in /tmp. Note: according to ChatGPT, .dylib is used on Mac, .so is used on Linux, and .dll is used on Windows.

Register Language in sgconfig.yml

  • Create a new ast-grep project

    cd /tmp
    sg new
    
    No sgconfig.yml found. Creating a new ast-grep project...
    > Where do you want to have your rules? rules
    > Do you want to create rule tests? No
    > Do you want to create folder for utility rules? No
    Your new ast-grep project has been created!
    
  • Edit sgconfig.yml to add GraphQL as a custom language

    /tmp/sgconfig.yml:

    ruleDirs:
    - ./rules
    customLanguages:
    graphql:
        libraryPath: graphql.dylib
        extensions: [graphql]
        expandoChar: $
    

Use It!

  • Search for all input types in /path/to/my/schema.graphql

    sg -p "input" -l graphql /path/to/my/schema.graphql
    
    /path/to/my/schema.graphql
    6│input AddOrUpdateResidualWorksheetInputType {
    120│input AutoAssignStyleCodeInputType {
    478│input BulkCreateVehicleReleaseInputType {
    483│input BulkDeleteVehicleReleaseInputType {
    487│input BulkEditVehicleReleaseInputType {
    492│input BulkImportStyleFromCppInputType {
    949│input CloneModelInputType {
    958│input CloneModelStyleInputType {
    973│input CloneStyleInputType {
    1749│input CppConfigurationInputType {
    
  • Search for all DateTime input fields (but not output fields) in /path/to/my/schema.graphql. Note: the tree-sitter-graphql grammer.js file contains different kinds that can be used for filtering.

    sg scan --inline-rules '
    id: datetimeinputs
    language: graphql
    rule:
      regex: "DateTime"
      kind: input_value_definition
    ' /path/to/my/schema.graphql
    
    help[datetimeinputs]:
         ┌─ /path/to/my/schema.graphql:2356:3
         │
    2356 │   configurationLastUpdatedAt: DateTime
         │   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    help[datetimeinputs]:
         ┌─ /path/to/my/schema.graphql:8331:69
         │
    8331 │   previousStyleSnapshot(styleSnapshotRowId: Guid, styleRowId: Long, versionDate: DateTime): StyleSnapshotTypeOrOperationErrorType
         │                                                                     ^^^^^^^^^^^^^^^^^^^^^
    
    help[datetimeinputs]:
         ┌─ /path/to/my/schema.graphql:8794:16
         │
    8794 │   whatsChanged(date: DateTime!, flags: [WhatsChangedFilterFlagsEnumType], includeCppOrEmbargoedData: CppEmbargoedDataFilterEnumType): WhatsChangedType
         │                ^^^^^^^^^^^^^^^
    
    help[datetimeinputs]:
          ┌─ /path/to/my/schema.graphql:11536:3
          │
    11536 │   configurationLastUpdatedAt: DateTime
          │   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    

Comments