mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-15 08:22:43 +00:00
Custom eslint plugin
This commit is contained in:
@@ -1,13 +1,14 @@
|
|||||||
import react from "eslint-plugin-react";
|
import react from "eslint-plugin-react";
|
||||||
import jest from "eslint-plugin-jest";
|
import jest from "eslint-plugin-jest";
|
||||||
import globals from "globals";
|
import globals from "globals";
|
||||||
|
import localPlugin from "./eslint_plugins/index.js";
|
||||||
|
|
||||||
export default [{
|
export default [{
|
||||||
ignores: ["build/"]
|
ignores: ["build/"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
files : ['**/*.js', '**/*.jsx'],
|
files : ['**/*.js', '**/*.jsx'],
|
||||||
plugins : { react, jest },
|
plugins : { react, jest, local: localPlugin },
|
||||||
languageOptions : {
|
languageOptions : {
|
||||||
ecmaVersion : "latest",
|
ecmaVersion : "latest",
|
||||||
sourceType : "module",
|
sourceType : "module",
|
||||||
@@ -65,7 +66,10 @@ export default [{
|
|||||||
"key-spacing" : ["warn", {
|
"key-spacing" : ["warn", {
|
||||||
multiLine : { beforeColon: true, afterColon: true, align: "colon" },
|
multiLine : { beforeColon: true, afterColon: true, align: "colon" },
|
||||||
singleLine : { beforeColon: false, afterColon: true }
|
singleLine : { beforeColon: false, afterColon: true }
|
||||||
}]
|
}],
|
||||||
|
|
||||||
|
"local/aligned-useState-pairs": "warn"
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
7
eslint_plugins/index.js
Normal file
7
eslint_plugins/index.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import alignedUseStatePairs from './rules/aligned-useState-pairs.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
rules: {
|
||||||
|
'aligned-useState-pairs': alignedUseStatePairs
|
||||||
|
}
|
||||||
|
};
|
||||||
104
eslint_plugins/rules/aligned-useState-pairs.js
Normal file
104
eslint_plugins/rules/aligned-useState-pairs.js
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
export default {
|
||||||
|
meta: {
|
||||||
|
type: "layout",
|
||||||
|
docs: {
|
||||||
|
description: "Enforce alignment of adjacent useState variable pairs",
|
||||||
|
},
|
||||||
|
fixable: "whitespace",
|
||||||
|
schema: [],
|
||||||
|
},
|
||||||
|
create(context) {
|
||||||
|
const sourceCode = context.getSourceCode();
|
||||||
|
const useStateDeclarations = [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
VariableDeclaration(node) {
|
||||||
|
for (const decl of node.declarations) {
|
||||||
|
const init = decl.init;
|
||||||
|
if (
|
||||||
|
init &&
|
||||||
|
init.type === "CallExpression" &&
|
||||||
|
init.callee.name === "useState" &&
|
||||||
|
decl.id.type === "ArrayPattern"
|
||||||
|
) {
|
||||||
|
useStateDeclarations.push(decl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Program:exit"() {
|
||||||
|
if (useStateDeclarations.length < 2) return;
|
||||||
|
|
||||||
|
// Sort by line number
|
||||||
|
useStateDeclarations.sort(
|
||||||
|
(a, b) => a.loc.start.line - b.loc.start.line
|
||||||
|
);
|
||||||
|
|
||||||
|
// Group adjacent lines
|
||||||
|
const groups = [];
|
||||||
|
let currentGroup = [useStateDeclarations[0]];
|
||||||
|
|
||||||
|
for (let i = 1; i < useStateDeclarations.length; i++) {
|
||||||
|
const prev = useStateDeclarations[i - 1];
|
||||||
|
const curr = useStateDeclarations[i];
|
||||||
|
|
||||||
|
if (curr.loc.start.line === prev.loc.end.line + 1) {
|
||||||
|
currentGroup.push(curr);
|
||||||
|
} else {
|
||||||
|
if (currentGroup.length > 1) groups.push(currentGroup);
|
||||||
|
currentGroup = [curr];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentGroup.length > 1) groups.push(currentGroup);
|
||||||
|
|
||||||
|
// Analyze each group
|
||||||
|
for (const group of groups) {
|
||||||
|
const positions = group.map((decl) => {
|
||||||
|
const text = sourceCode.getText(decl);
|
||||||
|
const commaIndex = text.indexOf(",");
|
||||||
|
const closingBracketIndex = text.lastIndexOf("]");
|
||||||
|
return {
|
||||||
|
node: decl,
|
||||||
|
comma: commaIndex,
|
||||||
|
closing: closingBracketIndex,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const maxComma = Math.max(...positions.map((p) => p.comma));
|
||||||
|
const maxClosing = Math.max(
|
||||||
|
...positions.map((p) => p.closing)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const pos of positions) {
|
||||||
|
if (
|
||||||
|
pos.comma !== maxComma ||
|
||||||
|
pos.closing !== maxClosing
|
||||||
|
) {
|
||||||
|
context.report({
|
||||||
|
node: pos.node,
|
||||||
|
message: "useState pair is not aligned with others in its block.",
|
||||||
|
fix(fixer) {
|
||||||
|
const text = sourceCode.getText(pos.node);
|
||||||
|
const parts = text.match(/^\[\s*(.+?)\s*,\s*(.+?)\s*\]\s*=\s*useState\((.+)\)$/);
|
||||||
|
if (!parts) return null;
|
||||||
|
|
||||||
|
const [_, left, right, value] = parts;
|
||||||
|
|
||||||
|
const paddedLeft = left.padEnd(maxComma - 1);
|
||||||
|
const paddedRight = right.padEnd(maxClosing - maxComma - 2);
|
||||||
|
const aligned = `[${paddedLeft}, ${paddedRight}] = useState(${value})`;
|
||||||
|
console.log("Pre: " + text);
|
||||||
|
console.log("Post: " + aligned);
|
||||||
|
return [
|
||||||
|
fixer.replaceText(pos.node, aligned),
|
||||||
|
fixer.insertTextBefore(pos.node.parent, ""),
|
||||||
|
fixer.insertTextAfter(pos.node.parent, "")
|
||||||
|
];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user