eslint-plugin-arrow-return-style
Version:
Enforce arrow function return style and automatically fix it
274 lines (263 loc) • 10.7 kB
JavaScript
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
// package.json
var name = "eslint-plugin-arrow-return-style";
var version = "1.3.1";
// src/utils/create-rule.ts
import { ESLintUtils } from "@typescript-eslint/utils";
var createRule = ESLintUtils.RuleCreator((rule) => {
return `https://github.com/u3u/eslint-plugin-arrow-return-style/tree/v${version}/docs/rules/${rule}.md`;
});
// src/utils/define-config.ts
var defineConfig = (config) => {
return config;
};
// src/utils/define-plugin.ts
var definePlugin = (plugin) => {
return plugin;
};
// src/configs/recommended.ts
var recommended_default = defineConfig({
plugins: ["arrow-return-style"],
rules: {
"arrow-body-style": "off",
"arrow-return-style/arrow-return-style": "warn",
"arrow-return-style/no-export-default-arrow": "warn"
}
});
// src/rules/arrow-return-style.ts
import { AST_NODE_TYPES, AST_TOKEN_TYPES, ASTUtils } from "@typescript-eslint/utils";
var RULE_NAME = "arrow-return-style";
var arrowReturnStyleRule = createRule({
create: (context) => {
const sourceCode = context.getSourceCode();
return {
ArrowFunctionExpression: (arrowFunction) => {
const { body: arrowBody, parent: arrowFunctionParent } = arrowFunction;
const {
jsxAlwaysUseExplicitReturn,
maxLen = 80,
namedExportsAlwaysUseExplicitReturn = true
} = context.options?.[0] || {};
const isMaxLen = (node = getArrowRoot()) => getTokensLength(node) > maxLen;
const isMultiline = (node = arrowBody) => {
return node.loc.start.line !== node.loc.end.line;
};
const isObjectLiteral = (node = arrowBody) => {
return node.type === AST_NODE_TYPES.ObjectExpression;
};
const isJsxElement = (node = arrowBody) => {
return node.type === AST_NODE_TYPES.JSXElement || node.type === AST_NODE_TYPES.JSXFragment;
};
const isNamedExport = () => {
return arrowFunctionParent.parent?.parent?.type === AST_NODE_TYPES.ExportNamedDeclaration;
};
const isCallExpression = (node) => {
return node?.type === AST_NODE_TYPES.CallExpression;
};
const isVariableDeclaration = (node) => {
return node?.type === AST_NODE_TYPES.VariableDeclaration;
};
const getArrowVariableDeclaration = () => {
return isVariableDeclaration(arrowFunctionParent.parent) ? arrowFunctionParent.parent : void 0;
};
const getArrowRoot = () => {
return isCallExpression(arrowFunctionParent) ? arrowFunction : getArrowVariableDeclaration() || arrowFunctionParent;
};
const getLength = (node) => {
return node.loc.end.column - node.loc.start.column;
};
const getTokensLength = (node = getArrowRoot()) => {
const tokens = sourceCode.getTokens(node);
const implicitReturnTokens = tokens.filter(ASTUtils.isNotOpeningBraceToken).filter((x) => !(x.type === AST_TOKEN_TYPES.Keyword && x.value === "return")).filter(ASTUtils.isNotClosingBraceToken).filter(ASTUtils.isNotSemicolonToken);
const length = implicitReturnTokens.reduce((acc, token) => acc + getLength(token), 0);
return length;
};
const getArrowToken = () => {
const tokens = sourceCode.getTokens(arrowFunction);
const arrowToken = tokens.find(ASTUtils.isArrowToken);
return arrowToken;
};
const commentsExistBetweenArrowTokenAndArrowBody = () => {
const arrowToken = getArrowToken();
return arrowToken && sourceCode.commentsExistBetween(arrowToken, arrowBody);
};
if (arrowBody.type === AST_NODE_TYPES.BlockStatement) {
const blockBody = arrowBody.body;
if (blockBody.length === 1 && blockBody[0].type === AST_NODE_TYPES.ReturnStatement) {
const returnStatement = blockBody[0];
const returnValue = returnStatement.argument;
if (!returnValue) return;
if (isMaxLen()) return;
if (isMaxLen(returnValue)) return;
if (isMultiline(returnValue)) return;
if (jsxAlwaysUseExplicitReturn && isJsxElement(returnValue)) return;
if (namedExportsAlwaysUseExplicitReturn && isNamedExport()) return;
const openingBrace = sourceCode.getFirstToken(arrowBody);
const closingBrace = sourceCode.getLastToken(arrowBody);
const firstToken = sourceCode.getFirstToken(returnStatement, 1);
const lastToken = sourceCode.getLastToken(returnStatement);
const commentsExist = sourceCode.commentsExistBetween(openingBrace, firstToken) || sourceCode.commentsExistBetween(lastToken, closingBrace);
if (commentsExist) return;
context.report({
fix: (fixer) => {
const fixes = [];
const returnText = sourceCode.getText(returnValue);
fixes.push(
fixer.remove(openingBrace),
fixer.remove(closingBrace),
fixer.replaceText(returnStatement, isObjectLiteral(returnValue) ? `(${returnText})` : returnText)
);
return fixes;
},
messageId: "useImplicitReturn",
node: arrowFunction
});
}
} else {
const commentsExist = commentsExistBetweenArrowTokenAndArrowBody();
if (
//
commentsExist || isMaxLen() || isMultiline() || jsxAlwaysUseExplicitReturn && isJsxElement() || namedExportsAlwaysUseExplicitReturn && isNamedExport()
) {
context.report({
fix: (fixer) => {
const fixes = [];
const firstToken = sourceCode.getTokenBefore(arrowBody);
const lastToken = sourceCode.getTokenAfter(arrowBody);
if (firstToken && lastToken && ASTUtils.isOpeningParenToken(firstToken) && ASTUtils.isClosingParenToken(lastToken))
fixes.push(fixer.remove(firstToken), fixer.remove(lastToken));
if (commentsExist) {
const arrowToken = getArrowToken();
fixes.push(
fixer.insertTextAfter(arrowToken, " {"),
fixer.insertTextBefore(arrowBody, "return "),
fixer.insertTextAfter(arrowBody, "\n}")
);
} else {
fixes.push(fixer.replaceText(arrowBody, `{ return ${sourceCode.getText(arrowBody)} }`));
}
return fixes;
},
messageId: "useExplicitReturn",
node: arrowFunction
});
}
}
}
};
},
defaultOptions: [
{
jsxAlwaysUseExplicitReturn: false,
maxLen: 80,
namedExportsAlwaysUseExplicitReturn: true
}
],
meta: {
docs: {
description: "Enforce arrow function return style"
},
fixable: "code",
messages: {
useExplicitReturn: "Use explicit return for multiline arrow function bodies.",
useImplicitReturn: "Use implicit return for single-line arrow function bodies."
},
schema: [
{
additionalProperties: true,
properties: {
jsxAlwaysUseExplicitReturn: { default: false, type: "boolean" },
maxLen: { default: 80, type: "number" },
namedExportsAlwaysUseExplicitReturn: { default: true, type: "boolean" }
},
type: "object"
}
],
type: "suggestion"
},
name: RULE_NAME
});
// src/rules/no-export-default-arrow.ts
import path from "path";
import { AST_NODE_TYPES as AST_NODE_TYPES2 } from "@typescript-eslint/utils";
import { camelCase, pascalCase } from "scule";
var RULE_NAME2 = "no-export-default-arrow";
var noExportDefaultArrowRule = createRule({
create: (context) => {
const sourceCode = context.getSourceCode();
let program;
return {
ArrowFunctionExpression: (arrowFunction) => {
const { body: arrowBody, parent: arrowFunctionParent } = arrowFunction;
const isJsxElement = (node) => {
return node.type === AST_NODE_TYPES2.JSXElement || node.type === AST_NODE_TYPES2.JSXFragment;
};
const getArrowReturnValues = () => {
if (arrowBody.type === AST_NODE_TYPES2.BlockStatement) {
const blockBody = arrowBody.body;
const returnValues = blockBody.filter((node) => {
return node.type === AST_NODE_TYPES2.ReturnStatement;
}).map((node) => node.argument).filter(Boolean);
return returnValues;
}
return [arrowBody];
};
const arrowReturnIsJsxElement = () => {
const returnValues = getArrowReturnValues();
return returnValues.some((node) => isJsxElement(node));
};
if (arrowFunctionParent.type === AST_NODE_TYPES2.ExportDefaultDeclaration) {
context.report({
fix: (fixer) => {
const fixes = [];
const lastToken = sourceCode.getLastToken(program, { includeComments: true }) || arrowFunctionParent;
const fileName = context.getPhysicalFilename?.() || context.getFilename() || "namedFunction";
const { name: fileNameWithoutExtension } = path.parse(fileName);
const funcName = arrowReturnIsJsxElement() ? pascalCase(fileNameWithoutExtension) : camelCase(fileNameWithoutExtension);
fixes.push(
fixer.replaceText(arrowFunctionParent, `const ${funcName} = ${sourceCode.getText(arrowFunction)}`),
fixer.insertTextAfter(lastToken, `
export default ${funcName}`)
);
return fixes;
},
messageId: "disallowExportDefaultArrow",
node: arrowFunction
});
}
},
Program: (node) => program = node
};
},
defaultOptions: [],
meta: {
docs: {
description: "Disallow export default anonymous arrow function<br/>_**Automatically fix using the current file name.**_"
},
fixable: "code",
messages: {
disallowExportDefaultArrow: "Disallow export default anonymous arrow function"
},
schema: [],
type: "suggestion"
},
name: RULE_NAME2
});
// src/index.ts
var src_default = definePlugin({
configs: {
recommended: recommended_default
},
meta: {
name,
version
},
rules: {
[RULE_NAME]: arrowReturnStyleRule,
[RULE_NAME2]: noExportDefaultArrowRule
}
});
export {
src_default as default
};