Tools: What's new in JavaScript Linter 🐊Putout v42

Tools: What's new in JavaScript Linter 🐊Putout v42

Source: Dev.to

📛 transform, transformAsync, findPlaces and findPlacesAsync no longer requires source argument ## 📛 @putout/filesystem a bit simplified ## 📛 Changes in default ignore ## 📛 Migrated @putout/operator-ignore to Traverser ## 📛 Dropped support of ESLint < v10 ## 🔥crawlFile - new way of searching files ## 🔥@putout/operator-remove-files ## 🔥@putout/operator-rename-properties ## 🔥@putout/operator-sort-ignore ## ignore ## 🔥Totally ESM ## using mock-require: ## ❌ Example of incorrect code ## ✅ Example of correct code ## using same names for exported and internal functions: ## ❌ Example of incorrect code ## ✅ Example of correct code ## and reserved words ## ❌ Example of incorrect code ## ✅ Example of correct code नित्य आत्माव्यय: शुद्ध: सर्वग: सर्ववित्पर: । धत्तेऽसावात्मनो लिङ्गं मायया विसृजन्गुणान् ॥ २२ ॥ nitya ātmāvyayaḥ śuddhaḥ sarvagaḥ sarva-vit paraḥ dhatte ’sāv ātmano liṅgaṁ māyayā visṛjan guṇān The spirit soul, the living entity, has no death, for he is eternal and inexhaustible. Being free from material contamination, he can go anywhere in the material or spiritual worlds. He is fully aware and completely different from the material body, but because of being misled by misuse of his slight independence, he is obliged to accept subtle and gross bodies created by the material energy and thus be subjected to so-called material happiness and distress. Therefore, no one should lament for the passing of the spirit soul from the body. (c) Śrīmad-Bhāgavatam 7.2.22 Hi folks 🎈! The time is come for a new major release of 🐊Putout, pluggable and configurable JavaScript Linter, code transformer and formatter. This release has a couple breaking changes and some new features, so get a cup of hot tea ☕️ and enjoy reading! When you run putout('const a = 3') behind the scene there is a couple operations: parse, transform and print. For a long time signature of transform looked this way: Why do we need source just after parse? - you ask. To keep tracking of shebang: In computing shebang is the character sequence #!, consisting of the characters number sign (also known as sharp or hash) and exclamation mark (also known as bang), at the beginning of a script. So all flow now looks like this: For a long time parsers like acorn, Babel and Espree (of ESLint) could not parse it, but with Hashbang Proposal everything changed, also recast couldn't handle hashbang but it is not longer supported so there is not more need of such a strange way to keep line numbers correct 🎉. As usual all changes handled by putout/remove-useless-source-argument. @putout/plugin-filesystem a bit simplified: If you changed .putout.json all modifications is handled by @putout/plugin-putout-config 😏. Default ignores changed: Now 🐊Putout sees coverage as directory, but ignores any files inside: We don't want to lint them, but RedLint needs to see coverage directory so he can remove it with @putout/coverage/remove-files we will came back to this is in a couple minutes. A .gitignore file specifies intentionally untracked files that Git should ignore. @putout/operator-ignore simplifies creating ignore-plugins. It was Replacer from the beginning, it is one of the easiest plugins to write, it is always had report and replace, but can also contain match to filter nodes that we want to ignore. Here is how it looked like: As you can see part of code is duplicated. It runs twice: In such cases much better to use Traverser. It has a couple benefits: One of the new features for @putout/operator-ignore is when you add mask *.log and your ignore file already has yarn-error.log, this both values are merged (that's right! not inserted and then with other rule cleaned up, but merged). Since the best way to modify ignore files is using group rules - it is made in operator. If you have any kind of concerns about it - I'll be happy to discuss 😏. ☝️ Take away: you should always use the simplest possible way to write transformation, and later, when you have tests written, and see places to improve - refactor and make code better 😏. ☝️ As always, this case is handled for you in @putout/plugin-putout and will be auto fixed on next lint 😏 eslint-plugin-putout requires ESLint >= v10 it is just released, so is better to upgrade. Babel also already supports it, so the time is come 🤷‍♂️. When you need to find files related to other files like in esm/apply-name-to-imported-file you can end up with: It will be very slow! And you can use crawlDirectory() instead: But it is easy to forget, or remove during refactoring, since everything will work as before, only twice slower on big amount of files. So you better use crawlFile: It works much faster, use it when you do not mutate filesystem tree: neither remove nor rename files. Welcome new operator in a hood! When you need to create a new plugin for RedLint that removes some kind of files just use: It gives ability to write such rules as coverage/remove-files easily. You still have ability to set files to you want to remove in .putout.json: But also coverage/remove-files handles files related to coverage for you. When you need to do some minor modifications to config files, like: Checkout in 🐊Putout Editor. When you need to sort things up 🐊Putout now 100% ESM, since it is: Linter that do not afraid to act like codemod You can migrate any kind of JavaScript codebase from CommonJS to ESM. Here is the algorithm: If you avoid any kinds of hacks, like: If you check your code before migration, or a bit fix after migration to avoid such patterns, it will be much easier! Any ways 🐊Putout always here to help you with any kind of your migrations 😏. That's all for today, have a nice evening 🐈! Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK: transform(ast, source, options); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: transform(ast, source, options); CODE_BLOCK: transform(ast, source, options); CODE_BLOCK: const source = parse(ast); transform(ast, options); const code = print(ast); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: const source = parse(ast); transform(ast, options); const code = print(ast); CODE_BLOCK: const source = parse(ast); transform(ast, options); const code = print(ast); CODE_BLOCK: { "rules": { - "filesystem/remove-travis-yml-file": "off", - "filesystem/remove-vim-swap-file": "off", - "filesystem/remove-ds-store-file": "off", - "filesystem/remove-empty-directory": "off", - "filesystem/remove-nyc-output": "off", + "filesystem/remove-files": "off", + "coverage/remove-files": "off", } } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: { "rules": { - "filesystem/remove-travis-yml-file": "off", - "filesystem/remove-vim-swap-file": "off", - "filesystem/remove-ds-store-file": "off", - "filesystem/remove-empty-directory": "off", - "filesystem/remove-nyc-output": "off", + "filesystem/remove-files": "off", + "coverage/remove-files": "off", } } CODE_BLOCK: { "rules": { - "filesystem/remove-travis-yml-file": "off", - "filesystem/remove-vim-swap-file": "off", - "filesystem/remove-ds-store-file": "off", - "filesystem/remove-empty-directory": "off", - "filesystem/remove-nyc-output": "off", + "filesystem/remove-files": "off", + "coverage/remove-files": "off", } } CODE_BLOCK: { "ignore": [ "**/*.lock", "**/*.log", "**/.nyc_output/*", "**/.yarn", "**/.pnp.*", "**/.idea", "**/.git", "**/package-lock.json", "**/node_modules", "**/fixture", - "**/coverage", + "**/coverage/*", "**/dist", "**/dist-dev", "**/build" ], } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: { "ignore": [ "**/*.lock", "**/*.log", "**/.nyc_output/*", "**/.yarn", "**/.pnp.*", "**/.idea", "**/.git", "**/package-lock.json", "**/node_modules", "**/fixture", - "**/coverage", + "**/coverage/*", "**/dist", "**/dist-dev", "**/build" ], } CODE_BLOCK: { "ignore": [ "**/*.lock", "**/*.log", "**/.nyc_output/*", "**/.yarn", "**/.pnp.*", "**/.idea", "**/.git", "**/package-lock.json", "**/node_modules", "**/fixture", - "**/coverage", + "**/coverage/*", "**/dist", "**/dist-dev", "**/build" ], } CODE_BLOCK: total 32136 drwxr-xr-x 13 coderaiser staff 416B Feb 17 16:23 ./ drwxr-xr-x 3 coderaiser staff 96B Feb 17 16:23 ../ -rw------- 1 coderaiser staff 6.7M Feb 17 16:23 coverage-95233-1771338202619-0.json -rw------- 1 coderaiser staff 88K Feb 17 16:23 coverage-95234-1771338198859-0.json -rw------- 1 coderaiser staff 1.3M Feb 17 16:23 coverage-95235-1771338199593-1.json -rw------- 1 coderaiser staff 460K Feb 17 16:23 coverage-95235-1771338199605-0.json -rw------- 1 coderaiser staff 1.3M Feb 17 16:23 coverage-95236-1771338199923-1.json -rw------- 1 coderaiser staff 460K Feb 17 16:23 coverage-95236-1771338199936-0.json -rw------- 1 coderaiser staff 1.3M Feb 17 16:23 coverage-95237-1771338200265-1.json -rw------- 1 coderaiser staff 384K Feb 17 16:23 coverage-95237-1771338200276-0.json -rw------- 1 coderaiser staff 1.2M Feb 17 16:23 coverage-95239-1771338200538-0.json -rw------- 1 coderaiser staff 1.2M Feb 17 16:23 coverage-95240-1771338200803-0.json -rw------- 1 coderaiser staff 1.2M Feb 17 16:23 coverage-95241-1771338201056-0.json Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: total 32136 drwxr-xr-x 13 coderaiser staff 416B Feb 17 16:23 ./ drwxr-xr-x 3 coderaiser staff 96B Feb 17 16:23 ../ -rw------- 1 coderaiser staff 6.7M Feb 17 16:23 coverage-95233-1771338202619-0.json -rw------- 1 coderaiser staff 88K Feb 17 16:23 coverage-95234-1771338198859-0.json -rw------- 1 coderaiser staff 1.3M Feb 17 16:23 coverage-95235-1771338199593-1.json -rw------- 1 coderaiser staff 460K Feb 17 16:23 coverage-95235-1771338199605-0.json -rw------- 1 coderaiser staff 1.3M Feb 17 16:23 coverage-95236-1771338199923-1.json -rw------- 1 coderaiser staff 460K Feb 17 16:23 coverage-95236-1771338199936-0.json -rw------- 1 coderaiser staff 1.3M Feb 17 16:23 coverage-95237-1771338200265-1.json -rw------- 1 coderaiser staff 384K Feb 17 16:23 coverage-95237-1771338200276-0.json -rw------- 1 coderaiser staff 1.2M Feb 17 16:23 coverage-95239-1771338200538-0.json -rw------- 1 coderaiser staff 1.2M Feb 17 16:23 coverage-95240-1771338200803-0.json -rw------- 1 coderaiser staff 1.2M Feb 17 16:23 coverage-95241-1771338201056-0.json CODE_BLOCK: total 32136 drwxr-xr-x 13 coderaiser staff 416B Feb 17 16:23 ./ drwxr-xr-x 3 coderaiser staff 96B Feb 17 16:23 ../ -rw------- 1 coderaiser staff 6.7M Feb 17 16:23 coverage-95233-1771338202619-0.json -rw------- 1 coderaiser staff 88K Feb 17 16:23 coverage-95234-1771338198859-0.json -rw------- 1 coderaiser staff 1.3M Feb 17 16:23 coverage-95235-1771338199593-1.json -rw------- 1 coderaiser staff 460K Feb 17 16:23 coverage-95235-1771338199605-0.json -rw------- 1 coderaiser staff 1.3M Feb 17 16:23 coverage-95236-1771338199923-1.json -rw------- 1 coderaiser staff 460K Feb 17 16:23 coverage-95236-1771338199936-0.json -rw------- 1 coderaiser staff 1.3M Feb 17 16:23 coverage-95237-1771338200265-1.json -rw------- 1 coderaiser staff 384K Feb 17 16:23 coverage-95237-1771338200276-0.json -rw------- 1 coderaiser staff 1.2M Feb 17 16:23 coverage-95239-1771338200538-0.json -rw------- 1 coderaiser staff 1.2M Feb 17 16:23 coverage-95240-1771338200803-0.json -rw------- 1 coderaiser staff 1.2M Feb 17 16:23 coverage-95241-1771338201056-0.json COMMAND_BLOCK: export const ignore = (type, {name, property, list}) => { const [, collector] = type.split(/[()]/); return { report: createReport(name), match: createMatch({ type, property, collector, list, }), replace: createReplace({ type, property, collector, list, }), }; }; const createMatch = ({type, property, collector, list}) => ({options}) => { const {dismiss = []} = options; const newNames = filterNames(list, dismiss); return { [type]: (vars) => { const elements = parseElements(vars, { property, collector, }); if (!elements) return false; const list = elements.map(getValue); for (const name of newNames) { if (!list.includes(name)) return true; } return false; }, }; }; const createReplace = ({type, property, collector, list}) => ({options}) => { const {dismiss = []} = options; const newNames = filterNames(list, dismiss); return { [type]: (vars, path) => { const elements = parseElements(vars, { property, collector, }); const list = elements.map(getValue); for (const name of newNames) { if (!list.includes(name)) elements.push(stringLiteral(name)); } return path; }, }; }; Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: export const ignore = (type, {name, property, list}) => { const [, collector] = type.split(/[()]/); return { report: createReport(name), match: createMatch({ type, property, collector, list, }), replace: createReplace({ type, property, collector, list, }), }; }; const createMatch = ({type, property, collector, list}) => ({options}) => { const {dismiss = []} = options; const newNames = filterNames(list, dismiss); return { [type]: (vars) => { const elements = parseElements(vars, { property, collector, }); if (!elements) return false; const list = elements.map(getValue); for (const name of newNames) { if (!list.includes(name)) return true; } return false; }, }; }; const createReplace = ({type, property, collector, list}) => ({options}) => { const {dismiss = []} = options; const newNames = filterNames(list, dismiss); return { [type]: (vars, path) => { const elements = parseElements(vars, { property, collector, }); const list = elements.map(getValue); for (const name of newNames) { if (!list.includes(name)) elements.push(stringLiteral(name)); } return path; }, }; }; COMMAND_BLOCK: export const ignore = (type, {name, property, list}) => { const [, collector] = type.split(/[()]/); return { report: createReport(name), match: createMatch({ type, property, collector, list, }), replace: createReplace({ type, property, collector, list, }), }; }; const createMatch = ({type, property, collector, list}) => ({options}) => { const {dismiss = []} = options; const newNames = filterNames(list, dismiss); return { [type]: (vars) => { const elements = parseElements(vars, { property, collector, }); if (!elements) return false; const list = elements.map(getValue); for (const name of newNames) { if (!list.includes(name)) return true; } return false; }, }; }; const createReplace = ({type, property, collector, list}) => ({options}) => { const {dismiss = []} = options; const newNames = filterNames(list, dismiss); return { [type]: (vars, path) => { const elements = parseElements(vars, { property, collector, }); const list = elements.map(getValue); for (const name of newNames) { if (!list.includes(name)) elements.push(stringLiteral(name)); } return path; }, }; }; COMMAND_BLOCK: const difference = (a, b) => new Set(a).difference(new Set(b)); export const ignore = ({name, property, list, type = __ignore}) => ({ report: createReport(name), fix, traverse: createTraverse({ type, property, list, }), }); const addQuotes = (a) => `'${a}'`; const createReport = (filename) => ({name, matchedElements}) => { let insteadOf = ''; if (matchedElements.length) { const replacedNames = matchedElements.map(getValue); const namesLine = replacedNames .map(addQuotes) .join(', '); insteadOf = ` instead of ${namesLine}`; } return `Add '${name}'${insteadOf} to '${filename}'`; }; export const fix = ({path, name, matchedElements}) => { path.node.elements.push(stringLiteral(name)); matchedElements.map(remove); }; const createTraverse = ({type, property, list}) => ({push, options}) => { const {dismiss = []} = options; const newNames = filterNames(list, dismiss); if (!newNames.length) return {}; return { [type]: (path) => { const [parentOfElements, elements] = parseElements(path, { property, }); if (!parentOfElements) return; const list = elements.map(getValue); for (const name of difference(newNames, list)) { const match = picomatch(name); const matchedElements = []; for (const current of elements.filter(exists)) { const {value} = current.node; if (match(value)) matchedElements.push(current); } push({ path: parentOfElements, matchedElements, elements, name, }); } }, }; }; Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: const difference = (a, b) => new Set(a).difference(new Set(b)); export const ignore = ({name, property, list, type = __ignore}) => ({ report: createReport(name), fix, traverse: createTraverse({ type, property, list, }), }); const addQuotes = (a) => `'${a}'`; const createReport = (filename) => ({name, matchedElements}) => { let insteadOf = ''; if (matchedElements.length) { const replacedNames = matchedElements.map(getValue); const namesLine = replacedNames .map(addQuotes) .join(', '); insteadOf = ` instead of ${namesLine}`; } return `Add '${name}'${insteadOf} to '${filename}'`; }; export const fix = ({path, name, matchedElements}) => { path.node.elements.push(stringLiteral(name)); matchedElements.map(remove); }; const createTraverse = ({type, property, list}) => ({push, options}) => { const {dismiss = []} = options; const newNames = filterNames(list, dismiss); if (!newNames.length) return {}; return { [type]: (path) => { const [parentOfElements, elements] = parseElements(path, { property, }); if (!parentOfElements) return; const list = elements.map(getValue); for (const name of difference(newNames, list)) { const match = picomatch(name); const matchedElements = []; for (const current of elements.filter(exists)) { const {value} = current.node; if (match(value)) matchedElements.push(current); } push({ path: parentOfElements, matchedElements, elements, name, }); } }, }; }; COMMAND_BLOCK: const difference = (a, b) => new Set(a).difference(new Set(b)); export const ignore = ({name, property, list, type = __ignore}) => ({ report: createReport(name), fix, traverse: createTraverse({ type, property, list, }), }); const addQuotes = (a) => `'${a}'`; const createReport = (filename) => ({name, matchedElements}) => { let insteadOf = ''; if (matchedElements.length) { const replacedNames = matchedElements.map(getValue); const namesLine = replacedNames .map(addQuotes) .join(', '); insteadOf = ` instead of ${namesLine}`; } return `Add '${name}'${insteadOf} to '${filename}'`; }; export const fix = ({path, name, matchedElements}) => { path.node.elements.push(stringLiteral(name)); matchedElements.map(remove); }; const createTraverse = ({type, property, list}) => ({push, options}) => { const {dismiss = []} = options; const newNames = filterNames(list, dismiss); if (!newNames.length) return {}; return { [type]: (path) => { const [parentOfElements, elements] = parseElements(path, { property, }); if (!parentOfElements) return; const list = elements.map(getValue); for (const name of difference(newNames, list)) { const match = picomatch(name); const matchedElements = []; for (const current of elements.filter(exists)) { const {value} = current.node; if (match(value)) matchedElements.push(current); } push({ path: parentOfElements, matchedElements, elements, name, }); } }, }; }; COMMAND_BLOCK: export const report = () => 'Add file'; export const fix = (file) => { writeFileContent(file, 'hello'); }; export const scan = (rootPath, {push, trackFile}) => { for (const file of trackFile(rootPath, 'hello.txt')) { findFile(rootPath, 'again and again'); } }; Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: export const report = () => 'Add file'; export const fix = (file) => { writeFileContent(file, 'hello'); }; export const scan = (rootPath, {push, trackFile}) => { for (const file of trackFile(rootPath, 'hello.txt')) { findFile(rootPath, 'again and again'); } }; COMMAND_BLOCK: export const report = () => 'Add file'; export const fix = (file) => { writeFileContent(file, 'hello'); }; export const scan = (rootPath, {push, trackFile}) => { for (const file of trackFile(rootPath, 'hello.txt')) { findFile(rootPath, 'again and again'); } }; COMMAND_BLOCK: export const report = () => 'Add file'; export const fix = (file) => { writeFileContent(file, 'hello'); }; export const scan = (rootPath, {push, trackFile}) => { const crawled = crawlDirectory(rootPath); for (const file of trackFile(rootPath, 'hello.txt')) { findFile(rootPath, 'again and again', { crawled, }); } }; Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: export const report = () => 'Add file'; export const fix = (file) => { writeFileContent(file, 'hello'); }; export const scan = (rootPath, {push, trackFile}) => { const crawled = crawlDirectory(rootPath); for (const file of trackFile(rootPath, 'hello.txt')) { findFile(rootPath, 'again and again', { crawled, }); } }; COMMAND_BLOCK: export const report = () => 'Add file'; export const fix = (file) => { writeFileContent(file, 'hello'); }; export const scan = (rootPath, {push, trackFile}) => { const crawled = crawlDirectory(rootPath); for (const file of trackFile(rootPath, 'hello.txt')) { findFile(rootPath, 'again and again', { crawled, }); } }; COMMAND_BLOCK: export const report = () => 'Add file'; export const fix = (file) => { writeFileContent(file, 'hello'); }; export const scan = (rootPath, {push, trackFile, crawlFile}) => { for (const file of trackFile(rootPath, 'hello.txt')) { const files = crawlFile(rootPath, 'no matter how many times'); } }; Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: export const report = () => 'Add file'; export const fix = (file) => { writeFileContent(file, 'hello'); }; export const scan = (rootPath, {push, trackFile, crawlFile}) => { for (const file of trackFile(rootPath, 'hello.txt')) { const files = crawlFile(rootPath, 'no matter how many times'); } }; COMMAND_BLOCK: export const report = () => 'Add file'; export const fix = (file) => { writeFileContent(file, 'hello'); }; export const scan = (rootPath, {push, trackFile, crawlFile}) => { for (const file of trackFile(rootPath, 'hello.txt')) { const files = crawlFile(rootPath, 'no matter how many times'); } }; CODE_BLOCK: import {operator} from 'putout'; const {removeFiles} = operator; export const { report, fix, scan, } = removeFiles(['.DS_Store']); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: import {operator} from 'putout'; const {removeFiles} = operator; export const { report, fix, scan, } = removeFiles(['.DS_Store']); CODE_BLOCK: import {operator} from 'putout'; const {removeFiles} = operator; export const { report, fix, scan, } = removeFiles(['.DS_Store']); CODE_BLOCK: { "match": { ".filesystem.json": { "filesystem/remove-files": ["on", { "names": ["coverage"] }], } } } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: { "match": { ".filesystem.json": { "filesystem/remove-files": ["on", { "names": ["coverage"] }], } } } CODE_BLOCK: { "match": { ".filesystem.json": { "filesystem/remove-files": ["on", { "names": ["coverage"] }], } } } CODE_BLOCK: { "rules": { - "filesystem/remove-nyc-output": "off" + "coverage/remove-files": "off" } } } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: { "rules": { - "filesystem/remove-nyc-output": "off" + "coverage/remove-files": "off" } } } CODE_BLOCK: { "rules": { - "filesystem/remove-nyc-output": "off" + "coverage/remove-files": "off" } } } CODE_BLOCK: import {operator} from 'putout'; const {renameProperties} = operator; export const { report, fix, traverse, } = renameProperties([ ['filesystem/remove-nyc-output', 'coverage/remove-files'], ]); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: import {operator} from 'putout'; const {renameProperties} = operator; export const { report, fix, traverse, } = renameProperties([ ['filesystem/remove-nyc-output', 'coverage/remove-files'], ]); CODE_BLOCK: import {operator} from 'putout'; const {renameProperties} = operator; export const { report, fix, traverse, } = renameProperties([ ['filesystem/remove-nyc-output', 'coverage/remove-files'], ]); CODE_BLOCK: node_modules *.swp yarn-error.log yarn.lock .idea .DS_Store deno.lock coverage .filesystem.json Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: node_modules *.swp yarn-error.log yarn.lock .idea .DS_Store deno.lock coverage .filesystem.json CODE_BLOCK: node_modules *.swp yarn-error.log yarn.lock .idea .DS_Store deno.lock coverage .filesystem.json CODE_BLOCK: .idea .filesystem.json .DS_Store *.swp yarn-error.log yarn.lock deno.lock node_modules coverage Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: .idea .filesystem.json .DS_Store *.swp yarn-error.log yarn.lock deno.lock node_modules coverage CODE_BLOCK: .idea .filesystem.json .DS_Store *.swp yarn-error.log yarn.lock deno.lock node_modules coverage CODE_BLOCK: import {operator} from 'putout'; const {sortIgnore} = operator; export const { report, fix, traverse, } = sortIgnore({ name: '.gitignore', }); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: import {operator} from 'putout'; const {sortIgnore} = operator; export const { report, fix, traverse, } = sortIgnore({ name: '.gitignore', }); CODE_BLOCK: import {operator} from 'putout'; const {sortIgnore} = operator; export const { report, fix, traverse, } = sortIgnore({ name: '.gitignore', }); CODE_BLOCK: { "ignore": [ "**/package-lock.json", "**/*.lock", "**/.git", "**/*.log", "**/node_modules" ] } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: { "ignore": [ "**/package-lock.json", "**/*.lock", "**/.git", "**/*.log", "**/node_modules" ] } CODE_BLOCK: { "ignore": [ "**/package-lock.json", "**/*.lock", "**/.git", "**/*.log", "**/node_modules" ] } CODE_BLOCK: { "ignore": [ "**/*.lock", "**/*.log", "**/.git", "**/package-lock.json", "**/node_modules" ], } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: { "ignore": [ "**/*.lock", "**/*.log", "**/.git", "**/package-lock.json", "**/node_modules" ], } CODE_BLOCK: { "ignore": [ "**/*.lock", "**/*.log", "**/.git", "**/package-lock.json", "**/node_modules" ], } CODE_BLOCK: import {operator} from 'putout'; const {sortIgnore, __json} = operator; export const { report, fix, traverse, } = sortIgnore({ name: '.gitignore', type: __json, property: 'ignore', }); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: import {operator} from 'putout'; const {sortIgnore, __json} = operator; export const { report, fix, traverse, } = sortIgnore({ name: '.gitignore', type: __json, property: 'ignore', }); CODE_BLOCK: import {operator} from 'putout'; const {sortIgnore, __json} = operator; export const { report, fix, traverse, } = sortIgnore({ name: '.gitignore', type: __json, property: 'ignore', }); CODE_BLOCK: // mock modules worked in CommonJS, but not in ESM mockRequire('fs', { readFile, }); run(code); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: // mock modules worked in CommonJS, but not in ESM mockRequire('fs', { readFile, }); run(code); CODE_BLOCK: // mock modules worked in CommonJS, but not in ESM mockRequire('fs', { readFile, }); run(code); CODE_BLOCK: // use dependency injection instead run(code, { readFile }); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: // use dependency injection instead run(code, { readFile }); CODE_BLOCK: // use dependency injection instead run(code, { readFile }); COMMAND_BLOCK: // 'run' already declared so we cannot just use 'const run = () => {run();}' module.exports.run = () => { return run(); }; function run() { } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: // 'run' already declared so we cannot just use 'const run = () => {run();}' module.exports.run = () => { return run(); }; function run() { } COMMAND_BLOCK: // 'run' already declared so we cannot just use 'const run = () => {run();}' module.exports.run = () => { return run(); }; function run() { } COMMAND_BLOCK: // easy to migrate to `export const run = () => {return runAll();}`, since no overlap with names module.exports.run = () => { return runAll(); }; function runAll() { } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: // easy to migrate to `export const run = () => {return runAll();}`, since no overlap with names module.exports.run = () => { return runAll(); }; function runAll() { } COMMAND_BLOCK: // easy to migrate to `export const run = () => {return runAll();}`, since no overlap with names module.exports.run = () => { return runAll(); }; function runAll() { } COMMAND_BLOCK: // 'delete is reserved word, you cannot use it to name variable `export const delete = () => {}` module.exports.delete = () => {}; Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: // 'delete is reserved word, you cannot use it to name variable `export const delete = () => {}` module.exports.delete = () => {}; COMMAND_BLOCK: // 'delete is reserved word, you cannot use it to name variable `export const delete = () => {}` module.exports.delete = () => {}; COMMAND_BLOCK: // it would be 'export const remove = () => {}`, so no problem at all module.exports.remove = () => {} Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: // it would be 'export const remove = () => {}`, so no problem at all module.exports.remove = () => {} COMMAND_BLOCK: // it would be 'export const remove = () => {}`, so no problem at all module.exports.remove = () => {} - to filter things out; - to replace things up; - ✅ We have more control over report; - ✅ We have no duplication, all checks works only once in traverse and if it is OK - fix; - ✅ @putout/plugin-esm - ✅ @putout/plugin-nodejs - Change type of your package.json from commonjs to module; - Run putout --fix ., it will fix any code that uses require and module.exports to import and export; - Run redlint fix, it will resolve import names and change import {readFile} from './reader' to import {readFile} from './reader.js' with help of Scanners introduced in Putout v34;