Documentation

Putout NPM version Dependency Status Build Status Coverage Status

Putout is a pluggable and configurable code transformer. It work's in a similar but more powerful than eslint way. While eslint can tell you about unused variable, debugger statement or redundant console.log, it fixes mostly whitespaces. Putout can fix all this things and much more.

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 -g

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: dump
  --fix                   aply found fixes of errors to code
  --fix-count             count of fixes rounds (defaults to 10)

To find possible transform places:

putout lib test

To apply transforms:

putout lib test --fix

Built-in transforms

remove unused variables
  function show() {
-     const message = 'hello';
      console.log('hello world');
  }
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 debugger statement
- debugger;
remove boolean from logical expressions
-const t = true && false;
-const t = false;
replace test.only to test calls
-test.only('some test here', (t) => {
+test('some test here', (t) => {
    t.end();
});
replace test.skip to 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;
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;
if absent strict mode directive in commonjs add it
+'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
-import hello from 'world';
+const hello = require('world');
convert commonjs to esm
-const hello = require('world');
+import hello from 'world';
apply destructuring
-const hello = world.hello;
-const a = b[0];
+const {hello} = world; 
+const [a] = b;
merge destructuring properties
-const {one} = require('numbers'):
-const {two} = require('numbers');
+ const {
+   one,
+   two
+} = require('numbers');
convert Math.pow to exponentiation operator
-Math.pow(2, 4);
+2 ** 4;
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.operate;
-const {isIdentifier} = putout.types;
+const {operate, types} = putout;
+const {replace} = operate;
+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;

Plugins

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

Package Version Dependencies
@putout/plugin-remove-unused-variables 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-process-exit npm Dependency Status
@putout/plugin-remove-debugger npm Dependency Status
@putout/plugin-remove-only npm Dependency Status
@putout/plugin-remove-skip npm Dependency Status
@putout/plugin-split-variable-declarations 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-convert-esm-to-commonjs npm Dependency Status
@putout/plugin-convert-commonjs-to-esm npm Dependency Status
@putout/plugin-apply-destructuring npm Dependency Status
@putout/plugin-merge-destructuring-properties npm Dependency Status
@putout/plugin-react-hooks npm Dependency Status
@putout/plugin-madrun npm Dependency Status
@putout/plugin-strict-mode npm Dependency Status
@putout/plugin-convert-math-pow npm Dependency Status
@putout/plugin-extract-sequence-expressions npm Dependency Status
@putout/plugin-extract-object-properties npm Dependency Status
@putout/plugin-convert-apply-to-spread npm Dependency Status
@putout/plugin-convert-arguments-to-rest 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-putout 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
Package Version Dependencies
@putout/formatter-dump npm Dependency Status
@putout/formatter-stream npm Dependency Status
@putout/formatter-progress npm Dependency Status
@putout/formatter-json npm Dependency Status
@putout/formatter-codeframe 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 putput, 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 option:

{
    "formatter": "dump",
    "match": {
        "madrun.js": {
            "madrun/*": true
        },
        "bin": {
            "remove-process-exit": false,
            "remove-console": false
        },
        "test|.spec.js": {
            "remove-only": true,
            "remove-skip": true,
            "putout": true
        }
    },
    "ignore": [
        "node_modules"
    ],
    "rules": {
        "madrun/*": false,
        "convert-esm-to-commonjs": false,
        "convert-commonjs-to-esm": false,
        "remove-only": false,
        "remove-skip": false
    },
    "plugins": [
        "apply-destructuring",
        "madrun",
        "remove-debugger",
        "remove-only",
        "remove-skip",
        "remove-process-exit",
        "remove-console",
        "remove-empty",
        "remove-unused-variables",
        "remove-unused-private-fields",
        "remove-unused-expressions",
        "remove-useless-variables",
        "remove-constant-conditions",
        "remove-boolean-from-logical-expressions",
        "split-variable-declarations",
        "convert-esm-to-commonjs",
        "convert-commonjs-to-esm",
        "convert-apply-to-spread",
        "convert-arguments-to-rest",
        "convert-object-assign-to-merge-spread",
        "convert-math-pow",
        "convert-binary-expression-to-boolean",
        "extract-sequence-expressions",
        "extract-object-properties",
        "merge-destructuring-properties",
        "strict-mode",
        "putout"
    ]
}

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": true
        }
    }
}

Ignore

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

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

Plugins

Putout supports plugins, there is to types: with prefix official @putout/plugin- and user plugins with prefix putout-plugin-. To use your plugin create plugin as npm package with keywords putout, putout-plugin and add it to .putout.json.

For example if you need to remove-something create putout plugin with name putout-plugin-remove-something and add it to .putout.json:

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

Add putout as a peerDependency to your packages.json and set keywords: putout, putout-plugin so other users can find it 🙂.

Plugins API

Every putout plugin should contain 3 functions:

  • report(path) - report error message to putout cli;
  • traverse({push}) - find errors and push them;
  • fix(path) - fixes paths using places array received using find function;

context of find function contains @babel/traverse and @babel/types. Also there is template. All of this can be get from putout:

const {
    types,
    template,
} = 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.

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
module.exports.traverse = ({push}) => {
    return {
        DebuggerStatement(path) {
            push(path);
        }
    }
};

// when user calls "putout --fix" node will be removed with
module.exports.fix = (path) => {
    path.remove();
};

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.

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 Support

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 && eslint --fix lib

API

To use in the browser use require("putout/dist/putout.js")

putout(source)

const {readFileSync} = require('fs');
const putout = require('putout');

const source = readFileSync('./1.js', 'utf8');
console.log(source);
// outputs
`
const t = 'hello';
const m = t + '!';
`

const result = putout(source);
// returns
`
const t = 'hello';
`

const result2 = putout(result);
// returns
``

License

MIT