π Pluggable and configurable JavaScript Linter, code transformer and formatter with built-in ESLint and Babel support for js, jsx typescript, flow, markdown, yaml and json. Write declarative codemods in a simplest possible way π
MIT License
Published by coderaiser almost 3 years ago
Published by coderaiser almost 3 years ago
Barefooted and naked of breast,
I mingle with the people of the world.
My clothes are ragged and dust-laden,
and I am ever blissful.
I use no magic to extend my life;
Now, before me, the dead trees
become alive.
(c) Zen Ten Bulls
Hi folks! The time is came for a major release!
@putout/operate
That's simple. For API was consistent two methods was renamed:
-findProperties(path, names);
-findProperty(path, name);
+getProperties(path, names);
+getProperty(path, name);
And of course putout/declare
rule declare it for you when you use it π.
remove-process-exit
was merged to @putout/plugin-nodejs
Since this rule is nodejs
related it was moved. So here is changes you should made to .putout.json
if you disabled this rule:
{
"rules": {
- "remove-process-exit": "off"
+ "nodejs/remove-process-exit": "off"
}
}
convert-top-level-return
was merged to @putout/plugin-nodejs
Since this rule is nodejs
related it was moved. So here is changes you should made to .putout.json
if you disabled this rule:
{
"rules": {
- "convert-top-level-return": "off"
+ "nodejs/convert-top-level-return": "off"
}
}
TypeScript
related plugins to @putout/plugin-typescript
All rules related to TypeScript
was moved to separate package. So if you disabled one of those, add prefix typescript
:
{
"rules: {
- "apply-as-type-assertions": "off",
- "apply-utility-types": "off",
- "convert-generic-to-shorthand": "off",
- "remove-duplicate-interface-keys": "off",
- "remove-useless-types": "off",
- "remove-useless-mapped-types": "off",
- "remove-useless-mapping-modifiers": "off",
- "remove-useless-types-from-constants": "off",
- "remove-unused-types": "off"
+ "typescript/apply-as-type-assertions": "off",
+ "typescript/apply-utility-types": "off",
+ "typescript/convert-generic-to-shorthand": "off",
+ "typescript/remove-duplicate-interface-keys": "off",
+ "typescript/remove-useless-types": "off",
+ "typescript/remove-useless-mapped-types": "off",
+ "typescript/remove-useless-mapping-modifiers": "off",
+ "typescript/remove-useless-types-from-constants": "off",
+ "typescript/remove-unused-types": "off"
}
}
Yes, there is a lot TS
rules in πPutout
for this moment :)!
apply-array-at
was removed from default installBecause array.at
support on node v16
and higher and πPutout
(still) supports node v14
. The rule is supported and can be installed separately with:
npm i @putout/plugin-apply-array-at
If you use it, update .putout.json
with:
{
+ "plugins": [
+ "apply-array-at"
+ ]
}
In this release we have 19%
less dependencies π! It makes installation a faster a bitπ.
That's all for today and this is latest day of Ten Bulls Zen marathon π! That's was crazy couple weeks :) I had a lot of fun π.
Cheers π₯€!
Published by coderaiser almost 3 years ago
AST
Too many steps have been taken
returning to the root and the source.
Better to have been blind and deaf
from the beginning!
Dwelling in one's true abode,
unconcerned with and without -
The river flows tranquilly on
and the flowers are red
(c) Zen Ten Bulls
Hi folks! New day, new release π!
findProperty
If you want to iterate over object searching for a property with a key, this method do this for you.
Let's suppose you have an object:
{
"test": "npm test"
}
Here is how you can remove property with a name test
:
import {operate} from 'putout';
const {getExportDefault} = operate;
const testPath = findProperty(path, 'test');
testPath.remove();
getExportDefault
When you need to get export default
this method will help you out!
import {operate} from 'putout';
const {getExportDefault} = operate;
const exportPath = getExportDefault(path);
exportPath.remove();
The thing is two new rules added to @putout/plugin-madrun
that improves using madly comfortable script runner madrun
.
Both of them using methods described earlier to convert code like this:
export default {
'test': () => [env, 'npm test'],
'test:only': () => 'npm test',
- 'coverage': async () => [env, await run('test')],
- 'coverage:only': async () => [env, await cutEnv('test:only')],
+ 'coverage': async () => [env, await cutEnv('test')],
+ 'coverage:only': async () => [env, await run('test:only')],
};
That's all for today! Have I nice day π¦!
Published by coderaiser almost 3 years ago
ESLint
APIWhip, rope, person, and Ox -
all merge in No Thing.
This heaven is so vast,
no message can stain it.
How may a snowflake exist
in a raging fire.
Here are the footprints of
the Ancestors.
(c) Zen Ten Bulls
Hi folks :)!
ESLint
begins his work as a formatter when πPutout
done his transformations. That's why it used a lot in different parts of application, for testing purpose and using API
in a simplest possible way. You can access it with:
import {eslint} from 'putout/eslint';
Usage as simple as:
const [source, places] = await eslint({
name: 'hello.js',
code: `const t = 'hi'\n`,
fix: false,
});
In a similar to πPutout
way.
βοΈ The only difference is πPutout
return object with code
and places
properties
Also it has a name
property it used by ESLint
to calculate configuration file.
This API
doesn't suppose to came in π΄ Public Space, anyways it is already used in eslint-plugin-putout
to test plugins, so why not :)? Anyways it's signature didn't changed from the beginning.
And starting from today you can override any ESLint
options with help of config
property π
const [source, places] = await eslint({
name: 'hello.js',
code: `const t = 'hi'\n`,
fix: false,
config: {
extends: [
'plugin:putout/recommended'
]
}
});
That's all for today guys! Have a nice day!
Published by coderaiser almost 3 years ago
Astride the Ox, I reach home.
I am serene. The Ox too can rest.
The dawn has come. In blissful repose,
Within my thatched dwelling
I have abandoned the whip and ropes.
(c) Zen Ten Bulls
Hi guys!
I think you know that πPutout
has 100%
test coverage. Why so? The thing is writing parsers is very hard task, but wary not! There is a helpers π !
Mock-import
exists for about a year and used to mock things when you using EcmaScript Modules
. It uses πPutout
and changes ImportDeclarations
to VariableDeclaration
when you need it the most.
So such code:
import {readFile} from 'fs/promises';
Becomes:
const {readFile} = global.__mockImportCache.get('fs/promises');
It works amazing! But there is a problem with coverage
:
nyc
doesn't support ESM
;c8
shows wrong coverage information when loaders used;Taking into accord this two factors...
One more coverage tool come into world: π© ESCover
. It's also based on πPutout
and what t does is changing:
export const sum = (a, b) => {
return a + b;
};
Into this:
export const sum = (a, b) => {
return __c4['π§¨'](5, 4), a + b;
};
__c4
is a variable with a special meaning. It provides function which gives information about line and number of current instruction.
Here is how report looks like when every thing is fine:
# CAP version 13
1..3
# files: 3
# covered: 3
# π΄ ok
When something isn't right:
# CAP version 13
# /Users/coderaiser/escover/example/example.js
𧨠should be covered
---
lines:
οΈ- 1 at file:///Users/coderaiser/escover/example/example.js:1
1..3
# files: 3
# covered: 2
# 𧨠fail: 1
π©ESCover
on early stages of development, so a lot things can change!
Putout
?You can trace execution of functions with help of ESTrace
. It provides such information:
coderaiser@cloudcmd:~/estrace$ node --loader estrace example/lint.js
..π£ lint([]) 16.05mb file:///Users/coderaiser/estrace/example/lint.js:5
....π£ getFiles([]) 16.05mb file:///Users/coderaiser/estrace/example/lint.js:12
....π₯ getFiles 16.06mb file:///Users/coderaiser/estrace/example/lint.js:12
....π£ lintFiles([]) 16.06mb file:///Users/coderaiser/estrace/example/lint.js:22
....π₯ lintFiles 16.06mb file:///Users/coderaiser/estrace/example/lint.js:22
..π₯ lint 16.06mb file:///Users/coderaiser/estrace/example/lint.js:5
And does such transformations. Let's suppost you have a function: const fn = (a) => a
. EStrace
will replace it with:
const fn = (a) => {
try {
var __estrace_context = __estrace.enter('<anonymous:2>', 'file://hello.js:2', arguments);
return a;
} finally {
__estrace.exit('<anonymous:2>', 'file://hello.js:2', __estrace_context);
}
};
@putout/plugin-convert-commonjs-to-esm
doesn't require simport
anymore. Only built-in modules πͺ (Thanks to @putout/plugin-declare-undefined-variables
which supports native node.js
modules, and will declare it, when you use it π).
@putout/plugin-nodejs
got new rule: convert-dirname-to-url
. What it does?
Changes:
import {readFile} from 'fs/promises';
const file1 = join(__dirname, '../../package.json');
To:
import {readFile} from 'fs/promises';
const file1 = new URL('../../package.json', import.meta.url).pathname;
This will help to get rid of __dirname
in EcmaScript Modules
. Here is fast draft and results.
I just found out (I knew that they were going, but who knewβ¦) that folks from Stylelint
used π Putout
to convert their codebase to ESM
π but got a little problems with jest
: it doesn't recognize import.meta.url
π€·ββοΈ. This is why πΌ Supertape
exists, simple testing with native speed.
Thank you guys! That's awesome and very inspiring!
That's all for today! Happy holidays π!
Published by coderaiser almost 3 years ago
Source Map
?Mounting the Ox, slowly
I return homeward.
The voice of my flute intones
through the evening.
Measuring with hand-beats
the pulsating harmony,
I direct the endless rhythm.
Whoever hears this melody
will join me
(c) Zen Ten Bulls
Hi folks!
Source maps
is everywhere! Each time you open a website, it loads tones of minified JavaScript
bundles and some of them has source maps
to simplify debugging.
Same thing exists in node.js
and one nice tool requires it. This is mock-import
. It helps to mock
import you are using, so you can test it easily with πΌSupertape
(of course π!).
When you need sourcemap
you can have it easily, just pass:
sourceFileName
;sourceMapName
;putout(source, {
fix: false,
sourceFileName: 'hello.js',
sourceMapName: 'world.js',
plugins: [
'remove-unused-variables',
],
});
// returns
({
code: '\n' +
` const hello = 'world';\n` +
` const hi = 'there';\n` +
' \n' +
' console.log(hello);\n' +
'// {"version": 3, ...}',
places: [{
rule: 'remove-unused-variables',
message: '"hi" is defined but never used',
position: {line: 3, column: 10},
}],
});
That's all for today! Have a nice maps π¦ π₯³ !
Published by coderaiser almost 3 years ago
Putout Editor
The whip and rope are necessary,
Else he might stray off down
some dusty road.
Being well-trained, he becomes
naturally gentle.
Then, unfettered, he obeys his master.
(c) Zen Ten Bulls
Hi folks! When you write plugin for πPutout
there is a lot things to consider:
AST
;And there is an answer to all of this questions π₯:
πPutout Editor
! It exists for a long time, but now it has ability to share the code βοΈ!
For example you can edit a brand new rule package-json/add-type
.
package-json/add-type
A couple more words about new transformation. What it does is:
{
"name": "hello",
"version": "1.0.0",
+ "type": "commonjs"
}
adds type
of used module to package.json
.
How it does it you ask? Read next!
operator.findProperties
When you need to find properties in your package.json
or any ObjectExpression
or ObjectPattern
you can use findProperties
:
const {homepagePath} = findProperties(__aPath, ['homepage']);
It takes a path
and array of top-level
elements you need to traverse, and if the found they returned with Path
prefix, so you can manipulate them in any way you like π!
That's all for today! Have a good day and nice transformations π¦!
Published by coderaiser almost 3 years ago
I seize him with a terrific struggle.
His great will and power
are inexhaustible.
He charges to the high plateau
far above the cloud-mists,
Or in an impenetrable ravine he stands.
(c) Zen Ten Bulls
Hi folks! It's a new day, and new release π!
Today I want to tell you about some useful feature that was improved drastically is:
operator.getBindingPath
and variable declarationsLet's start from the code example:
const x = {
y: 'hello',
};
typeof hello === 'string';
typeof x.y === 'number';
How do you determine whether hello
variable is declared or not? Let's try to solve it.
When you have a path
like this one you get using match
in Replacer
:
const {operator} = require('putout');
const {getBindingPath} = operator;
module.exports.match = () => ({
'typeof __a === "__b"': ({__a}, path) => {
// when __a declared proceed to replace
return getBindingPath(path, __a))
}
});
And want to know is node __a
is declared you can use getBindingPath
, and it will solve it for you:
getBindingPath(path, 'hello');
// returns
null;
getBindingPath(path, 'x');
// returns
Path;
But what if you don't know what is the type of node
you going to receive, but you know definitely that you need a name of this node
to proceed? Just pass the node
and getBindingPath
will parse the name for you :)!
getBindingPath(path, node); // node can be Identifier or MemberExpression
// parse the name first, and then find it's declaration
Path;
convert-typeof-to-is-type
.Now convert-typeof-to-is-type
supports MemberExpressions
is well:
const hello = {
world: '',
};
-typeof hello.world === 'string'
+isString(hello.world);
And with help of declare-undefined-variables
you will get:
+const isString = (a) => typeof a === 'string';
const hello = {
world: '',
};
isString(hello.world);
So use both of them to get the most benefit π!
Of course, both of them enabled by default π
Includer
When you using Includer
and for some reason forget the it requires a function which returns an array
putout/includer
will get you covered!
And such code:
module.exports.include = [
'const __a = __b',
];
And even such:
module.exports.include = 'const __a = __b';
Will be converted to the only one correct:
module.exports.include = () => [
'const __a = __b',
];
βοΈ To get things working enable putout
rule in .putout.json
:
{
"rules": {
"putout": "on"
}
}
Always use the simplest possible plugin type for your needs π¬.
That's all for today! Happy holidays π!
Published by coderaiser almost 3 years ago
I hear the song of the nightingale.
The sun is warm, the wind is mild,
willows are green along the shore -
Here no Ox can hide!
What artist can draw that massive head,
those majestic horns?
(c) Zen Ten Bulls
Hi folks!
A couple updates for today π.
reuse-duplicate-init
now supports MemberExpression
And such code:
const {operator} = require('putout');
const {replaceWith} = require('putout').operator
Will be easily transformed to π:
const {operator} = require('putout');
const {replaceWith} = operator
When you need to store the data of traversing for future processing, and want to do it right: use Stores
.
There was two types of stores:
And now we have upstore
, that hav ability to update information you put in accordance to the name you provide.
not-rule-
prefix for Rules
When you using rules specific to your project, and don't want to publish them (but do want to keep them in repository) - use --rulesdir
:
putout --rulesdir rules
Similar to the way to it's done in πPutout
repository in directory /ruels
π
That's all for today! Happy holidays π₯³!
Published by coderaiser almost 3 years ago
Array.entries()
Along the riverbank under the trees,
I discover footprints.
Even under the fragrant grass,
I see his prints.
Deep in remote mountains they are found.
These traces can no more be hidden
than one's nose, looking heavenward.
(c) Zen Ten Bulls
Todays πPutout
minor release notes will be about Array.entries
, that can help you to get rid of for
loops even when you need to use index
. In good old days we used to use:
for (let i = 0; i < elements.length; i++) {
const element = elements[i];
console.log(i, element);
}
But now we are using for-of
most of the time, because it's simpler and does only what's needed:
for (const element of elements) {
console.log(i, element); // <-- we have no i π
}
But hold up, what with i
variable? Where we should get it from?
Worry not π, array.entries()
to the rescue!
for (const [i, element] of elements.entries()) {
console.log(i, element);
}
convert-for-to-for-of
Of course such transformation already waiting for you in convert-for-to-for-of
π .
remove-useless-array-entries
But what if you used array.entries()
for some time with index
but then stoped using index and your code became looking like this:
for (const [, element] of elements.entries()) {
console.log(element);
}
Worry not! remove-useless-array-entries
will convert code to usual for-of
:
for (const element of elements) {
console.log(element);
}
Guys have fun and always use for-of
(with the exception of hot code that requires a lot of speed βοΈ!) π¦.
Published by coderaiser almost 3 years ago
JSCodeshift
In the pasture of the world,
I endlessly push aside the tall
grasses in search of the Ox.
Following unnamed rivers,
lost upon the interpenetrating
paths of distant mountains,
My strength failing and my vitality exhausted, I cannot find the Ox.
(c) Zen Ten Bulls
Hi folks!
JSCodeshift
inspired me a lot from the beginning of work on πPutout
. It started from the great idea: codemods simple and popular.
BUT, for last couple years I see no growth in this tool.
Here is the most significant downsides of JSCodeshift
:
plugins
support;codemods
;API
;So yes, JSCodeshift
inspired me. Inspired in a way how things should be made in totally different way!
Every time I install πPutout
I see this deprecations notice:
Which I'm tired of a lot, and don't want see it again. Ever.
And installing now will be a little bit faster, what is amazing π₯³ !
About supported JSCodeshift
codemods. This feature wasn't very popular. Any codemod
could be connected to πPutout
, but it will work slower, because of recast
printing that should be done to distinguish transformed and original sources. Very naive and slow approach. And with release of @putout/recast v1.5.0
the time is come to drop JSCodeshift
because it started work incorrect and if recast
is heart of πPutout
, JSCodeshift
was just one of supported tools.
So the time is come to say Good Bye!
That's all for today. Have fun guys π!
Published by coderaiser almost 3 years ago
Published by coderaiser almost 3 years ago
Published by coderaiser almost 3 years ago
Published by coderaiser almost 3 years ago
Zoology of Egypt, 1898
βOnce upon a time, I dreamt I was a butterfly, fluttering hither and thither, to all intents and purposes a butterfly. I was conscious only of my happiness as a butterfly, unaware that I was myself. Soon I awaked, and there I was, veritably myself again. Now I do not know whether I was then a man dreaming I was a butterfly, or whether I am now a butterfly, dreaming I am a man.β
β Zhuangzi
Thanks to @EvgenyOrekhov, today was added first class support of Yarn PnP π (https://github.com/coderaiser/putout/issues/93).
All you need to get started is:
yarn set version berry
;yarn
;After that a file .pnp.cjs
will be created, this is a bundle with content of all your node_modules
.
Then if you put putout
into your scripts, you can run it with: yarn putout .
.
Have fun π!
Published by coderaiser almost 3 years ago
Lubumbashi, Democratic Republic of the Congo 1940s
putout/check-replace-code
improvementsThanks to compute
of @putout/operate
, such code can be checked:
const BODIES = {
function: `typeof __a === 'function'`,
};
module.exports.replace = () => ({
[BODIES.function]: 'isFn(__a)',
});
Published by coderaiser almost 3 years ago
Published by coderaiser almost 3 years ago
Published by coderaiser almost 3 years ago
Published by coderaiser almost 3 years ago