{"id":30430537,"url":"https://github.com/automutate/automutate","last_synced_at":"2025-09-11T13:39:19.539Z","repository":{"id":46198993,"uuid":"73160777","full_name":"automutate/automutate","owner":"automutate","description":"Applies waves of mutations provided by other tools, such as linters or codemods.","archived":false,"fork":false,"pushed_at":"2022-07-25T14:33:43.000Z","size":319,"stargazers_count":16,"open_issues_count":10,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-17T01:52:57.941Z","etag":null,"topics":["linter","mutators","static-analysis"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/automutate.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-11-08T07:31:24.000Z","updated_at":"2025-02-17T11:21:51.000Z","dependencies_parsed_at":"2022-09-08T10:10:52.895Z","dependency_job_id":null,"html_url":"https://github.com/automutate/automutate","commit_stats":null,"previous_names":["autolint/automutate"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/automutate/automutate","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/automutate%2Fautomutate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/automutate%2Fautomutate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/automutate%2Fautomutate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/automutate%2Fautomutate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/automutate","download_url":"https://codeload.github.com/automutate/automutate/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/automutate%2Fautomutate/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270796257,"owners_count":24647327,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-16T02:00:11.002Z","response_time":91,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["linter","mutators","static-analysis"],"created_at":"2025-08-22T18:23:01.671Z","updated_at":"2025-08-22T18:23:02.441Z","avatar_url":"https://github.com/automutate.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# automutate\n\n[![Build Status](https://travis-ci.org/automutate/automutate.svg?branch=master)](https://travis-ci.org/automutate/automutate)\n[![npm](https://img.shields.io/npm/v/automutate.svg)](https://www.npmjs.com/package/automutate)\n\nApplies waves of mutations provided by other tools, such as linters or codemods.\n\nThere are [many](https://github.com/eslint/eslint) [linters](https://github.com/stylelint/stylelint) [out](https://github.com/lesshint/lesshint) [there](https://github.com/sasstools/sass-lint) and most include ways to `--fix` rule failures automatically.\nThis is great but hard to do for a couple of reasons:\n\n- **Overlapping mutations** - The possibility of mutations applying to overlapping sets of characters requires logic to handle applying one, then re-running linting, and so on.\n- **Code bloat verses duplication** - Most linters either provide hooks to apply fixes themselves (which can result in code bloat) or have an external project (which duplicates logic for finding rules).\n\n`automutate` proposes that linters only propose **how** to fix rules, via a standardized JSON format.\n\nHaving a standardized source-agnostic project to apply mutations brings a couple of benefits:\n\n- **Reduced overhead** - Projects no longer need to do this work themselves.\n- **Standardized base** - Ramp-up time to switch between projects using `automutate` is reduced with common code.\n\nIn general, _detecting_ rule failures is a separate concern from _fixing_ them.\nLinters need to run quickly over a read-only set of files, often during built processes, while fixers typically run slowly and modify files on user request.\n\n## How it works\n\nThe main `automutate` algorithm is started in [`autoMutator.ts`](../src/autoMutator.ts) and mostly applied in [`mutationsApplier.ts`](../src/mutationsApplier.ts):\n\n```swift\nwhile mutationsWave = getMutationsWave():\n    for (file, fileMutations) of groupMutationsByFile(mutationsWave):\n        for mutation of getNonOverlappingMutationsInReverse(fileMutations):\n            applyMutation(file, mutation)\n```\n\n1. `getMutationsWave` calls to an external tool, such as a linter, to receive a wave of suggested mutations.\n2. `groupMutationsByFile` organizes the suggested mutations by file.\n3. `getNonOverlappingMutationsInReverse` removes overlapping mutations that would conflict with each other, and sorts the remainder in reverse order so that later mutations don't interfere with character positions of earlier mutations.\n4. `applyMutation` modifies files on disk using the remaining mutations.\n\n## Mutations\n\nA single mutation contains a unique `type` identifier, a range of character position(s) to apply to, and optionally other logic.\n\nThe following basic text manipulations are provided out of the box:\n\n- **`multiple`** - Container for multiple mutations. This indicates to `automutate` that these must be applied all at once or not at all, which guarantees consistency with the built-in mutation overlap detection.\n- **`text-delete`** - Deletes a range of characters.\n- **`text-insert`** - Inserts a string at a point.\n- **`text-replace`** - Replaces characters matching a string or regular expression within a range.\n- **`text-swap`** - Swaps a range of characters with a new string.\n\nFor example:\n\n```json\n{\n  \"ugly-file.txt\": [\n    {\n      \"range\": {\n        \"begin\": 7,\n        \"end\": 14\n      },\n      \"type\": \"text-delete\"\n    },\n    {\n      \"insertion\": \"inconceivable!\",\n      \"range\": {\n        \"begin\": 21\n      },\n      \"type\": \"text-insert\"\n    }\n  ]\n}\n```\n\nLinter-specific utilities may define their own mutations.\nFor example, a language's linter may define a `node-rename` mutation rather than use a `multiple` mutation containing `text-swap` mutations.\n\nSee [Mutators](docs/mutators.md) for more on custom mutators.\n\n## Project Onboarding\n\nSee [Onboarding](docs/onboarding.md).\n\n`automutate` requires NodeJS \u003e= 14.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fautomutate%2Fautomutate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fautomutate%2Fautomutate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fautomutate%2Fautomutate/lists"}