/putout

🗜 Pluggable and configurable code transformer with built-in eslint, babel plugins and jscodeshift codemods support for js, jsx typescript and flow files

Primary LanguageJavaScriptMIT LicenseMIT

Putout NPM version Dependency Status Build Status Coverage Status

Putout is a pluggable and configurable code transformer with built-in eslint, babel plugins and jscodeshift codemods support for js, jsx typescript and flow files. It has a lot of transforms that will keep your codebase in a clean state transforming any code smell to readable code according to best practices.

putout

Why?

The main difference of putout is saving code transformation results directly in a source code in a day-to-day baisis.

Install

npm i putout -D

Usage

Usage: putout [options] [path]
Options
   -h, --help                  display this help and exit
   -v, --version               output version information and exit
   -f, --format                use a specific output format - default: progress-bar/dump on CI
   -s, --staged                add staged files when in git repository
   --fix                       apply fixes of errors to code
   --fix-count                 count of fixes rounds (defaults to 10)
   --rulesdir                  use additional rules from directory
   --transform                 apply inline transform
   --plugins                   plugins to use splited by comma
   --enable                    enable rule by name in .putout.json
   --disable                   disable rule by name in .putout.json
   --enable-all                enable all rules in .putout.json
   --disable-all               disable all rules in .putout.json
   --ext                       specify JavaScript file extensions
   --jsx                       enable jsx (try to determine by default)
   --flow                      enable flow
   --no-jsx                    disable jsx
   --no-flow                   disable flow (default)
   --cache                     enable .putoutcache to speed up processing (default)
   --fresh                     generate a fresh .putoutcache
   --no-config                 avoid reading config file (.putout.json)
   --no-ci                     disable CI detection
   --no-cache                  disable .putoutcache

To find possible transform places:

putout lib test

To apply transforms:

putout lib test --fix

Environment variables

Putout supports next environment variables:

  • PUTOUT_FILES - files that should be processed by putout, divided by ",";
PUTOUT_FILES=lib,test putout --fix

Architecture

Putout consists of a couple simple parts, here is workflow representation.

putout

API

putout(source, options)

First things first, require putout:

const putout = require('putout');

Let's consider next source with two variables and one call expression:

const hello = 'world';
const hi = 'there';

console.log(hello);

We can declare it as source:

const source = `
    const hello = 'world';
    const hi = 'there';
    
    console.log(hello);
`;

Plugins

Putout supports dynamic loading of plugins from node_modules. Let's consider example of using remove-unused-variables plugin:

putout(source, {
    plugins: [
        'remove-unused-variables',
    ]
});
// returns
{
  code: "\n    const hello = 'world';\n\n    console.log(hello);\n",
  places: []
}

As you see places is empty, but the code is changed: there is no hi variable.

No fix

From the first day putout was developed with ability to split main process into two concepts: find and fix. This conception mirrorod in the code, so you can find all places with redundant variables:

putout(source, {
    fix: false,
    plugins: [
        'remove-unused-variables',
    ]
});
// returns
{
  code: '\n' +
    "    const hello = 'world';\n" +
    "    const hi = 'there';\n" +
    '    \n' +
    '    console.log(hello);\n',
  places: [
    {
      rule: 'remove-unused-variables',
      message: '"hi" is defined but never used',
      position: { line: 3, column: 10 }
    }
  ]
}

Built-in transforms

remove unused variables
  function show() {
-     const message = 'hello';
      console.log('hello world');
  }
