UNPKG

eslint-plugin-vtex

Version:
353 lines (342 loc) 12.2 kB
var __getOwnPropNames = Object.getOwnPropertyNames; var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; // src/configs/recommended.ts var recommended; var init_recommended = __esm({ "src/configs/recommended.ts"() { "use strict"; recommended = { rules: { // Prefer an early return to prevent nesting and improve code readability. "vtex/prefer-early-return": [ "warn", { maxStatements: 2 } ], "vtex/consistent-props-type": "off" } }; } }); // src/utils/react/isComponentName.ts var isComponentName; var init_isComponentName = __esm({ "src/utils/react/isComponentName.ts"() { "use strict"; isComponentName = (name) => name.length > 0 && name[0] === name[0].toUpperCase(); } }); // src/createRule.ts import { ESLintUtils } from "@typescript-eslint/utils"; var createRule; var init_createRule = __esm({ "src/createRule.ts"() { "use strict"; createRule = ESLintUtils.RuleCreator( (name) => `https://github.com/vtex/typescript/tree/main/packages/eslint-plugin-vtex/docs/rules/${name}.md` ); } }); // src/utils/estree/isFunctionNode.ts var isFunctionNode; var init_isFunctionNode = __esm({ "src/utils/estree/isFunctionNode.ts"() { "use strict"; isFunctionNode = (node) => { return node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression" || node.type === "FunctionDeclaration"; }; } }); // src/utils/estree/isInsideAnotherFunction.ts var isInsideAnotherFunction; var init_isInsideAnotherFunction = __esm({ "src/utils/estree/isInsideAnotherFunction.ts"() { "use strict"; init_isFunctionNode(); isInsideAnotherFunction = (context) => { const functions = context.getAncestors().filter(isFunctionNode); return functions.length > 1; }; } }); // src/utils/estree/getFunctionNodeName.ts var getFunctionNodeName; var init_getFunctionNodeName = __esm({ "src/utils/estree/getFunctionNodeName.ts"() { "use strict"; getFunctionNodeName = (node) => { var _a, _b; if (node.type === "FunctionDeclaration") { return ((_a = node.id) == null ? void 0 : _a.name) ?? ""; } if (((_b = node.parent) == null ? void 0 : _b.type) === "VariableDeclarator" && node.parent.id.type === "Identifier") { return node.parent.id.name; } return ""; }; } }); // src/rules/consistent-props-type.ts var consistentPropsType; var init_consistent_props_type = __esm({ "src/rules/consistent-props-type.ts"() { "use strict"; init_isComponentName(); init_createRule(); init_isInsideAnotherFunction(); init_getFunctionNodeName(); init_isFunctionNode(); consistentPropsType = createRule({ name: "consistent-props-type", meta: { type: "suggestion", docs: { recommended: false, description: "Consistent way to define props type" }, messages: { invalidPropsTypeName: "Invalid props type name. Must be: {{expectedPropsTypeName}}", propsTypeIncomplete: "Invalid inline props type. {{expectedPropsTypeName}} must define all the props.", invalidInlinePropsType: "Invalid inline props type. You must to create an type {{expectedPropsTypeName}}", genericsPropsTypeName: "Invalid generics props type name. Must be: Props" }, schema: [] }, defaultOptions: [], create(context) { return { ":function > :matches(Identifier, ObjectPattern) > TSTypeAnnotation": function(node) { var _a, _b, _c; if (isInsideAnotherFunction(context)) return; const functionNode = context.getAncestors().reverse().find(isFunctionNode); const functionName = getFunctionNodeName(functionNode); const [typeParameter] = ((_a = functionNode.typeParameters) == null ? void 0 : _a.params) ?? []; if (!isComponentName(functionName)) return; const expectedPropsTypeName = `${functionName}Props`; if (typeParameter) { if (typeParameter.name.name !== "Props") { context.report({ node: typeParameter.name, messageId: "genericsPropsTypeName" }); } if (typeParameter.constraint && ((_b = typeParameter.constraint) == null ? void 0 : _b.type) !== "TSTypeReference") { context.report({ node: typeParameter.constraint, messageId: "propsTypeIncomplete", data: { expectedPropsTypeName } }); } if (((_c = typeParameter.constraint) == null ? void 0 : _c.type) === "TSTypeReference" && typeParameter.constraint.typeName.type === "Identifier" && typeParameter.constraint.typeName.name !== expectedPropsTypeName) { context.report({ node: typeParameter.constraint, messageId: "invalidPropsTypeName", data: { expectedPropsTypeName } }); } return; } if (node.typeAnnotation.type !== "TSTypeReference") { context.report({ node: node.typeAnnotation, messageId: "invalidInlinePropsType", data: { expectedPropsTypeName } }); return; } if (node.typeAnnotation.typeName.type === "Identifier" && node.typeAnnotation.typeName.name !== expectedPropsTypeName) { context.report({ node: node.typeAnnotation, messageId: "invalidPropsTypeName", data: { expectedPropsTypeName } }); } }, "VariableDeclarator > Identifier > TSTypeAnnotation": function(node) { if (isInsideAnotherFunction(context)) return; const expectedPropsTypeName = `${node.parent.name}Props`; if (node.typeAnnotation.type === "TSTypeReference" && node.typeAnnotation.typeName.type === "Identifier" && node.typeAnnotation.typeName.name === "FC" && node.typeAnnotation.typeParameters) { const [typeParameter] = node.typeAnnotation.typeParameters.params; if (typeParameter.type === "TSTypeReference" && typeParameter.typeName.type === "Identifier" && typeParameter.typeName.name !== expectedPropsTypeName) { context.report({ node: typeParameter, messageId: "invalidPropsTypeName", data: { expectedPropsTypeName } }); } if (typeParameter.type !== "TSTypeReference") { context.report({ node: typeParameter, messageId: "propsTypeIncomplete", data: { expectedPropsTypeName } }); } } } }; } }); } }); // src/rules/prefer-early-return.ts function isLonelyIfStatement(node) { return node.type === "IfStatement" && node.alternate == null; } var preferEarlyReturn; var init_prefer_early_return = __esm({ "src/rules/prefer-early-return.ts"() { "use strict"; init_createRule(); preferEarlyReturn = createRule({ name: "prefer-early-return", meta: { type: "layout", docs: { recommended: "error", description: "Prefer early returns over full-body conditional wrapping in function declarations." }, schema: [ { type: "object", properties: { maxStatements: { type: "integer" } }, additionalProperties: false } ], messages: { default: "Prefer an early return to prevent nesting and improve code readability" } }, defaultOptions: [{ maxStatements: 1 }], create(context) { const options = { ...context.options[0] }; const { maxStatements } = options; function isOffendingConsequent(consequentNode) { return consequentNode.type === "ExpressionStatement" && maxStatements === 0 || consequentNode.type === "BlockStatement" && consequentNode.body.length > maxStatements; } function isOffendingIfStatement(ifNode) { return isLonelyIfStatement(ifNode) && isOffendingConsequent(ifNode.consequent); } function checkFunctionBody(fnNode) { const bodyNode = fnNode.body; if (bodyNode.type !== "BlockStatement" || bodyNode.body.length === 0) { return; } const lastNode = bodyNode.body[bodyNode.body.length - 1]; if (!isOffendingIfStatement(lastNode)) { return; } context.report({ node: lastNode, messageId: "default" }); } return { FunctionDeclaration: checkFunctionBody, FunctionExpression: checkFunctionBody, ArrowFunctionExpression: checkFunctionBody }; } }); } }); // src/rules/prefer-use-effect-named-callback.ts function checkCallExpressionIsUseEffect(node) { if (node.type !== "CallExpression") { return false; } if (!node.callee) { return false; } if (node.callee.type === "Identifier" && node.callee.name === USE_EFFECT_NAME) { return true; } if (node.callee.type === "MemberExpression" && node.callee.property && node.callee.property.type === "Identifier" && node.callee.property.name === USE_EFFECT_NAME) { return true; } return false; } var USE_EFFECT_NAME, preferUseEffectNamedCallback; var init_prefer_use_effect_named_callback = __esm({ "src/rules/prefer-use-effect-named-callback.ts"() { "use strict"; init_createRule(); USE_EFFECT_NAME = "useEffect"; preferUseEffectNamedCallback = createRule({ name: "prefer-use-effect-named-callback", meta: { type: "layout", docs: { recommended: "error", description: "Prefer useEffect with named function or constant callbacks." }, messages: { default: "Prefer useEffect with named function or constant callbacks." }, schema: [] }, defaultOptions: [], create(context) { function checkUseEffectCallExpression(callExpressionNode) { if (!checkCallExpressionIsUseEffect(callExpressionNode)) { return; } const [useEffectFirstArgument] = callExpressionNode.arguments; let hasError = false; if (useEffectFirstArgument.type === "ArrowFunctionExpression" && useEffectFirstArgument.id === null) { hasError = true; } if (useEffectFirstArgument.type === "FunctionExpression" && useEffectFirstArgument.id === null) { hasError = true; } if (hasError) { context.report({ node: useEffectFirstArgument, messageId: "default" }); } } return { CallExpression: checkUseEffectCallExpression }; } }); } }); // src/index.ts var require_src = __commonJS({ "src/index.ts"(exports, module) { init_recommended(); init_consistent_props_type(); init_prefer_early_return(); init_prefer_use_effect_named_callback(); module.exports = { configs: { recommended }, // TODO: these rules could be auto-generated using fs+path, // but tsdx doesn't works well with dynamic imports, // so we need to change our build system first rules: { "consistent-props-type": consistentPropsType, "prefer-early-return": preferEarlyReturn, "prefer-use-effect-named-callback": preferUseEffectNamedCallback } }; } }); export default require_src();