UNPKG

@putout/operator-add-args

Version:

🐊Putout operator adds ability to add args to referenced variables that was not defined

172 lines (133 loc) 4.3 kB
import {compareAny} from '@putout/compare'; import {template} from '@putout/engine-parser'; import {types} from '@putout/babel'; const isString = (a) => typeof a === 'string'; const {isArray} = Array; const { isBlockStatement, isFunction, isLabeledStatement, isObjectPattern, isCallExpression, isSequenceExpression, objectPattern, objectProperty, } = types; const {entries} = Object; const COMPUTED = true; const SHORTHAND = true; const isCallOrStatement = (path) => path.isCallExpression() || path.isStatement(); const isCall = (path) => { return isCallExpression(path.find(isCallOrStatement)); }; const report = ({name}) => `Argument '${name}' is missing`; export const addArgs = (args) => ({ report, fix, traverse: traverse(args), }); const fix = ({declaration, pattern, params, index}) => { const declarationNode = template.ast.fresh(declaration); if (isSequenceExpression(declarationNode)) { const {expressions} = declarationNode; params.push(...expressions.slice(params.length)); return; } if (isBlockStatement(declarationNode)) { const prop = createProperty(declarationNode.body[0]); pattern.properties.push(prop); if (!isObjectPattern(params[index])) return params.push(pattern); return; } params.push(declarationNode); }; const traverse = (args) => ({push, options}) => { const allArgs = { ...options.args, ...args, }; return { ReferencedIdentifier(path) { for (const [name, config] of entries(allArgs)) { const [declaration, type, include, exclude] = parseConfig(config); if (path.node.name !== name) continue; if (path.scope.hasBinding(name)) continue; if (type === 'call' && !isCall(path)) continue; const fnPath = path.find(isFunction); if (!fnPath) continue; if (!compareAny(path.scope.path, include)) continue; if (compareAny(path.scope.path, exclude)) continue; const {params} = fnPath.node; const [index, lastParam] = getObjectPattern(params); if (isObjectPattern(lastParam)) { push({ name, index, declaration, path, params, pattern: lastParam, }); return; } if (params.length >= 3) return; push({ name, index, declaration, path, params, pattern: objectPattern([]), }); } }, }; }; function createProperty(node) { if (!isLabeledStatement(node)) { const {expression} = node; return objectProperty(expression, expression, !COMPUTED, SHORTHAND); } const {label, body} = node; return objectProperty(label, body.expression, !COMPUTED, SHORTHAND); } function getObjectPattern(params) { for (const [i, param] of params.entries()) { if (isObjectPattern(param)) return [i, param]; } return [ -1, null, ]; } const ALL_TYPES = ''; function parseConfig(config) { const [declaration, patternsInclude, patternsExclude] = config; if (isArray(patternsInclude) || isString(patternsInclude)) return [ declaration, ALL_TYPES, patternsInclude, patternsExclude, ]; const { type = ALL_TYPES, include, exclude, } = patternsInclude; return [ declaration, type, include, exclude, ]; }