index.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. module.exports = {
  2. rules: {
  3. // auto fixable
  4. 'no-spaces-in-control-flow-statements': {
  5. meta: {
  6. docs: {
  7. description: 'Disallow spaces in control flow statements',
  8. category: 'Z8 Stylistic Issues',
  9. recommended: true,
  10. },
  11. fixable: 'code',
  12. schema: [],
  13. },
  14. create: function (context) {
  15. return {
  16. IfStatement: function (node) {
  17. const tokens = context.getSourceCode().getTokens(node);
  18. const firstToken = tokens[0];
  19. const secondToken = tokens[1];
  20. if (firstToken.type === 'Keyword' &&
  21. firstToken.value === 'if' &&
  22. secondToken.type === 'Punctuator' &&
  23. secondToken.value === '(' &&
  24. secondToken.range[0] - firstToken.range[1] > 0
  25. ) {
  26. context.report({
  27. node: node,
  28. message: 'Do not use spaces between \'if\' and its condition',
  29. fix: function(fixer) {
  30. return fixer.replaceTextRange([firstToken.range[1], secondToken.range[0]], '');
  31. }
  32. });
  33. }
  34. },
  35. ForStatement: function (node) {
  36. const tokens = context.getSourceCode().getTokens(node);
  37. const firstToken = tokens[0];
  38. const secondToken = tokens[1];
  39. if (
  40. firstToken.type === 'Keyword' &&
  41. firstToken.value === 'for' &&
  42. secondToken.type === 'Punctuator' &&
  43. secondToken.value === '(' &&
  44. secondToken.range[0] - firstToken.range[1] > 0
  45. ) {
  46. context.report({
  47. node: node,
  48. message: 'Do not use spaces between \'for\' and its condition',
  49. fix: function(fixer) {
  50. return fixer.replaceTextRange([firstToken.range[1], secondToken.range[0]], '');
  51. }
  52. });
  53. }
  54. },
  55. WhileStatement: function (node) {
  56. const tokens = context.getSourceCode().getTokens(node);
  57. const firstToken = tokens[0];
  58. const secondToken = tokens[1];
  59. if (
  60. firstToken.type === 'Keyword' &&
  61. firstToken.value === 'while' &&
  62. secondToken.type === 'Punctuator' &&
  63. secondToken.value === '(' &&
  64. secondToken.range[0] - firstToken.range[1] > 0
  65. ) {
  66. context.report({
  67. node: node,
  68. message: 'Do not use spaces between \'while\' and its condition',
  69. fix: function(fixer) {
  70. return fixer.replaceTextRange([firstToken.range[1], secondToken.range[0]], '');
  71. }
  72. });
  73. }
  74. },
  75. SwitchStatement: function (node) {
  76. const tokens = context.getSourceCode().getTokens(node);
  77. const firstToken = tokens[0];
  78. const secondToken = tokens[1];
  79. if (
  80. firstToken.type === 'Keyword' &&
  81. firstToken.value === 'switch' &&
  82. secondToken.type === 'Punctuator' &&
  83. secondToken.value === '(' &&
  84. secondToken.range[0] - firstToken.range[1] > 0
  85. ) {
  86. context.report({
  87. node: node,
  88. message: 'Do not use spaces between \'switch\' and its expression',
  89. fix: function(fixer) {
  90. return fixer.replaceTextRange([firstToken.range[1], secondToken.range[0]], '');
  91. }
  92. });
  93. }
  94. },
  95. };
  96. },
  97. },
  98. 'no-strict-equality': {
  99. meta: {
  100. docs: {
  101. description: 'Disallow strict equality operator',
  102. category: 'Z8 Stylistic Issues',
  103. recommended: true,
  104. },
  105. fixable: 'code',
  106. schema: [],
  107. },
  108. create: function (context) {
  109. return {
  110. BinaryExpression: function (node) {
  111. if (node.operator === '===') {
  112. context.report({
  113. node: node,
  114. message: 'Avoid using strict equality operator \'===\' (use loose equality operator \'==\' instead)',
  115. });
  116. }
  117. },
  118. };
  119. },
  120. },
  121. // auto fixable
  122. 'no-double-quotes': {
  123. meta: {
  124. docs: {
  125. description: 'Disallow double quotes',
  126. category: 'Z8 Stylistic Issues',
  127. recommended: true,
  128. },
  129. fixable: 'code',
  130. schema: [],
  131. },
  132. create: function (context) {
  133. return {
  134. Literal: function (node) {
  135. if (typeof node.value === 'string' && node.raw[0] === '"') {
  136. context.report({
  137. node: node,
  138. message: 'Avoid using double quotes (use single quotes instead)',
  139. fix: function(fixer) {
  140. return fixer.replaceTextRange([node.start, node.end], "'" + node.value.replace(/'/g, '\\\'') + "'");
  141. }
  142. });
  143. }
  144. },
  145. };
  146. },
  147. },
  148. 'no-arrow-functions': {
  149. meta: {
  150. docs: {
  151. description: 'Disallow arrow functions',
  152. category: 'Z8 Stylistic Issues',
  153. recommended: true,
  154. },
  155. schema: [],
  156. },
  157. create: function (context) {
  158. return {
  159. ArrowFunctionExpression: function (node) {
  160. context.report({
  161. node: node,
  162. message: 'Avoid using arrow functions',
  163. });
  164. },
  165. };
  166. },
  167. },
  168. 'no-function-declaration': {
  169. meta: {
  170. docs: {
  171. description: 'Disallow function declaration',
  172. category: 'Z8 Stylistic Issues',
  173. recommended: true,
  174. },
  175. schema: [],
  176. },
  177. create: function (context) {
  178. return {
  179. FunctionDeclaration: function (node) {
  180. context.report({
  181. node: node,
  182. message: 'Use function expression instead of function declaration',
  183. });
  184. },
  185. VariableDeclarator: function (node) {
  186. if (
  187. node.init &&
  188. node.init.type === 'FunctionExpression' &&
  189. node.init.id &&
  190. node.init.id.type === 'Identifier' &&
  191. node.id &&
  192. node.id.type === 'Identifier' &&
  193. node.init.id.name === node.id.name
  194. ) {
  195. context.report({
  196. node: node,
  197. message: 'Use function expression instead of function declaration',
  198. });
  199. }
  200. },
  201. };
  202. },
  203. },
  204. 'todo': {
  205. meta: {
  206. type: 'suggestion',
  207. docs: {
  208. description: 'TODO tracking',
  209. category: 'Other',
  210. recommended: true
  211. },
  212. schema: []
  213. },
  214. create: function (context) {
  215. const sourceCode = context.getSourceCode();
  216. const comments = sourceCode.getAllComments();
  217. return {
  218. Program: function(node) {
  219. comments.forEach(comment => {
  220. if (comment.value.toLowerCase().includes('todo')) {
  221. context.report({
  222. node,
  223. loc: comment.loc,
  224. message: comment.value.trim(),
  225. });
  226. }
  227. });
  228. },
  229. };
  230. }
  231. },
  232. 'whitespace-only-lines': {
  233. meta: {
  234. type: 'suggestion',
  235. docs: {
  236. description: 'Disallow empty lines with only whitespace',
  237. category: 'Z8 Stylistic Issues',
  238. recommended: true
  239. },
  240. fixable: 'code',
  241. schema: []
  242. },
  243. create: function (context) {
  244. const sourceCode = context.getSourceCode();
  245. return {
  246. Program: function (node) {
  247. const lines = sourceCode.lines;
  248. lines.forEach((line, index) => {
  249. if (/^\s+$/.test(line)) {
  250. context.report({
  251. node: node,
  252. loc: {line: index + 1, column: 0},
  253. message: 'Empty line with only whitespace',
  254. fix: function(fixer) {
  255. return fixer.replaceTextRange([sourceCode.getIndexFromLoc({line: index + 1, column: 0}), sourceCode.getIndexFromLoc({line: index + 1, column: line.length})], '');
  256. }
  257. });
  258. }
  259. });
  260. }
  261. };
  262. }
  263. }
  264. },
  265. };
  266. /**
  267. * Preferable .eslintrc.js:
  268. *
  269. * module.exports = {
  270. * 'plugins': [
  271. * 'z8'
  272. * ],
  273. * 'rules': {
  274. * 'z8/no-spaces-in-control-flow-statements': ['error'],
  275. * 'z8/no-strict-equality': ['warn'], // errpr?
  276. * 'z8/no-double-quotes': ['error'],
  277. * 'z8/no-arrow-functions': ['error'],
  278. * 'z8/no-function-declaration': ['error'],
  279. * 'object-shorthand': ['error', 'never'],
  280. * 'object-curly-spacing': ['error', 'always'],
  281. * 'space-before-blocks': ['error', 'always'],
  282. * 'func-call-spacing': ['error', 'never'],
  283. * 'space-before-function-paren': ['error', 'never'],
  284. * 'semi': ['error', 'always'],
  285. * 'space-infix-ops': ['error'],
  286. * 'indent': ['error', 'tab'],
  287. * 'no-console': ['warn'],
  288. * 'z8/todo': ['warn'],
  289. * 'z8/whitespace-only-lines': ['error']
  290. * },
  291. * 'parserOptions': {
  292. * 'ecmaVersion': 2022
  293. * }
  294. * }
  295. *
  296. */