Просмотр исходного кода

v1.0.3: let/const usage is prohibited

CrazyDoctor 1 год назад
Сommit
c42ae99da7
3 измененных файлов с 354 добавлено и 0 удалено
  1. 1 0
      README.md
  2. 339 0
      index.js
  3. 14 0
      package.json

+ 1 - 0
README.md

@@ -0,0 +1 @@
+## Z8 ESLint plugin

+ 339 - 0
index.js

@@ -0,0 +1,339 @@
+module.exports = {
+	rules: {
+		// auto fixable
+		'no-spaces-in-control-flow-statements': {
+			meta: {
+				docs: {
+					description: 'Disallow spaces in control flow statements',
+					category: 'Z8 Stylistic Issues',
+					recommended: true,
+				},
+				fixable: 'code',
+				schema: [],
+			},
+			create: function (context) {
+				return {
+					IfStatement: function (node) {
+						const tokens = context.getSourceCode().getTokens(node);
+						const firstToken = tokens[0];
+						const secondToken = tokens[1];
+
+						if (firstToken.type === 'Keyword' &&
+							firstToken.value === 'if' &&
+							secondToken.type === 'Punctuator' &&
+							secondToken.value === '(' &&
+							secondToken.range[0] - firstToken.range[1] > 0
+						) {
+							context.report({
+								node: node,
+								message: 'Do not use spaces between \'if\' and its condition',
+								fix: function(fixer) {
+									return fixer.replaceTextRange([firstToken.range[1], secondToken.range[0]], '');
+								}
+							});
+						}
+					},
+					ForStatement: function (node) {
+						const tokens = context.getSourceCode().getTokens(node);
+						const firstToken = tokens[0];
+						const secondToken = tokens[1];
+
+						if (
+							firstToken.type === 'Keyword' &&
+							firstToken.value === 'for' &&
+							secondToken.type === 'Punctuator' &&
+							secondToken.value === '(' &&
+							secondToken.range[0] - firstToken.range[1] > 0
+						) {
+							context.report({
+								node: node,
+								message: 'Do not use spaces between \'for\' and its condition',
+								fix: function(fixer) {
+									return fixer.replaceTextRange([firstToken.range[1], secondToken.range[0]], '');
+								}
+							});
+						}
+					},
+					WhileStatement: function (node) {
+						const tokens = context.getSourceCode().getTokens(node);
+						const firstToken = tokens[0];
+						const secondToken = tokens[1];
+
+						if (
+							firstToken.type === 'Keyword' &&
+							firstToken.value === 'while' &&
+							secondToken.type === 'Punctuator' &&
+							secondToken.value === '(' &&
+							secondToken.range[0] - firstToken.range[1] > 0
+						) {
+							context.report({
+								node: node,
+								message: 'Do not use spaces between \'while\' and its condition',
+								fix: function(fixer) {
+									return fixer.replaceTextRange([firstToken.range[1], secondToken.range[0]], '');
+								}
+							});
+						}
+					},
+					SwitchStatement: function (node) {
+						const tokens = context.getSourceCode().getTokens(node);
+						const firstToken = tokens[0];
+						const secondToken = tokens[1];
+
+						if (
+							firstToken.type === 'Keyword' &&
+							firstToken.value === 'switch' &&
+							secondToken.type === 'Punctuator' &&
+							secondToken.value === '(' &&
+							secondToken.range[0] - firstToken.range[1] > 0
+						) {
+							context.report({
+								node: node,
+								message: 'Do not use spaces between \'switch\' and its expression',
+								fix: function(fixer) {
+									return fixer.replaceTextRange([firstToken.range[1], secondToken.range[0]], '');
+								}
+							});
+						}
+					},
+				};
+			},
+		},
+		'no-strict-equality': {
+			meta: {
+				docs: {
+					description: 'Disallow strict equality operator',
+					category: 'Z8 Stylistic Issues',
+					recommended: true,
+				},
+				fixable: 'code',
+				schema: [],
+			},
+			create: function (context) {
+				return {
+					BinaryExpression: function (node) {
+						if (
+							(node.operator === '===' || node.operator === '!==') && (
+								(node.right.type === 'Literal' && node.left.type === 'Literal') ||
+								(node.right.type === 'Literal' && ![true, false].includes(node.right.value)) ||
+								(node.left.type === 'Literal' && ![true, false].includes(node.left.value)) ||
+								(node.right.type === 'Identifier' && node.left.type === 'Identifier' && !(node.right.name === "undefined" || node.left.name === "undefined"))
+							)
+						) {
+							context.report({
+								node: node,
+								message: 'Avoid using strict equality operator \''+node.operator+'\' (use loose equality operator \''+node.operator.substring(0, 2)+'\' instead)',
+							});
+						}
+					},
+				};
+			},
+		},
+		// auto fixable
+		'no-double-quotes': {
+			meta: {
+				docs: {
+					description: 'Disallow double quotes',
+					category: 'Z8 Stylistic Issues',
+					recommended: true,
+				},
+				fixable: 'code',
+				schema: [],
+			},
+			create: function (context) {
+				return {
+					Literal: function (node) {
+						if (typeof node.value === 'string' && node.raw[0] === '"') {
+							context.report({
+								node: node,
+								message: 'Avoid using double quotes (use single quotes instead)',
+								fix: function(fixer) {
+									return fixer.replaceTextRange([node.start, node.end], "'" + node.value.replace(/'/g, '\\\'') + "'");
+								}
+							});
+						}
+					},
+				};
+			},
+		},
+		'no-arrow-functions': {
+			meta: {
+				docs: {
+					description: 'Disallow arrow functions',
+					category: 'Z8 Stylistic Issues',
+					recommended: true,
+				},
+				schema: [],
+			},
+			create: function (context) {
+				return {
+					ArrowFunctionExpression: function (node) {
+						context.report({
+							node: node,
+							message: 'Avoid using arrow functions',
+						});
+					},
+				};
+			},
+		},
+		'no-function-declaration': {
+			meta: {
+				docs: {
+					description: 'Disallow function declaration',
+					category: 'Z8 Stylistic Issues',
+					recommended: true,
+				},
+				schema: [],
+			},
+			create: function (context) {
+				return {
+					FunctionDeclaration: function (node) {
+						context.report({
+							node: node,
+							message: 'Use function expression instead of function declaration',
+						});
+					},
+					VariableDeclarator: function (node) {
+						if (
+							node.init &&
+							node.init.type === 'FunctionExpression' &&
+							node.init.id &&
+							node.init.id.type === 'Identifier' &&
+							node.id &&
+							node.id.type === 'Identifier' &&
+							node.init.id.name === node.id.name
+						) {
+							context.report({
+								node: node,
+								message: 'Use function expression instead of function declaration',
+							});
+						}
+					},
+				};
+			},
+		},
+		'todo': {
+			meta: {
+				type: 'suggestion',
+				docs: {
+					description: 'TODO tracking',
+					category: 'Other',
+					recommended: true
+				},
+				schema: []
+			},
+			create: function (context) {
+				const sourceCode = context.getSourceCode();
+				const comments = sourceCode.getAllComments();
+
+				return {
+					Program: function(node) {
+						comments.forEach(comment => {
+							if (comment.value.toLowerCase().includes('todo')) {
+								context.report({
+									node,
+									loc: comment.loc,
+									message: comment.value.trim(),
+								});
+							}
+						});
+					},
+				};
+			}
+		},
+		// auto fixable
+		'whitespace-only-lines': {
+			meta: {
+				type: 'suggestion',
+				docs: {
+					description: 'Disallow empty lines with only whitespace',
+					category: 'Z8 Stylistic Issues',
+					recommended: true
+				},
+				fixable: 'code',
+				schema: []
+			},
+			create: function (context) {
+				const sourceCode = context.getSourceCode();
+
+				return {
+					Program: function (node) {
+						const lines = sourceCode.lines;
+						lines.forEach((line, index) => {
+							if (/^\s+$/.test(line)) {
+								context.report({
+									node: node,
+									loc: {line: index + 1, column: 0},
+									message: 'Empty line with only whitespace',
+									fix: function(fixer) {
+										return fixer.replaceTextRange([sourceCode.getIndexFromLoc({line: index + 1, column: 0}), sourceCode.getIndexFromLoc({line: index + 1, column: line.length})], '');
+									}
+								});
+							}
+						});
+					}
+				};
+			}
+		},
+		// auto fixable
+		'prefer-var': {
+			meta: {
+				docs: {
+					description: 'Disallow let/const usage',
+					category: 'Z8 Stylistic Issues',
+					recommended: true,
+				},
+				fixable: 'code',
+				schema: [],
+			},
+			create: function(context) {
+				return {
+					VariableDeclaration(node) {
+						if (node.kind !== 'var') {
+							context.report({
+								node: node,
+								message: 'Prefer using `var` instead of `let` or `const`.',
+								fix: function(fixer) {
+									return fixer.replaceTextRange([node.start, node.start + node.kind.length], 'var');
+								}
+							});
+						}
+					}
+				};
+			}
+		}
+	},
+};
+
+/**
+ * Preferable .eslintrc.js:
+ * 
+ * module.exports = {
+ * 	'plugins': [
+ * 		'z8'
+ * 	],
+ * 	'rules': {
+ * 		'z8/no-spaces-in-control-flow-statements': ['error'],
+ * 		'z8/no-strict-equality': ['error'],
+ * 		'z8/no-double-quotes': ['error'],
+ * 		'z8/no-arrow-functions': ['error'],
+ * 		'z8/no-function-declaration': ['error'],
+ * 		'object-shorthand': ['error', 'never'],
+ * 		'object-curly-spacing': ['error', 'always'],
+ * 		'space-before-blocks': ['error', 'always'],
+ * 		'func-call-spacing': ['error', 'never'],
+ * 		'space-before-function-paren': ['error', 'never'],
+ * 		'semi': ['error', 'always'],
+ * 		'space-infix-ops': ['error'],
+ * 		'indent': ['error', 'tab'],
+ * 		'no-console': ['warn'],
+ *		'z8/todo': ['warn'],
+ *		'z8/whitespace-only-lines': ['error']
+ * 	},
+ * 	'parserOptions': {
+ * 		'ecmaVersion': 2022,
+ * 		'sourceType': 'module'
+ * 	}
+ * }
+ * 
+ */

+ 14 - 0
package.json

@@ -0,0 +1,14 @@
+{
+  "name": "eslint-plugin-z8",
+  "version": "1.0.3",
+  "private": true,
+  "main": "index.js",
+  "dependencies": {
+    "eslint": "8.38.0"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://git.crazydoctor.org/dz/eslint-plugin-z8.git"
+  },
+  "author": "Oleg Karataev (CrazyDoctor)"
+}