remove unused for-of variables
-for (const {a, b} of c) {
+for (const {a} of c) {
    console.log(a);
}
remove unreferenced variables
-let a;
- a = 1;
let b;
b = 2;
console.log(b);
remove duplicate keys
const a = {
-    x: 'hello',
-    ...y,
    x: 'world',
    ...y,
}
remove unused private fields
  class Hello {
    #a = 5;
-   #b = 3;
    get() {
        return this.#a;
    };
}
remove unused expressions
  function show(error) {
-     showError;
  }
remove useless variables
-   function hi(a) {
-       const b = a;
    };
+   function hi(b) {
    };
remove useless type convertion
-const a = Boolean(b.includes(c));
+const a = b.includes(c);
remove useless functions
-   const f = (...a) => fn(...a);
+   const f = fn;
remove useless typeof
- typeof typeof 'hello';
+ typeof 'hello';
remove useless await
-   await await Promise.resolve('hello');
+   await Promise.resolve('hello');
remove useless async
-const show = async () => {
+const show = () => {
    console.log('hello');
};
remove useless arguments
onIfStatement({
    push,
-   generate,
-   abc,
})

function onIfStatement({push}) {
}
remove useless template expressions
-let y =`${"hello"} + ${"world"}`;
+let y =`hello + world`;
remove useless for-of
-for (const a of ['hello']) {
-    console.log(a);
-}
+console.log('hello');
reuse duplicateinit
const putout = require('putout');
-const {operator} = require('putout');
+const {operator} = putout;
convert templates with one expression to string
-const c = `${a + b}`;
+const c = String(a + b);
convert equal to strict equal
-if (a == b) {
+if (a === b) {
}
convert indexOf to includes
-if (~array.indexOf(element)) {
+if (array.includes(element)) {
}
convert generic to shorthand for typescript (why)
interface A {
-    x: Array<X>;
+    x: X[];
}
remove useless types for typescript
type oldType = number;

-type newType = oldType;
-const x: newType = 5;
+const x: oldType = 5;
remove duplicate interface keys for typescript (disabled, because of additional `semicolon`);
interface Hello {
-   'hello': any
    'hello': string
}
remove unused types for typescript
type n = number;
-type s = string;
+const x: n = 5;
remove useless escape
-const t = 'hello \"world\"';
-const s1 = `hello \"world\"`;
-const s = `hello \'world\'`;
+const t = 'hello "world"';
+const s1 = `hello "world"`;
+const s = `hello 'world'`;
remove useless Array.from
-for (const x of Array.from(y)) {}
+for (const x of y) {}
remove useless spread
-for (const x of [...y]) {}
+for (const x of y) {}
remove useless Promise.resolve
async () => {
-    return Promise.resolve('x');
+    return 'x';
}
convertPromise.reject to throw
async () => {
-    return Promise.reject('x');
+    throw 'x';
}
remove debugger statement
- debugger;
remove iife
-(function() {
-    console.log('hello world');
-}());
+console.log('hello world');
remove boolean from logical expressions
-const t = true && false;
+const t = false;
remove nested blocks
for (const x of Object.keys(a)) {
-   {
-       console.log(x);
-   }
+   console.log(x);
}
remove double negations
--if (!!a)
++if (a)
    console.log('hi');
remove unreachable code
function hi() {
    return 5;
-  console.log('hello');
}
replace test.only with test calls
-test.only('some test here', (t) => {
+test('some test here', (t) => {
    t.end();
});
replace test.skip with test calls
-test.skip('some test here', (t) => {
+test('some test here', (t) => {
    t.end();
});
remove process.exit call
-process.exit();
split variable declarations
-let a, b;
+let a;
+let b;
split nested destructuring
-const {a: {b}} = c;
+const {a} = c;
+const {b} = a;
simplify ternary
-module.exports = fs.copyFileSync ? fs.copyFileSync : copyFileSync;
+module.exports = fs.copyFileSync || copyFileSync;
remove console.log calls
-console.log('hello');
remove empty block statements
-if (x > 0) {
-}
remove empty patterns
-const {} = process;
remove strict mode directive from esm
-'use strict';
-
import * from fs;
Add strict mode directive in commonjs if absent
+'use strict';
+
const fs = require('fs');
remove constant conditions
function hi(a) {
-   if (2 < 3) {
-       console.log('hello');
-       console.log('world');
-   }
+   console.log('hello');
+   console.log('world');
};

function world(a) {
-   if (false) {
-       console.log('hello');
-       console.log('world');
-   }
};
convert esm to commonjs (disabled)
-import hello from 'world';
+const hello = require('world');
convert commonjs to esm (disabled)
-const hello = require('world');
+import hello from 'world';
apply destructuring
-const hello = world.hello;
-const a = b[0];
+const {hello} = world;
+const [a] = b;
apply top-level-await (proposal-top-level-await, disabled)
import fs from 'fs';

-(async () => {
-    const data = await fs.promises.readFile('hello.txt');
-})();
+const data = await fs.promises.readFile('hello.txt');
apply numeric separators(proposal-numeric-separator)
-const a = 100000000;
+const a = 100_000_000;
convert throw statement into expression (proposal-throw-expressions, disabled)
-const fn = (a) => {throw Error(a);}
+const fn = (a) => throw Error(a);
apply optional chaining (proposal-optional-chaining, disabled)
-const result = hello && hello.world;
+const result = hello?.world;
apply nullish coalescing (proposal-nullish-coalescing, disabled)
-result = typeof result  === 'undefined' ? 'hello': result;
result = result ?? 'hello';
apply shorthand properties
-export const setSession = (session) => ({
-    payload: session,
+export const setSession = (payload) => ({
+    payload,
});
merge destructuring properties
-const {one} = require('numbers'):
-const {two} = require('numbers');
+ const {
+   one,
+   two
+} = require('numbers');
merge duplicate imports
-import {m as b} from 'y';
-import {z} from 'y';
-import x from 'y';
+import x, {m as b, z} from 'y';
merge if statements
-if (a > b)
-    if (b < c)
-        console.log('hi');
+if (a > b && b < c)
+    console.log('hi');
convert Math.pow to exponentiation operator
-Math.pow(2, 4);
+2 ** 4;
convert anonymous to arrow function
-module.exports = function(a, b) {
+module.exports = (a, b) => {
}
convert for to for-of
-for (let i = 0; i < items.length; i++) {
+for (const item of items) {
-   const item = items[i];
    log(item);
}
convert forEach to for-of
-Object.keys(json).forEach((name) => {
+for (const name of Object.keys(json)) {
    manage(name, json[name]);
-});
+}
convert for-in to for-of
-for (const name in object) {
-   if (object.hasOwnProperty(name)) {
+for (const name of Object.keys(object)) {
    console.log(a);
-   }
}
convert array copy to slice
-const places = [
-    ...items,
-];
+const places = items.slice();
extract sequence expressions
-module.exports.x = 1,
-module.exports.y = 2;
+module.exports.x = 1;
+module.exports.y = 2;
extract object properties into variable
-const {replace} = putout.operator;
-const {isIdentifier} = putout.types;
+const {operator, types} = putout;
+const {replace} = operator;
+const {isIdentifier} = types;
convert apply to spread
-console.log.apply(console, arguments);
+console.log(...arguments);
convert arguments to rest
-function hello() {
-    console.log(arguments);
+function hello(...args) {
+    console.log(args);
}
convert Object.assign to merge spread
function merge(a) {
-   return Object.assign({}, a, {
-       hello: 'world'
-   });
+   return {
+       ...a,
+       hello: 'world'
+   };
};
convert binary expression to boolean
-   const a = b === b;
+   const a = true;
convert top-level return into process.exit()(because EcmaScript Modules doesn't support top level return)
-   return;
+   process.exit();
add await to return promise() statements (because it's faster, produces call stack and more readable)
async run () {
-   return promise();
+   return await promise();
}

Plugins

The putout repo is comprised of many npm packages. It is a lerna monorepo similar to babel.

Appliers

Package Version Dependencies
@putout/plugin-apply-destructuring npm Dependency Status
@putout/plugin-apply-top-level-await npm Dependency Status
@putout/plugin-apply-optional-chaining npm Dependency Status
@putout/plugin-apply-nullish-coalescing npm Dependency Status
@putout/plugin-apply-shorthand-properties npm Dependency Status
@putout/plugin-apply-numeric-separators npm Dependency Status

Splitters

Package Version Dependencies
@putout/plugin-split-variable-declarations npm Dependency Status
@putout/plugin-split-nested-destructuring npm Dependency Status

Mergers

Package Version Dependencies
@putout/plugin-merge-destructuring-properties npm Dependency Status
@putout/plugin-merge-duplicate-imports npm Dependency Status
@putout/plugin-merge-if-statements npm Dependency Status

Converters

Package Version Dependencies
@putout/plugin-convert-esm-to-commonjs npm Dependency Status
@putout/plugin-convert-commonjs-to-esm npm Dependency Status
@putout/plugin-convert-array-copy-to-slice npm Dependency Status
@putout/plugin-convert-template-to-string npm Dependency Status
@putout/plugin-convert-equal-to-strict-equal npm Dependency Status
@putout/plugin-convert-index-of-to-includes npm Dependency Status
@putout/plugin-convert-generic-to-shorthand npm Dependency Status
@putout/plugin-convert-math-pow npm Dependency Status
@putout/plugin-convert-to-arrow-function npm Dependency Status
@putout/plugin-convert-for-to-for-of npm Dependency Status
@putout/plugin-convert-for-each-to-for-of npm Dependency Status
@putout/plugin-convert-for-in-to-for-of npm Dependency Status
@putout/plugin-convert-object-assign-to-merge-spread npm Dependency Status
@putout/plugin-convert-binary-expression-to-boolean npm Dependency Status
@putout/plugin-convert-top-level-return npm Dependency Status

Removers

Package Version Dependencies
@putout/plugin-remove-unused-variables npm Dependency Status
@putout/plugin-remove-unused-for-of-variables npm Dependency Status
@putout/plugin-remove-unused-types npm Dependency Status
@putout/plugin-remove-unreferenced-variables npm Dependency Status
@putout/plugin-remove-duplicate-keys npm Dependency Status
@putout/plugin-remove-duplicate-interface-keys npm Dependency Status
@putout/plugin-remove-unused-expressions npm Dependency Status
@putout/plugin-remove-unused-private-fields npm Dependency Status
@putout/plugin-remove-useless-variables npm Dependency Status
@putout/plugin-remove-useless-type-convertion npm Dependency Status
@putout/plugin-remove-useless-functions npm Dependency Status
@putout/plugin-remove-useless-async npm Dependency Status
@putout/plugin-remove-useless-await npm Dependency Status
@putout/plugin-remove-useless-typeof npm Dependency Status
@putout/plugin-remove-useless-types npm Dependency Status
@putout/plugin-remove-useless-array-from npm Dependency Status
@putout/plugin-remove-useless-spread npm Dependency Status
@putout/plugin-remove-useless-arguments npm Dependency Status
@putout/plugin-remove-useless-escape npm Dependency Status
@putout/plugin-remove-useless-template-expressions npm Dependency Status
@putout/plugin-remove-useless-for-of npm Dependency Status
@putout/plugin-remove-process-exit npm Dependency Status
@putout/plugin-remove-debugger npm Dependency Status
@putout/plugin-remove-iife npm Dependency Status
@putout/plugin-remove-only npm Dependency Status
@putout/plugin-remove-skip npm Dependency Status
@putout/plugin-remove-double-negations npm Dependency Status
@putout/plugin-remove-unreachable-code npm Dependency Status
@putout/plugin-remove-console npm Dependency Status
@putout/plugin-remove-empty npm Dependency Status
@putout/plugin-remove-empty-pattern npm Dependency Status
@putout/plugin-remove-constant-conditions npm Dependency Status
@putout/plugin-remove-boolean-from-logical-expressions npm Dependency Status
@putout/plugin-remove-nested-blocks npm Dependency Status

Not bundled

Next packages not bundled with putout but can be installed separately.

Package Version Dependencies
@putout/plugin-react-hooks npm Dependency Status
@putout/plugin-convert-spread-to-array-from npm Dependency Status
@putout/plugin-cloudcmd npm Dependency Status
@putout/plugin-postcss npm Dependency Status

Other

Package Version Dependencies
@putout/plugin-reuse-duplicate-init npm Dependency Status
@putout/plugin-madrun npm Dependency Status
@putout/plugin-strict-mode npm Dependency Status
@putout/plugin-extract-sequence-expressions npm Dependency Status
@putout/plugin-extract-object-properties npm Dependency Status
@putout/plugin-simplify-ternary npm Dependency Status
@putout/plugin-putout npm Dependency Status
@putout/plugin-webpack npm Dependency Status
@putout/plugin-add-return-await npm Dependency Status
@putout/plugin-promises npm Dependency Status

Formatters

putout use formatters similar to eslint's formatters. You can specify a formatter using the --format or -f flag on the command line. For example, --format codeframe uses the codeframe formatter.

The built-in formatter options are:

  • dump
  • stream
  • json
  • codeframe
  • progress
  • progress-bar
  • frame (codeframe + progress)
Package Version Dependencies
@putout/formatter-dump npm Dependency Status
@putout/formatter-stream npm Dependency Status
@putout/formatter-progress npm Dependency Status
@putout/formatter-progress-bar npm Dependency Status
@putout/formatter-json npm Dependency Status
@putout/formatter-codeframe npm Dependency Status
@putout/formatter-frame npm Dependency Status
@putout/formatter-eslint npm Dependency Status

Custom Formatter

Formatter function executes on every processed file, it should return output string.

module.exports = ({name, source, places, index, count, filesCount, errorsCount}) => {
    return '';
};

Here is list of options:

  • name - name of processed file
  • source - source code of processed file
  • index - current index
  • count - processing files count
  • filesCount - count of files with errors
  • errorsCount count of errors

You can avoid any of this and use only what you nead. To make possible using with putout add prefix putout-formatter- to your npm package, and add tags putout, formatter, putout-formatter.

Eslint Formatters

eslint formatters can be used as well with help of @putout/formatter-eslint this way:

Install:

npm i putout @putout/formatter-eslint eslint-formatter-pretty -D

Run:

ESLINT_FORMATTER=pretty putout -f eslint lib

Configuration

To configure putout add section putout to your package.json file or create .putout.json file and override any of default options.

Rules

All rules located in plugins section and built-in rules are enabled by default. You can disable rules using "off", or enable them (in match section) using "on".

{
    "rules": {
        "remove-unused-variables": "off"
    }
}

Or pass options using rules section:

{
    "rules": {
        "remove-unused-variables": ["on", {
            "exclude": "const global = __"
        }]
    }
}

With help of exclude you can set code pattern to exclude for current rule. Pass an array when you have a couple templates to exclude.

exclude is cross-plugins function supported by core, when develop your plugin, please use other name to keep users ability to customize all plugins in a way they need to.

Match

When you need to match paths to rules you can use match section for this purpose in .putout.json:

{
    "match": {
        "server": {
            "remove-process-exit": "on"
        }
    }
}

Ignore

When you need to ignore some routes no metter what, you can use ignore section in .putout.json:

{
    "ignore": [
        "test/fixture"
    ]
}

Plugins

There is two types of plugins supported by putout, their names in npm started with prefix:

  • @putout/plugin- for official plugins
  • putout-plugin- for user plugins

Example If you need to remove-something create putout plugin with a name putout-plugin-remove-something and add it to .putout.json:

{
    "plugins": [
        "remove-something"
    ]
}

Add putout as a peerDependency to your packages.json.

Always add keywords putout, putout-plugin when publish putout plugin to npm so others can easily find it.

Plugins API

Let's consider a couple plugin types, that can be used.

Replacer

The simplest putout plugin type, consits of 2 functions:

  • report(path) - report error message to putout cli;
  • replace - replace key template into value template;
module.exports.report = () => 'use optional chaining';
module.exports.replace = () => ({
    '__a && __a.__b': '__a?.__b',
});

This plugin will find and sugest to replace all occurences of code: object && object.property into object?.property.

Includer

More powerful plugin type, when you need more control over traversing. It should contain next 2 functions:

  • report(path) - report error message to putout cli;
  • fix(path) - fixes paths using places array received using find function;

and one or more of this:

  • filter(path) - filter path, should return true, or false (don't use with traverse);
  • include - returns array of templates, or node names to include;
  • exclude - returns array of templates, or node names to exclude;
module.exports.report = () => 'use optional chaining';
module.exports.include = () => [
    'debugger'
];

module.exports.fix = (path) => {
    path.remove(path);
};

More information about supported plugin types you can find at @putout/engine-runner. About the process of plugins loading you can find at @putout/engine-loader.

When you need, you can use @babel/types, template and generate. All of this can be get from putout:

const {
    types,
    template,
    generate,
} = require('putout');

Most information you can find in Babel Plugin Handbook is relevant to putout plugins. To understand how things works from the inside take a look at Super Tiny Compiler.

Operator

When you need to use replaceWith, replaceWithMultiple, or insertAfter, please use operator instead of path-methods.

const {template, operator} = require('putout');
const {replaceWith} = operator;

const ast = template.ast(`
  const str = 'hello';
`);

module.exports.fix = (path) => {
    // wrong
    path.replaceWith(ast);
    
    // correct
    replaceWith(path, ast);
}

This should be done to preserve loc and comments information, which is different in babel and recast. putout will handle this case for you :), just use methods of operator.

Putout Plugin

When you work on a plugin or codemod please add rule putout into .putout.json:

{
    "rules": {
        "putout": "on"
    }
}

@putout/plugin-putout will handle plugin-specific cases for you :).

Example

Let's consider simplest possible plugin for removing debugger statements @putout/plugin-remove-debugger:

// this is a message to show in putout cli
module.exports.report = () => 'Unexpected "debugger" statement';

// lets find all "debugger" statements and replace them with ""
module.exports.replace = () => {
    'debugger': '',
};

Visitor used in traverse function can be code template as well. So when you need to find module.exports = <something>, you can use:

module.exports.traverse = ({push}) => {
    return {
        'module.exports = __'(path) {
            push(path);
        }
    }
};

Where __ is a placeholder for anything.

Remember: template key should be valid JavaScript, or Type name like in previous example.

You can aslo use include and/or exclude insead of traverse and filter (more sophisticated example):

// should be always used include/or exclude, when traverse not used
module.exports.include = () => [
    'debugger',
];

// optional
module.exports.exclude = () => [
    'console.log'
];

// optional
module.exports.filter = (path) => {
    // do some checks
    return true;
}

Template

There is predefined placeholders:

  • __ - any code;
  • "__" - any string literal;
  • __ - any template string literal;

Testing

That was the simplest module to remove debugger statements in your code. Let's look how to test it using @putout/test:

const removeDebugger = require('..');
const test = require('@putout/test')(__dirname, {
    'remove-debugger': removeDebugger,
});

// this is how we test that messages is correct
test('remove debugger: report', (t) => {
    t.reportCode('debugger', 'Unexpected "debugger" statement');
    t.end();
});

// stetement should be removed so result is empty
test('remove debugger: transformCode', (t) => {
    t.transformCode('debugger', '');
    t.end();
});

As you see test runner it is little bit modifed tape. To see more sophisticated example look at @putout/remove-console.

Babel Plugins

You can add babel to plugins section of .putout.json with babel/ prefix.

You can disable rule, or use match in a similar, way Remember to omit babel-plugin- or @babel/plugin: putout will set it up for you :)

Example Let's add babel-plugin-transform-inline-consecutive-adds to .putout.json:

{
    "plugins": [
        "babel/transform-inline-consecutive-adds"
    ]
}

Then create a file and process it with help of babel plugin.

coderaiser@cloudcmd:~$ cat > a.js
const t = [];
t.push(1);
t.push(2);

coderaiser@cloudcmd:~$ putout a.js -f codeframe
/home/coderaiser/a.js:4:0
  2 | t.push(1);
  3 | t.push(2);
> 4 |
    | ^ transform inline consecutive adds

✖ 1 errors in 1 files
  fixable with the `--fix` option
coderaiser@cloudcmd:~$ putout --fix a.js
coderaiser@cloudcmd:~$ cat a.js
const t = [1, 2];

Using putout as a runner for babel plugins you can not only change file content, but also see what exactly will be changed. You can use your already written babel plugins or reuse work in progress plugins made for babel, but remember that putout plugins gave more accurate information about changing places, and works faster (no need to find infromation about changes in transformed file).

Babel plugins list

Here you can find babel plugins which feets the most main purpose of putout and adviced to use:

transform-inline-consecutive-adds
-const foo = {};
-foo.a = 42;
-foo.b = ["hi"];
-foo.c = bar();
-foo.d = "str";
+const foo = {
+  a: 42,
+  b: ["hi"],
+  c: bar(),
+  d: "str"
+};

-const bar = [];
-bar.push(1);
-bar.push(2);
+const bar = [1, 2];
codemod-object-assign-to-object-spread
function merge(a) {
-   return Object.assign({}, a, {
-       hello: 'world'
-   });
+   return {
+       ...a,
+       hello: 'world'
+   };
};
codemod-optional-catch-binding
try {
    throw 0;
-} catch (err) {
+} catch {
    console.log("it failed, but this code executes");
}

Please send pull requests with babel plugins which can be used as codemods, or simplify, fix, makes code more readable.

JSCodeshift

jscodeshift codemods can be added to plugins section of .putout.json with prefix jscodeshift/. This way:

Example

{
    "plugins": [
        "jscodeshift/async-await-codemod"
    ]
}

JSCodeshift codemods list

Here you can find jscodeshift codemods which feets the most main purpose of putout and adviced to use:

async-await-codemod
-function makeRequest() {
-  return getJSON().then(data => {
-    console.log(data);
-    return 'done';
-  });
+ async function makeRequest() {
+  const data = await getJSON();
+  console.log(data);
+  return 'done';
}

Please send pull requests with jscodeshift codemods which can be used to simplify, fix or makes code more readable.

Codemods

putout supports codemodes in the similar to plugins way, just create a directory ~/.putout and put your plugins there. Here is example: convert-tape-to-supertape and this is example of work.

ESLint

If you see that putout brokes formatting of your code, use eslint plugin eslint-plugin-putout.

Install eslint-plugin-putout with:

npm i eslint eslint-plugin-putout -D

Then create .eslintrc.json:

{
    "extends": [
        "plugin:putout/recommended"
    ],
    "plugins": [
        "putout"
    ]
}

And use with putout this way:

putout --fix lib

To set custom eslint config file use ESLINT_CONFIG_FILE env variable:

ESLINT_CONFIG_FILE=test.eslintrc.json putout --fix lib

You can even use only eslint, because putout bundled to eslint-plugin-putout with:

eslint --fix lib

Will uses putout transformations for you :).

Babel

Putout can be used as babel plugin. Just create .babelrc file with configuration you need.

{
  "plugins": [
      ["putout", {
          "rules": {
              "remove-unused-variables": "off"
          }
      }]
  ]
}

License

MIT