|
@@ -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'
|
|
|
+ * }
|
|
|
+ * }
|
|
|
+ *
|
|
|
+ */
|