# Gritql > Documentation for Gritql --- # Source: https://docs.grit.io ## Grit Blog ## July update July 8, 2024 ## May update May 20, 2024 ## We've raised $7M to erase technical debt August 15, 2023 Subscribe to updates --- # Source: https://docs.grit.io ## CLI Quickstart The Grit CLI is a command-line interface for Grit. It allows you to apply GritQL patterns and run [custom workflows](/workflows/overview). ## Installation You can install the Grit CLI from NPM: ```bash npm install --location=global @getgrit/cli ``` Alternatively, you can also install Grit with an installation script: ```bash curl -fsSL https://docs.grit.io/install | bash ``` You can optionally run `grit init` to initialize a local [Grit configuration](/guides/config) and `grit install` to install additional binaries used for custom workflows. ## Usage ### Overview The primary purpose of the Grit CLI is to apply **automatic changes**. For example, you can automatically upgrade React Class Components to hooks with the following command from our [standard library](https://github.com/getgrit/stdlib/blob/main/.grit/patterns/js/react_to_hooks.md): ```bash grit apply react_to_hooks ``` ### Applying patterns Use `grit apply` to apply a pattern to the current directory including anything from our [standard library](https://github.com/getgrit/stdlib/tree/main/.grit/patterns) and custom patterns written in [GritQL](/language/overview). Some examples include: ```bash grit apply "const" # Simply shows all const declarations grit apply no_console_log # Removes console.log statements from JavaScript grit apply ternary_op # Converts if-else statements to ternary operators in Python grit apply cypress_to_playwright # Converts Cypress tests to Playwright tests ``` Parameters can be passed to some patterns as well: ```bash grit apply 'remove_import(from=`"react"`)' # Removes all import statements from React ``` To view the full list of [available patterns](/patterns) in the standard library, run: ```bash grit list ``` ## Telemetry We collect usage data to help us improve the CLI. We **do not** collect any source code as part of this. You can opt out of sending usage data by setting the `GRIT_TELEMETRY_DISABLED` environment variable to `true`. --- # Source: https://docs.grit.io ## Grit CLI Reference This document contains the help content for the `grit` command-line program. **Command Overview:** - [`grit`↴](#grit) - [`grit check`↴](#grit-check) - [`grit list`↴](#grit-list) - [`grit apply`↴](#grit-apply) - [`grit doctor`↴](#grit-doctor) - [`grit blueprints`↴](#grit-blueprints) - [`grit blueprints list`↴](#grit-blueprints-list) - [`grit blueprints pull`↴](#grit-blueprints-pull) - [`grit blueprints push`↴](#grit-blueprints-push) - [`grit auth`↴](#grit-auth) - [`grit auth login`↴](#grit-auth-login) - [`grit auth logout`↴](#grit-auth-logout) - [`grit auth get-token`↴](#grit-auth-get-token) - [`grit auth refresh`↴](#grit-auth-refresh) - [`grit install`↴](#grit-install) - [`grit init`↴](#grit-init) - [`grit workflows`↴](#grit-workflows) - [`grit workflows list`↴](#grit-workflows-list) - [`grit workflows upload`↴](#grit-workflows-upload) - [`grit patterns`↴](#grit-patterns) - [`grit patterns list`↴](#grit-patterns-list) - [`grit patterns test`↴](#grit-patterns-test) - [`grit patterns edit`↴](#grit-patterns-edit) - [`grit patterns describe`↴](#grit-patterns-describe) - [`grit version`↴](#grit-version) - [`grit format`↴](#grit-format) ## `grit` Software maintenance on autopilot, from grit.io **Usage:** `grit [OPTIONS] ` For help with a specific command, run `grit help `. **Subcommands:** - `check` — Check the current directory for pattern violations - `list` — List everything that can be applied to the current directory - `apply` — Apply a pattern or migration to a set of files - `doctor` — Print diagnostic information about the current environment - `blueprints` — Manage blueprints for the Grit Agent - `auth` — Authentication commands, run `grit auth --help` for more information - `install` — Install supporting binaries - `init` — Install grit modules - `workflows` — Workflow commands, run `grit workflows --help` for more information - `patterns` — Patterns commands, run `grit patterns --help` for more information - `version` — Display version information about the CLI and agents - `format` — Format grit files under current directory **Options:**- `--json` — Enable JSON output, only supported on some commands Possible values: `true`, `false` - `--jsonl` — Enable JSONL output, only supported on some commands Possible values: `true`, `false` - `--log-level ` — Override the default log level (info) - `--grit-dir ` — Override the default .grit directory location ## `grit check` Check the current directory for pattern violations **Usage:** `grit check [OPTIONS] [PATHS]...` **Arguments:**- `` — The target paths to apply the checks to Default value: `.` **Options:**- `--fix` — Apply fixes to all rewrites Possible values: `true`, `false` - `--verbose` — Show verbose output Possible values: `true`, `false` - `--level ` — Check only patterns at or above an enforcement level - `--no-cache` — Do not use cache Possible values: `true`, `false` - `--refresh-cache` — Clear cache before running check Possible values: `true`, `false` - `--github-actions` — Output annotations for a GitHub actions workflow Possible values: `true`, `false` - `--only-in-json ` — Only analyze ranges inside a provided eslint-style JSON string. The JSON should be an array of objects formatted as `[{"filePath": "path/to/file", "messages": [{"line": 1, "column": 1, "endLine": 1, "endColumn": 1}]}]`. ## `grit list` List everything that can be applied to the current directory **Usage:** `grit list [OPTIONS]` **Options:**- `--level ` — List only at or above an enforcement level - `--source ` — List items from a specific source Default value: `all` Possible values: - `all`: All patterns - `local`: Only patterns from the local repo - `user`: Only patterns from the user config - `--language ` — List only items targeting a specific language Possible values: `js`, `html`, `css`, `json`, `java`, `kotlin`, `csharp`, `python`, `markdown`, `go`, `rust`, `ruby`, `elixir`, `solidity`, `hcl`, `yaml`, `sql`, `vue`, `toml`, `php`, `php` ## `grit apply` Apply a pattern or migration to a set of files **Usage:** `grit apply [OPTIONS] [PATHS]...` **Arguments:** - `` — The pattern to apply, in a few forms: - A pattern name (ex. `raw_no_console_log`) - A pattern by itself (ex. `'`console.log` => `console.error`'`) - A pattern call, with arguments (ex. `'openai_main(client=`openai`)'`) - A path to a pattern file (ex. `./patterns/raw_no_console_log.grit`) - A workflow name (ex. `lint`) - `` Default value: `.` **Options:**- `--input ` — JSON input parameter to pass to the workflow - `--remote` — Run the workflow remotely on Grit Cloud Possible values: `true`, `false` - `--workflow-id ` — Workflow ID to set, only applicable when running remotely - `--watch` — Watch the workflow for updates (only applicable when running remotely) Possible values: `true`, `false` - `--verbose` — Print verbose output Possible values: `true`, `false` - `--output ` Default value: `standard` Possible values: `none`, `standard`, `compact` - `-m`, `--limit ` - `--dry-run` — Show a dry-run of the changes that would be applied Default value: `false` Possible values: `true`, `false` - `--force` — Force apply, even if there are uncommitted changes Default value: `false` Possible values: `true`, `false` - `-i`, `--interactive` — Selectively apply changes interactively Default value: `false` Possible values: `true`, `false` - `--output-file ` — Path to a file to write the results to, defaults to stdout - `--stdin` — Use this option when you want to transform code piped from `stdin`, and print the output to `stdout`. If you use this option, you *must* specify a file path, to allow Grit to determine the language of the code. Example: `echo 'console.log(hello)' | grit apply '`hello`=>`goodbye`' file.js --stdin This will print `console.log(goodbye)` to stdout Possible values: `true`, `false` - `--cache` — Use cache Possible values: `true`, `false` - `--refresh-cache` — Clear cache before running apply Possible values: `true`, `false` - `--ai` — Interpret the request as a natural language request Possible values: `true`, `false` - `--language ` — Change the default language to use for the pattern (if unset, JavaScript is used by default) Possible values: `js`, `html`, `css`, `json`, `java`, `kotlin`, `csharp`, `python`, `markdown`, `go`, `rust`, `ruby`, `elixir`, `solidity`, `hcl`, `yaml`, `sql`, `vue`, `toml`, `php`, `php` - `--only-in-json ` — Only analyze ranges inside a provided eslint-style JSON string. The JSON should be an array of objects formatted as `[{"filePath": "path/to/file", "messages": [{"line": 1, "column": 1, "endLine": 1, "endColumn": 1}]}]`. ## `grit doctor` Print diagnostic information about the current environment **Usage:** `grit doctor` ## `grit blueprints` Manage blueprints for the Grit Agent **Usage:** `grit blueprints ` **Subcommands:** - `list` — List available blueprints - `pull` — Pull a blueprint by workflow ID - `push` — Push a blueprint by workflow ID ## `grit blueprints list` List available blueprints **Usage:** `grit blueprints list` ## `grit blueprints pull` Pull a blueprint by workflow ID **Usage:** `grit blueprints pull [OPTIONS] --workflow-id ` **Options:** - `--workflow-id ` — The workflow ID of the blueprint to pull - `-f`, `--force` — Force pull even if the blueprint already exists Possible values: `true`, `false` - `--file ` — File to save the blueprint to (defaults to blueprint.md) Default value: `blueprint.md` ## `grit blueprints push` Push a blueprint by workflow ID **Usage:** `grit blueprints push [OPTIONS] --workflow-id ` **Options:** - `--workflow-id ` — The workflow ID of the blueprint to push - `--file ` — File containing the blueprint (defaults to blueprint.md) Default value: `blueprint.md` ## `grit auth` Authentication commands, run `grit auth --help` for more information **Usage:** `grit auth ` **Subcommands:** - `login` — Log in with grit.io - `logout` — Remove your grit.io credentials - `get-token` — Get your grit.io token - `refresh` — Refresh your grit.io auth (this will also happen automatically when your token expires) ## `grit auth login` Log in with grit.io **Usage:** `grit auth login` ## `grit auth logout` Remove your grit.io credentials **Usage:** `grit auth logout` ## `grit auth get-token` Get your grit.io token **Usage:** `grit auth get-token` ## `grit auth refresh` Refresh your grit.io auth (this will also happen automatically when your token expires) **Usage:** `grit auth refresh` ## `grit install` Install supporting binaries **Usage:** `grit install [OPTIONS]` **Options:** - `--update` — Look for updates and install them Possible values: `true`, `false` - `--app ` — Specify a specific app to install Possible values: `grit`, `gouda`, `workflow-runner` ## `grit init` Install grit modules **Usage:** `grit init [OPTIONS]` **Options:** - `--global` — Update global grit modules Default value: `false` Possible values: `true`, `false` ## `grit workflows` Workflow commands, run `grit workflows --help` for more information **Usage:** `grit workflows ` **Subcommands:** - `list` — List all available workflows - `upload` — Upload a workflow ## `grit workflows list` List all available workflows **Usage:** `grit workflows list` ## `grit workflows upload` Upload a workflow **Usage:** `grit workflows upload ` **Arguments:** - `` - `` ## `grit patterns` Patterns commands, run `grit patterns --help` for more information **Usage:** `grit patterns ` **Subcommands:** - `list` — List all available named patterns - `test` — Test patterns against expected output - `edit` — Open a pattern in the studio - `describe` — Describe a pattern ## `grit patterns list` List all available named patterns **Usage:** `grit patterns list [OPTIONS]` **Options:** - `--level ` — List only at or above an enforcement level - `--source ` — List items from a specific source Default value: `all` Possible values: - `all`: All patterns - `local`: Only patterns from the local repo - `user`: Only patterns from the user config - `--language ` — List only items targeting a specific language Possible values: `js`, `html`, `css`, `json`, `java`, `kotlin`, `csharp`, `python`, `markdown`, `go`, `rust`, `ruby`, `elixir`, `solidity`, `hcl`, `yaml`, `sql`, `vue`, `toml`, `php`, `php` ## `grit patterns test` Test patterns against expected output **Usage:** `grit patterns test [OPTIONS]` **Options:** - `--filter ` — Regex of a specific pattern to test - `--exclude ` — Tags and pattern names to exclude. Only direct matches will be excluded. - `--verbose` — Show verbose output Possible values: `true`, `false` - `--update` — Update expected test outputs Possible values: `true`, `false` - `--watch` — Enable watch mode on .grit dir Possible values: `true`, `false` ## `grit patterns edit` Open a pattern in the studio **Usage:** `grit patterns edit ` **Arguments:** - `` — The pattern path to edit ## `grit patterns describe` Describe a pattern **Usage:** `grit patterns describe ` **Arguments:** - `` — The pattern name to describe ## `grit version` Display version information about the CLI and agents **Usage:** `grit version` ## `grit format` Format grit files under current directory **Usage:** `grit format [OPTIONS]` **Options:** - `--write` — Write formats to file instead of just showing them Possible values: `true`, `false` - `--output ` Default value: `compact` Possible values: `none`, `standard`, `compact` - `--filter ` — Regex of a specific pattern to test --- **Source:** https://docs.grit.io # Authoring custom patterns We recommend authoring Grit patterns in `.md` files, as this format allows you to combine documentation, GritQL, and source code examples in a single file. ## VS Code We recommend installing the [Grit extension](/guides/vscode) to get syntax highlighting and authoring assistance for Grit patterns. You can watch [this video](https://www.youtube.com/embed/WICqSP-w2IU?si=KAtvT12uWXaroS_z%22) to see the extension in action. ## Tips - Use the `grit patterns test` command to test all defined patterns in your repo. - As you are iterating on a pattern, you can use `--watch` to automatically re-run tests when you save a file. - You can use `--filter` to run a subset of patterns: this speeds up the test cycle when you are working on a specific pattern. ## Markdown Format Patterns can be stored in `*.md` files within a `.grit/patterns` folder at the root of your repo. - The `name` of the file is used as the name of the pattern (without the `.md` extension). - The `title` of the pattern is retrieved from the first heading in the file. This can be overridden by adding a `title` field in the front matter. - The `description` of the pattern is retrieved from the first non-heading paragraph in the file. - The GritQL `body` is retrieved from the first fenced code block in the file. - Additional pattern metadata can be configured as YAML in the frontmatter of the markdown file, delineated between `---` lines at the start of the document. - `level`: (Optional, one of `none`, `info`, `warn`, `error`) The enforcement level of the pattern for running diagnostics via `grit check`. Defaults to `info`. - `tags`: (Optional, `string[]`) A list of tags which can be used to filter patterns. - Subheadings are used for each test case, following these conventions: - If a subheading has a single code block, it represents a test case that should be matched by the pattern. You don't need to provide a transformed example. - If a subheading has two code blocks, the first represents the input and the second represents the expected output. - A negative test case should have two identical code blocks. - Within the sample patterns, you can use `// @filename: example.js` to represent multiple input/output files that should be tested as a group - like [this example](https://github.com/getgrit/stdlib/blob/main/.grit/patterns/js/split_trpc_router.md?plain=1#L129). ## Example Here is an example of a markdown pattern file: markdown .grit/patterns/remove_console_log.md --- tags: [optional, tags, here] --- # Remove console.log Remove console.log in production code. ```grit `console.log($_)` => . ``` ## Test case one This is the first test case. You can include an explanation of the case here. ```typescript console.error("keep this"); console.log('remove this!'); ``` It is fine to include additional descriptive text around the test cases. This is often used to explain the context of the test case, or to explain a convention. ```typescript console.error("keep this"); ``` You can find many more examples in the [Grit standard library](https://github.com/getgrit/stdlib/tree/main/.grit/patterns). --- **Source:** https://docs.grit.io # Continuous Integration Grit patterns can be configured to run as part of your CI pipeline on GitHub. ## Setup To get started, create a [configuration file](/guides/config) at `.grit/grit.yaml`. Add patterns that you want checked to your `grit.yaml`. ## Enabling hosted checks Ensure that you have installed Grit's [GitHub App](https://github.com/apps/grit-app). Once installed, navigate to your [project](https://app.grit.io/projects) from the web app and enable the Checks toggle. Bear in mind that patterns must be enforced (i.e. have a level of `error` or `warn`) in order to be run as part of CI, as per the details below. ### How it works By default, CI checks run on all files in a commit which are not excluded by `.gritignore`. Grit then reports on patterns which have a level of `warn` or higher, and fails the CI check if any files trigger an `error` level pattern which did not previously trigger that pattern on the last commit to the repo's default branch. This means that Grit's CI mechanism is sensitive to the *trend* of the codebase: you can add currently failing patterns to your configuration without breaking the build, and fix them incrementally in the knowledge that Grit will catch regressions. ## GitHub Action As an alternative to the Grit GitHub App, you can use GitHub Actions to run Grit checks. The Grit GitHub Action is [available here](https://github.com/getgrit/github-action-check). - **Note**: The GitHub action does not include a cache of previous results, so it will annotate all patterns with a level of `warn` or higher—even if they were previously reported on the main branch. --- **Source:** https://docs.grit.io # Grit Configuration Grit does not require any configuration to use patterns from the standard library. If you need to customize Grit's behavior further, add a `.grit/grit.yaml` file in your repository. shell /repo ├── .grit │ └── grit.yaml ├── foobar | └── baz ├── src └── etc A Grit configuration might look like this. yaml grit.yaml ```yaml version: 0.0.1 patterns: - name: avoid_only tags: ["style", "debugging"] level: error body: | `$testlike.only` => `$testlike` where { `$testlike` <: or { `describe` `it` `test` } } description: | .only is usually a mistake left over from local debugging. Pushing it to main risks false negatives on CI. samples: - input: | describe.only('this is a test', () => { // ... }); output: | describe('this is a test', () => { // ... }); - name: disabled_pattern level: none # This pattern is disabled body: | `something` => `somewhere` - file: ../other/doc/file.md ``` ## Configuration Reference Grit can be configured using a `.grit/grit.yaml` file. The file is in YAML format. ### Patterns The `patterns` field is a list of patterns which are applicable to your codebase. Each pattern can specify the following fields: - `name`: (Required, `string`) The name of the pattern. Conventionally in snake case. - `title`: (Optional, `string`) A short, human-readable title for the pattern. Defaults to the name if not set. - `body`: (Optional, `string`) The body of the pattern. This is useful for short, inline patterns. If not provided, the pattern should either be imported from a remote Grit module, or defined in a separate file within the `.grit` directory. - `description`: (Optional, `string`) A longer description of the pattern. - `level`: (Optional, one of `none`, `info`, `warn`, `error`) The enforcement level of the pattern for running diagnostics via `grit check`. Defaults to `info`. - `tags`: (Optional, `string[]`) A list of tags which can be used to filter patterns. - **Note**: You do *not* need to list patterns which are [defined](/guides/patterns) in Markdown or `.grit` files, they are automatically merged in. #### Importing Files By default, all patterns in the `.grit/patterns` directory are imported. If you want to import a Markdown or .grit pattern file from elsewhere in your repository, you can use the `file` field to do so. The path is relative to the `.grit` directory. yaml grit.yaml ```yaml patterns: - file: ../other/doc/file.md ``` #### Imported Remote Patterns The patterns listed in the `patterns` field don't need be local to your module. If you want to enable a pattern from another module, you just need to name it in your list, prefixed with the module name and a `#`. For example, to enable the [`no_console_log` pattern](https://github.com/getgrit/stdlib/blob/main/.grit/patterns/js/no_console_log.md) from the JavaScript standard library, you would add the following to your `grit.yaml` file. yaml grit.yaml ```yaml patterns: - name: github.com/getgrit/stdlib#no_console_log ``` If you want to enable all patterns from another module, just use a `*` for the pattern name. For example, to enable all patterns from the standard library, you would add the following to your `grit.yaml` file. yaml grit.yaml ```yaml patterns: - name: github.com/getgrit/stdlib#* ``` By default, all patterns from the standard library are automatically imported. If you define custom patterns, make sure their names don't conflict with the standard library. - Grit modules are simply referenced by their git URL. This means that you can import configurations from any git repository, including private repositories. To download remote patterns, run the `grit init` command through the Grit CLI. Grit searches up the directory tree from your current working directory to find the nearest `grit.yaml` config. For each remote gritmodule specified in the config, Grit clones a [sparse](https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---sparse), [shallow](https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---depthltdepthgt) copy of the repository's root `.grit` directory into your local `.gritmodules` directory. Because the remote module's pattern can itself be a reference to another module's pattern, Grit recursively traverses the dependency graph and downloads all modules referenced by `grit.yaml` files (but only the patterns specified in your `grit.yaml` will be enforced). Since gritmodules' patterns are identified by their root `.grit/grit.yaml` file, you must ensure that any patterns you wish to export from a repository you are using as a remote gritmodule are exposed in the root `grit.yaml`. - **Note**: The result of the pattern module downloading process is flat. Defining two patterns with the same name in different modules will cause an error. Grit considers all pattern names that are referenced within modules in the dependency graph, so you may get a pattern naming conflict even if you don't directly reference the conflicting pattern in your `grit.yaml` file. ### Version The version field specifies the version of this configuration file. We follow semantic versioning. The current version is `0.0.2`. yaml grit.yaml ```yaml version: 0.0.2 ``` ### GitHub The GitHub field contains GitHub-specific configuration. #### Reviewers The `reviewers` field is a list of GitHub users and teams which will be requested to review pull requests opened by Grit. Teams should be prefixed with an `@` symbol and use the full team name, including the organization. yaml grit.yaml ```yaml version: 0.0.2 patterns: [] github: reviewers: - gritbot - "@getgrit/core" ``` ## Markdown patterns Patterns can be stored in `*.md` files within a `.grit/patterns` folder at the root of your repo, including in subdirectories. ### Spec - The `name` of the file is used as the name of the pattern (without the `.md` extension). - The `title` of the pattern is retrieved from the first heading in the file. This can be overridden by adding a `title` field in the front matter. - The `description` of the pattern is retrieved from the first non-heading paragraph in the file. - The GritQL `body` is retrieved from the first fenced code block in the file. - Additional pattern metadata can be configured as YAML in the frontmatter of the markdown file, delineated between `---` lines at the start of the document. - `level`: (Optional, one of `none`, `info`, `warn`, `error`) The enforcement level of the pattern for running diagnostics via `grit check`. Defaults to `info`. - `tags`: (Optional, `string[]`) A list of tags which can be used to filter patterns. - Subheadings are used for each test case, following these conventions: - If a subheading has a single code block, it represents a test case that should be matched by the pattern. You don't need to provide a transformed example. - If a subheading has two code blocks, the first represents the input and the second represents the expected output. - A negative test case should have two identical code blocks. - Within the sample patterns, you can use `// @filename: example.js` to represent multiple input/output files that should be tested as a group - like [this example](https://github.com/getgrit/stdlib/blob/main/.grit/patterns/js/split_trpc_router.md?plain=1#L129). - **Note**: Grit's pattern resolution logic relies on each named pattern being associated with a single unique `body`. You should avoid defining multiple patterns with the same file name in different subdirectories, as this will cause conflicts. ### Example markdown .grit/patterns/remove_console_log.md --- tags: [optional, tags, here] --- # Remove console.log Remove console.log in production code. ```grit `console.log($_)` => . ``` ## Test case one This is the first test case. You can include an explanation of the case here. ```typescript console.error("keep this"); console.log('remove this!'); ``` It is fine to include additional descriptive text around the test cases. This is often used to explain the context of the test case, or to explain a convention. ```typescript console.error("keep this"); ``` You can find many more examples in the [Grit standard library](https://github.com/getgrit/stdlib/tree/main/.grit/patterns). ## Ignoring patterns If `grit check` is flagging too many files, you can ignore specific patterns or files. Pattern suppression should be used when you want to ignore a *true positive* result. For example, if you have a pattern that flags a deprecated function, but you have a good reason to use it, you can suppress the pattern. If patterns are yielding *false positives*, you should consider modifying the pattern to add [conditions](/language/conditions) that better capture the intent of the code. ### .gritignore files By default, Grit will ignore any files listed in your `.gitignore` file and any files in a `.grit` directory. You can tell Grit to ignore additional files and directories by creating `.gritignore` files. A `.gritignore` file is a plain text file where each line is a glob pattern indicating which paths should be omitted from Grit's analysis. For example, the following omits all JavaScript files: .gritignore ``` **/*.js ``` `.gritignore` files are cascading, so you can define them at multiple levels of the directory hierarchy. The exact semantics are detailed [here](https://docs.rs/ignore/latest/ignore/struct.WalkBuilder.html#ignore-rules). ### Inline suppression Named GritQL patterns can be suppressed for a particular line by adding a comment with the `grit-ignore` directive alongside or above the line to be disabled. pattern `console.$method($message)` => `console.info($message)` Before ``` console.log("Hello, world!"); // grit-ignore console.log("Can you hear me?"); ``` After ``` console.info("Hello, world!"); // grit-ignore console.log("Can you hear me?"); ``` To suppress only a particular set of rules, include their comma-separated names after the `grit-ignore` directive: js index.js ``` console.log('This will be rewritten!') // grit-ignore no_console_log, replace_console_log console.log('This won't!') ``` Then when the patterns `no_console_log` or `replace_console_log` are invoked by name, such as by running `grit apply no_console_log`, the second `console.log` statement will not be rewritten. Additional context can be added after the rule names, separated by a colon. We recommend using this to explain why the rule is being ignored. py index.py ``` print('Hello world!') # grit-ignore print_to_log: We want to keep this ``` ## User configuration In addition to the repository-level configuration described above, Grit also supports user-level configuration under a user `.grit/patterns` directory. By default, this is located in your home directory at `~/.grit/patterns`. You can override its location by setting the `GRIT_USER_CONFIG` environment variable to point at a `.grit` directory of your choice. Repository patterns always take precedence over user patterns. If a pattern is defined in both the repository and user configuration, the repository pattern will be used. User patterns cannot be imported by repository configurations, but can be applied through `grit apply` invocations on the CLI. --- **Source:** https://docs.grit.io # File Imports Since managing package imports is a common task, Grit includes standard patterns for declaratively adding, removing, and updating imports. `ensure_import_from`This pattern ensures that the given `$value` is imported from the given `$source` (adding the import if it does not exist). Note: because this is a *pattern* you must match some `$value` against it. pattern `class $_ extends $comp { $_ }` where { $comp <: `Component`, $source = `"React"`, $comp <: ensure_import_from($source) }Before class Button extends Component { // ... }After import { Component } from 'React'; class Button extends Component { // ... } --- **Source:** https://docs.grit.io # Custom Patterns The true power of `grit` comes with the ability to write and share your own patterns. There are several ways to add custom patterns. Once configured in your repo, a custom pattern can be used through the CLI, the VSC extension, or the web app in the same way as any pattern from the Grit standard library. ## Pattern definitions The basic syntax for defining a pattern is to use the `pattern` keyword followed by the pattern name and a body. The `pattern` keyword can be used to define a named pattern that is used by other patterns. Patterns can only be defined at the top level of a file - you can't nest pattern definitions. pattern pattern console_method_to_info($method) { `console.$method($message)` => `console.info($message)` } console_method_to_info(method = `log`)Before console.log('Hello, world!'); console.warn('Can you hear me?');After console.info('Hello, world!'); console.warn('Can you hear me?');Pattern names must begin with a letter or an underscore, followed by any combination of alphanumeric characters and underscores. Pattern parameter names can be omitted as long as arguments are provided in the same order as the parameters are defined. For example, the `console_method_to_info` pattern from above could be called like this: pattern pattern console_method_to_info($method) { `console.$method($message)` => `console.info($message)` } console_method_to_info(`log`)Before console.log('Hello, world!'); console.warn('Can you hear me?');After console.info('Hello, world!'); console.warn('Can you hear me?'); ### Private patterns Patterns can be marked as private by including the `private` keyword before the pattern. Private patterns are not visible in the pattern list and are only meant to be used within a module's context. grit private pattern this_pattern_is_hidden() { `console` } ## Predicate definitions In addition to patterns, you can also define predicates, which comprise full statements specifying a metavariable to match against as well as a pattern to check. In other words, a predicate definition consists exclusively of clauses that would be valid within a `where` condition. For example, the following is a valid predicate definition that returns true as long as the program contains a `logger` instance: grit predicate program_contains_logger() { $program <: contains `logger` }For illustration, two patterns below are equivalent: grit predicate program_contains_logger() { $program <: contains `logger` } `console.log` => `logger.info` where { program_contains_logger() }grit pattern contains_logger() { contains `logger` } `console.log` => `logger.info` where { $program <: contains_logger() } ## .grit pattern files Any patterns defined in `.grit` files within a `.grit/patterns` folder at the root of your repo will be automatically loaded for use within your repo. The filename does not matter, so you can organize them however you want within that directory. You can also use subdirectories inside `.grit/patterns` to organize your patterns. For example, place this file at `.grit/patterns/example.grit`: grit pattern replace_console_log() { `console.log($args)` => `console.error($args)` } ## YAML configuration patterns If you prefer to inline pattern definitions with other Grit config settings, custom patterns can be included in `grit.yaml` configuration files. For more information, see the [config](./config#patterns) guide. ## Markdown pattern files Patterns can also be defined in markdown files. See the [markdown patterns spec](/guides/config#spec) for more information. --- # Source: https://docs.grit.io # Sharing Patterns Patterns can be shared with others by publishing them to a Git repository. This allows you to reuse patterns across different repositories, or to use patterns created by others (including the Grit standard library). ## Publishing Patterns Publishing a Grit pattern is as simple as pushing it to a Git repository. The repository can be public or private, but currently only GitHub is supported. If you want to run your pattern through the Grit web app, private pattern repositories must be configured under the same [GitHub app](https://github.com/apps/grit-app) installation as all repositories which depend on them. As long as the repository has `.grit` directory (in the root) with at least one [pattern](/guides/patterns) defined, it will be recognized as a pattern repository. ## Importing Patterns Once a pattern repository is published, you can reference patterns from it in your own repository simply be prefixing the pattern name with the repository name and a hash. Patterns are always pulled from the default branch of the repository. - You can set an enforcement level - for example, `level: error` or `level: warn` - to override the default enforcement setting for the pattern. For example, to import the [no dead code](https://github.com/getgrit/stdlib/blob/main/.grit/patterns/js/no_dead_code.md) pattern from the JavaScript standard library, you would add the pattern your `.grit/grit.yaml` file: ```yaml version: 0.0.1 patterns: - name: github.com/getgrit/stdlib#no_dead_code level: info ``` As a reminder, the file name for `.grit` patterns is [not important](/guides/patterns#grit-pattern-files). You should import patterns by the name of the pattern, not the file name. --- # Source: https://docs.grit.io # Testing GritQL If you are crafting a complex query, we recommend iterating on test cases to ensure that your query behaves as expected. Run the `grit patterns test` command to test all defined patterns in your repo. You can also use `--filter` to run a subset of patterns. ```shell grit patterns test --filter=pattern_to_test ``` ## Markdown format Reusable queries and test cases can be bundled together in a Markdown file to create a pattern. This allows you to combine documentation, GritQL, and source code examples in a single file. The markdown file should be placed in a `.grit/patterns` folder at the root of your repo. It can be formatted however you like, so long as you follow these conventions: - The `name` of the file is used as the name of the pattern (without the `.md` extension). - The `title` of the pattern is retrieved from the first heading in the file. This can be overridden by adding a `title` field in the front matter. - The `description` of the pattern is retrieved from the first non-heading paragraph in the file. - The GritQL `body` is retrieved from the first fenced code block in the file. - Additional pattern metadata can be configured as YAML in the frontmatter of the markdown file, delineated between `---` lines at the start of the document. - `level`: (Optional, one of `none`, `info`, `warn`, `error`) The enforcement level of the pattern for running diagnostics via `grit check`. Defaults to `info`. - `tags`: (Optional, `string[]`) A list of tags which can be used to filter patterns. Subheadings are used for each test case, following these conventions: - If a subheading has a single code block, it represents a test case that should be matched by the pattern. You don't need to provide a transformed example. - If a subheading has two code blocks, the first represents the input and the second represents the expected output. - A negative test case should have two identical code blocks. - Within the sample patterns, you can use `// @filename: example.js` to represent multiple input/output files that should be tested as a group - like [this example](https://github.com/getgrit/stdlib/blob/main/.grit/patterns/js/split_trpc_router.md?plain=1#L129). ### Markdown example ````markdown .grit/patterns/remove_console_log.md--- tags: [optional, tags, here] --- # Remove console.log Remove console.log in production code. ```grit `console.log($_)` => . ``` ## Test case one This is the first test case. You can include an explanation of the case here. ```typescript console.error("keep this"); console.log('remove this!'); ``` It is fine to include additional descriptive text around the test cases. This is often used to explain the context of the test case, or to explain a convention. ```typescript console.error("keep this"); ``` ```` You can find many more examples in the [Grit standard library](https://github.com/getgrit/stdlib/tree/main/.grit/patterns). ## YAML format If you define patterns directly in the `.grit/grit.yaml` [file](http://localhost:3200/guides/config#configuration-reference), you can also include before and after test cases underneath the `samples` key. Each sample *must* include a `input` and `output` key. ### YAML example ```yaml patterns: - name: avoid_only body: | `$testlike.only` => `$testlike` where { `$testlike` <: or { `describe` `it` `test` } } samples: - input: | describe.only('this is a test', () => { // ... }); output: | describe('this is a test', () => { // ... }); ``` --- # Source: https://docs.grit.io # Grit - Declarative Code Search and Transformation GritQL is a declarative query language for searching and modifying source code. ## Why GritQL? - 📖 **Start simply** without learning AST details: any code snippet is a valid GritQL query - ⚡️ **Scale to millions of lines** using Rust and query optimization for repositories with 10M+ lines - 📦 **Reuse patterns** with Grit's built-in module system to access 200+ standard patterns or share your own - ♻️ **Works everywhere**: use GritQL to rewrite JavaScript/TypeScript, Python, JSON, Java, Terraform, Solidity, CSS, Markdown, YAML, Rust, Go, or SQL - 🔧 **Auto-fix built in**: GritQL makes it easy to include auto-fix rules for faster remediation ## Quick Example Search for patterns using backticks and metavariables (like `$msg`): ```shell grit apply '`console.log($msg)` => `winston.log($msg)`' ``` Save patterns to enforce as custom lints, with powerful where clauses to exclude specific cases: ```gritql `console.log($msg)` => `winston.log($msg)` where { $msg <: not within or { `it($_, $_)`, `test($_, $_)` } } ``` ## Use Cases GritQL excels at automating: - **Large-scale migrations** - Migrate APIs, frameworks, or refactor codebases across millions of lines - **Custom linting** - Enforce team-specific code standards and best practices - **Code quality improvements** - Systematically improve code quality with consistent patterns - **Dependency upgrades** - Handle breaking changes when upgrading dependencies Interactive Tutorial Learn GritQL step-by-step Language Reference Complete GritQL documentation CLI Quickstart Install and use the Grit CLI --- # Source: https://docs.grit.io ## Variable Scoping Within a pattern, all metavariables share a common scope. Once a metavariable is bound to a value, it retains this value throughout the target code. Therefore, the scope of the metavariable spans the entire target file. For instance, if your pattern matches the snippet `console.log($x)`, then according to the standard Grit scoping rules, it would only match the first occurrence of `$x` in the target file. ## Defined patterns When you [define a pattern](/guides/patterns#pattern-definitions), it establishes a new variable scope. However, pattern parameters bridge this scope. In the following pattern, `$method` will cross the scope boundary while `$message` remains local: grit pattern console_method_to_info($method) { `console.$method($message)` => `console.info($message)` } ## bubble clause The `bubble` clause introduces a new scope without the necessity of defining a separate pattern. Like with separate patterns, variables inside the `bubble` clause are isolated from the surrounding code. This is particularly useful when you want to match a pattern in multiple places without requiring metavariables to hold the same value in each location. pattern `function() { $body }` where { $body <: contains bubble `console.log($message)`=> `console.warn($message)` } Before function() { console.log('Hello, world!'); console.log('How are you?'); } After function() { console.warn('Hello, world!'); console.warn('How are you?'); } In the above pattern, the `bubble` clause ensures the metavariable `$message` is not bound to the same value in both `console.log` calls. Absent the `bubble`, only the first `console.log` call would be rewritten. After it was rewritten, `$message` would be bound to the value `'Hello, world!'`. When attempting to match the second `console.log`, `$message` would try to bind to `'How are you?'` and fail. ### Arguments for the `bubble` clause Like pattern definitions, the `bubble` clause can accept arguments. Such arguments are used to "pierce" the bubble by allowing metavariables to preserve the values they had outside the bubble scope. For example: pattern `function $name() { $body }` where { $body <: contains bubble($name) `console.log($message)` => `console.warn($message, $name)` } Before function logger() { console.log('Hello, world!'); console.log('How are you?'); } After function logger() { console.warn('Hello, world!', logger); console.warn('How are you?', logger); } If `$name` weren't specified as an argument for `bubble`, the metavariable `$name` would be undefined inside the bubble, leading to an error during rewriting. ## Pattern auto-wrap Unless a root pattern targets either `file` or `body`, it is automatically wrapped to become `file(body = contains bubble $root_pattern)`. The main effect of auto-wrap is that patterns can match multiple times within a file without needing to manually specify `bubble`. grit file(body=contains bubble $THE_PATTERN) This default behavior often proves beneficial as it facilitates matching multiple sections within a file independently. To override this, encapsulate the pattern within `file`. For example, if you want to transform `console.$method` calls but only for one method at a time, you can use a pattern like this: pattern file(body = contains `console.$method` => `println`) Before console.info('Hello, world!'); console.info('How are you?'); console.error('Hello?'); After println('Hello, world!'); println('How are you?'); console.error('Hello?'); In the pattern shown above, the metavariable `$method` consistently binds to a singular value, `info`, throughout the pattern's application. ## Global metavariables While these scoping rules suffice in most scenarios, there are times when you might want to share scope across a file within a defined pattern. This can be achieved using global metavariables, denoted with a `$GLOBAL_` prefix and written in uppercase. For example, the default [import utilities](https://github.com/getgrit/stdlib/blob/main/.grit/patterns/js/imports.grit) use global metavariables to keep a running list of all imports in a file. --- # Source: https://docs.grit.io ## Conditional Operators `where` clauseThe `where` clause introduces one or more conditions that must be true for the pattern preceding it to execute. pattern `console.log($message)` => . where $message <: js"'Hello, world!'" Before console.log('Hi'); console.log('Hello, world!'); After console.log('Hi'); The `where` clause can be followed by either a single condition or a list of conditions, wrapped in braces and separated by commas (`{ cond1, cond2 }`). If a `where` clause contains multiple conditions, all must be true for the pattern to match. Match (`<:`) conditionGrit's most common condition, the match operator `<:` specifies what part of the codebase you want to target. It does this by matching a [metavariable](/language/patterns#metavariables) to its left against a pattern to its right. grit `console.log('$message')` where $message <: `Hello, world!` `!` condition The `!` operator is used to negate a condition. pattern `console.log('$message');` => `console.warn('$message');` where { ! $message <: "Hello, world!" } Before console.log('Hello, world!'); console.log('Hello, people!'); After console.log('Hello, world!'); console.warn('Hello, people!'); `!` is similar to [the `not` modifier](/language/modifiers#not-clause), except it applies to the entire condition, rather than the pattern within the condition. For example, `! $message <: "Hello, world!"` is equivalent to `$message <: not "Hello, world!"`. `and` condition The `and` operator is true if all of the conditions are true. Note that the `and` in the following example is redundant, as the `where` clause already requires all conditions to be true. pattern `console.$method('$message');` => `console.warn('$message');` where { and { $message <: r"Hello, .*!", $method <: `log` } } Before console.log('Hello, world!'); console.error('Hello, world!'); After console.warn('Hello, world!'); console.error('Hello, world!'); `or` condition The `or` operator is true if any of the conditions are true. pattern `console.$method('$message');` => `console.warn('$message');` where { or { $message <: "Hello, world!", $method <: `error` } } Before console.log('Hello, world!'); console.error('Hello, people!'); console.info('Hello, people!'); After console.warn('Hello, world!'); console.warn('Hello, people!'); console.info('Hello, people!'); `if` as a condition The `if` clause can be used to add a condition only if another condition is true. pattern `$method('$message')` where { if ($message <: r"Hello, .*!") { $method => `console.info` } else { $method => `console.warn` } } Before console.log('Hello, world!'); console.log('Hello, people!'); console.log('Hi!'); After console.info('Hello, world!'); console.info('Hello, people!'); console.warn('Hi!'); Rewrite (`=>`) as conditionRewrites can also be used within conditions. The syntax is the same as for rewrites, but the left-hand side of the rewrite **must** be a metavariable. The rewrite will be applied to the code locations referenced by the metavariable, and the condition will match if the rewrite succeeds for all locations. pattern `console.log('$message')` where $message => `Hello, world!` Before console.log('Hello?'); After console.log('Hello, world!'); Assignment `=` The assignment operator `=` is used to assign a value to a metavariable, much in the same way as you would assign to a variable in other programming languages. The value can be anything that can appear on the right-hand side of a rewrite. An assignment can appear inside a where clause, and always succeeds. grit `console.log($message)` as $log where { $new_log_call = `logger.log($message)`, $log => $new_log_call } --- # Source: https://docs.grit.io ## Functions While patterns are used to match against AST nodes, functions are used as replacement values. Thus, functions can only be used on the right hand side of GritQL queries. Specifically: - In assignments: `$x = function_call()` - In insertions: `$x += 'function_call()` - In rewrites: `$x => function_call()` ## Function definitions Just like with patterns, custom functions can be defined using the `function` keyword. Functions must have a name, and can optionally take parameters. The body of the function consists of [predicates](/guides/patterns#predicate-definitions) that are evaluated in order. Below the hood, a function is simply a predicate that produces a return value if all constituent statements evaluate to true. The final line of the function should be `return` followed by the value to return. pattern // define a lines function function lines($string) { return split($string, separator=`\n`) } // Define a my_todo function function my_todo($target, $message) { if($message <: undefined) { $message = "This requires manual intervention." }, $lines = lines(string = $message), $lines <: some bubble($result) $x where { if ($result <: undefined) { $result = `// TODO: $x` } else { $result += `\n// $x` } }, $target_lines = lines(string = $target), $target_lines <: some bubble($result) $x where { $result += `\n// $x` }, return $result, } // Use the my_todo function `module.exports = $_` as $x => my_todo(target=$x, message=`Fix this\nAnd that`) Before module.exports = { king, queen: '8', }; After // TODO: Fix this // And that // module.exports = { // king, // queen: '8', // }; Function parameter names can be omitted as long as arguments are provided in the same order as the parameters are defined. For example, the `my_todo` function from above could be called like this: grit `module.exports = $_` as $x => my_todo($x, `Fix this\nAnd that`) ## JavaScript functions **Warning**: This is an alpha feature in active development, without stability guarantees. Please contact us on [Discord](/discord) or Slack if you have any questions. Functions are not limited to using GritQL. By including `js` after the function parameters, you can define a function that is implemented in JavaScript. pattern language js function fizzbuzz($x) js { // Use $var.text to access the binding's value const parsed = parseInt($x.text, 10); let output = ''; if (parsed % 3 === 0) output += 'Fizz'; if (parsed % 5 === 0) output += 'Buzz'; return output || parsed; } `console.log($x)` => fizzbuzz($x) Before console.log(3); console.log(8); console.log(10); console.log(15); After Fizz; 8; Buzz; FizzBuzz; ### Limitations - JavaScript functions *must* return a stringable value. This means that the return value must be a string, or an object with a `toString` method. - Functions are executed in a WebAssembly sandbox. They cannot access the filesystem or make network requests. - If the function throws an error, the pattern will error and fail to match. - Parameters are accessible *only* via the `$variable.text` property. Metavariables without a string representation cannot be accessed. If you need to parse a parameter as a number, you can use `parseInt` or `parseFloat`. - Foreign functions cannot bind new variables. They can only access the variables that are passed in as parameters. ## Built-in functions GritQL provides several built-in functions that can be used in queries targeting any language. `log` `log` takes a message and a variable (both optional) and logs their values to the console. It is useful for debugging. For example, the following query produces log output in addition to rewriting the source code: pattern `console.log($arg)` => . where { log(message="This is a debug log", variable=$arg), } Before console.log("grape"); After bash Log in index.js: This is a debug log - Range: 1:13 - 1:20 - Source: 'grape' - Syntax tree: (string fragment: (string_fragment)) `capitalize` `capitalize` takes a string and returns a new string with the first character capitalized. grit capitalize(string = "hello") // "Hello" `trim` `trim(s)` returns a new string with the whitespace removed from the beginning and end of `s`. grit trim(string = " hello ", trim_chars = " ") // returns "hello" `join` `join(delimiter, strings)` concatenates a list of `strings` with a `delimiter` to form a single string. grit join(list = ["a", "b", "c"], separator = "_") // returns "a_b_c" join(list = ["a", "b", "c"], separator = "") // returns "abc" `split` `split(string, separator)` splits a `string` into a list of substrings, using `separator` as the delimiter. grit split(string = "a_b_c", separator = "_") // returns ["a", "b", "c"] `todo` In some cases, a transformation cannot be completed fully automatically. You can use the `todo` function to mark a `target` snippet as incomplete and add a comment to the generated code. An optional `message` can be provided to give more information about the incomplete transformation. pattern or { `console.log($msg)` => todo(target=$msg), `console.error($msg)` as $log => todo(target=$log, message="Consider a lower error level.") } Before console.log("hello"); console.error("This is an error"); After // TODO: This requires manual intervention. // "hello" // TODO: Consider a lower error level. // console.error("This is an error") `uppercase` `uppercase` takes a string and returns a new string with all characters uppercased. grit uppercase(string = "HELLO") // "HELLO" uppercase(string = "Hello") // "HELLO" uppercase(string = "hello") // "HELLO" `lowercase` `lowercase` takes a string and returns a new string with all characters lowercased. grit lowercase(string = "HELLO") // "hello" lowercase(string = "Hello") // "hello" lowercase(string = "hello") // "hello" `resolve` `resolve` takes a string and interprets it as a path relative to the current file. It returns the absolute path to the resolved file. grit resolve(path = "./index.js") // returns "/path/to/index.js" resolve(".././foo.js") // returns "/path/foo.js" resolve(path = "../index.js") // returns "/path/index.js" The target file does not need to exist on disk - the resolve function just looks at the string. It does not follow symlinks, but it does normalize `..` and `.` segments. `length` `length(target=$items)` returns the number of elements in a `target` list. It can also be used to get the length of a target string. grit length(target=[7, 8, 9]) // returns 3 length(target="hello") // returns 5 `shuffle` `shuffle(target=$items)` returns a new list with the elements of `target` in a pseudo-random order. grit shuffle(target=[7, 8, 9]) // returns [8, 9, 7] `text` `text($target)` captures the *current* text of the target node as a string. This is typically useful for cases where you want to keep the original text of a node before modifying it, such as when duplicating code. grit `console.log($msg)` where { $clone = text($msg), // keep a clone of $msg, since we are about to modify it $msg => `"log", $clone` } `random` `random` returns a pseudorandom number between 0 and 1. If a `random($min, $max)` is provided, it returns a random integer between `$min` and `$max`, inclusive. grit random() // returns a random number between 0 and 1, ex. 0.123456789 random(min = 1, max = 10) // returns a random integer between 1 and 10, ex. 7 - - **Note**: The Grit runtime is deterministic, so the same query will always return the same result. `distinct` `distinct` takes a list and returns a new list with all duplicate elements removed. grit distinct(list = [1, 2, 3, 2, 1]) // returns [1, 2, 3] --- # Source: https://docs.grit.io ## Common Idioms This page covers some common techniques for writing GritQL patterns. ## List and string accumulation Within `where` clauses, you can assign a value to a metavariable using the `=` operator and then use `+=` to accumulate values. This is particularly common when combined with [`some`](/language/modifiers#some-clause) to iterate over a list of values, such as in [this pattern](https://github.com/getgrit/stdlib/blob/main/.grit/patterns/js/es6_imports.md). Strings and lists can both be accumulated, as shown in this example: pattern `namedColors = { $colors }` where { $keys = "", $values = [], $colors <: some bubble($keys, $values) `$name: $color` where { // Push the name onto the string $keys += $name, // Push the values onto the list $values += `$color` }, // Join the values together to make a string $new_colors = join(list=$values, separator=", ") } => `$keys = [ $new_colors ]` Before const namedColors = { red: '#ff0000', green: '#00ff00', blue: '#0000ff', }; After const redgreenblue = [ '#ff0000', '#00ff00', '#0000ff' ]; ## Creating new files The `$new_files` metavariable allows creating a new file as a side-effect of a pattern. For example, for following pattern moves all functions which start with `test` to a separate file (`stuff.test.js`): pattern `function $functionName($_) {$_}` as $f where { $functionName <: r"test.*", $f => ., $new_file_name = `$functionName.test.js`, $new_files += file(name = $new_file_name, body = $f) } Before function doStuff() {} function testStuff() {} After // stuff.js function doStuff() {} // testStuff.test.js function testStuff() {} - - **Warning:** `$new_files` does not consider the existing files, so it is possible to overwrite existing files. ## Accessing the current file name The `$filename` metavariable always contains the name of the current file the query is processing. For example, the following pattern we move the matching function to a new file that uses the same name as the original file, but with a `.test.js` extension: pattern `function $functionName($_) {$_}` as $f where { $functionName <: r"test.*", $f => ., $filename <: r"(.*)\.js$"($base_name), $new_file_name = `$base_name.test.js`, $new_files += file(name = $new_file_name, body = $f) } Before function doStuff() {} function testStuff() {} After // stuff.js function doStuff() {} // stuff.test.js function testStuff() {} ## Comments Comments are supported in GritQL. Any line starting with `//` is considered a comment and ignored by the parser. grit `console.log($message)` => . where { // This is a comment $message <: js"'Hello, world!'" } ## Specific rewrites Since GritQL supports nested patterns, it is usually preferable to match a larger pattern and then only *rewrite* the specific metavariables you want to change inside a `where` clause. This helps in several ways: - It avoids duplication, as often the right-hand side of the rewrite repeats the left-hand side. - It helps preserve syntactic and semantic details of the original code. For example, the following rewrite is not specific and therefore loses the `async` keyword and the comment about arguments: pattern `function foo($args) { $body }` => `function bar($args) {$body}` Before async function foo(/* no args */) { console.log('Hello, world!'); } After function bar() { console.log('Hello, world!'); } The following rewrite is specific and preserves the `async` keyword and the comment about arguments: pattern `function $name($args) { $body }` where { $name <: `foo` => `bar` } Before async function foo(/* no args */) { console.log('Hello, world!'); } After async function bar(/* no args */) { console.log('Hello, world!'); } ## Making small pull requests In general, huge pull requests are harder to review and more likely to have merge conflicts. You can use the [limit clause](/language/modifiers#limit-clause) to limit the number of files a pattern is applied to. ## Targeting specific code blocks You can use `range` patterns together with the [`$filename` metavariable](#accessing-the-current-file-name) to target specific code blocks that are known ahead of time. This is often helpful for integrating Grit with other static analysis tools. For example, to target the `console.log` on the 2nd line of a file called `stuff.js`: grit `console.log($message)` where { $message <: within range(start_line=2, end_line=2), $filename <: includes "stuff.js" } typescript // @filename: stuff.js console.log("hello"); console.log("goodbye"); console.log("wow"); console.log("multiline"); --- # Source: https://docs.grit.io Various modifiers can be applied to patterns to transform how they are matched. All pattern modifiers yield a new pattern with the modified behavior. Importantly, none of these modifiers represent base patterns by themselves, but instead modify or combine other patterns. `and` clause`and` clauses are used to combine one or more comma-separated patterns. The `and` clause matches only if all of the patterns match. In the example below, we combine [the `where` clause](/language/conditions#where-clause) with `and` to rewrite two types of React hooks in one pattern. pattern arrow_function($body) where $body <: and { contains js"React.useState" => js"useState", contains js"React.useMemo" => js"useMemo", }Before const someComponent = () => { const [count, setCount] = React.useState(0); }; const anotherComponent = () => { const [count, setCount] = React.useState(0); const math = React.useMemo(() => count + 5, [count]); };After const someComponent = () => { const [count, setCount] = React.useState(0); }; const anotherComponent = () => { const [count, setCount] = useState(0); const math = useMemo(() => count + 5, [count]); };`or` clause`or` clauses are used to match against multiple patterns. They are written using the `or` keyword, and their arguments are separated by a comma. The `or` clause matches if any of the patterns match. `or` is short-circuited, so the first pattern that matches will be the one that is used. pattern arrow_function($body) where $body <: or { contains js"React.useState" => js"useState", contains js"React.useMemo" => js"useMemo", }Before const someComponent = () => { const [count, setCount] = React.useState(0); }; const anotherComponent = () => { const [count, setCount] = React.useState(0); const math = React.useMemo(() => count + 5, [count]); };After const someComponent = () => { const [count, setCount] = useState(0); }; const anotherComponent = () => { const [count, setCount] = useState(0); const math = React.useMemo(() => count + 5, [count]); };`any` clauseThe `any` clause is a non-short-circuiting version of `or`. It will match if any of the provided patterns matches, but will continue to try to match all of the patterns and execute any applicable transformations: pattern arrow_function($body) where $body <: any { contains js"React.useState" => js"useState", contains js"React.useMemo" => js"useMemo", }Before const someComponent = () => { const [count, setCount] = React.useState(0); }; const anotherComponent = () => { const [count, setCount] = React.useState(0); const math = React.useMemo(() => count + 5, [count]); };After const someComponent = () => { const [count, setCount] = useState(0); }; const anotherComponent = () => { const [count, setCount] = useState(0); const math = useMemo(() => count + 5, [count]); };`not` clause`not` clauses are used to negate a pattern. They are written using the `not` keyword. A `not` clause matches if the pattern it negates does not match. pattern `$method($message)` => `console.warn($message)` where { $method <: not `console.error` }Before console.log("Hello, world!"); console.error("Hello, world!");After console.warn("Hello, world!"); console.error("Hello, world!");`maybe` clause`maybe` clauses are used to optionally match against a pattern. If the pattern inside does not match, the `maybe` clause will still match as a whole. pattern `throw new Error($err)` as $thrown => `throw new CustomError($err);` where { $err <: maybe string(fragment=$fun) => `{ message: $err }` }Before if (Math.random() < 0.5) { throw new Error(); } else { throw new Error("nest the message in an object"); }After if (Math.random() < 0.5) { throw new CustomError(); } else { throw new CustomError({ message: "nest the message in an object" }); } **Warning**: In the pattern above, the metavariable `$fun` serves only an illustrative example and so isn't actually used after it's assigned. However, if we were to use it, we'd have to be careful: metavariables in `maybe` clauses are bound only if the `maybe` clause matches, and Grit will throw an error if you attempt to use one when the clause does not match. `contains` operatorThe `contains` keyword is used to modify a pattern to match any node that contains a specific pattern by traversing downwards through the syntax tree. grit `function ($args) { $body }` where { $args <: contains `x` }In the example above, the pattern will only match functions where one of the arguments is `x`. `until` modifierThe `until` modifier is appended to `contains` pattern is used to stop traversal within a `contains` clause. The `contains` pattern will only traverse within the parts of the node that are not matched by the `until` pattern. When the `until` pattern is reached, traversal will not continue any deeper. For example, the `until` here stops traversal inside `sanitized` calls: grit `console.$_($content)` where { $content <: contains `secret` until `sanitized($_)` }javascript console.log(secret); console.log(sanitized(secret)); console.log(random(secret)); console.log(other + sanitized(secret)); console.log(random(secret) + sanitized(secret));`as` modifier`as` can be used to assign a matched snippet to a metavariable. This is often useful when you want to perform some change on a section of code as a whole while also making more granular changes within that section. For example: pattern engine marzano(0.1) language js `function $name ($args) { $body }` as $func where { $func => `const $name = ($args) => { $body }`, $args <: contains `apple` => `mango` }Before function fruits(apple, pear) { console.log("fruits"); }After const fruits = (mango, pear) => { console.log("fruits"); };`within` clause`within` restricts the pattern to only match if the target node appears within code matching another pattern. For example, the pattern below only matches the second console.log, as it appears within the if block. grit `console.log($arg)` where { $arg <: within `if (DEBUG) { $_ }` }typescript console.log('Hello, world!'); if (DEBUG) { console.log('Hello, world!'); }`after` clause`after` clauses are used to restrict the pattern to only match if the target node is directly after a node matching a specific pattern. They are written using the `after` keyword. grit `console.warn($_)` as $warn where { $warn <: after `console.log($_)` }`after` can also be used to *retrieve* the node that comes after the target node. pattern `const $x = 5` as $const where { $next = after $const } += `// Next: $next`Before const x = 5; const nine = 10;After const x = 5; // Next: const nine = 10; const nine = 10;`before` clause`before` clauses are used to restrict the pattern to only match if the target node is directly before a node matching a specific pattern. They are written using the `before` keyword. grit `console.warn($_)` as $warn where { $warn <: before `console.log($_)` }`before` can also be used to *retrieve* the node that comes before the target node. pattern `const $_ = 10` as $const where { $next = before $const, $next <: `const $name = $_` } += `// Comes after $name`Before const x = 5; const nine = 10;After const x = 5; const nine = 10; // Comes after x`some` clauseThe `some` clause can be used to match a metavariable which represents a list against a pattern which represents some element of that list. The list can be any ordered set of syntax tree nodes, for example, a list of statements. As long as one of the nodes matches the pattern, the `some` clause will match. pattern `var $x = [$names]` => `var coolPeople = [$names]` where { $names <: some { `"andrew"` } }Before var people = ["alex", "andrew", "susan"]; var peopleTwo = ["beth", "max", "right"];After var coolPeople = ["alex", "andrew", "susan"]; var peopleTwo = ["beth", "max", "right"]; - - **Tip**: If you would like the `some` clause to succeed even if no match is found, prefix it with `maybe`. Ex. `$names <: maybe some { `"andrew"` }` `every` clauseThe `every` clause can be used to match a pattern against every element of a list. It matches only if all elements of the list match the pattern. pattern `var $x = [$names]` => `var coolPeople = [$names]` where { $names <: every or {`"andrew"`, `"alex"`} }Before var people = ["alex", "andrew", "alex"]; var peopleTwo = ["alex", "max", "right"]; var nobody = ["sam", "susan"];After var coolPeople = ["alex", "andrew", "susan"]; var peopleTwo = ["alex", "max", "right"]; var nobody = ["sam", "susan"]; - - **Tip**: `every` is short-circuited, so the first element that does not match will cause the `every` clause to fail. [ ## List patterns ](https://docs.grit.io/language/modifiers#list-patterns)List patterns are used to match against a list of terms. They are written using the `[]` syntax. The list pattern matches if all of the terms in the list match in order. pattern `var $x = [$numbers]` => `var firstPrimes = [$numbers]` where { $numbers <: [ `2`, `3`, `5` ] }Before var someNumbers = [2, 3, 5];After var firstPrimes = [2, 3, 5];`...` clauseThe `...` clause is used to match against a sublist of a list, ignoring the `...` elements. pattern `var $x = [$numbers]` => `var firstPrimes = [$numbers]` where { $numbers <: [ `2`, `3`, ..., `11` ] }Before var someNumbers = [2, 3, 5, 7, 11];After var firstPrimes = [2, 3, 5, 7, 11];`limit` clauseThe `limit` clause can be added after any root pattern to limit the number of matches that are returned. Limits can only be defined in the root query and are enforced globally across all files queried. pattern `console.$method($message)` => `console.warn($message)` where { $method <: not `error` } limit 2 // Only find 2 filesBefore // @filename: file.tsx console.log("Hello, world!"); console.log("Hello, world 2!"); // @filename: file2 console.log("This is another file"); // @filename: file3 console.log("This is a third file");After // @filename: file.tsx console.warn("Hello, world!"); console.warn("Hello, world 2!"); // @filename: file2 console.warn("This is another file"); // @filename: file3 console.log("This is a third file"); - - **Note**: The `limit` is moved to apply to the *file* as part of [pattern auto-wrapping](/language/bubble#pattern-auto-wrap). If you use `sequential` or `multifile` patterns, you will need to be careful to place the `limit` clause in the desired location. When running a `limit` query through the studio, paid customers can configure Grit to [automatically generate a new PR](/workflows/sequence) whenever the previous PR is merged. --- # Source: https://docs.grit.io - GritQL Documentation- - **Documentation**GitHubDiscordBlogTutorialPlaygroundDocsGetting Started OverviewCLI QuickstartCLI ReferencePattern LibraryConfigLanguage OverviewTutorialPatternsConditionsPattern ModifiersTarget LanguagesBubble and ScopingDefining PatternsFunctionsCommon IdiomsSyntax ReferenceTesting GritQLGuides Continuous IntegrationAuthoring GritQLImportsVS CodeSharing Patterns GritQL language is Grit's embedded query language for searching and transforming source code. It is designed to match developer intuition wherever possible, reducing the overhead of adding it to your stack, while offering the power and versatility to execute complex transformations in just a few lines of code. The best place to start for writing custom GritQL patterns is the [tutorial](/tutorials/gritql). ## Topics Intro tutorial Learn the basics of GritQL Syntax reference Cheatsheet for GritQL syntax Configuration Configure GritQL in your repo --- # Source: https://docs.grit.io The primary construct in Grit is a pattern, which searches in a codebase for matching clauses and optionally executes a specified transformation. Patterns are also commonly referenced in side conditions, which Grit uses to execute [rewrites](/language/patterns#rewrite-operator). Patterns come in several forms. [ ## Code snippets ](https://docs.grit.io/language/patterns#code-snippets)The most basic form of a pattern is just a snippet in the target language surrounded by backticks (```). For example: grit `console.log('Hello, world!')`typescript console.log('Hello, world!')To match on variations of a snippet, code snippets can include [metavariables](/language/patterns#metavariables): grit `console.log($message)` - - **Tip:** Dollar signs and backticks can be escaped with a backslash (`\`) if they are needed in the snippet. Ex. ``console.log(\`$template\`)``. [ ### Alternative syntax ](https://docs.grit.io/language/patterns#alternative-syntax)Code snippets can alternatively be enclosed by double quotes prefixed with a language annotation. Language annotations correspond to one of Grit's supported target languages and limit the snippet to matching only code in that language. The language annotation for JavaScript/TypeScript is `js`. These two examples are equivalent: grit `console.log('Hello, world!')`grit js"console.log('Hello, world!')"`raw` outputThe `raw` prefix can be added to snippets to output them directly, bypassing any of Grit's built-in attempts at ensuring that the output code is valid. Use `raw` for snippets that you *always* want to output as-is even if it generates invalid code. For example: pattern `console.log($message)` => raw`if(' // I like broken code"`Before console.log("Hello, world!");After if(' // I like broken code[ ## Metavariables ](https://docs.grit.io/language/patterns#metavariables)Metavariables are used to create a binding to a specific part of the syntax tree. They are prefixed with a dollar sign and must be alphanumeric. Metavariables start with a letter and should use `$lowercase_snake_case`. All metavariables must conform to the regex `$[a-zA-Z_][a-zA-Z0-9_]*`. Metavariables' default scope is the entire file, but this scope is usually restricted automatically by pattern auto-wrapping. The [`bubble`](/language/bubble) clause can be used to limit the scope of a metavariable. grit `console.log($foo)`typescript console.log('Hello, world!')`$filename`, `$new_files` and `$program` are reserved metavariables used internally by Grit and should not be used as metavariable names. Likewise, metavariables starting with `$grit_` are reserved for internal use. [ ### Anonymous metavariables ](https://docs.grit.io/language/patterns#anonymous-metavariables)When we do not care about the value of a metavariable, we can use the wildcard metavariable `$_`. This allow is to match without being bound to a specific value. grit `console.log($_)`typescript console.log($_)[ ### Spread metavariables ](https://docs.grit.io/language/patterns#spread-metavariables)By default, metavariables match a single node in the syntax tree. However, sometimes it is useful to match a variable number of nodes. This can be done with the spread metavariable `$...`. It matches 0 or more nodes, and can be used anywhere a metavariable can be used. Spread metavariables are anonymous, so they cannot be bound to a name. pattern `console.log($message, $...)` => `// Removed console.log: $message`Before console.log("Hello, world!"); console.log("Message 2", "stuff");After // Removed console.log: 'Hello, world!' // Removed console.log: 'Message 2'[ ### Explicit assignment ](https://docs.grit.io/language/patterns#explicit-assignment)As illustrated in the `console.log($message)` example, metavariables can be used without being declared, simply by replacing some of a code snippet with a metavariable meant to represent the substitution's part of the syntax tree. It is also possible to explicitly declare and assign metavariables: grit `const $logger = logger.$action($message)` where { $special_logger = js"$[action]Logger", $logger => $special_logger } - - **Tip:** When using a metavariable in an output snippet, you can wrap its name in braces `[]` to distinguish the metavariable name from the snippet literal. (ex. `$[name]Class`) [ ### Predefined metavariables ](https://docs.grit.io/language/patterns#predefined-metavariables)Grit also includes a few predefined metavariables which are always available. - `$program` is a metavariable which contains the entire (current) program. You can use this to match against the entire program, even deep inside a pattern. - `$filename` is a metavariable which contains the current (relative) file path. - `$new_files` is a metavariable which contains a list of new files. Appending to this metavariable is useful for [creating new files](/language/idioms#creating-new-files). Rewrite operator `=>`Once you have matched the relevant code, rewrites can be used to *transform* the matched pattern. They are written using the `=>` operator, with a patterns on the left and right hand sides. The whole rewrite itself is a pattern and can be used anywhere a pattern is used. pattern `println` => `console.log`Before println("Hello, world!");After console.log("Hello, world!");Rewrites can contain metavariables. So we can restrict the rewrite to only match `println` calls, and no other references to `println`. pattern `println($message)` => `console.log($message)`Before const foo = println; println("Hello, world!");After const foo = println; console.log("Hello, world!"); - - **Tip:** Rewrites will work better if you [keep them as specific as possible](./idioms#specific-rewrites). The right-hand side of a rewrite is a code snippet or metavariable bound to a code snippet. Rewrites are just a special kind of pattern with a side effect. Anywhere that other kinds of patterns can be used, so can a rewrite. This means that you can "match a rewrite" to keep your GritQL concise: pattern engine marzano(0.1) language js `$test($_)` where { $test <: js"test.only" => js"test" }Before test.only(() => { expect(true).toEqual(true); });After test(() => { expect(true).toEqual(true); });[ ## Syntax tree (AST) nodes ](https://docs.grit.io/language/patterns#syntax-tree-ast-nodes)Syntax tree nodes are one of the most important types in Grit, as they allow you to match directly against the underlying abstract syntax tree (AST) of the code. Nodes represent unique instances in the syntax tree of the input code. For example, every distinct appearance of the string `"foo"` in a single file is represented by an individual `string()` node. Nodes can represent larger groupings of code as well (ex. every arrow function in JavaScript is represented by an `arrow_function()` node). For example, the following matches any `augmented_assignment_expression`, regardless of its `operator` (which can be one of `=`, `+=`, ...). grit augmented_assignment_expression(operator = $op, left = $x, right = $v)typescript x = 1; x += 5;The fields that we do not care about can be omitted, so the following patterns are equivalent: grit augmented_assignment_expression(operator = $op) // same as augmented_assignment_expression(operator = $op, left = $_, right = $_) - - **Tip:** If you click the "debug" button in the [Grit studio](https://app.grit.io/studio) you can see the syntax tree of the input code. This is the best way to understand the structure of the code you are working with. [ ### Node structure ](https://docs.grit.io/language/patterns#node-structure)Each node has a type, which determines the node's available fields and their own types. Each child field of a node can be another syntax-tree node, a list of syntax-tree nodes, or a primitive value. For example, `"foo"` is a `string` and has the following structure: grit string(fragment = "foo")We distinguish syntax-tree nodes, which represent nodes in the input code, from patterns, which are Grit constructs used to match syntax-tree nodes. [ ## Primitives ](https://docs.grit.io/language/patterns#primitives)Grit has several primitive types which are language-agnostic. [ ### Strings ](https://docs.grit.io/language/patterns#strings)Grit allows specifying strings outside of a code snippet in a language-agnostic way. Grit strings are surrounded by double quotes (`"`). They can contain any character except for a double quote (`"`) or a backslash (`\`). To include one of these characters in a string, you can escape it with a backslash (`\`). In most cases, code snippets can be used interchangeably with Grit strings. Occasionally, however, Grit strings are useful for matching against exact strings instead of being AST-aware. In general, any code which would be matched by a Grit string would also be matched by a code snippet, but not vice versa. This means that Grit strings can be used to apply transformations such as formatting changes that would not be possible with code snippets. grit "Hello, world!" // string[ ### Numbers ](https://docs.grit.io/language/patterns#numbers)Grit has two number types, `int` and `double`. Functionally they can be used interchangeably, and you do not need to specify which type you want. The compiler will infer the type based on the context. Numbers are useful for transforming code based on arithmetic operations: grit js"multiply($x)" where { $y = $x * 2, $x => $y }[ ### Lists ](https://docs.grit.io/language/patterns#lists)Grit has a single list type, analogous to lists or arrays in other languages. List are constructed with square brackets (`[]`). List elements can be accessed via the `$list[index]` syntax, with negative and positive indices supported. Out of bounds indices will return `undefined`. grit $list = [`1`, `2`, `3`] $list[0] // `1` $list[-1] // `3`Lists have two primary uses: - Accumulating items, which can be done with the `+=` operator. - Matching against a specific ordered list of AST nodes. For example, the following pattern accumulates numbers with the `+=` operator, then uses the built-in [join](/language/functions#join) function to format them for rewriting: pattern engine marzano(0.1) language js and { $new_numbers = [], $new_numbers += 3, $new_numbers += 4, $new_numbers = join(list = $new_numbers, separator = ", "), `const numbers = [$numbers]` => `const numbers = [$new_numbers]` }Before const numbers = [1, 2];After const numbers = [3, 4];Meanwhile, this pattern looks specifically for a function declaration followed by a return statement (and removes it, for illustrative clarity): pattern engine marzano(0.1) language js statement_block($statements) where { $statements <: [function_declaration(), return_statement()], $statements => . }Before var sumToValue = function (x, y) { function Value(v) { this.value = v; } return new Value(x + y); }; var times = (x, y) => { return x * y; };After var sumToValue = function (x, y) { }; var times = (x, y) => { return x * y; };[ ### Maps ](https://docs.grit.io/language/patterns#maps)Grit has an immutable map type. Maps are constructed with curly braces (`{}`). Keys can contain letters and underscores, and values can be any valid Grit pattern. Maps can be accessed using `$key.value` syntax, where `$key` is a metavariable bound to a map and `value` is a key in the map. Accessors can be matched against like variables and used within a rewrite. If the key does not exist, the accessor will return `undefined`. pattern `const capital = $val` where { $capitals = { england: `london`, ours: $val }, $capitals.ours <: `paris`, $val => $capitals.england, }Before const capital = paris;After const capital = london;`Sequential` patterns - - **Warning**: `sequential` is *only* supported at the top level of a Grit program. You cannot nest it inside other patterns or clauses. By default, a Grit program consist of a single top-level pattern, which is matched against the input code. In some cases, it is simpler to have the Grit program consist of multiple patterns, processed sequentially. To do this, wrap the individual steps in a top level `sequential` clause. Note: the individual `sequential` steps are not [auto-wrapped](#pattern-auto-wrap) so you will need to include `contains` yourself. For example: pattern language js sequential { bubble file($body) where $body <: contains `console.log($message)` => `console.warn($message)`, bubble file($body) where $body <: contains `console.warn($message)` => `console.info($message)` }Before console.log("Hello, world!"); console.warn("Hello, world!");After console.info("Hello, world!"); console.info("Hello, world!");`sequential` is equivalent to writing several separate patterns and running each one in order, waiting for the previous to complete before starting the next. [ ## Multifile patterns ](https://docs.grit.io/language/patterns#multifile)In most cases, patterns can be evaluated in the context of a single file. However, in some cases, it is useful to evaluate a pattern in the context of other files or to apply a refactoring from one file to another. This can be done by using the `multifile` pattern. The `multifile` patternThe `multifile` pattern is a special pattern which allows matching against multiple files while keeping a single *global* state. Like `sequential` it accepts multiple steps - but each step is evaluated against *all* files with the same state. This is typically used to gather some information from one file in the first step, and then apply it to all other files in the second step. For example, this pattern will find a `Prop` type in one file and rename it, then make sure that rename is applied to all other files: pattern language js multifile { // Since multifile has a single global state, // we need to use `bubble` to restrict the scope of $body bubble($prop, $source_file, $new_prop) file($body) where $body <: contains `type $prop = $_` where { // If we already have found a specific $source_file, // we don't want to keep looking for new props $source_file <: undefined, $prop <: `Props`, $new_prop = `New$prop`, // Rename the prop in this file $prop => $new_prop, $source_file = $filename }, // Now that we have renamed the prop in the source file, look for usages in other files bubble($prop, $source_file, $new_prop) file($body) where { $body <: contains `$prop` where { // Make sure the prop we are looking at came from the source file $prop <: imported_from(from=includes $source_file), }, // Rename it in all other files $body <: contains `$prop` => $new_prop, } }Before // file1.js type Props = { name: string; }; // file2.js type Props = { age: string; }; // file3.js import { Props } from './file1.js'; const foo: Props = { name: 'foo', }; // file4.js import { Props } from './file2.js'; const foo: Props = { age: 'foo', };After // file1.js type NewProps = { name: string; }; // file2.js type Props = { age: string; }; // file3.js import { NewProps } from './file1.js'; const foo: NewProps = { name: 'foo', }; // file4.js import { Props } from './file2.js'; const foo: Props = { age: 'foo', }; The top level of a `multifile` step *must* be a `file()` pattern, optionally preceded by `bubble`. - **Tip:** If you merely want to create new files, use the `$new_files` metavariable—it is much faster on large codebases. [ ## Empty pattern ](https://docs.grit.io/language/patterns#empty-pattern)The dot (`.`) can be used exclusively on the right-hand side of a rewrite to delete code. Semantically, it represents an empty syntax-tree node, so rewriting to a dot (`=> .`) will remove the matched code. [ ## Regular expressions ](https://docs.grit.io/language/patterns#regular-expressions)Strings prefixed with the letter `r` are interpreted as regular expressions. Capture groups can be named by postfixing the regular expression with a list of metavariable names surrounded by parentheses (`(` and `)`). For example, the following pattern will match the string `"Hello, world!"` and bind the metavariable `$name` to the string `"world"`. grit "Hello, world!" <: r"Hello, (.*)"($name) // $name is now bound to "world"The patterns are interpreted using the [Rust regular expression syntax](https://docs.rs/regex/latest/regex). Regular expressions can also be constructed dynamically using snippet syntax: pattern `console.log("$message")` where { $name = "Lucy", $message <: r`([a-zA-Z]*), $name`($greeting) => `$name, $greeting` }Before console.log("Hello, Lucy");After console.log("Lucy, Hello");Regardless of which syntax you use, regular expressions are matched against the entire variable or node. To match a regular expression corresponding to part of a node, use wildcard characters. `file` and `program`The `file` and `program` patterns are syntax-tree nodes which match the entire file or program within a file, respectively. They are useful if your pattern requires context across an entire file. `file` allows you to match and rewrite the file name as well as its body: grit engine marzano(0.1) language js file($name, $body) where { $name => `$name.bak`, $body => `// This file was renamed with Grit!\n\n$body` }`program` is special in that it is available by default no matter what pattern you are writing. For example, the following pattern rewrites `console.log`s to `logger.log`s only if the file already contains a usage of `logger`: grit engine marzano(0.1) language js `console.log($log)` => `logger.log($log)` where { $program <: contains `logger` }[ ## Range patterns ](https://docs.grit.io/language/patterns#range-patterns)The `range` pattern is used to target code by its location within the file. Range takes three parameters: - `start_line`: The start line of the range, starting at 1 (inclusive). - `end_line`: The end line of the range, starting at 1 (inclusive, optional). If not provided, the range will go until the end of the file. - `start_column`: The start column of the range, starting at 1 (inclusive), within the start line (inclusive). - `end_column`: The end column of the range, starting at 1, within the end line (inclusive). For example, this pattern will remove the first 3 lines of all files: grit range(start_line=1, end_line=3) => .Range patterns are particularly useful when combined with [`contains`](/language/modifiers#contains-operator) to narrow down the code you are targeting. Any AST node will be considered to "contain" a range if the range intersects with the node. For example, this pattern will remove the `console.log` statements on lines 2 and 5: pattern `console.log($_)` as $log => . where { $log <: contains or { range(start_line=2, end_line=2), range(start_line=5, end_line=6) } }Before console.log("hello"); console.log("ok"); console.log("wow"); console.log("multiline");After console.log("hello"); console.log("wow"); --- # Source: https://docs.grit.io Grit is a query language for searching and modifying codebases. SyntaxExplanation`console.log($_)`The **root** of a Grit query is a pattern.`console.log`A **pattern** can be a code snippet surrounded in backticks.`console.log($message)`Code snippets can contain **metavariables** prefixed with `$`.$_The **anonymous metavariable** can be bound to without a name.$...`$...` is a **spread metavariable** that matches 0 or more nodes.call_expression()**[AST nodes](/language/patterns#syntax-tree-nodes)** can also be used as patterns.call_expression(callee=$callee)AST nodes can specify **fields**, where each field value is bound to a pattern or metavariable.`console.log($message)` => `console.warn($message)`A **[rewrite](/language/patterns#rewrite-operator)** is a pattern followed by a `=>` followed by a pattern. Rewrites are usable as patterns.$x => .The `.` is an **empty/null pattern**, it replaces the matched node with nothing. r"console\.(log\|warn)"($method)**Regular expressions** can be used as patterns, prefixed with `r"` and followed by capture variables.$list = [1, 2, 3] $one = $list[0] $three = $list[-1]**Lists** can be accessed via index, where negative indices count from the end of the list.$map = { a: 1, b: 2, c: 3 } $c = $map.c**Maps** can be accessed via dot notation. Map keys must be strings.or {`console.log`, `console.warn`}**Compound patterns** can be combined with `and`, `or`, and `any` clauses.not `console.log`**`not` clauses** can be used to negate a pattern.maybe `console.log`**`maybe` clauses** can be used to optionally match against a pattern, without failing the query.contains `console`**`contains` clauses** can be used to match any node that contains a specific pattern by traversing downwards through the syntax tree.within `function() { $_ }`**`within` clauses** search up the syntax tree for a matching node.after `console.warn($_)`**`after` clauses** search for a pattern that occurs after another pattern. `console.log($message)` as $log**`as` clauses** can assign a pattern to a metavariable.$statements <: some `console.log($_)`**`some`** matches lists where at least one element matches a pattern.`console.log($message)` where { ... }A **`where` clause** can be added after a pattern to introduce conditions that restrict when the pattern matches.`console.log($message)` where { $message <: "Hello world" }Inside the `where` clause, the **`<:` operator** is used to match metavariables against a pattern.`console.log($_)` where { $new = "Hello world" } => `console.log($new)`The where block can also **assign values** to metavariables using `=`. Variables can be lists, code snippets, or strings.`console.log($message)` where { $new = "Hello", $new += "world" } => $newStrings and lists can be appended to using `+=`. (ex. )`function() { $body }` where { $body <: contains bubble { `console.log($body)` => `console.warn($body)` } }The default scope of a GritQL pattern is global. The **`bubble` clause** can be used to create a new scope within which all metavariables are isolated from surrounding code.`function $name() { $body }` where { $body <: contains bubble($name) { `console.log($message)` => `console.warn($message, $name)` } }The `bubble` clause optionally accepts **arguments**, which can be used to "pierce" the bubble by allowing metavariables to preserve the values they had outside the bubble scope.`console.log` limit 10The **`limit` clause** can be used to limit the number of files returned by a query.// this is a comment**Comments** in GritQL start with `//` and are ignored by the parser.sequential { `console.log` => `console.error`, `console.warn` => `console.info` }`sequential` clauses can be used to contain multiple patterns that are applied to the same file in order.function custom_example($name) { return `name: $name` }**[Custom functions](/language/functions)** can be used to define reusable right-side values.function mod($value) js { return $value % 2 }Custom functions be written in **JavaScript** by adding the `js` keyword parameters.range(start_line=1, end_line=3)The **`range` pattern** is used to target code by its location within the file. It takes parameters `start_line`, `end_line`, `start_column`, and `end_column` to specify the range. --- # Source: https://docs.grit.io The target language is the programming language of the code you want to migrate. Currently, Grit supports the following target languages: - JavaScript/TypeScript - Python - JSON - Java (Alpha) - Terraform HCL (Alpha) - Solidity (Alpha) - CSS - Markdown (Alpha) - YAML (Alpha) - Rust - Ruby (Beta) - PHP (Beta) - Go (Alpha) - SQL (Alpha) If you would like to see support for a language that is not listed here, please [let us know](/language/request). [ ## Language declaration ](https://docs.grit.io/language/target-languages#language-declaration)Grit supports multiple target languages. To select a language, start a pattern file with a language declaration. grit language js `myPattern`If no language is specified, Grit defaults to JavaScript. This behavior may change in the future, so it is recommended to always specify a language. [ ## Language variants ](https://docs.grit.io/language/target-languages#language-variants)In some rare cases, you may need to specify an alternative parser for a language. Alternative parsers can be specified in parentheses with the language declaration. grit language js(jsx)The above example specifies the `jsx` version of JavaScript, which enables JSX syntax. The following are a few examples of available language versions: - `language js(typescript,jsx)` -- TypeScript with JSX enabled - `language js(typescript)` -- TypeScript without JSX For JavaScript without a language version, Grit will default to TypeScript with JSX enabled (specifically `language js(typescript,jsx)`). When parsing JavaScript code files, Grit decides which language version to use based the language of the GritQL pattern being executed. --- # Source: https://docs.grit.io # Pattern Library You can find the standard library patterns [on GitHub](http://github.com/getgrit/stdlib). --- # Source: https://docs.grit.io - - **Note**: You do not need to learn GritQL to use Grit. New users should start with one of our many [built-in migrations](https://docs.grit.io/patterns). GritQL is a query language designed for searching and modifying *source code*, with semantics similar to SQL or other declarative query languages. [ ## Patterns ](https://docs.grit.io/tutorials/gritql#patterns)The root of every GritQL query is a *pattern* to search for in a codebase. If you know JavaScript, you already know GritQL: any valid JavaScript code snippet is a valid GritQL pattern if you surround it with backticks. For example, this query will find everywhere we log "Hello world!" in our codebase: `console.log("Hello world!")`Click the "Run Pattern" button to run the query on a sample file. Notice how it is able to find the cases where the call is split across multiple lines or uses single quotes instead of double quotes, but it ignores the comment. This is because GritQL is designed to do *structural* matching, not just string matching. Every code snippet is automatically converted into a [syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) before it is matched against the codebase. **Warning**: The downside of structural matching is that each code snippet must be valid JavaScript. If you want to match arbitrary strings, you can use quotes instead: `"console.log("` [ ## Metavariables ](https://docs.grit.io/tutorials/gritql#metavariables)In many cases, you will want to match a pattern that is *similar* to a specific code snippet, but not exactly the same. To do this, just replace the parts you want to match with a *metavariable*: any identifier that starts with a dollar sign (`$`). For example, this query will find all console.log calls regardless of what message they are logging: `console.log($my_message)`You can name metavariables anything you want, but we recommend using descriptive names that make it easy to understand what the metavariable represents. [ ## Rewrites ](https://docs.grit.io/tutorials/gritql#rewrites)GritQL is useful for searching, but the real power comes from its ability to rewrite code. To convert a query into a rewrite, just add a `=>` followed by a code snippet you want to replace it with. For example: this query will find all console.log calls and replace them with the [Winston logging](https://github.com/winstonjs/winston) library: `console.log($my_message)` => `winston.info($my_message)`Notice how you can use metavariables in the replacement code snippet to refer to the parts of the original code snippet you want to keep. [ ## Conditions ](https://docs.grit.io/tutorials/gritql#conditions)Simple queries are often enough to get the job done, but you can add a `where` clause to filter the results. The `where` clause is similar to the `where` clause in SQL and introduces a set of side conditions that must **all** be true for the query to match. The most common condition uses the `<:` match operator to check if a given metavariable matches another pattern. For example, we could use a side query to turn our user-facing console.log call into an `alert` instead: `console.log($my_message)` => `alert($my_message)` where { $my_message <: `"This is a user-facing message"` }Conditions in GritQL support all the normal [boolean operators](https://docs.grit.io/language/conditions) like `and`, `or`, or negation. For example, you can use negation to exclude console.log calls that are user-facing from the Winston rewrite. `console.log($my_message)` => `winston.info($my_message)` where { !$my_message <: r".+user-facing.+" }[ ## Alternative patterns ](https://docs.grit.io/tutorials/gritql#alternative-patterns)Notice how in the last example we used a regular expression to match the message instead of a code snippet. You can also use regular expressions, string literals, and [AST nodes](/language/patterns#syntax-tree-nodes) as patterns in GritQL. For example, you can exclude `console.log` calls that are not strings by referencing the `string()` AST node: `console.log($my_message)` => `winston.info($my_message)` where { $my_message <: string() }[ ## Compound patterns ](https://docs.grit.io/tutorials/gritql#compound-patterns)Boolean clauses can also be used to combine multiple patterns into a single pattern. For example, `or` can be used to match either a `console.log` or `console.error` call: or { `console.log($my_message)`, `console.error($my_message)` } => `winston.info($my_message)`Note that rewrites are *also* patterns, so you can use them in conditional clauses as well. This allows you to construct nested rewrites. For example, this compound query rewrites all `console.log` calls to `winston.debug` and all `console.error` calls to `winston.warn`: `console.$method($my_message)` => `winston.$method($my_message)` where { $method <: or { `log` => `debug`, `error` => `warn` } }[ ## Pattern modifiers ](https://docs.grit.io/tutorials/gritql#pattern-modifiers)In clauses, you often want to match based on the *context* of a pattern, not just the pattern itself. You can use [pattern modifier keywords](https://docs.grit.io/language/modifiers) to adjust the behavior of a pattern. For example, you can use the `within` modifier to traverse upwards through the syntax tree and match `console.log` statements only if they are inside a `try/catch` block: `console.log($my_message)` => `winston.error($my_message)` where { $my_message <: within `try { $_ } catch($_) { $_ }` }You might have noticed the `$_` metavariable in the `within` clause. This is an anonymous metavariable that matches any pattern. It is useful when you want to include a placeholder without binding the value to a metavariable. [ ## Pattern calls ](https://docs.grit.io/tutorials/gritql#pattern-calls)When refactoring code, you often want to reuse common patterns. You can "call" a pre-defined pattern by surrounding it with parentheses. This is similar to calling a function in a programming language, and pattern calls can also take named arguments. Grit includes a robust standard library of [built-in patterns](https://docs.grit.io/patterns) that you can use in your queries. For example, if you want to match all JavaScript literals with a value of "42" you can call the [literal pattern](https://github.com/getgrit/stdlib/blob/main/.grit/patterns/js/util_literal.md) from the Grit standard library: `console.log($my_message)` => `winston.info($my_message)` where { $my_message <: literal(value="42") }[ ## Next steps ](https://docs.grit.io/tutorials/gritql#next-steps)This tutorial only covers the basics of GritQL. For more information, try: - Reading the [GritQL reference](/language/overview) documentation. - Writing your own patterns in the [GritQL studio](https://app.grit.io/studio). - Exploring the [standard library](https://github.com/getgrit/stdlib/tree/main/.grit/patterns) to see what patterns are available.