An open‑source implementation of Google's internal IfThisThenThat (IFTTT) linter tool. Enforce atomic pull requests by declaring file dependencies in your code: If this file changes, then that file or region must also change.
- Declare conditional change directives with optional labels (e.g.,
// LINT.IfChange,// LINT.IfChange('g'), or// LINT.IfChange("g")). - Specify target files or regions with
// LINT.ThenChangepragmas (e.g.,// LINT.ThenChange( ['path/to/file1', 'path/to/file2#label'])or// LINT.ThenChange('path/to/file1')). - Support for labeled regions (
// LINT.Label('name') ... // LINT.EndLabel) to constrain where changes must occur. - True parallel parsing and linting across CPU cores using Node.js worker threads.
- CLI and programmatic API for integration in custom workflows.
# path/to/Makefile
# important config bit
# LINT.IfChange
SOMEVAR = 1
# LINT.ThenChange(
# ['path/to/py_config.py',
# 'path/to/ts_config.ts#foo'],
# )# path/to/py_config.py
SOMEVAR = 1// path/to/ts_config.ts
// LINT.Label('foo')
const SOMEVAR = 1;
// LINT.EndLabel# path/to/file1.py
# LINT.IfChange('foo')
class Blah(Enum):
FOO = 1
BAR = 2
# LINT.ThenChange('path/to/file2.json#bar')/* path/to/file2.json */
// LINT.IfChange('bar')
{
"foo": 1,
"bar": 2
}
// LINT.ThenChange('path/to/file1.py#foo')Install via npm (when published):
npm install -g ifttt-lintOr add to your project as a dev dependency:
npm install --save-dev ifttt-lint# From a patch file:
$ ifttt-lint path/to/changes.diff
# From stdin, ignoring all .bak files
$ git diff HEAD~1 | ifttt-lint -i '**/*.bak' -The CLI prints debug info to stderr and exits with:
0if no lint errors1if any conditional lint failures2on fatal errors (e.g., missing input)
$ cat sample.diff | ifttt-lint --verbose -
Parallelism: 8
Processing changed file: src/foo.ts
Finished processing changed file: src/foo.ts
Processing target file: src/bar.ts
Finished processing target file: src/bar.ts
[ifttt] src/foo.ts#feature:10 -> ThenChange 'src/bar.ts' (line 12): target file 'src/bar.ts' not changed.Clone, install dependencies, and build (requires Node.js >=12 for worker_threads):
$ git clone https://github.com/your-org/ifttt-lint.git
$ cd ifttt-lint
$ npm install
$ npm run buildRun tests:
$ npm testRun performance benchmark:
$ npm run perfStart linting locally:
$ npm run start -- path/to/changes.diffThe project structure:
src/- TypeScript source modulesdist/- Compiled JavaScript outputtests/- Jest unit tests
To run ifttt-lint automatically on pull requests, add a GitHub Actions workflow (e.g., .github/workflows/ifttt-lint.yml) with the following configuration:
name: IFTTT Lint
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0 # Fetch full history for merge-base
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: "23" # or your preferred version
- name: Install IFTT-lint
run: npm install -g ifttt-lint
- name: Validate lint pragmas
run: ifttt-lint --verbose --scan .
- name: Compute diff from common ancestor
run: |
BASE_SHA=${{ github.event.pull_request.base.sha }}
MERGE_BASE=$(git merge-base HEAD $BASE_SHA)
git diff $MERGE_BASE HEAD > changes.diff
- name: Run IFTTT Lint
run: npx ifttt-lint -i '**/*.md' changes.diffThis workflow computes the diff from the common ancestor between the PR branch (HEAD) and the base branch, saves it to changes.diff, and runs ifttt-lint against that diff.
- Fork the repo
- Create a branch (
git checkout -b feature/xyz) - Write code & tests
- Ensure all tests pass (
npm test) - Submit a Pull Request
Licensed under the Mozilla Public License, v. 2.0. See LICENSE for details.