Socket
Socket
Sign inDemoInstall

eslint-plugin-grules

Package Overview
Dependencies
164
Maintainers
1
Versions
23
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.0.14 to 0.0.15

.eslintrc.json

10

package.json

@@ -5,3 +5,3 @@ {

"license": "MIT",
"version": "0.0.14",
"version": "0.0.15",
"type": "commonjs",

@@ -12,3 +12,6 @@ "main": "./src/index.js",

},
"scripts": {},
"scripts": {
"test": "eslint .",
"test:fix": "eslint --fix ."
},
"dependencies": {

@@ -23,2 +26,5 @@ "eslint": "^8.54.0",

},
"devDependencies": {
"eslint-plugin-grules": "^0.0.14"
},
"repository": {

@@ -25,0 +31,0 @@ "type": "git",

@@ -47,7 +47,5 @@ module.exports = {

// Conventions that might as well be considered language features
"no-duplicate-imports": "error",
"no-extend-native": "error",
"no-new-native-nonconstructor": "error",
"no-new-wrappers": "error",
"no-unmodified-loop-condition": "error",
"no-unreachable-loop": "error",
"no-unused-private-class-members": "error",

@@ -68,6 +66,6 @@ "class-methods-use-this": "error",

"no-constant-binary-expression": "error",
"no-duplicate-imports": "error",
"no-else-return": ["error", { allowElseIf: false }],
"no-empty": ["error", { allowEmptyCatch: true }],
"no-empty-static-block": "error",
"no-extend-native": "error",
"no-extra-bind": "error",

@@ -80,2 +78,4 @@ "no-array-constructor": "error",

"no-unneeded-ternary": "error",
"no-unmodified-loop-condition": "error",
"no-unreachable-loop": "error",
"no-useless-call": "error",

@@ -114,3 +114,2 @@ "no-useless-computed-key": "error",

"grules/prefer-index-access": "error",
"grules/prefer-string-length-comparison": "error",

@@ -186,4 +185,3 @@ // Unicorn conventions

"prefer-index-access": require("./rules/prefer-index-access.js"),
"prefer-string-length-comparison": require("./rules/prefer-string-length-comparison.js"),
},
};

