putout: a linter that is allowed to rewrite your code
🐊 Pluggable and configurable JavaScript Linter, code transformer and formatter with superpowers 💪: built-in support of js, jsx, ts, markdown, yaml, toml, json and ignore. Write declarative codemods in a simplest possible way 😏
At a glance
- What is it?
- putout is a JavaScript linter and code transformer that positions itself as a drop-in ESLint replacement, and it is deliberately willing to apply fixes that change runtime behaviour, such as declaring variables before reference or converting CommonJS to ESM. It ships as a monorepo of plugins, supports the same config formats as ESLint, and moves quickly, with version 42 releases in September and October 2026.
- Who is it for?
- putout fits a team that wants one tool to lint and to rewrite a JavaScript or TypeScript codebase, that is willing to accept automated changes that alter runtime behaviour, and that wants the same configuration formats it already has for ESLint.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The design centre is a fix that changes behaviour
putout describes itself as a JavaScript linter, a pluggable and configurable code transformer, and a drop-in ESLint replacement, with a built-in code printer and the ability to fix syntax errors.
The reason it exists is stated as a list of what the alternatives will not do. ESLint avoids fixes that could change the runtime behavior, by policy. Babel has no ability to report errors, it only runs transformations. Prettier is a formatter. JSCodeshift has no config and plugins support.
putout takes the opposite position, and says so directly: it can make more drastic code transformations that directly affect your codebase, making it a better place to code. The transformations it lists include fixing reference errors, declaring variables, and converting CommonJS to ESM.
That is the trade you are making when you adopt it. A linter that refuses to change behaviour is a tool you can run on a codebase you do not fully control. A transformer like this one can repair code that would throw at runtime, and it can rewrite module systems wholesale. Both are useful, and the second one is powerful enough that the choice is a policy decision rather than a preference.
It also means putout is not a replacement for Prettier or for Babel, whatever the marketing language implies. The credit list in the README names ESLint for its stable API, Babel for its API and its responsiveness, Prettier for its minimalistic options and uniform codestyle, and JSCodeshift for making codemods simple. putout sits on top of that lineage rather than replacing it.
One rule in five lines, against forty-nine in a competitor
The README makes its case with a size comparison, and it is the clearest illustration of how the plugin model works. The rule it uses is removing a debugger statement, and the point is not that the rule is hard, it is that the rule is small.
export const report = () => `Avoid 'debugger' statement`;
export const replace = () => ({
debugger: '',
});The same rule in other tools, as listed in the README, takes SWCLint 49 lines, RSLint 48 lines, ESLint 43 lines, and Rome 28 lines. putout does it in 5.
The reason is structural rather than clever. A putout plugin is a module that exports a report function and a replace function, and the replace function returns a map of node types to replacements. There is no rule metadata, no schema, no fixer DSL to learn, and no separate object for reporting. The plugin is the transformation, and the report text is a one liner.
The cost of that simplicity is that the ecosystem is not the ESLint ecosystem. An existing ESLint rule has to be ported, and the port is a small file, but it is still a port. The upside is that a rule becomes cheap enough that you write your own for your own codebase, which is the direction the project pushes when it points at its own plugin, remove-debugger, as the example of what a five line plugin looks like.
The plugin directories in the repository are organised for that: packages holds the plugins, rules holds rule definitions, codemods holds the larger scripted transformations, and casts holds the helpers for converting between node types. A separate documentation page covers the plugins API, and another covers codemods.
One engine for nine kinds of file
The main target is JavaScript, and then the list grows: JSX, TypeScript, JSON, SQL, YAML, TOML, Markdown, Ignore files, and Dockerfile. That list is the strongest practical argument for the project, and it comes from the same idea as the plugin design.
Most linters are tied to a language because their rules are tied to a syntax tree. putout builds on a parser and a printer abstraction instead, so the same transformation machinery runs over a JSON document, a YAML file, a Markdown file, or a Dockerfile. The configuration is the same shape, the plugin model is the same shape, and the reporting is the same shape.
For a repository that holds a package.json, a GitHub Actions workflow in YAML, a README in Markdown, a Dockerfile, and a pile of TypeScript, that means one tool and one configuration rather than five. The trade is that a language-agnostic rule set has to be expressed in a language-agnostic way, so the deep, type-aware rules that a specialist linter for one ecosystem can offer are not the same kind of thing, and the interesting work for a project using this tool is usually in its own plugins rather than in the built-in set.
The project documentation also covers the tree of syntax, the architecture, the API, the operators, the formatters, and what it calls the laws of the jungle, which is the README's name for the set of principles the built-in transformations follow.
A drop-in replacement, with the config formats you already have
The replacement claim is specific, and it is the practical reason a team would try this.
Alongside its own format, .putout.json, putout supports both eslint.config.js and .eslintrc.json, and it has the ability to autodetect the format you use. There is also a .putout-off.json file in the repository root, which is the opt-out mechanism for turning the tool off in specific places, and a separate eslint-off.config.js.
The monorepo point is the subtle one and it comes straight from the README. It works well with a monorepository, since it uses eslint.config.js that is closer to the linting file, instead of the cwd of the ESLint run. The usual failure mode of a linter in a monorepo is that it resolves its configuration relative to where the command was invoked, so running it from the root lints the wrong files or misses nested packages. A tool that anchors on the config file next to the source it is linting avoids that class of problem, and it is a real advantage over the behaviour it replaces.
The documentation index also lists integration sections for ESLint, for Babel, for Yarn, and a section on using putout as a loader, plus a section on exit codes, which matters if you wire this into a CI pipeline where the exit code is the contract.
One caveat on the drop-in claim. The manifest of this repository itself lists eslint-plugin-putout at a major version 31, while the project is at version 42, and it also depends on eslint 10 and on oxlint. So in practice putout is used alongside other linters in the same repository, and a migration is better understood as a gradual move than a single swap.
Installing it, and the tooling around the repository
Installation is a single development dependency, and the README asks for it to be added as such.
npm i putout -D The sentence after the command is cut off in the copy of the README available here, so whatever caveat follows it, most likely a note about the version of Node to use, is something to read directly in the repository.
The repository is a monorepo with an unusual shape, and the shape tells you how the project is run. The root package is private and is named root, and it is the workspace that contains the packages, rules, codemods, and casts directories. The scripts in the root manifest are almost all delegations to a task runner called madrun, which is a dev dependency, and the same runner appears in the .madrun.js configuration file at the root.
The script names are informative about the project's philosophy. Alongside the usual test, there are test:break, test:update, test:inspect, and test:fail, which suggests a test suite that can be run in several different modes, including one that checks that a rule actually breaks the code it is supposed to break. There is a lint family with variants, lint:dot, lint:frame, lint:fresh, lint:memory, lint:time, lint:only:putout, lint:only:eslint, and a fresh family that runs the linters over fresh checkouts of other projects, fresh, fresh:only:putout, fresh:only:eslint, and a fresh:fix that applies fixes. There are also memory and time scripts, which are performance comparisons rather than correctness checks.
The rest of the tooling is conventional: husky for git hooks, oxlint with its own .oxlintrc.json configuration, a .deepsource.toml, a .typos.toml, a bunfig.toml, and a deno.json. The presence of oxlint and eslint-plugin-putout side by side is the clearest statement of how the maintainer himself uses the tool: as one linter among several, compared against the alternatives on real code.
The documentation site is at putout.cloudcmd.io, and the README links to it for the style guide of the built-in transformations, which is the reference to read before you enable a large set of them.
Codemods, Ruler, and the versioning policy
Three parts of the documentation index deserve a mention because they describe the extension points rather than the built-in rules.
The codemods section covers the larger scripted transformations, the ones that rewrite code in bulk rather than adjust a single node, and the convert CommonJS to ESM transformation is the example the README links to from the plugin-nodejs package. That transformation is the one with the largest blast radius in the whole tool, since it changes how every module in a project loads, and it is also the one that motivates the "more drastic transformations" stance.
The Ruler sections, one asking what Ruler is and one asking how it can help, are the parts of the documentation this copy of the README does not expand on, so the safe summary is that the feature exists and is documented separately.
The versioning policy section is worth reading before you pin a version, and the release numbers tell you a story on their own. v42.20.0 and v42.20.1 were both published on 2026-09-28, an hour apart, and v42.21.0 followed on 2026-10-10, with the last push to the repository on 2026-10-17. A major version number of 42 on a tool that is a few years old says that this project breaks compatibility often, which is consistent with a tool whose transformations change the code it touches and whose plugin API is short enough to evolve.
The licence is MIT, and the README closes with a section inviting contributions, with a contributing guide in the repository.
Editorial conclusion
putout fits a team that wants one tool to lint and to rewrite a JavaScript or TypeScript codebase, that is willing to accept automated changes that alter runtime behaviour, and that wants the same configuration formats it already has for ESLint. It does not fit a project whose rules forbid behaviour changing fixes, because that is the design centre of this tool rather than an option, and it does not fit a team that needs a slow moving dependency, since the version number has reached 42. Verify first that the fixes you enable are safe for your code, that your existing eslint.config.js or .eslintrc.json is picked up as expected, and that you run the tool on a clean branch, because a transformation tool with this much reach is not something to point at an uncommitted working tree.
Frequently asked questions
What is putout and how is it different from ESLint?
putout is a JavaScript linter, a pluggable code transformer, and a drop-in ESLint replacement that supports eslint.config.js and .eslintrc.json and autodetects which one you use. The key difference is that ESLint avoids fixes that could change runtime behavior, while putout deliberately applies more drastic transformations.
How do I install putout?
The README documents adding it as a development dependency with npm i putout -D. The project is also published on npm under the name putout, and its documentation site is at putout.cloudcmd.io.
What languages and file types does putout support?
The main target is JavaScript, and JSX, TypeScript, JSON, SQL, YAML, TOML, Markdown, Ignore files, and Dockerfiles are also supported. The plugin model and the configuration are the same across those file types.
How small is a putout plugin?
A plugin exports a report function and a replace function, so the remove-debugger rule in the README is five lines. The README compares the same rule at 49 lines in SWCLint, 48 in RSLint, 43 in ESLint, and 28 in Rome.
How often is putout released?
The repository is not archived, the last push was on 2026-10-17, and the recent releases are v42.21.0 on 2026-10-10 with v42.20.0 and v42.20.1 both published on 2026-09-28. The version number has reached 42, so compatibility breaks are part of the normal release rhythm.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/coderaiser-putout)Was this useful?