🐊Putout plugin helps with plugins development.
npm i @putout/plugin-putout -D
- ✅ add-await-to-progress;
- ✅ add-index-to-import;
- ✅ add-places-to-compare-places;
- ✅ add-path-arg-to-fix;
- ✅ add-test-args;
- ✅ add-traverse-args;
- ✅ add-track-file;
- ✅ apply-async-formatter;
- ✅ apply-create-test;
- ✅ apply-declare;
- ✅ apply-for-of-to-track-file;
- ✅ apply-insert-after;
- ✅ apply-insert-before;
- ✅ apply-namespace-specifier;
- ✅ apply-processors-destructuring;
- ✅ apply-remove;
- ✅ apply-rename;
- ✅ apply-short-processors;
- ✅ check-match;
- ✅ check-replace-code;
- ✅ convert-add-argument-to-add-args;
- ✅ convert-babel-types;
- ✅ convert-destructuring-to-identifier;
- ✅ convert-dirname-to-url;
- ✅ convert-find-to-traverse;
- ✅ convert-get-rule-to-require;
- ✅ convert-match-to-function;
- ✅ convert-method-to-property;
- ✅ convert-node-to-path-in-get-template-values;
- ✅ convert-number-to-numeric;
- ✅ convert-process-to-find;
- ✅ convert-progress-to-track-file;
- ✅ convert-putout-test-to-create-test;
- ✅ convert-replace-to-function;
- ✅ convert-replace-with;
- ✅ convert-replace-with-multiple;
- ✅ convert-report-to-function;
- ✅ convert-to-no-transform-code;
- ✅ convert-traverse-to-include;
- ✅ convert-traverse-to-replace;
- ✅ convert-traverse-to-scan;
- ✅ convert-url-to-dirname;
- ✅ create-test;
- ✅ declare;
- ✅ includer;
- ✅ move-require-on-top-level;
- ✅ remove-empty-array-from-process;
- ✅ remove-unused-get-properties-argument;
- ✅ rename-operate-to-operator;
- ✅ replace-operate-with-operator;
- ✅ replace-test-message;
- ✅ shorten-imports;
- ✅ simplify-replace-template;
{
"rules": {
"putout/add-places-to-compare-places": "on",
"putout/add-path-arg-to-fix": "on",
"putout/add-test-args": "on",
"putout/add-traverse-args": "on",
"putout/add-track-file": "on",
"putout/add-await-to-progress": "on",
"putout/add-index-to-import": "on",
"putout/apply-create-test": "on",
"putout/apply-processors-destructuring": "on",
"putout/apply-async-formatter": "on",
"putout/apply-declare": "on",
"putout/apply-rename": "on",
"putout/apply-remove": "on",
"putout/apply-insert-before": "on",
"putout/apply-insert-after": "on",
"putout/apply-short-processors": "on",
"putout/apply-namespace-specifier": "on",
"putout/apply-for-of-to-track-file": "on",
"putout/check-match": "on",
"putout/check-replace-code": ["on", {
"once": true
}],
"putout/convert-putout-test-to-create-test": "on",
"putout/convert-to-no-transform-code": "on",
"putout/convert-number-to-numeric": "on",
"putout/convert-replace-with": "on",
"putout/convert-replace-with-multiple": "on",
"putout/convert-replace-to-function": "on",
"putout/convert-match-to-function": "on",
"putout/convert-babel-types": "on",
"putout/convert-destructuring-to-identifier": "on",
"putout/convert-node-to-path-in-get-template-values": "on",
"putout/convert-traverse-to-include": "on",
"putout/convert-traverse-to-replace": "on",
"putout/convert-traverse-to-scan": "on",
"putout/convert-process-to-find": "on",
"putout/convert-method-to-property": "on",
"putout/convert-add-argument-to-add-args": "on",
"putout/convert-dirname-to-url": "on",
"putout/convert-url-to-dirname": "on",
"putout/convert-report-to-function": "on",
"putout/convert-get-rule-to-require": "on",
"putout/convert-progress-to-track-file": "on",
"putout/create-test": "on",
"putout/shorten-imports": "on",
"putout/declare": "on",
"putout/includer": "on",
"putout/move-require-on-top-level": "on",
"putout/replace-test-message": "on",
"putout/remove-unused-get-properties-argument": "on",
"putout/remove-empty-array-from-process": "on",
"putout/simplify-replace-template": "on"
}
}
test('', async (t) => {
await t.process({});
});
test('', async ({process}) => {
await process({});
});
Apply short names of processors, for example __json
instead of __putout_processor_json
. Checkout out in 🐊Putout Editor.
export const match = () => ({
'__putout_processor_ignore(__a)': ({__a}) => {
const list = __a.elements.map(getValue);
},
'__putout_processor_filesystem(__a)': ({__a}) => {
const list = __a.elements.map(getValue);
},
});
export const match = () => ({
[__ignore]: ({__array}) => {
const list = __array.elements.map(getValue);
},
[__filesystem]: ({__object}) => {
const list = __object.elements.map(getValue);
},
});
Better use rename(path, from, to)
method of operator
.
Check out in 🐊Putout Editor.
export const fix = ({path, from, to}) => {
path.scope.rename(from, to);
};
import {operator} from 'putout';
const {rename} = operator;
export const fix = ({path, from, to}) => {
rename(path, from, to);
};
Better to use remove(path)
method of operator
.
It helps to preserve comments.
export const fix = (path) => {
path.remove();
};
import {operator} from 'putout';
const {remove} = operator;
export const fix = (path) => {
remove(path);
};
Better to use insertBefore(a, b)
method of operator
.
export const fix = (path) => {
path.insertBefore(path.get('init'));
};
import {operator} from 'putout';
const {insertBefore} = operator;
export const fix = (path) => {
insertBefore(path, path.get('init'));
};
Better to use insertAfter(a, b)
method of operator
.
It helps to avoid duplication of comments.
export const fix = (path) => {
path.insertAfter(path.get('init'));
};
import {operator} from 'putout';
const {insertAfter} = operator;
export const fix = (path) => {
insertAfter(path, path.get('init'));
};
Better to use Declareator
instead of operator.declare()
.
Check out in 🐊Putout Editor.
const {operator} = require('putout');
const {declare} = operator;
module.exports = declare({
tryCatch: `import tryCatch from 'try-catch'`,
tryToCatch: `import tryToCatch from 'try-to-catch'`,
});
module.exports.declare = () => ({
tryCatch: `import tryCatch from 'try-catch'`,
tryToCatch: `import tryToCatch from 'try-to-catch'`,
});
test('formatter: codeframea', (t) => {
t.format(codeframe, 1);
t.end();
});
test('formatter: codeframea', async ({format}) => {
await format(codeframe, 1);
});
const test = require('@putout/test')({
'remove-debugger': plugin,
});
const {createTest} = require('@putout/test');
const test = createTest({
'remove-debugger': plugin,
});
The Generator object is returned by a
generator function
and it conforms to both the iterable protocol and theiterator
protocol.(c) MDN
trackFile
is generator function used to count progress that can be used in Scanner.
Checkout in 🐊Putout Editor
module.exports.scan = (path, {push, trackFile}) => {
trackFile(path, '*.swp').map(push);
};
module.exports.scan = (path, {push, trackFile}) => {
for (const file of trackFile(path, '*.swp')) {
push(file);
}
};
Add properties to createTest
options, here is exmample of .putout.json
:
{
"rules": {
"putout/create-test": ["on", {
"add": [
["printer", "putout"]
]
}]
}
}
Check it out in 🐊Putout Editor.
createTest(__dirname, {
'putout/create-test': plugin,
});
createTest(__dirname, {
printer: 'putout',
plugins: [
['putout/create-test', plugin],
],
});
Prevent Babel
warning: The node type NumberLiteral has been renamed to NumericLiteral
.
const {isNumberLiteral} = types;
isNumberLiteral(node);
const {isNumericLiteral} = types;
isNumericLiteral(node);
Fixes results of @putout/convert-commonjs-to-esm work.
import putoutTest from '@putout/test';
const test = putoutTest(__dirname, {
'remove-unused-variables': rmVars,
});
import {createTest} from '@putout/test';
const test = createTest(__dirname, {
'remove-unused-variables': rmVars,
});
test('plugin-apply-destructuring: transform: array: destructuring', (t) => {
const code = 'const {name} = array[0]';
t.transform(code, '');
t.end();
});
test('plugin-apply-destructuring: transform: array: destructuring', (t) => {
const code = 'const {name} = array[0]';
t.noTransformCode(code);
t.end();
});
module.exports.fix = (path) => {
path.replaceWith(Identifier('hello'));
};
const {replaceWith} = require('putout').operator;
module.exports.fix = (path) => {
replaceWith(path, Identifier('hello'));
};
module.exports.fix = (path) => {
path.replaceWithMultiple([
Identifier('hello'),
]);
};
const {replaceWithMultiple} = require('putout').operator;
module.exports.fix = (path) => {
replaceWithMultiple(path, [
Identifier('hello'),
]);
};
module.exports.replace = {
'let __a = __b': 'const __b = __a',
};
module.exports.replace = () => ({
'let __a = __b': 'const __b = __a',
});
module.exports.match = {
'let __a = __b': () => false,
};
module.exports.match = () => ({
'let __a = __b': () => false,
});
const {
ObjectExpression,
SpreadElement,
isObjectExpression,
isIdentifier,
} = require('@babel/types');
const {
ObjectExpression,
SpreadElement,
isObjectExpression,
isIdentifier,
} = require('putout').types;
module.exports.replace = () => ({
'const __a = __b': ({}) => {},
'const __c = __d': ({}, path) => {},
});
module.exports.replace = () => ({
'const __a = __b': (vars) => {},
'const __c = __d': (vars, path) => {},
});
const {__a, __b} = getTemplateValues(path.node, 'const __a = __b');
const {__a, __b} = getTemplateValues(path, 'const __a = __b');
const parseOptions = require('putout/lib/parse-options');
const parseOptions = require('putout/parse-options');
module.exports.traverse = ({push}) => ({
TSTypeAssertion(path) {
push(path);
},
});
module.exports.include = () => [
'TSTypeAssertion',
];
module.exports.traverse = () => ({
'async (__a) => __b': 'async ({process}) => __b',
});
module.exports.replace = () => ({
'async (__a) => __b': 'async ({process}) => __b',
});
Checkout in 🐊Putout Editor:
module.exports.traverse = ({push, options}) => ({
[__filesystem](path) {
const {names} = options;
for (const name of names) {
const files = findFile(path, name);
for (const file of files) {
push({
name,
path: file,
});
}
}
},
});
module.exports.scan = (path, {push, options}) => {
const {names} = options;
for (const name of names) {
const files = findFile(path, name);
for (const file of files) {
push(file, {
name,
});
}
}
};
module.exports.preProcess = () => {};
module.exports.postProcess = () => {};
module.exports.branch = (rawSource) => [];
module.exports.merge = (processedSource, list) => '';
- property simpler to work with;
- support of
convert-destructuring-to-identifier
which isReplacer
, whileconvert-method-to-property
isIncluder
(searches forObjectMethod
node);
module.exports.match = () => ({
'module.exports.traverse = __a'({}, path) {},
});
module.exports.match = () => ({
'module.exports.traverse = __a': ({}, path) => {},
});
Checks that Replacer transform is possible.
Pass once=false
to always fail no matter how many fixCounts
passed.
module.exports.replace = () => ({
'if (__a = __b) __body': 'if (__a === "__b") __body',
});
☝️ There is no fix
for this rule, it used internally to be more confident about test coverage
, because of declaration form, transforms cannon be checked by nyc
and c8
, and uncovered lines can find unfixable false positives when running on code.
This is additional tests, if you forget to test some case (from a big list of rules that is supported) it will be checked with this rule
and make transforms more stable.
Checks that Replacer match()
keys exists in replace
.
Checkout in 🐊Putout Editor.
module.exports.match = () => ({
'__a = __b': (vars, path) => {},
});
module.exports.replace = () => ({
'__a = __': '__a',
});
☝️ There is no fix
for this rule, it used internally to be more confident about test coverage
, because of declaration form, transforms cannon be checked by nyc
and c8
, and uncovered lines can find unfixable false positives when running on code.
This is additional tests, if you forget to test some case (from a big list of rules that is supported) it will be checked with this rule
and make transforms more stable.
Depends on @putout/convert-esm-to-commonjs and @putout/declare.
compare(a, 'const __a = __b');
isIdentifier(a);
const {operator, types} = require('putout');
const {compare} = operator;
const {isIdentifier} = types;
compare(a, 'const __a = __b');
isIdentifier(a);
comparePlaces
takes two or more arguments.
Checkout in 🐊Putout Editor.
comparePlaces('hello');
comparePlaces('hello', []);
Checkout in 🐊Putout Editor.
export const fix = () => {
path.remove();
};
export const fix = (path) => {
path.remove();
};
test('', () => {
comparePlaces();
});
test('', ({comparePlaces}) => {
comparePlaces();
});
Checkout in 🐊Putout Editor. Supported args:
push
:
module.exports.traverse = () => ({
'__a.replace(/__b/g, __c)': (path) => {
push(path);
},
});
module.exports.traverse = ({push}) => ({
'__a.replace(/__b/g, __c)': (path) => {
push(path);
},
});
module.exports.traverse = () => ({
ImportDeclaration(path) {
const {node} = path;
const {name} = node.specifiers[0].local;
store('name', name);
},
});
module.exports.traverse = ({store}) => ({
ImportDeclaration(path) {
const {node} = path;
const {name} = node.specifiers[0].local;
store('name', name);
},
});
export const traverse = () => ({
ImportDeclaration(path) {
listStore(path);
},
});
module.exports.traverse = ({listStore}) => ({
ImportDeclaration(path) {
listStore(path);
},
});
export const traverse = () => ({
'module.exports.match = __object': pathStore,
});
export const traverse = ({pathStore}) => ({
'module.exports.match = __object': pathStore,
});
Checkout in 🐊Putout Editor.
test('', ({progress}) => {
progress();
});
test('', async ({progress}) => {
await progress();
});
Checkout in 🐊Putout Editor.
export const scan = (root, {push, progress}) => {
trackFile();
};
export const scan = (root, {push, progress, trackFile}) => {
trackFile();
};
ESM doesn't add index.js
, so it can be left after @putout/plugin-convert-esm-to-commonjs
.
Checkout in 🐊Putout Editor.
import insertRust from './insert-rust.js';
import addAction from './add-action.js';
export const rules = {};
import insertRust from './insert-rust/index.js';
import addAction from './add-action/index.js';
export const rules = {};
const {operator} = require('putout');
const {addArgument} = operator;
module.exports = addArgument({
t: ['t', 'test("__a", (__args) => __body)'],
});
const {operator} = require('putout');
const {addArgs} = operator;
module.exports = addArgs({
t: ['t', 'test("__a", (__args) => __body)'],
});
import {createTest} from '@putout/test';
import plugin from '@putout/plugin-debugger';
import {createSimport} from 'simport';
const {__dirname} = createSimport(import.meta.url);
const test = createTest(__dirname, {
'remove-debugger': plugin,
});
import {createTest} from '@putout/test';
import plugin from '@putout/plugin-debugger';
const test = createTest(import.meta.url, {
'remove-debugger': plugin,
});
const {createTest} = require('@putout/test');
const plugin = require('@putout/plugin-debugger');
const test = createTest(__dirname, {
'remove-debugger': plugin,
});
const {createTest} = require('@putout/test');
const plugin = require('@putout/plugin-debugger');
const test = createTest(import.meta.url, {
'remove-debugger': plugin,
});
module.exports.report = `'report' should be a 'function'`;
module.exports.report = () => `'report' should be a 'function'`;
Checkout in 🐊Putout Editor.
module.exports.scan = (root, {push, progress}) => {
const files = findFile(root, ['*']);
const n = files.length;
for (const [i, file] of files.entries()) {
push(file);
progress({
i,
n,
});
}
};
module.exports.scan = (root, {push, trackFile}) => {
for (const file of trackFile(root, ['*'])) {
push(file);
}
};
- ✅ import Nested plugins in Deno and Browser;
- ✅ easier bundle with rollup without
dynamicRequireTargets
; - ✅ easier to migrate to ESM;
Checkout in 🐊Putout Editor.
module.exports.rules = {
...getRule('remove-unused-variables'),
};
const removeUnusedVariables = require('./remove-unused-variables');
module.exports.rules = {
'remove-unused-variables': removeUnusedVariables,
};
const test = require('@putout/test')(__dirname, {
'remove-debugger': require('..'),
});
test('remove debugger: report', (t) => {
t.transform('debugger', {
'remove-debugger': require('..'),
});
t.end();
});
const removeDebugger = require('..');
const test = require('@putout/test')(__dirname, {
'remove-debugger': removeDebugger,
});
test('remove debugger: report', (t) => {
t.transform('debugger', {
'remove-debugger': removeDebugger,
});
t.end();
});
module.exports.include = () => 'cons __a = __b';
module.exports.exclude = () => 'var __a = __b';
module.exports.include = 'cons __a = __b';
module.exports.exclude = 'var __a = __b';
module.exports.include = [
'cons __a = __b',
];
module.exports.exclude = [
'var __a = __b',
];
module.exports.include = () => [
'cons __a = __b',
];
module.exports.exclude = () => [
'var __a = __b',
];
module.exports.include = () => [
'cons __a = __b',
];
module.exports.exclude = () => [
'var __a = __b',
];
module.exports.include = () => [
'cons __a = __b',
];
module.exports.exclude = () => [
'var __a = __b',
];
Checks that test message
and used operator
are synchronized.
Check it out in 🐊Putout Editor.
test('plugin-putout: rename-operate-to-operator: transform: operator exist', (t) => {
t.noTransform('operator');
t.end();
});
test('plugin-putout: rename-operate-to-operator: report: operator exist', (t) => {
t.noReport('operator');
t.end();
});
test('plugin-putout: rename-operate-to-operator: no transform: operator exist', (t) => {
t.noTransform('operator');
t.end();
});
test('plugin-putout: rename-operate-to-operator: no report: operator exist', (t) => {
t.noReport('operator');
t.end();
});
Check it out in 🐊Putout Editor.
await process('input', []);
await process('input');
Check it out in 🐊Putout Editor.
const {
overridesPath,
parserPath,
rulesPath,
} = getProperties(__jsonPath, [
'parser',
'rules',
'overrides',
'extends',
]);
const {
overridesPath,
parserPath,
rulesPath,
} = getProperties(__jsonPath, ['parser', 'rules', 'extends']);
Checkout in 🐊Putout Editor.
module.exports.replace = () => ({
'if (__a) {__b} else {__c}': () => 'if (__a) __b; else __c',
});
module.exports.replace = () => ({
'if (__a) {__b} else {__c}': 'if (__a) __b; else __c',
});
MIT