Among the maxims on Lord Naoshige’s wall there was this one: ”Matters of’ great concern should be treated lightly.” Master lttei commented, “Matters of small concern should be treated seriously.”
(c) Yamamoto Tsunetomo "Hagakure"
🦎PutoutScript — JavaScript-compatible language which adds additional meaning to Identifiers
in AST-template. It is supported by all types of 🐊Putout plugins.
Take a look at rule syntax for more information.
- ☝️ In the command line, patterns are specified with a flag
--transform
. - ☝️ Read more about AST nodes in The Book of AST.
Pattern matching searches code for a given pattern. For example, the expression pattern say('hello 🐊')
can match a full expression or be part of a subexpression:
loud(say('hello 🐊'))
In the same way, the Statement Pattern return __
can match a Statement in a function
or on any level of nesting:
const when = () => {
if (isTime())
return 'now';
return 'later';
};
The double low dush template value (__
) abstracts away Identifiers
, Expressions
and Literals
.
Linked Value (__a
) abstracts away Identifiers
, Expressions
and Literals
.
The __args
value abstracts away a sequence of zero or more arguments, for (__args) => __a
:
(a, b, c) => a + b + c;
__args
can be linked. Linked args can be used to link to values with the same Expressions
, Identifiers
or Literals
): ((__args__a) => __c(__args__a))(__args__b)
will find:
fn(value);
But not:
((a) => fn(42))(value);
Use the __args
template value to search for function calls with arguments. For example, the pattern sey(__args)
finds calls regardless of its arguments.
say('hello 🐊');
The __args
template value can also be used to search for method calls. For example, the pattern __object.say(__args)
matches:
crocodile.say('hello 🐊');
The __
template value can also be used for the function name. Indeed, In some cases, you may want to match any function definitions: regular functions, methods, but also arrow functions.
In that case you can use __
in place of the name of the function to match named or anonymous functions. For example, the pattern function __(__a) {}
will match any function with one parameter:
function say(a) {
return 'hello 🐊';
}
const talk = function(a) {
return 'hello 🐊';
};
The "__a"
template value can be used to search for strings containing any data. The pattern crocodile.say("__a")
matches:
crocodile.say('hello 🐊');
This also works with constant propagation.
T he pattern /__a/
will match any regular expression construct:
const animalRegExp = /🐊|🦛/;
There is a base support of JSX in template values, this template <h1>__a</h1>
will find:
<h1>hello world</h1>;
When you need to find one or more children, <h1>__jsx_children</h1>
will find:
<h1>hello world</h1>;
<h1></h1>;
The __a
can match arguments to binary operations. The pattern const __a = __b + __c
matches:
const friends = '🐊' + '🦛';
The __array
and __object
template values can match container data structures like, arrays, objects.
The pattern const friends = __array
matches:
const friends = [
'🐊',
'🦛',
];
The pattern const animal = __object
matches:
const animal = {
'🐊': '🦛',
};
The __
can be used inside conditionals or loops. 🦎 PutoutScript:
if (__a)
__b;
matches:
if (friends.includes('🐊'))
return `🐊 friend of 🦛`;
Template variables can match a conditional or loop body if the body statement information is re-used later. 🦎 PutoutScript:
if (__a)
__body;
matches:
if (friends.includes('🦛'))
return `🦛 friend of 🐊`;
Template variables are an abstraction to match code when you don’t know the value or contents ahead of time, similar to capture groups in regular expressions. Template variables can be used to track values across a specific code scope. This includes variables, functions, arguments, classes, object methods, imports and more.
Template variables look like __a
, __b
, etc. They begin with a __
and can only contain one character.
🦎 PutoutScript __a + __b
matches the following code examples:
'🐊' + '📼';
Template variable __imports
can be used to match imports. For example, import __imports
matches:
import fs from 'node:fs/promises';
Re-using template variables shows their true power. Detect assignment of sum of duplicate nodes:
const __a = __b + __b;
Assignment of duplicate nodes sum detected:
const sum = 2 + 2;
You can use "__a"
to match any string literal. This is similar to using __a
, but the content of the string is stored in the template variable __a
, which can then be used later.
Same thing works with TypeScript
types, such pattern const __a: __b = __c
, finds:
const answer: number = 42;
JavaScript differentiate between expressions and statements. Expressions can appear inside if
conditions, in function
call arguments
, etc. Statements can not appear everywhere; they are sequence of operations (using ;
as a separator/terminator) or special control flow constructs (if
, while
, etc.):
- ✅
say()
is an expression; - ✅
say();
is a statement;
When you write the expression foo()
in a pattern, 🦎PutoutScript
will visit every expression and sub-expression in your program and try to find a match.
Partial expressions are not valid patterns. For example, the following is invalid:
'🐊' +
A complete expression is needed (like '🐊' + __a
). Same with partial statements.
☝️ Find what you needed in this doc? Create an issue if you need any help 😏!