@@ -5,5 +5,5 @@ module.exports = {

},
create: function (context) {
create: (context) => {
return {
CallExpression(node) {
CallExpression: (node) => {
if (

@@ -16,12 +16,8 @@ node.callee.type === "MemberExpression" &&

.getText(node.callee.object);
const argument = node.arguments[0];
let replacement;
const [argument] = node.arguments;
if (argument && argument.type === "Literal") {
replacement = `${objectText}[${argument.raw}]`;
} else {
replacement = `${objectText}[${context
.getSourceCode()
.getText(argument)}]`;
}
const replacement =
argument && argument.type === "Literal"
? `${objectText}[${argument.raw}]`
: `${objectText}[${context.getSourceCode().getText(argument)}]`;

@@ -31,3 +27,3 @@ context.report({

message: "Use bracket notation instead of .charAt()",
fix(fixer) {
fix: (fixer) => {
return fixer.replaceText(node, replacement);

@@ -34,0 +30,0 @@ },

400

src/rules/prefer-arrow-functions.js

@@ -30,39 +30,4 @@ /**

module.exports = {
meta: {
docs: {
description: "prefer arrow functions",
category: "emcascript6",
recommended: false,
},
fixable: "code",
schema: [
{
type: "object",
properties: {
disallowPrototype: {
type: "boolean",
},
singleReturnOnly: {
type: "boolean",
},
classPropertiesAllowed: {
type: "boolean",
},
allowStandaloneDeclarations: {
type: "boolean",
},
},
additionalProperties: false,
},
],
},
create: (context) => ({
"FunctionDeclaration:exit": (node) => inspectNode(node, context),
"FunctionExpression:exit": (node) => inspectNode(node, context),
}),
};
const isPrototypeAssignment = (node) => {
let parent = node.parent;
let { parent } = node;

@@ -72,4 +37,5 @@ while (parent) {

case "MemberExpression":
if (parent.property && parent.property.name === "prototype")
if (parent.property && parent.property.name === "prototype") {
return true;
}
parent = parent.object;

@@ -82,3 +48,3 @@ break;

case "ObjectExpression":
parent = parent.parent;
({ parent } = parent.parent);
break;

@@ -94,3 +60,3 @@ default:

const isConstructor = (node) => {
let parent = node.parent;
const { parent } = node;
return parent && parent.kind === "constructor";

@@ -100,10 +66,19 @@ };

const containsThis = (node) => {
if (typeof node !== "object" || node === null) return false;
if (node.type === "FunctionDeclaration") return false;
if (node.type === "FunctionExpression") return false;
if (node.type === "ThisExpression") return true;
if (typeof node !== "object" || node === null) {
return false;
}
if (node.type === "FunctionDeclaration") {
return false;
}
if (node.type === "FunctionExpression") {
return false;
}
if (node.type === "ThisExpression") {
return true;
}
return Object.keys(node).some((field) => {
if (field === "parent") {
return false;
} else if (Array.isArray(node[field])) {
}
if (Array.isArray(node[field])) {
return node[field].some(containsThis);

@@ -115,113 +90,67 @@ }

const isNamed = (node) =>
node.type === "FunctionDeclaration" && node.id && node.id.name;
const isNamed = (node) => {
return node.type === "FunctionDeclaration" && node.id && node.id.name;
};
const functionOnlyContainsReturnStatement = (node) =>
node.body.body.length === 1 && node.body.body[0].type === "ReturnStatement";
const functionOnlyContainsReturnStatement = (node) => {
return (
node.body.body.length === 1 && node.body.body[0].type === "ReturnStatement"
);
};
const isNamedDefaultExport = (node) =>
node.id && node.id.name && node.parent.type === "ExportDefaultDeclaration";
const isNamedDefaultExport = (node) => {
return (
node.id && node.id.name && node.parent.type === "ExportDefaultDeclaration"
);
};
const isClassMethod = (node) => node.parent.type === "MethodDefinition";
const isClassMethod = (node) => {
return node.parent.type === "MethodDefinition";
};
const isGeneratorFunction = (node) => node.generator === true;
const isGeneratorFunction = (node) => {
return node.generator === true;
};
const isGetterOrSetter = (node) =>
node.parent.kind === "set" || node.parent.kind === "get";
const isGetterOrSetter = (node) => {
return node.parent.kind === "set" || node.parent.kind === "get";
};
const isCommonJSModuleProp = (node, name = "module") =>
node &&
node.type === "MemberExpression" &&
node.object &&
node.object.type === "Identifier" &&
node.object.name === name;
const isCommonJSModuleProp = (node, name = "module") => {
return (
node &&
node.type === "MemberExpression" &&
node.object &&
node.object.type === "Identifier" &&
node.object.name === name
);
};
const isModuleExport = (node) =>
node.parent.type === "AssignmentExpression" &&
(isCommonJSModuleProp(node.parent.left) ||
isCommonJSModuleProp(node.parent.left, "exports") ||
isCommonJSModuleProp(node.parent.left.object));
const isModuleExport = (node) => {
return (
node.parent.type === "AssignmentExpression" &&
(isCommonJSModuleProp(node.parent.left) ||
isCommonJSModuleProp(node.parent.left, "exports") ||
isCommonJSModuleProp(node.parent.left.object))
);
};
const isStandaloneDeclaration = (node) =>
node.type === "FunctionDeclaration" &&
(!node.parent ||
node.parent.type === "Program" ||
node.parent.type === "ExportNamedDeclaration" ||
node.parent.type === "ExportDefaultDeclaration");
const isStandaloneDeclaration = (node) => {
return (
node.type === "FunctionDeclaration" &&
(!node.parent ||
node.parent.type === "Program" ||
node.parent.type === "ExportNamedDeclaration" ||
node.parent.type === "ExportDefaultDeclaration")
);
};
const inspectNode = (node, context) => {
const opts = context.options[0] || {};
const tokenStart = (token) => {
return token.start === undefined ? token.range[0] : token.start;
};
if (isConstructor(node)) return;
if (
!isClassMethod(node) &&
(containsThis(node.params) || containsThis(node.body))
)
return;
if (isGeneratorFunction(node)) return;
if (isGetterOrSetter(node)) return;
if (isClassMethod(node) && !opts.classPropertiesAllowed) return;
if (
opts.allowStandaloneDeclarations &&
(isStandaloneDeclaration(node) || isModuleExport(node))
)
return;
if (opts.singleReturnOnly) {
if (
functionOnlyContainsReturnStatement(node) &&
!isNamedDefaultExport(node) &&
(opts.classPropertiesAllowed || !isClassMethod(node))
)
return context.report({
node,
message:
"Prefer using arrow functions over plain functions which only return a value",
fix(fixer) {
const src = context.getSourceCode();
let newText = null;
if (node.type === "FunctionDeclaration") {
newText = fixFunctionDeclaration(src, node);
} else if (node.type === "FunctionExpression") {
newText = fixFunctionExpression(src, node);
// In the case of an async method definition, we remove the "async" prefix
if (node.async && node.parent.type === "MethodDefinition") {
const parentTokens = src.getTokens(node.parent);
const asyncToken = parentTokens.find(
tokenMatcher("Identifier", "async")
);
const nextToken = parentTokens.find(
(_, i, arr) => arr[i - 1] && arr[i - 1] === asyncToken
);
return [
fixer.replaceText(node, newText),
fixer.replaceTextRange(
[tokenStart(asyncToken), tokenStart(nextToken)],
""
),
];
}
}
if (newText !== null) {
return fixer.replaceText(node, newText);
}
},
});
} else if (opts.disallowPrototype || !isPrototypeAssignment(node)) {
return context.report(
node,
isNamed(node)
? "Use const or class constructors instead of named functions"
: "Prefer using arrow functions over plain functions"
);
}
const tokenEnd = (token) => {
return token.end === undefined ? token.range[1] : token.end;
};
const tokenStart = (token) =>
token.start === undefined ? token.range[0] : token.start;
const tokenEnd = (token) =>
token.end === undefined ? token.range[1] : token.end;
const replaceTokens = (origSource, tokens, replacements) => {

@@ -234,5 +163,5 @@ let removeNextLeadingSpace = false;

if (lastTokenEnd >= 0) {
let between = origSource.substring(lastTokenEnd, tokenStart(token));
let between = origSource.slice(lastTokenEnd, tokenStart(token));
if (removeNextLeadingSpace) {
between = between.replace(/^\s+/, "");
between = between.replace(/^\s+/u, "");
}

@@ -245,8 +174,8 @@ result += between;

if (replaceInfo[2]) {
result = result.replace(/\s+$/, "");
result = result.replace(/\s+$/u, "");
}
result += replaceInfo[0];
removeNextLeadingSpace = !!replaceInfo[1];
removeNextLeadingSpace = Boolean(replaceInfo[1]);
} else {
result += origSource.substring(tokenStart(token), tokenEnd(token));
result += origSource.slice(tokenStart(token), tokenEnd(token));
}

@@ -258,7 +187,10 @@ lastTokenEnd = tokenEnd(token);

const tokenMatcher =
(type, value = undefined) =>
(token) =>
token.type === type &&
(typeof value === "undefined" || token.value === value);
const tokenMatcher = (type, value) => {
return (token) => {
return (
token.type === type &&
(typeof value === "undefined" || token.value === value)
);
};
};

@@ -270,3 +202,3 @@ const fixFunctionExpression = (src, node) => {

let swap = {};
const swap = {};
const fnKeyword = tokens.find(tokenMatcher("Keyword", "function"));

@@ -306,11 +238,12 @@ let prefix = "";

const returnRange = node.body.body.find(
(n) => n.type === "ReturnStatement"
).range;
const semicolon = bodyTokens.find(
(t) =>
tokenEnd(t) == returnRange[1] &&
const returnRange = node.body.body.find((n) => {
return n.type === "ReturnStatement";
}).range;
const semicolon = bodyTokens.find((t) => {
return (
tokenEnd(t) === returnRange[1] &&
t.value === ";" &&
t.type === "Punctuator"
);
);
});
if (semicolon) {

@@ -325,3 +258,3 @@ swap[tokenStart(semicolon)] = [parens ? ")" : "", true];

prefix +
replaceTokens(orig, tokens, swap).replace(/ $/, "") +
replaceTokens(orig, tokens, swap).replace(/ $/u, "") +
(parens && !semicolon ? ")" : "") +

@@ -336,3 +269,3 @@ suffix

const bodyTokens = src.getTokens(node.body);
let swap = {};
const swap = {};
const asyncKeyword = node.async ? "async " : "";

@@ -358,3 +291,3 @@ const omitVar =

const functionKeywordToken = tokens.find(
tokenMatcher("Keyword", "function")
tokenMatcher("Keyword", "function"),
);

@@ -375,11 +308,12 @@ const nameToken = src.getTokenAfter(functionKeywordToken);

const returnRange = node.body.body.find(
(n) => n.type === "ReturnStatement"
).range;
const semicolon = bodyTokens.find(
(t) =>
tokenEnd(t) == returnRange[1] &&
const returnRange = node.body.body.find((n) => {
return n.type === "ReturnStatement";
}).range;
const semicolon = bodyTokens.find((t) => {
return (
tokenEnd(t) === returnRange[1] &&
t.value === ";" &&
t.type === "Punctuator"
);
);
});
if (semicolon) {

@@ -393,5 +327,127 @@ swap[tokenStart(semicolon)] = [parens ? ")" : "", true];

return (
replaceTokens(orig, tokens, swap).replace(/ $/, "") +
replaceTokens(orig, tokens, swap).replace(/ $/u, "") +
(parens && !semicolon ? ");" : ";")
);
};
const inspectNode = (node, context) => {
const opts = context.options[0] || {};
if (isConstructor(node)) {
return;
}
if (
!isClassMethod(node) &&
(containsThis(node.params) || containsThis(node.body))
) {
return;
}
if (isGeneratorFunction(node)) {
return;
}
if (isGetterOrSetter(node)) {
return;
}
if (isClassMethod(node) && !opts.classPropertiesAllowed) {
return;
}
if (
opts.allowStandaloneDeclarations &&
(isStandaloneDeclaration(node) || isModuleExport(node))
) {
return;
}
if (opts.singleReturnOnly) {
if (
functionOnlyContainsReturnStatement(node) &&
!isNamedDefaultExport(node) &&
(opts.classPropertiesAllowed || !isClassMethod(node))
) {
return context.report({
node,
message:
"Prefer using arrow functions over plain functions which only return a value",
fix: (fixer) => {
const src = context.getSourceCode();
let newText = null;
if (node.type === "FunctionDeclaration") {
newText = fixFunctionDeclaration(src, node);
} else if (node.type === "FunctionExpression") {
newText = fixFunctionExpression(src, node);
// In the case of an async method definition, we remove the "async" prefix
if (node.async && node.parent.type === "MethodDefinition") {
const parentTokens = src.getTokens(node.parent);
const asyncToken = parentTokens.find(
tokenMatcher("Identifier", "async"),
);
const nextToken = parentTokens.find((_, i, arr) => {
return arr[i - 1] && arr[i - 1] === asyncToken;
});
return [
fixer.replaceText(node, newText),
fixer.replaceTextRange(
[tokenStart(asyncToken), tokenStart(nextToken)],
"",
),
];
}
}
if (newText !== null) {
return fixer.replaceText(node, newText);
}
},
});
}
} else if (opts.disallowPrototype || !isPrototypeAssignment(node)) {
return context.report(
node,
isNamed(node)
? "Use const or class constructors instead of named functions"
: "Prefer using arrow functions over plain functions",
);
}
};
module.exports = {
meta: {
docs: {
description: "prefer arrow functions",
category: "emcascript6",
recommended: false,
},
fixable: "code",
schema: [
{
type: "object",
properties: {
disallowPrototype: {
type: "boolean",
},
singleReturnOnly: {
type: "boolean",
},
classPropertiesAllowed: {
type: "boolean",
},
allowStandaloneDeclarations: {
type: "boolean",
},
},
additionalProperties: false,
},
],
},
create: (context) => {
return {
"FunctionDeclaration:exit": (node) => {
return inspectNode(node, context);
},
"FunctionExpression:exit": (node) => {
return inspectNode(node, context);
},
};
},
};

@@ -1,2 +0,11 @@

const comparisonOperators = ["==", "===", "!=", "!==", ">", "<", ">=", "<="];
const comparisonOperators = new Set([
"==",
"===",
"!=",
"!==",
">",
"<",
">=",
"<=",
]);

@@ -10,3 +19,3 @@ const enforceExplicitComparisonRecursively = (context, node) => {

node.type === "BinaryExpression" &&
comparisonOperators.includes(node.operator) === true
comparisonOperators.has(node.operator) === true
)

@@ -13,0 +22,0 @@ ) {

@@ -12,3 +12,3 @@ module.exports = {

message: "Use '++' instead of '+= 1'",
fix: function (fixer) {
fix: (fixer) => {
return fixer.replaceText(node, `++${node.left.name}`);

@@ -15,0 +15,0 @@ },

@@ -5,5 +5,5 @@ module.exports = {

},
create: function (context) {
create: (context) => {
return {
CallExpression(node) {
CallExpression: (node) => {
if (

@@ -16,6 +16,6 @@ node.callee.type === "MemberExpression" &&

const objectText = context
.getSourceCode()
.getText(node.callee.object),
argument = node.arguments[0];
let value;
.getSourceCode()
.getText(node.callee.object);
const [argument] = node.arguments;
let val;

@@ -26,6 +26,6 @@ if (

) {
value =
val =
argument.operator === "-"
? -argument.argument.value
: +argument.argument.value;
: Number(argument.argument.value);
} else if (

@@ -35,18 +35,16 @@ argument.type === "Literal" &&

) {
value = argument.value;
val = argument.value;
}
let replacement;
if (value === undefined) {
if (val === undefined) {
replacement = `${objectText}[${context
.getSourceCode()
.getText(argument)}]`;
} else if (val >= 0) {
replacement = `${objectText}[${val}]`;
} else {
if (value >= 0) {
replacement = `${objectText}[${value}]`;
} else {
replacement = `${objectText}[${objectText}.length - ${Math.abs(
value
)}]`;
}
replacement = `${objectText}[${objectText}.length - ${Math.abs(
val,
)}]`;
}

@@ -57,3 +55,3 @@

message: "Use array indexing instead of .at()",
fix(fixer) {
fix: (fixer) => {
return fixer.replaceText(node, replacement);

@@ -60,0 +58,0 @@ },

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc