eslint 8.57.1 → 9.39.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (458) hide show
  1. package/README.md +165 -115
  2. package/bin/eslint.js +112 -89
  3. package/conf/default-cli-options.js +22 -22
  4. package/conf/ecma-version.js +16 -0
  5. package/conf/globals.js +109 -94
  6. package/conf/replacements.json +24 -20
  7. package/conf/rule-type-list.json +89 -26
  8. package/lib/api.js +16 -20
  9. package/lib/cli-engine/cli-engine.js +841 -810
  10. package/lib/cli-engine/file-enumerator.js +384 -390
  11. package/lib/cli-engine/formatters/formatters-meta.json +17 -45
  12. package/lib/cli-engine/formatters/html.js +110 -102
  13. package/lib/cli-engine/formatters/json-with-metadata.js +5 -5
  14. package/lib/cli-engine/formatters/json.js +2 -2
  15. package/lib/cli-engine/formatters/stylish.js +97 -76
  16. package/lib/cli-engine/hash.js +1 -1
  17. package/lib/cli-engine/index.js +1 -1
  18. package/lib/cli-engine/lint-result-cache.js +165 -148
  19. package/lib/cli-engine/load-rules.js +17 -17
  20. package/lib/cli.js +481 -399
  21. package/lib/config/config-loader.js +816 -0
  22. package/lib/config/config.js +674 -0
  23. package/lib/config/default-config.js +57 -46
  24. package/lib/config/flat-config-array.js +170 -333
  25. package/lib/config/flat-config-schema.js +389 -389
  26. package/lib/config-api.js +12 -0
  27. package/lib/eslint/eslint-helpers.js +1196 -663
  28. package/lib/eslint/eslint.js +1262 -607
  29. package/lib/eslint/index.js +3 -3
  30. package/lib/eslint/legacy-eslint.js +786 -0
  31. package/lib/eslint/worker.js +173 -0
  32. package/lib/languages/js/index.js +336 -0
  33. package/lib/languages/js/source-code/index.js +7 -0
  34. package/lib/languages/js/source-code/source-code.js +1364 -0
  35. package/lib/languages/js/source-code/token-store/backward-token-comment-cursor.js +61 -0
  36. package/lib/languages/js/source-code/token-store/backward-token-cursor.js +57 -0
  37. package/lib/{source-code → languages/js/source-code}/token-store/cursor.js +36 -36
  38. package/lib/languages/js/source-code/token-store/cursors.js +120 -0
  39. package/lib/{source-code → languages/js/source-code}/token-store/decorative-cursor.js +17 -18
  40. package/lib/{source-code → languages/js/source-code}/token-store/filter-cursor.js +19 -20
  41. package/lib/languages/js/source-code/token-store/forward-token-comment-cursor.js +65 -0
  42. package/lib/languages/js/source-code/token-store/forward-token-cursor.js +62 -0
  43. package/lib/languages/js/source-code/token-store/index.js +721 -0
  44. package/lib/{source-code → languages/js/source-code}/token-store/limit-cursor.js +17 -18
  45. package/lib/languages/js/source-code/token-store/padded-token-cursor.js +45 -0
  46. package/lib/{source-code → languages/js/source-code}/token-store/skip-cursor.js +19 -20
  47. package/lib/languages/js/source-code/token-store/utils.js +110 -0
  48. package/lib/languages/js/validate-language-options.js +196 -0
  49. package/lib/linter/apply-disable-directives.js +490 -371
  50. package/lib/linter/code-path-analysis/code-path-analyzer.js +650 -674
  51. package/lib/linter/code-path-analysis/code-path-segment.js +215 -216
  52. package/lib/linter/code-path-analysis/code-path-state.js +2118 -2096
  53. package/lib/linter/code-path-analysis/code-path.js +307 -317
  54. package/lib/linter/code-path-analysis/debug-helpers.js +183 -163
  55. package/lib/linter/code-path-analysis/fork-context.js +297 -272
  56. package/lib/linter/code-path-analysis/id-generator.js +22 -23
  57. package/lib/linter/esquery.js +332 -0
  58. package/lib/linter/file-context.js +144 -0
  59. package/lib/linter/file-report.js +608 -0
  60. package/lib/linter/index.js +3 -5
  61. package/lib/linter/interpolate.js +38 -16
  62. package/lib/linter/linter.js +2328 -1785
  63. package/lib/linter/rule-fixer.js +136 -107
  64. package/lib/linter/rules.js +37 -46
  65. package/lib/linter/source-code-fixer.js +96 -94
  66. package/lib/linter/source-code-traverser.js +333 -0
  67. package/lib/linter/source-code-visitor.js +81 -0
  68. package/lib/linter/timing.js +145 -97
  69. package/lib/linter/vfile.js +115 -0
  70. package/lib/options.js +464 -326
  71. package/lib/rule-tester/index.js +3 -1
  72. package/lib/rule-tester/rule-tester.js +1371 -998
  73. package/lib/rules/accessor-pairs.js +333 -259
  74. package/lib/rules/array-bracket-newline.js +250 -220
  75. package/lib/rules/array-bracket-spacing.js +286 -229
  76. package/lib/rules/array-callback-return.js +401 -354
  77. package/lib/rules/array-element-newline.js +358 -295
  78. package/lib/rules/arrow-body-style.js +400 -278
  79. package/lib/rules/arrow-parens.js +206 -155
  80. package/lib/rules/arrow-spacing.js +169 -145
  81. package/lib/rules/block-scoped-var.js +125 -123
  82. package/lib/rules/block-spacing.js +186 -158
  83. package/lib/rules/brace-style.js +262 -181
  84. package/lib/rules/callback-return.js +203 -174
  85. package/lib/rules/camelcase.js +403 -380
  86. package/lib/rules/capitalized-comments.js +253 -228
  87. package/lib/rules/class-methods-use-this.js +231 -168
  88. package/lib/rules/comma-dangle.js +379 -328
  89. package/lib/rules/comma-spacing.js +193 -177
  90. package/lib/rules/comma-style.js +375 -298
  91. package/lib/rules/complexity.js +180 -144
  92. package/lib/rules/computed-property-spacing.js +236 -193
  93. package/lib/rules/consistent-return.js +181 -170
  94. package/lib/rules/consistent-this.js +167 -141
  95. package/lib/rules/constructor-super.js +418 -411
  96. package/lib/rules/curly.js +407 -468
  97. package/lib/rules/default-case-last.js +39 -32
  98. package/lib/rules/default-case.js +89 -83
  99. package/lib/rules/default-param-last.js +69 -53
  100. package/lib/rules/dot-location.js +122 -92
  101. package/lib/rules/dot-notation.js +193 -153
  102. package/lib/rules/eol-last.js +122 -102
  103. package/lib/rules/eqeqeq.js +191 -155
  104. package/lib/rules/for-direction.js +150 -122
  105. package/lib/rules/func-call-spacing.js +261 -213
  106. package/lib/rules/func-name-matching.js +294 -209
  107. package/lib/rules/func-names.js +165 -164
  108. package/lib/rules/func-style.js +209 -86
  109. package/lib/rules/function-call-argument-newline.js +152 -111
  110. package/lib/rules/function-paren-newline.js +349 -273
  111. package/lib/rules/generator-star-spacing.js +229 -192
  112. package/lib/rules/getter-return.js +208 -170
  113. package/lib/rules/global-require.js +85 -58
  114. package/lib/rules/grouped-accessor-pairs.js +201 -148
  115. package/lib/rules/guard-for-in.js +72 -63
  116. package/lib/rules/handle-callback-err.js +108 -87
  117. package/lib/rules/id-blacklist.js +182 -187
  118. package/lib/rules/id-denylist.js +174 -179
  119. package/lib/rules/id-length.js +197 -157
  120. package/lib/rules/id-match.js +350 -286
  121. package/lib/rules/implicit-arrow-linebreak.js +102 -61
  122. package/lib/rules/indent-legacy.js +1345 -1102
  123. package/lib/rules/indent.js +2272 -1741
  124. package/lib/rules/index.js +320 -294
  125. package/lib/rules/init-declarations.js +139 -106
  126. package/lib/rules/jsx-quotes.js +94 -64
  127. package/lib/rules/key-spacing.js +750 -615
  128. package/lib/rules/keyword-spacing.js +648 -587
  129. package/lib/rules/line-comment-position.js +143 -108
  130. package/lib/rules/linebreak-style.js +115 -88
  131. package/lib/rules/lines-around-comment.js +540 -430
  132. package/lib/rules/lines-around-directive.js +233 -185
  133. package/lib/rules/lines-between-class-members.js +305 -216
  134. package/lib/rules/logical-assignment-operators.js +582 -398
  135. package/lib/rules/max-classes-per-file.js +69 -68
  136. package/lib/rules/max-depth.js +146 -143
  137. package/lib/rules/max-len.js +473 -416
  138. package/lib/rules/max-lines-per-function.js +201 -176
  139. package/lib/rules/max-lines.js +158 -162
  140. package/lib/rules/max-nested-callbacks.js +102 -104
  141. package/lib/rules/max-params.js +102 -75
  142. package/lib/rules/max-statements-per-line.js +205 -180
  143. package/lib/rules/max-statements.js +168 -164
  144. package/lib/rules/multiline-comment-style.js +638 -460
  145. package/lib/rules/multiline-ternary.js +241 -158
  146. package/lib/rules/new-cap.js +233 -232
  147. package/lib/rules/new-parens.js +88 -61
  148. package/lib/rules/newline-after-var.js +287 -233
  149. package/lib/rules/newline-before-return.js +229 -204
  150. package/lib/rules/newline-per-chained-call.js +142 -109
  151. package/lib/rules/no-alert.js +90 -79
  152. package/lib/rules/no-array-constructor.js +175 -113
  153. package/lib/rules/no-async-promise-executor.js +30 -24
  154. package/lib/rules/no-await-in-loop.js +79 -70
  155. package/lib/rules/no-bitwise.js +113 -87
  156. package/lib/rules/no-buffer-constructor.js +61 -37
  157. package/lib/rules/no-caller.js +39 -33
  158. package/lib/rules/no-case-declarations.js +61 -45
  159. package/lib/rules/no-catch-shadow.js +76 -62
  160. package/lib/rules/no-class-assign.js +51 -48
  161. package/lib/rules/no-compare-neg-zero.js +62 -48
  162. package/lib/rules/no-cond-assign.js +148 -132
  163. package/lib/rules/no-confusing-arrow.js +98 -63
  164. package/lib/rules/no-console.js +202 -188
  165. package/lib/rules/no-const-assign.js +58 -41
  166. package/lib/rules/no-constant-binary-expression.js +501 -407
  167. package/lib/rules/no-constant-condition.js +158 -131
  168. package/lib/rules/no-constructor-return.js +49 -49
  169. package/lib/rules/no-continue.js +25 -26
  170. package/lib/rules/no-control-regex.js +125 -121
  171. package/lib/rules/no-debugger.js +28 -30
  172. package/lib/rules/no-delete-var.js +29 -29
  173. package/lib/rules/no-div-regex.js +47 -40
  174. package/lib/rules/no-dupe-args.js +79 -69
  175. package/lib/rules/no-dupe-class-members.js +102 -89
  176. package/lib/rules/no-dupe-else-if.js +100 -77
  177. package/lib/rules/no-dupe-keys.js +133 -110
  178. package/lib/rules/no-duplicate-case.js +50 -43
  179. package/lib/rules/no-duplicate-imports.js +266 -188
  180. package/lib/rules/no-else-return.js +430 -385
  181. package/lib/rules/no-empty-character-class.js +57 -50
  182. package/lib/rules/no-empty-function.js +197 -128
  183. package/lib/rules/no-empty-pattern.js +63 -56
  184. package/lib/rules/no-empty-static-block.js +61 -35
  185. package/lib/rules/no-empty.js +135 -85
  186. package/lib/rules/no-eq-null.js +37 -32
  187. package/lib/rules/no-eval.js +258 -249
  188. package/lib/rules/no-ex-assign.js +42 -39
  189. package/lib/rules/no-extend-native.js +161 -160
  190. package/lib/rules/no-extra-bind.js +201 -190
  191. package/lib/rules/no-extra-boolean-cast.js +398 -295
  192. package/lib/rules/no-extra-label.js +150 -130
  193. package/lib/rules/no-extra-parens.js +1654 -1307
  194. package/lib/rules/no-extra-semi.js +146 -126
  195. package/lib/rules/no-fallthrough.js +200 -136
  196. package/lib/rules/no-floating-decimal.js +74 -48
  197. package/lib/rules/no-func-assign.js +54 -55
  198. package/lib/rules/no-global-assign.js +78 -72
  199. package/lib/rules/no-implicit-coercion.js +350 -262
  200. package/lib/rules/no-implicit-globals.js +174 -133
  201. package/lib/rules/no-implied-eval.js +150 -112
  202. package/lib/rules/no-import-assign.js +145 -159
  203. package/lib/rules/no-inline-comments.js +101 -96
  204. package/lib/rules/no-inner-declarations.js +115 -78
  205. package/lib/rules/no-invalid-regexp.js +223 -174
  206. package/lib/rules/no-invalid-this.js +145 -117
  207. package/lib/rules/no-irregular-whitespace.js +266 -250
  208. package/lib/rules/no-iterator.js +29 -33
  209. package/lib/rules/no-label-var.js +59 -61
  210. package/lib/rules/no-labels.js +138 -131
  211. package/lib/rules/no-lone-blocks.js +127 -123
  212. package/lib/rules/no-lonely-if.js +105 -67
  213. package/lib/rules/no-loop-func.js +245 -184
  214. package/lib/rules/no-loss-of-precision.js +236 -201
  215. package/lib/rules/no-magic-numbers.js +339 -217
  216. package/lib/rules/no-misleading-character-class.js +548 -253
  217. package/lib/rules/no-mixed-operators.js +188 -164
  218. package/lib/rules/no-mixed-requires.js +253 -224
  219. package/lib/rules/no-mixed-spaces-and-tabs.js +135 -103
  220. package/lib/rules/no-multi-assign.js +46 -47
  221. package/lib/rules/no-multi-spaces.js +163 -125
  222. package/lib/rules/no-multi-str.js +42 -40
  223. package/lib/rules/no-multiple-empty-lines.js +196 -140
  224. package/lib/rules/no-native-reassign.js +90 -74
  225. package/lib/rules/no-negated-condition.js +79 -74
  226. package/lib/rules/no-negated-in-lhs.js +45 -32
  227. package/lib/rules/no-nested-ternary.js +33 -31
  228. package/lib/rules/no-new-func.js +71 -62
  229. package/lib/rules/no-new-native-nonconstructor.js +43 -39
  230. package/lib/rules/no-new-object.js +48 -39
  231. package/lib/rules/no-new-require.js +48 -31
  232. package/lib/rules/no-new-symbol.js +61 -43
  233. package/lib/rules/no-new-wrappers.js +43 -41
  234. package/lib/rules/no-new.js +28 -29
  235. package/lib/rules/no-nonoctal-decimal-escape.js +149 -121
  236. package/lib/rules/no-obj-calls.js +66 -53
  237. package/lib/rules/no-object-constructor.js +104 -97
  238. package/lib/rules/no-octal-escape.js +40 -43
  239. package/lib/rules/no-octal.js +29 -32
  240. package/lib/rules/no-param-reassign.js +236 -218
  241. package/lib/rules/no-path-concat.js +66 -51
  242. package/lib/rules/no-plusplus.js +60 -63
  243. package/lib/rules/no-process-env.js +49 -32
  244. package/lib/rules/no-process-exit.js +48 -28
  245. package/lib/rules/no-promise-executor-return.js +205 -204
  246. package/lib/rules/no-proto.js +26 -29
  247. package/lib/rules/no-prototype-builtins.js +146 -124
  248. package/lib/rules/no-redeclare.js +154 -155
  249. package/lib/rules/no-regex-spaces.js +183 -161
  250. package/lib/rules/no-restricted-exports.js +208 -174
  251. package/lib/rules/no-restricted-globals.js +254 -112
  252. package/lib/rules/no-restricted-imports.js +824 -384
  253. package/lib/rules/no-restricted-modules.js +222 -186
  254. package/lib/rules/no-restricted-properties.js +218 -153
  255. package/lib/rules/no-restricted-syntax.js +56 -52
  256. package/lib/rules/no-return-assign.js +56 -49
  257. package/lib/rules/no-return-await.js +147 -120
  258. package/lib/rules/no-script-url.js +53 -46
  259. package/lib/rules/no-self-assign.js +148 -145
  260. package/lib/rules/no-self-compare.js +63 -46
  261. package/lib/rules/no-sequences.js +135 -115
  262. package/lib/rules/no-setter-return.js +176 -178
  263. package/lib/rules/no-shadow-restricted-names.js +84 -36
  264. package/lib/rules/no-shadow.js +598 -310
  265. package/lib/rules/no-spaced-func.js +82 -60
  266. package/lib/rules/no-sparse-arrays.js +46 -28
  267. package/lib/rules/no-sync.js +61 -44
  268. package/lib/rules/no-tabs.js +83 -54
  269. package/lib/rules/no-template-curly-in-string.js +33 -32
  270. package/lib/rules/no-ternary.js +25 -28
  271. package/lib/rules/no-this-before-super.js +332 -298
  272. package/lib/rules/no-throw-literal.js +31 -36
  273. package/lib/rules/no-trailing-spaces.js +208 -174
  274. package/lib/rules/no-unassigned-vars.js +80 -0
  275. package/lib/rules/no-undef-init.js +86 -60
  276. package/lib/rules/no-undef.js +52 -47
  277. package/lib/rules/no-undefined.js +73 -74
  278. package/lib/rules/no-underscore-dangle.js +370 -322
  279. package/lib/rules/no-unexpected-multiline.js +112 -102
  280. package/lib/rules/no-unmodified-loop-condition.js +254 -254
  281. package/lib/rules/no-unneeded-ternary.js +212 -146
  282. package/lib/rules/no-unreachable-loop.js +145 -140
  283. package/lib/rules/no-unreachable.js +255 -248
  284. package/lib/rules/no-unsafe-finally.js +93 -85
  285. package/lib/rules/no-unsafe-negation.js +105 -81
  286. package/lib/rules/no-unsafe-optional-chaining.js +193 -177
  287. package/lib/rules/no-unused-expressions.js +199 -158
  288. package/lib/rules/no-unused-labels.js +139 -124
  289. package/lib/rules/no-unused-private-class-members.js +206 -182
  290. package/lib/rules/no-unused-vars.js +1708 -687
  291. package/lib/rules/no-use-before-define.js +327 -229
  292. package/lib/rules/no-useless-assignment.js +654 -0
  293. package/lib/rules/no-useless-backreference.js +212 -143
  294. package/lib/rules/no-useless-call.js +58 -53
  295. package/lib/rules/no-useless-catch.js +40 -40
  296. package/lib/rules/no-useless-computed-key.js +144 -108
  297. package/lib/rules/no-useless-concat.js +65 -59
  298. package/lib/rules/no-useless-constructor.js +160 -97
  299. package/lib/rules/no-useless-escape.js +364 -291
  300. package/lib/rules/no-useless-rename.js +183 -153
  301. package/lib/rules/no-useless-return.js +344 -307
  302. package/lib/rules/no-var.js +245 -212
  303. package/lib/rules/no-void.js +51 -46
  304. package/lib/rules/no-warning-comments.js +191 -183
  305. package/lib/rules/no-whitespace-before-property.js +131 -97
  306. package/lib/rules/no-with.js +24 -26
  307. package/lib/rules/nonblock-statement-body-position.js +149 -112
  308. package/lib/rules/object-curly-newline.js +306 -247
  309. package/lib/rules/object-curly-spacing.js +360 -296
  310. package/lib/rules/object-property-newline.js +137 -88
  311. package/lib/rules/object-shorthand.js +632 -500
  312. package/lib/rules/one-var-declaration-per-line.js +104 -82
  313. package/lib/rules/one-var.js +686 -536
  314. package/lib/rules/operator-assignment.js +219 -158
  315. package/lib/rules/operator-linebreak.js +295 -233
  316. package/lib/rules/padded-blocks.js +346 -290
  317. package/lib/rules/padding-line-between-statements.js +443 -421
  318. package/lib/rules/prefer-arrow-callback.js +371 -315
  319. package/lib/rules/prefer-const.js +418 -373
  320. package/lib/rules/prefer-destructuring.js +309 -278
  321. package/lib/rules/prefer-exponentiation-operator.js +176 -132
  322. package/lib/rules/prefer-named-capture-group.js +160 -141
  323. package/lib/rules/prefer-numeric-literals.js +121 -112
  324. package/lib/rules/prefer-object-has-own.js +116 -82
  325. package/lib/rules/prefer-object-spread.js +214 -193
  326. package/lib/rules/prefer-promise-reject-errors.js +140 -118
  327. package/lib/rules/prefer-reflect.js +126 -103
  328. package/lib/rules/prefer-regex-literals.js +561 -463
  329. package/lib/rules/prefer-rest-params.js +79 -80
  330. package/lib/rules/prefer-spread.js +47 -43
  331. package/lib/rules/prefer-template.js +266 -194
  332. package/lib/rules/preserve-caught-error.js +535 -0
  333. package/lib/rules/quote-props.js +373 -289
  334. package/lib/rules/quotes.js +374 -308
  335. package/lib/rules/radix.js +152 -134
  336. package/lib/rules/require-atomic-updates.js +316 -282
  337. package/lib/rules/require-await.js +153 -82
  338. package/lib/rules/require-unicode-regexp.js +296 -108
  339. package/lib/rules/require-yield.js +53 -54
  340. package/lib/rules/rest-spread-spacing.js +128 -98
  341. package/lib/rules/semi-spacing.js +281 -232
  342. package/lib/rules/semi-style.js +176 -116
  343. package/lib/rules/semi.js +456 -418
  344. package/lib/rules/sort-imports.js +307 -229
  345. package/lib/rules/sort-keys.js +219 -181
  346. package/lib/rules/sort-vars.js +127 -91
  347. package/lib/rules/space-before-blocks.js +199 -171
  348. package/lib/rules/space-before-function-paren.js +186 -148
  349. package/lib/rules/space-in-parens.js +359 -270
  350. package/lib/rules/space-infix-ops.js +237 -183
  351. package/lib/rules/space-unary-ops.js +356 -280
  352. package/lib/rules/spaced-comment.js +363 -301
  353. package/lib/rules/strict.js +266 -229
  354. package/lib/rules/switch-colon-spacing.js +130 -104
  355. package/lib/rules/symbol-description.js +45 -48
  356. package/lib/rules/template-curly-spacing.js +148 -124
  357. package/lib/rules/template-tag-spacing.js +98 -70
  358. package/lib/rules/unicode-bom.js +54 -54
  359. package/lib/rules/use-isnan.js +237 -110
  360. package/lib/rules/utils/ast-utils.js +2139 -1688
  361. package/lib/rules/utils/char-source.js +247 -0
  362. package/lib/rules/utils/fix-tracker.js +99 -88
  363. package/lib/rules/utils/keywords.js +59 -59
  364. package/lib/rules/utils/lazy-loading-rule-map.js +81 -78
  365. package/lib/rules/utils/regular-expressions.js +35 -19
  366. package/lib/rules/utils/unicode/index.js +9 -4
  367. package/lib/rules/utils/unicode/is-combining-character.js +1 -1
  368. package/lib/rules/utils/unicode/is-emoji-modifier.js +1 -1
  369. package/lib/rules/utils/unicode/is-regional-indicator-symbol.js +1 -1
  370. package/lib/rules/utils/unicode/is-surrogate-pair.js +1 -1
  371. package/lib/rules/valid-typeof.js +153 -109
  372. package/lib/rules/vars-on-top.js +152 -144
  373. package/lib/rules/wrap-iife.js +204 -173
  374. package/lib/rules/wrap-regex.js +77 -47
  375. package/lib/rules/yield-star-spacing.js +145 -116
  376. package/lib/rules/yoda.js +283 -274
  377. package/lib/services/parser-service.js +65 -0
  378. package/lib/services/processor-service.js +101 -0
  379. package/lib/services/suppressions-service.js +302 -0
  380. package/lib/services/warning-service.js +98 -0
  381. package/lib/shared/ajv.js +14 -14
  382. package/lib/shared/assert.js +21 -0
  383. package/lib/shared/ast-utils.js +7 -6
  384. package/lib/shared/deep-merge-arrays.js +62 -0
  385. package/lib/shared/directives.js +3 -2
  386. package/lib/shared/flags.js +108 -0
  387. package/lib/shared/logging.js +24 -16
  388. package/lib/shared/naming.js +109 -0
  389. package/lib/shared/option-utils.js +63 -0
  390. package/lib/shared/relative-module-resolver.js +18 -40
  391. package/lib/shared/runtime-info.js +138 -128
  392. package/lib/shared/serialization.js +78 -0
  393. package/lib/shared/severity.js +22 -22
  394. package/lib/shared/stats.js +30 -0
  395. package/lib/shared/string-utils.js +19 -21
  396. package/lib/shared/text-table.js +68 -0
  397. package/lib/shared/translate-cli-options.js +281 -0
  398. package/lib/shared/traverser.js +153 -146
  399. package/lib/types/config-api.d.ts +12 -0
  400. package/lib/types/index.d.ts +1473 -0
  401. package/lib/types/rules.d.ts +5589 -0
  402. package/lib/types/universal.d.ts +6 -0
  403. package/lib/types/use-at-your-own-risk.d.ts +87 -0
  404. package/lib/universal.js +10 -0
  405. package/lib/unsupported-api.js +8 -9
  406. package/messages/all-files-ignored.js +3 -3
  407. package/messages/all-matched-files-ignored.js +21 -0
  408. package/messages/config-file-missing.js +16 -0
  409. package/messages/config-plugin-missing.js +14 -0
  410. package/messages/config-serialize-function.js +30 -0
  411. package/messages/eslintrc-incompat.js +35 -16
  412. package/messages/eslintrc-plugins.js +8 -5
  413. package/messages/extend-config-missing.js +3 -3
  414. package/messages/failed-to-read-json.js +3 -3
  415. package/messages/file-not-found.js +3 -3
  416. package/messages/invalid-rule-options.js +2 -2
  417. package/messages/invalid-rule-severity.js +2 -2
  418. package/messages/no-config-found.js +4 -4
  419. package/messages/plugin-conflict.js +9 -9
  420. package/messages/plugin-invalid.js +4 -4
  421. package/messages/plugin-missing.js +4 -4
  422. package/messages/print-config-with-directory-path.js +2 -2
  423. package/messages/shared.js +6 -1
  424. package/messages/whitespace-found.js +3 -3
  425. package/package.json +105 -60
  426. package/conf/config-schema.js +0 -93
  427. package/lib/cli-engine/formatters/checkstyle.js +0 -60
  428. package/lib/cli-engine/formatters/compact.js +0 -60
  429. package/lib/cli-engine/formatters/jslint-xml.js +0 -41
  430. package/lib/cli-engine/formatters/junit.js +0 -82
  431. package/lib/cli-engine/formatters/tap.js +0 -95
  432. package/lib/cli-engine/formatters/unix.js +0 -58
  433. package/lib/cli-engine/formatters/visualstudio.js +0 -63
  434. package/lib/cli-engine/xml-escape.js +0 -34
  435. package/lib/config/flat-config-helpers.js +0 -111
  436. package/lib/config/rule-validator.js +0 -158
  437. package/lib/eslint/flat-eslint.js +0 -1159
  438. package/lib/linter/config-comment-parser.js +0 -185
  439. package/lib/linter/node-event-generator.js +0 -354
  440. package/lib/linter/report-translator.js +0 -369
  441. package/lib/linter/safe-emitter.js +0 -52
  442. package/lib/rule-tester/flat-rule-tester.js +0 -1131
  443. package/lib/rules/require-jsdoc.js +0 -122
  444. package/lib/rules/utils/patterns/letters.js +0 -36
  445. package/lib/rules/valid-jsdoc.js +0 -516
  446. package/lib/shared/config-validator.js +0 -347
  447. package/lib/shared/deprecation-warnings.js +0 -58
  448. package/lib/shared/types.js +0 -216
  449. package/lib/source-code/index.js +0 -5
  450. package/lib/source-code/source-code.js +0 -1055
  451. package/lib/source-code/token-store/backward-token-comment-cursor.js +0 -57
  452. package/lib/source-code/token-store/backward-token-cursor.js +0 -58
  453. package/lib/source-code/token-store/cursors.js +0 -90
  454. package/lib/source-code/token-store/forward-token-comment-cursor.js +0 -57
  455. package/lib/source-code/token-store/forward-token-cursor.js +0 -63
  456. package/lib/source-code/token-store/index.js +0 -627
  457. package/lib/source-code/token-store/padded-token-cursor.js +0 -38
  458. package/lib/source-code/token-store/utils.js +0 -107
@@ -20,1107 +20,1350 @@ const astUtils = require("./utils/ast-utils");
20
20
  //------------------------------------------------------------------------------
21
21
  // this rule has known coverage issues, but it's deprecated and shouldn't be updated in the future anyway.
22
22
  /* c8 ignore next */
23
- /** @type {import('../shared/types').Rule} */
23
+ /** @type {import('../types').Rule.RuleModule} */
24
24
  module.exports = {
25
- meta: {
26
- type: "layout",
27
-
28
- docs: {
29
- description: "Enforce consistent indentation",
30
- recommended: false,
31
- url: "https://eslint.org/docs/latest/rules/indent-legacy"
32
- },
33
-
34
- deprecated: true,
35
-
36
- replacedBy: ["indent"],
37
-
38
- fixable: "whitespace",
39
-
40
- schema: [
41
- {
42
- oneOf: [
43
- {
44
- enum: ["tab"]
45
- },
46
- {
47
- type: "integer",
48
- minimum: 0
49
- }
50
- ]
51
- },
52
- {
53
- type: "object",
54
- properties: {
55
- SwitchCase: {
56
- type: "integer",
57
- minimum: 0
58
- },
59
- VariableDeclarator: {
60
- oneOf: [
61
- {
62
- type: "integer",
63
- minimum: 0
64
- },
65
- {
66
- type: "object",
67
- properties: {
68
- var: {
69
- type: "integer",
70
- minimum: 0
71
- },
72
- let: {
73
- type: "integer",
74
- minimum: 0
75
- },
76
- const: {
77
- type: "integer",
78
- minimum: 0
79
- }
80
- }
81
- }
82
- ]
83
- },
84
- outerIIFEBody: {
85
- type: "integer",
86
- minimum: 0
87
- },
88
- MemberExpression: {
89
- type: "integer",
90
- minimum: 0
91
- },
92
- FunctionDeclaration: {
93
- type: "object",
94
- properties: {
95
- parameters: {
96
- oneOf: [
97
- {
98
- type: "integer",
99
- minimum: 0
100
- },
101
- {
102
- enum: ["first"]
103
- }
104
- ]
105
- },
106
- body: {
107
- type: "integer",
108
- minimum: 0
109
- }
110
- }
111
- },
112
- FunctionExpression: {
113
- type: "object",
114
- properties: {
115
- parameters: {
116
- oneOf: [
117
- {
118
- type: "integer",
119
- minimum: 0
120
- },
121
- {
122
- enum: ["first"]
123
- }
124
- ]
125
- },
126
- body: {
127
- type: "integer",
128
- minimum: 0
129
- }
130
- }
131
- },
132
- CallExpression: {
133
- type: "object",
134
- properties: {
135
- parameters: {
136
- oneOf: [
137
- {
138
- type: "integer",
139
- minimum: 0
140
- },
141
- {
142
- enum: ["first"]
143
- }
144
- ]
145
- }
146
- }
147
- },
148
- ArrayExpression: {
149
- oneOf: [
150
- {
151
- type: "integer",
152
- minimum: 0
153
- },
154
- {
155
- enum: ["first"]
156
- }
157
- ]
158
- },
159
- ObjectExpression: {
160
- oneOf: [
161
- {
162
- type: "integer",
163
- minimum: 0
164
- },
165
- {
166
- enum: ["first"]
167
- }
168
- ]
169
- }
170
- },
171
- additionalProperties: false
172
- }
173
- ],
174
- messages: {
175
- expected: "Expected indentation of {{expected}} but found {{actual}}."
176
- }
177
- },
178
-
179
- create(context) {
180
- const DEFAULT_VARIABLE_INDENT = 1;
181
- const DEFAULT_PARAMETER_INDENT = null; // For backwards compatibility, don't check parameter indentation unless specified in the config
182
- const DEFAULT_FUNCTION_BODY_INDENT = 1;
183
-
184
- let indentType = "space";
185
- let indentSize = 4;
186
- const options = {
187
- SwitchCase: 0,
188
- VariableDeclarator: {
189
- var: DEFAULT_VARIABLE_INDENT,
190
- let: DEFAULT_VARIABLE_INDENT,
191
- const: DEFAULT_VARIABLE_INDENT
192
- },
193
- outerIIFEBody: null,
194
- FunctionDeclaration: {
195
- parameters: DEFAULT_PARAMETER_INDENT,
196
- body: DEFAULT_FUNCTION_BODY_INDENT
197
- },
198
- FunctionExpression: {
199
- parameters: DEFAULT_PARAMETER_INDENT,
200
- body: DEFAULT_FUNCTION_BODY_INDENT
201
- },
202
- CallExpression: {
203
- arguments: DEFAULT_PARAMETER_INDENT
204
- },
205
- ArrayExpression: 1,
206
- ObjectExpression: 1
207
- };
208
-
209
- const sourceCode = context.sourceCode;
210
-
211
- if (context.options.length) {
212
- if (context.options[0] === "tab") {
213
- indentSize = 1;
214
- indentType = "tab";
215
- } else /* c8 ignore start */ if (typeof context.options[0] === "number") {
216
- indentSize = context.options[0];
217
- indentType = "space";
218
- }/* c8 ignore stop */
219
-
220
- if (context.options[1]) {
221
- const opts = context.options[1];
222
-
223
- options.SwitchCase = opts.SwitchCase || 0;
224
- const variableDeclaratorRules = opts.VariableDeclarator;
225
-
226
- if (typeof variableDeclaratorRules === "number") {
227
- options.VariableDeclarator = {
228
- var: variableDeclaratorRules,
229
- let: variableDeclaratorRules,
230
- const: variableDeclaratorRules
231
- };
232
- } else if (typeof variableDeclaratorRules === "object") {
233
- Object.assign(options.VariableDeclarator, variableDeclaratorRules);
234
- }
235
-
236
- if (typeof opts.outerIIFEBody === "number") {
237
- options.outerIIFEBody = opts.outerIIFEBody;
238
- }
239
-
240
- if (typeof opts.MemberExpression === "number") {
241
- options.MemberExpression = opts.MemberExpression;
242
- }
243
-
244
- if (typeof opts.FunctionDeclaration === "object") {
245
- Object.assign(options.FunctionDeclaration, opts.FunctionDeclaration);
246
- }
247
-
248
- if (typeof opts.FunctionExpression === "object") {
249
- Object.assign(options.FunctionExpression, opts.FunctionExpression);
250
- }
251
-
252
- if (typeof opts.CallExpression === "object") {
253
- Object.assign(options.CallExpression, opts.CallExpression);
254
- }
255
-
256
- if (typeof opts.ArrayExpression === "number" || typeof opts.ArrayExpression === "string") {
257
- options.ArrayExpression = opts.ArrayExpression;
258
- }
259
-
260
- if (typeof opts.ObjectExpression === "number" || typeof opts.ObjectExpression === "string") {
261
- options.ObjectExpression = opts.ObjectExpression;
262
- }
263
- }
264
- }
265
-
266
- const caseIndentStore = {};
267
-
268
- /**
269
- * Creates an error message for a line, given the expected/actual indentation.
270
- * @param {int} expectedAmount The expected amount of indentation characters for this line
271
- * @param {int} actualSpaces The actual number of indentation spaces that were found on this line
272
- * @param {int} actualTabs The actual number of indentation tabs that were found on this line
273
- * @returns {string} An error message for this line
274
- */
275
- function createErrorMessageData(expectedAmount, actualSpaces, actualTabs) {
276
- const expectedStatement = `${expectedAmount} ${indentType}${expectedAmount === 1 ? "" : "s"}`; // e.g. "2 tabs"
277
- const foundSpacesWord = `space${actualSpaces === 1 ? "" : "s"}`; // e.g. "space"
278
- const foundTabsWord = `tab${actualTabs === 1 ? "" : "s"}`; // e.g. "tabs"
279
- let foundStatement;
280
-
281
- if (actualSpaces > 0 && actualTabs > 0) {
282
- foundStatement = `${actualSpaces} ${foundSpacesWord} and ${actualTabs} ${foundTabsWord}`; // e.g. "1 space and 2 tabs"
283
- } else if (actualSpaces > 0) {
284
-
285
- /*
286
- * Abbreviate the message if the expected indentation is also spaces.
287
- * e.g. 'Expected 4 spaces but found 2' rather than 'Expected 4 spaces but found 2 spaces'
288
- */
289
- foundStatement = indentType === "space" ? actualSpaces : `${actualSpaces} ${foundSpacesWord}`;
290
- } else if (actualTabs > 0) {
291
- foundStatement = indentType === "tab" ? actualTabs : `${actualTabs} ${foundTabsWord}`;
292
- } else {
293
- foundStatement = "0";
294
- }
295
- return {
296
- expected: expectedStatement,
297
- actual: foundStatement
298
- };
299
- }
300
-
301
- /**
302
- * Reports a given indent violation
303
- * @param {ASTNode} node Node violating the indent rule
304
- * @param {int} needed Expected indentation character count
305
- * @param {int} gottenSpaces Indentation space count in the actual node/code
306
- * @param {int} gottenTabs Indentation tab count in the actual node/code
307
- * @param {Object} [loc] Error line and column location
308
- * @param {boolean} isLastNodeCheck Is the error for last node check
309
- * @returns {void}
310
- */
311
- function report(node, needed, gottenSpaces, gottenTabs, loc, isLastNodeCheck) {
312
- if (gottenSpaces && gottenTabs) {
313
-
314
- // To avoid conflicts with `no-mixed-spaces-and-tabs`, don't report lines that have both spaces and tabs.
315
- return;
316
- }
317
-
318
- const desiredIndent = (indentType === "space" ? " " : "\t").repeat(needed);
319
-
320
- const textRange = isLastNodeCheck
321
- ? [node.range[1] - node.loc.end.column, node.range[1] - node.loc.end.column + gottenSpaces + gottenTabs]
322
- : [node.range[0] - node.loc.start.column, node.range[0] - node.loc.start.column + gottenSpaces + gottenTabs];
323
-
324
- context.report({
325
- node,
326
- loc,
327
- messageId: "expected",
328
- data: createErrorMessageData(needed, gottenSpaces, gottenTabs),
329
- fix: fixer => fixer.replaceTextRange(textRange, desiredIndent)
330
- });
331
- }
332
-
333
- /**
334
- * Get the actual indent of node
335
- * @param {ASTNode|Token} node Node to examine
336
- * @param {boolean} [byLastLine=false] get indent of node's last line
337
- * @returns {Object} The node's indent. Contains keys `space` and `tab`, representing the indent of each character. Also
338
- * contains keys `goodChar` and `badChar`, where `goodChar` is the amount of the user's desired indentation character, and
339
- * `badChar` is the amount of the other indentation character.
340
- */
341
- function getNodeIndent(node, byLastLine) {
342
- const token = byLastLine ? sourceCode.getLastToken(node) : sourceCode.getFirstToken(node);
343
- const srcCharsBeforeNode = sourceCode.getText(token, token.loc.start.column).split("");
344
- const indentChars = srcCharsBeforeNode.slice(0, srcCharsBeforeNode.findIndex(char => char !== " " && char !== "\t"));
345
- const spaces = indentChars.filter(char => char === " ").length;
346
- const tabs = indentChars.filter(char => char === "\t").length;
347
-
348
- return {
349
- space: spaces,
350
- tab: tabs,
351
- goodChar: indentType === "space" ? spaces : tabs,
352
- badChar: indentType === "space" ? tabs : spaces
353
- };
354
- }
355
-
356
- /**
357
- * Checks node is the first in its own start line. By default it looks by start line.
358
- * @param {ASTNode} node The node to check
359
- * @param {boolean} [byEndLocation=false] Lookup based on start position or end
360
- * @returns {boolean} true if its the first in the its start line
361
- */
362
- function isNodeFirstInLine(node, byEndLocation) {
363
- const firstToken = byEndLocation === true ? sourceCode.getLastToken(node, 1) : sourceCode.getTokenBefore(node),
364
- startLine = byEndLocation === true ? node.loc.end.line : node.loc.start.line,
365
- endLine = firstToken ? firstToken.loc.end.line : -1;
366
-
367
- return startLine !== endLine;
368
- }
369
-
370
- /**
371
- * Check indent for node
372
- * @param {ASTNode} node Node to check
373
- * @param {int} neededIndent needed indent
374
- * @returns {void}
375
- */
376
- function checkNodeIndent(node, neededIndent) {
377
- const actualIndent = getNodeIndent(node, false);
378
-
379
- if (
380
- node.type !== "ArrayExpression" &&
381
- node.type !== "ObjectExpression" &&
382
- (actualIndent.goodChar !== neededIndent || actualIndent.badChar !== 0) &&
383
- isNodeFirstInLine(node)
384
- ) {
385
- report(node, neededIndent, actualIndent.space, actualIndent.tab);
386
- }
387
-
388
- if (node.type === "IfStatement" && node.alternate) {
389
- const elseToken = sourceCode.getTokenBefore(node.alternate);
390
-
391
- checkNodeIndent(elseToken, neededIndent);
392
-
393
- if (!isNodeFirstInLine(node.alternate)) {
394
- checkNodeIndent(node.alternate, neededIndent);
395
- }
396
- }
397
-
398
- if (node.type === "TryStatement" && node.handler) {
399
- const catchToken = sourceCode.getFirstToken(node.handler);
400
-
401
- checkNodeIndent(catchToken, neededIndent);
402
- }
403
-
404
- if (node.type === "TryStatement" && node.finalizer) {
405
- const finallyToken = sourceCode.getTokenBefore(node.finalizer);
406
-
407
- checkNodeIndent(finallyToken, neededIndent);
408
- }
409
-
410
- if (node.type === "DoWhileStatement") {
411
- const whileToken = sourceCode.getTokenAfter(node.body);
412
-
413
- checkNodeIndent(whileToken, neededIndent);
414
- }
415
- }
416
-
417
- /**
418
- * Check indent for nodes list
419
- * @param {ASTNode[]} nodes list of node objects
420
- * @param {int} indent needed indent
421
- * @returns {void}
422
- */
423
- function checkNodesIndent(nodes, indent) {
424
- nodes.forEach(node => checkNodeIndent(node, indent));
425
- }
426
-
427
- /**
428
- * Check last node line indent this detects, that block closed correctly
429
- * @param {ASTNode} node Node to examine
430
- * @param {int} lastLineIndent needed indent
431
- * @returns {void}
432
- */
433
- function checkLastNodeLineIndent(node, lastLineIndent) {
434
- const lastToken = sourceCode.getLastToken(node);
435
- const endIndent = getNodeIndent(lastToken, true);
436
-
437
- if ((endIndent.goodChar !== lastLineIndent || endIndent.badChar !== 0) && isNodeFirstInLine(node, true)) {
438
- report(
439
- node,
440
- lastLineIndent,
441
- endIndent.space,
442
- endIndent.tab,
443
- { line: lastToken.loc.start.line, column: lastToken.loc.start.column },
444
- true
445
- );
446
- }
447
- }
448
-
449
- /**
450
- * Check last node line indent this detects, that block closed correctly
451
- * This function for more complicated return statement case, where closing parenthesis may be followed by ';'
452
- * @param {ASTNode} node Node to examine
453
- * @param {int} firstLineIndent first line needed indent
454
- * @returns {void}
455
- */
456
- function checkLastReturnStatementLineIndent(node, firstLineIndent) {
457
-
458
- /*
459
- * in case if return statement ends with ');' we have traverse back to ')'
460
- * otherwise we'll measure indent for ';' and replace ')'
461
- */
462
- const lastToken = sourceCode.getLastToken(node, astUtils.isClosingParenToken);
463
- const textBeforeClosingParenthesis = sourceCode.getText(lastToken, lastToken.loc.start.column).slice(0, -1);
464
-
465
- if (textBeforeClosingParenthesis.trim()) {
466
-
467
- // There are tokens before the closing paren, don't report this case
468
- return;
469
- }
470
-
471
- const endIndent = getNodeIndent(lastToken, true);
472
-
473
- if (endIndent.goodChar !== firstLineIndent) {
474
- report(
475
- node,
476
- firstLineIndent,
477
- endIndent.space,
478
- endIndent.tab,
479
- { line: lastToken.loc.start.line, column: lastToken.loc.start.column },
480
- true
481
- );
482
- }
483
- }
484
-
485
- /**
486
- * Check first node line indent is correct
487
- * @param {ASTNode} node Node to examine
488
- * @param {int} firstLineIndent needed indent
489
- * @returns {void}
490
- */
491
- function checkFirstNodeLineIndent(node, firstLineIndent) {
492
- const startIndent = getNodeIndent(node, false);
493
-
494
- if ((startIndent.goodChar !== firstLineIndent || startIndent.badChar !== 0) && isNodeFirstInLine(node)) {
495
- report(
496
- node,
497
- firstLineIndent,
498
- startIndent.space,
499
- startIndent.tab,
500
- { line: node.loc.start.line, column: node.loc.start.column }
501
- );
502
- }
503
- }
504
-
505
- /**
506
- * Returns a parent node of given node based on a specified type
507
- * if not present then return null
508
- * @param {ASTNode} node node to examine
509
- * @param {string} type type that is being looked for
510
- * @param {string} stopAtList end points for the evaluating code
511
- * @returns {ASTNode|void} if found then node otherwise null
512
- */
513
- function getParentNodeByType(node, type, stopAtList) {
514
- let parent = node.parent;
515
- const stopAtSet = new Set(stopAtList || ["Program"]);
516
-
517
- while (parent.type !== type && !stopAtSet.has(parent.type) && parent.type !== "Program") {
518
- parent = parent.parent;
519
- }
520
-
521
- return parent.type === type ? parent : null;
522
- }
523
-
524
- /**
525
- * Returns the VariableDeclarator based on the current node
526
- * if not present then return null
527
- * @param {ASTNode} node node to examine
528
- * @returns {ASTNode|void} if found then node otherwise null
529
- */
530
- function getVariableDeclaratorNode(node) {
531
- return getParentNodeByType(node, "VariableDeclarator");
532
- }
533
-
534
- /**
535
- * Check to see if the node is part of the multi-line variable declaration.
536
- * Also if its on the same line as the varNode
537
- * @param {ASTNode} node node to check
538
- * @param {ASTNode} varNode variable declaration node to check against
539
- * @returns {boolean} True if all the above condition satisfy
540
- */
541
- function isNodeInVarOnTop(node, varNode) {
542
- return varNode &&
543
- varNode.parent.loc.start.line === node.loc.start.line &&
544
- varNode.parent.declarations.length > 1;
545
- }
546
-
547
- /**
548
- * Check to see if the argument before the callee node is multi-line and
549
- * there should only be 1 argument before the callee node
550
- * @param {ASTNode} node node to check
551
- * @returns {boolean} True if arguments are multi-line
552
- */
553
- function isArgBeforeCalleeNodeMultiline(node) {
554
- const parent = node.parent;
555
-
556
- if (parent.arguments.length >= 2 && parent.arguments[1] === node) {
557
- return parent.arguments[0].loc.end.line > parent.arguments[0].loc.start.line;
558
- }
559
-
560
- return false;
561
- }
562
-
563
- /**
564
- * Check to see if the node is a file level IIFE
565
- * @param {ASTNode} node The function node to check.
566
- * @returns {boolean} True if the node is the outer IIFE
567
- */
568
- function isOuterIIFE(node) {
569
- const parent = node.parent;
570
- let stmt = parent.parent;
571
-
572
- /*
573
- * Verify that the node is an IIEF
574
- */
575
- if (
576
- parent.type !== "CallExpression" ||
577
- parent.callee !== node) {
578
-
579
- return false;
580
- }
581
-
582
- /*
583
- * Navigate legal ancestors to determine whether this IIEF is outer
584
- */
585
- while (
586
- stmt.type === "UnaryExpression" && (
587
- stmt.operator === "!" ||
588
- stmt.operator === "~" ||
589
- stmt.operator === "+" ||
590
- stmt.operator === "-") ||
591
- stmt.type === "AssignmentExpression" ||
592
- stmt.type === "LogicalExpression" ||
593
- stmt.type === "SequenceExpression" ||
594
- stmt.type === "VariableDeclarator") {
595
-
596
- stmt = stmt.parent;
597
- }
598
-
599
- return ((
600
- stmt.type === "ExpressionStatement" ||
601
- stmt.type === "VariableDeclaration") &&
602
- stmt.parent && stmt.parent.type === "Program"
603
- );
604
- }
605
-
606
- /**
607
- * Check indent for function block content
608
- * @param {ASTNode} node A BlockStatement node that is inside of a function.
609
- * @returns {void}
610
- */
611
- function checkIndentInFunctionBlock(node) {
612
-
613
- /*
614
- * Search first caller in chain.
615
- * Ex.:
616
- *
617
- * Models <- Identifier
618
- * .User
619
- * .find()
620
- * .exec(function() {
621
- * // function body
622
- * });
623
- *
624
- * Looks for 'Models'
625
- */
626
- const calleeNode = node.parent; // FunctionExpression
627
- let indent;
628
-
629
- if (calleeNode.parent &&
630
- (calleeNode.parent.type === "Property" ||
631
- calleeNode.parent.type === "ArrayExpression")) {
632
-
633
- // If function is part of array or object, comma can be put at left
634
- indent = getNodeIndent(calleeNode, false).goodChar;
635
- } else {
636
-
637
- // If function is standalone, simple calculate indent
638
- indent = getNodeIndent(calleeNode).goodChar;
639
- }
640
-
641
- if (calleeNode.parent.type === "CallExpression") {
642
- const calleeParent = calleeNode.parent;
643
-
644
- if (calleeNode.type !== "FunctionExpression" && calleeNode.type !== "ArrowFunctionExpression") {
645
- if (calleeParent && calleeParent.loc.start.line < node.loc.start.line) {
646
- indent = getNodeIndent(calleeParent).goodChar;
647
- }
648
- } else {
649
- if (isArgBeforeCalleeNodeMultiline(calleeNode) &&
650
- calleeParent.callee.loc.start.line === calleeParent.callee.loc.end.line &&
651
- !isNodeFirstInLine(calleeNode)) {
652
- indent = getNodeIndent(calleeParent).goodChar;
653
- }
654
- }
655
- }
656
-
657
- /*
658
- * function body indent should be indent + indent size, unless this
659
- * is a FunctionDeclaration, FunctionExpression, or outer IIFE and the corresponding options are enabled.
660
- */
661
- let functionOffset = indentSize;
662
-
663
- if (options.outerIIFEBody !== null && isOuterIIFE(calleeNode)) {
664
- functionOffset = options.outerIIFEBody * indentSize;
665
- } else if (calleeNode.type === "FunctionExpression") {
666
- functionOffset = options.FunctionExpression.body * indentSize;
667
- } else if (calleeNode.type === "FunctionDeclaration") {
668
- functionOffset = options.FunctionDeclaration.body * indentSize;
669
- }
670
- indent += functionOffset;
671
-
672
- // check if the node is inside a variable
673
- const parentVarNode = getVariableDeclaratorNode(node);
674
-
675
- if (parentVarNode && isNodeInVarOnTop(node, parentVarNode)) {
676
- indent += indentSize * options.VariableDeclarator[parentVarNode.parent.kind];
677
- }
678
-
679
- if (node.body.length > 0) {
680
- checkNodesIndent(node.body, indent);
681
- }
682
-
683
- checkLastNodeLineIndent(node, indent - functionOffset);
684
- }
685
-
686
-
687
- /**
688
- * Checks if the given node starts and ends on the same line
689
- * @param {ASTNode} node The node to check
690
- * @returns {boolean} Whether or not the block starts and ends on the same line.
691
- */
692
- function isSingleLineNode(node) {
693
- const lastToken = sourceCode.getLastToken(node),
694
- startLine = node.loc.start.line,
695
- endLine = lastToken.loc.end.line;
696
-
697
- return startLine === endLine;
698
- }
699
-
700
- /**
701
- * Check indent for array block content or object block content
702
- * @param {ASTNode} node node to examine
703
- * @returns {void}
704
- */
705
- function checkIndentInArrayOrObjectBlock(node) {
706
-
707
- // Skip inline
708
- if (isSingleLineNode(node)) {
709
- return;
710
- }
711
-
712
- let elements = (node.type === "ArrayExpression") ? node.elements : node.properties;
713
-
714
- // filter out empty elements example would be [ , 2] so remove first element as espree considers it as null
715
- elements = elements.filter(elem => elem !== null);
716
-
717
- let nodeIndent;
718
- let elementsIndent;
719
- const parentVarNode = getVariableDeclaratorNode(node);
720
-
721
- // TODO - come up with a better strategy in future
722
- if (isNodeFirstInLine(node)) {
723
- const parent = node.parent;
724
-
725
- nodeIndent = getNodeIndent(parent).goodChar;
726
- if (!parentVarNode || parentVarNode.loc.start.line !== node.loc.start.line) {
727
- if (parent.type !== "VariableDeclarator" || parentVarNode === parentVarNode.parent.declarations[0]) {
728
- if (parent.type === "VariableDeclarator" && parentVarNode.loc.start.line === parent.loc.start.line) {
729
- nodeIndent += (indentSize * options.VariableDeclarator[parentVarNode.parent.kind]);
730
- } else if (parent.type === "ObjectExpression" || parent.type === "ArrayExpression") {
731
- const parentElements = node.parent.type === "ObjectExpression" ? node.parent.properties : node.parent.elements;
732
-
733
- if (parentElements[0] &&
734
- parentElements[0].loc.start.line === parent.loc.start.line &&
735
- parentElements[0].loc.end.line !== parent.loc.start.line) {
736
-
737
- /*
738
- * If the first element of the array spans multiple lines, don't increase the expected indentation of the rest.
739
- * e.g. [{
740
- * foo: 1
741
- * },
742
- * {
743
- * bar: 1
744
- * }]
745
- * the second object is not indented.
746
- */
747
- } else if (typeof options[parent.type] === "number") {
748
- nodeIndent += options[parent.type] * indentSize;
749
- } else {
750
- nodeIndent = parentElements[0].loc.start.column;
751
- }
752
- } else if (parent.type === "CallExpression" || parent.type === "NewExpression") {
753
- if (typeof options.CallExpression.arguments === "number") {
754
- nodeIndent += options.CallExpression.arguments * indentSize;
755
- } else if (options.CallExpression.arguments === "first") {
756
- if (parent.arguments.includes(node)) {
757
- nodeIndent = parent.arguments[0].loc.start.column;
758
- }
759
- } else {
760
- nodeIndent += indentSize;
761
- }
762
- } else if (parent.type === "LogicalExpression" || parent.type === "ArrowFunctionExpression") {
763
- nodeIndent += indentSize;
764
- }
765
- }
766
- }
767
-
768
- checkFirstNodeLineIndent(node, nodeIndent);
769
- } else {
770
- nodeIndent = getNodeIndent(node).goodChar;
771
- }
772
-
773
- if (options[node.type] === "first") {
774
- elementsIndent = elements.length ? elements[0].loc.start.column : 0; // If there are no elements, elementsIndent doesn't matter.
775
- } else {
776
- elementsIndent = nodeIndent + indentSize * options[node.type];
777
- }
778
-
779
- /*
780
- * Check if the node is a multiple variable declaration; if so, then
781
- * make sure indentation takes that into account.
782
- */
783
- if (isNodeInVarOnTop(node, parentVarNode)) {
784
- elementsIndent += indentSize * options.VariableDeclarator[parentVarNode.parent.kind];
785
- }
786
-
787
- checkNodesIndent(elements, elementsIndent);
788
-
789
- if (elements.length > 0) {
790
-
791
- // Skip last block line check if last item in same line
792
- if (elements[elements.length - 1].loc.end.line === node.loc.end.line) {
793
- return;
794
- }
795
- }
796
-
797
- checkLastNodeLineIndent(node, nodeIndent +
798
- (isNodeInVarOnTop(node, parentVarNode) ? options.VariableDeclarator[parentVarNode.parent.kind] * indentSize : 0));
799
- }
800
-
801
- /**
802
- * Check if the node or node body is a BlockStatement or not
803
- * @param {ASTNode} node node to test
804
- * @returns {boolean} True if it or its body is a block statement
805
- */
806
- function isNodeBodyBlock(node) {
807
- return node.type === "BlockStatement" || node.type === "ClassBody" || (node.body && node.body.type === "BlockStatement") ||
808
- (node.consequent && node.consequent.type === "BlockStatement");
809
- }
810
-
811
- /**
812
- * Check indentation for blocks
813
- * @param {ASTNode} node node to check
814
- * @returns {void}
815
- */
816
- function blockIndentationCheck(node) {
817
-
818
- // Skip inline blocks
819
- if (isSingleLineNode(node)) {
820
- return;
821
- }
822
-
823
- if (node.parent && (
824
- node.parent.type === "FunctionExpression" ||
825
- node.parent.type === "FunctionDeclaration" ||
826
- node.parent.type === "ArrowFunctionExpression")
827
- ) {
828
- checkIndentInFunctionBlock(node);
829
- return;
830
- }
831
-
832
- let indent;
833
- let nodesToCheck = [];
834
-
835
- /*
836
- * For this statements we should check indent from statement beginning,
837
- * not from the beginning of the block.
838
- */
839
- const statementsWithProperties = [
840
- "IfStatement", "WhileStatement", "ForStatement", "ForInStatement", "ForOfStatement", "DoWhileStatement", "ClassDeclaration", "TryStatement"
841
- ];
842
-
843
- if (node.parent && statementsWithProperties.includes(node.parent.type) && isNodeBodyBlock(node)) {
844
- indent = getNodeIndent(node.parent).goodChar;
845
- } else if (node.parent && node.parent.type === "CatchClause") {
846
- indent = getNodeIndent(node.parent.parent).goodChar;
847
- } else {
848
- indent = getNodeIndent(node).goodChar;
849
- }
850
-
851
- if (node.type === "IfStatement" && node.consequent.type !== "BlockStatement") {
852
- nodesToCheck = [node.consequent];
853
- } else if (Array.isArray(node.body)) {
854
- nodesToCheck = node.body;
855
- } else {
856
- nodesToCheck = [node.body];
857
- }
858
-
859
- if (nodesToCheck.length > 0) {
860
- checkNodesIndent(nodesToCheck, indent + indentSize);
861
- }
862
-
863
- if (node.type === "BlockStatement") {
864
- checkLastNodeLineIndent(node, indent);
865
- }
866
- }
867
-
868
- /**
869
- * Filter out the elements which are on the same line of each other or the node.
870
- * basically have only 1 elements from each line except the variable declaration line.
871
- * @param {ASTNode} node Variable declaration node
872
- * @returns {ASTNode[]} Filtered elements
873
- */
874
- function filterOutSameLineVars(node) {
875
- return node.declarations.reduce((finalCollection, elem) => {
876
- const lastElem = finalCollection[finalCollection.length - 1];
877
-
878
- if ((elem.loc.start.line !== node.loc.start.line && !lastElem) ||
879
- (lastElem && lastElem.loc.start.line !== elem.loc.start.line)) {
880
- finalCollection.push(elem);
881
- }
882
-
883
- return finalCollection;
884
- }, []);
885
- }
886
-
887
- /**
888
- * Check indentation for variable declarations
889
- * @param {ASTNode} node node to examine
890
- * @returns {void}
891
- */
892
- function checkIndentInVariableDeclarations(node) {
893
- const elements = filterOutSameLineVars(node);
894
- const nodeIndent = getNodeIndent(node).goodChar;
895
- const lastElement = elements[elements.length - 1];
896
-
897
- const elementsIndent = nodeIndent + indentSize * options.VariableDeclarator[node.kind];
898
-
899
- checkNodesIndent(elements, elementsIndent);
900
-
901
- // Only check the last line if there is any token after the last item
902
- if (sourceCode.getLastToken(node).loc.end.line <= lastElement.loc.end.line) {
903
- return;
904
- }
905
-
906
- const tokenBeforeLastElement = sourceCode.getTokenBefore(lastElement);
907
-
908
- if (tokenBeforeLastElement.value === ",") {
909
-
910
- // Special case for comma-first syntax where the semicolon is indented
911
- checkLastNodeLineIndent(node, getNodeIndent(tokenBeforeLastElement).goodChar);
912
- } else {
913
- checkLastNodeLineIndent(node, elementsIndent - indentSize);
914
- }
915
- }
916
-
917
- /**
918
- * Check and decide whether to check for indentation for blockless nodes
919
- * Scenarios are for or while statements without braces around them
920
- * @param {ASTNode} node node to examine
921
- * @returns {void}
922
- */
923
- function blockLessNodes(node) {
924
- if (node.body.type !== "BlockStatement") {
925
- blockIndentationCheck(node);
926
- }
927
- }
928
-
929
- /**
930
- * Returns the expected indentation for the case statement
931
- * @param {ASTNode} node node to examine
932
- * @param {int} [providedSwitchIndent] indent for switch statement
933
- * @returns {int} indent size
934
- */
935
- function expectedCaseIndent(node, providedSwitchIndent) {
936
- const switchNode = (node.type === "SwitchStatement") ? node : node.parent;
937
- const switchIndent = typeof providedSwitchIndent === "undefined"
938
- ? getNodeIndent(switchNode).goodChar
939
- : providedSwitchIndent;
940
- let caseIndent;
941
-
942
- if (caseIndentStore[switchNode.loc.start.line]) {
943
- return caseIndentStore[switchNode.loc.start.line];
944
- }
945
-
946
- if (switchNode.cases.length > 0 && options.SwitchCase === 0) {
947
- caseIndent = switchIndent;
948
- } else {
949
- caseIndent = switchIndent + (indentSize * options.SwitchCase);
950
- }
951
-
952
- caseIndentStore[switchNode.loc.start.line] = caseIndent;
953
- return caseIndent;
954
-
955
- }
956
-
957
- /**
958
- * Checks whether a return statement is wrapped in ()
959
- * @param {ASTNode} node node to examine
960
- * @returns {boolean} the result
961
- */
962
- function isWrappedInParenthesis(node) {
963
- const regex = /^return\s*?\(\s*?\);*?/u;
964
-
965
- const statementWithoutArgument = sourceCode.getText(node).replace(
966
- sourceCode.getText(node.argument), ""
967
- );
968
-
969
- return regex.test(statementWithoutArgument);
970
- }
971
-
972
- return {
973
- Program(node) {
974
- if (node.body.length > 0) {
975
-
976
- // Root nodes should have no indent
977
- checkNodesIndent(node.body, getNodeIndent(node).goodChar);
978
- }
979
- },
980
-
981
- ClassBody: blockIndentationCheck,
982
-
983
- BlockStatement: blockIndentationCheck,
984
-
985
- WhileStatement: blockLessNodes,
986
-
987
- ForStatement: blockLessNodes,
988
-
989
- ForInStatement: blockLessNodes,
990
-
991
- ForOfStatement: blockLessNodes,
992
-
993
- DoWhileStatement: blockLessNodes,
994
-
995
- IfStatement(node) {
996
- if (node.consequent.type !== "BlockStatement" && node.consequent.loc.start.line > node.loc.start.line) {
997
- blockIndentationCheck(node);
998
- }
999
- },
1000
-
1001
- VariableDeclaration(node) {
1002
- if (node.declarations[node.declarations.length - 1].loc.start.line > node.declarations[0].loc.start.line) {
1003
- checkIndentInVariableDeclarations(node);
1004
- }
1005
- },
1006
-
1007
- ObjectExpression(node) {
1008
- checkIndentInArrayOrObjectBlock(node);
1009
- },
1010
-
1011
- ArrayExpression(node) {
1012
- checkIndentInArrayOrObjectBlock(node);
1013
- },
1014
-
1015
- MemberExpression(node) {
1016
-
1017
- if (typeof options.MemberExpression === "undefined") {
1018
- return;
1019
- }
1020
-
1021
- if (isSingleLineNode(node)) {
1022
- return;
1023
- }
1024
-
1025
- /*
1026
- * The typical layout of variable declarations and assignments
1027
- * alter the expectation of correct indentation. Skip them.
1028
- * TODO: Add appropriate configuration options for variable
1029
- * declarations and assignments.
1030
- */
1031
- if (getParentNodeByType(node, "VariableDeclarator", ["FunctionExpression", "ArrowFunctionExpression"])) {
1032
- return;
1033
- }
1034
-
1035
- if (getParentNodeByType(node, "AssignmentExpression", ["FunctionExpression"])) {
1036
- return;
1037
- }
1038
-
1039
- const propertyIndent = getNodeIndent(node).goodChar + indentSize * options.MemberExpression;
1040
-
1041
- const checkNodes = [node.property];
1042
-
1043
- const dot = sourceCode.getTokenBefore(node.property);
1044
-
1045
- if (dot.type === "Punctuator" && dot.value === ".") {
1046
- checkNodes.push(dot);
1047
- }
1048
-
1049
- checkNodesIndent(checkNodes, propertyIndent);
1050
- },
1051
-
1052
- SwitchStatement(node) {
1053
-
1054
- // Switch is not a 'BlockStatement'
1055
- const switchIndent = getNodeIndent(node).goodChar;
1056
- const caseIndent = expectedCaseIndent(node, switchIndent);
1057
-
1058
- checkNodesIndent(node.cases, caseIndent);
1059
-
1060
-
1061
- checkLastNodeLineIndent(node, switchIndent);
1062
- },
1063
-
1064
- SwitchCase(node) {
1065
-
1066
- // Skip inline cases
1067
- if (isSingleLineNode(node)) {
1068
- return;
1069
- }
1070
- const caseIndent = expectedCaseIndent(node);
1071
-
1072
- checkNodesIndent(node.consequent, caseIndent + indentSize);
1073
- },
1074
-
1075
- FunctionDeclaration(node) {
1076
- if (isSingleLineNode(node)) {
1077
- return;
1078
- }
1079
- if (options.FunctionDeclaration.parameters === "first" && node.params.length) {
1080
- checkNodesIndent(node.params.slice(1), node.params[0].loc.start.column);
1081
- } else if (options.FunctionDeclaration.parameters !== null) {
1082
- checkNodesIndent(node.params, getNodeIndent(node).goodChar + indentSize * options.FunctionDeclaration.parameters);
1083
- }
1084
- },
1085
-
1086
- FunctionExpression(node) {
1087
- if (isSingleLineNode(node)) {
1088
- return;
1089
- }
1090
- if (options.FunctionExpression.parameters === "first" && node.params.length) {
1091
- checkNodesIndent(node.params.slice(1), node.params[0].loc.start.column);
1092
- } else if (options.FunctionExpression.parameters !== null) {
1093
- checkNodesIndent(node.params, getNodeIndent(node).goodChar + indentSize * options.FunctionExpression.parameters);
1094
- }
1095
- },
1096
-
1097
- ReturnStatement(node) {
1098
- if (isSingleLineNode(node)) {
1099
- return;
1100
- }
1101
-
1102
- const firstLineIndent = getNodeIndent(node).goodChar;
1103
-
1104
- // in case if return statement is wrapped in parenthesis
1105
- if (isWrappedInParenthesis(node)) {
1106
- checkLastReturnStatementLineIndent(node, firstLineIndent);
1107
- } else {
1108
- checkNodeIndent(node, firstLineIndent);
1109
- }
1110
- },
1111
-
1112
- CallExpression(node) {
1113
- if (isSingleLineNode(node)) {
1114
- return;
1115
- }
1116
- if (options.CallExpression.arguments === "first" && node.arguments.length) {
1117
- checkNodesIndent(node.arguments.slice(1), node.arguments[0].loc.start.column);
1118
- } else if (options.CallExpression.arguments !== null) {
1119
- checkNodesIndent(node.arguments, getNodeIndent(node).goodChar + indentSize * options.CallExpression.arguments);
1120
- }
1121
- }
1122
-
1123
- };
1124
-
1125
- }
25
+ meta: {
26
+ type: "layout",
27
+
28
+ docs: {
29
+ description: "Enforce consistent indentation",
30
+ recommended: false,
31
+ url: "https://eslint.org/docs/latest/rules/indent-legacy",
32
+ },
33
+
34
+ deprecated: {
35
+ message: "Formatting rules are being moved out of ESLint core.",
36
+ url: "https://eslint.org/blog/2023/10/deprecating-formatting-rules/",
37
+ deprecatedSince: "4.0.0",
38
+ availableUntil: "11.0.0",
39
+ replacedBy: [
40
+ {
41
+ message:
42
+ "ESLint Stylistic now maintains deprecated stylistic core rules.",
43
+ url: "https://eslint.style/guide/migration",
44
+ plugin: {
45
+ name: "@stylistic/eslint-plugin",
46
+ url: "https://eslint.style",
47
+ },
48
+ rule: {
49
+ name: "indent",
50
+ url: "https://eslint.style/rules/indent",
51
+ },
52
+ },
53
+ ],
54
+ },
55
+
56
+ fixable: "whitespace",
57
+
58
+ schema: [
59
+ {
60
+ oneOf: [
61
+ {
62
+ enum: ["tab"],
63
+ },
64
+ {
65
+ type: "integer",
66
+ minimum: 0,
67
+ },
68
+ ],
69
+ },
70
+ {
71
+ type: "object",
72
+ properties: {
73
+ SwitchCase: {
74
+ type: "integer",
75
+ minimum: 0,
76
+ },
77
+ VariableDeclarator: {
78
+ oneOf: [
79
+ {
80
+ type: "integer",
81
+ minimum: 0,
82
+ },
83
+ {
84
+ type: "object",
85
+ properties: {
86
+ var: {
87
+ type: "integer",
88
+ minimum: 0,
89
+ },
90
+ let: {
91
+ type: "integer",
92
+ minimum: 0,
93
+ },
94
+ const: {
95
+ type: "integer",
96
+ minimum: 0,
97
+ },
98
+ },
99
+ },
100
+ ],
101
+ },
102
+ outerIIFEBody: {
103
+ type: "integer",
104
+ minimum: 0,
105
+ },
106
+ MemberExpression: {
107
+ type: "integer",
108
+ minimum: 0,
109
+ },
110
+ FunctionDeclaration: {
111
+ type: "object",
112
+ properties: {
113
+ parameters: {
114
+ oneOf: [
115
+ {
116
+ type: "integer",
117
+ minimum: 0,
118
+ },
119
+ {
120
+ enum: ["first"],
121
+ },
122
+ ],
123
+ },
124
+ body: {
125
+ type: "integer",
126
+ minimum: 0,
127
+ },
128
+ },
129
+ },
130
+ FunctionExpression: {
131
+ type: "object",
132
+ properties: {
133
+ parameters: {
134
+ oneOf: [
135
+ {
136
+ type: "integer",
137
+ minimum: 0,
138
+ },
139
+ {
140
+ enum: ["first"],
141
+ },
142
+ ],
143
+ },
144
+ body: {
145
+ type: "integer",
146
+ minimum: 0,
147
+ },
148
+ },
149
+ },
150
+ CallExpression: {
151
+ type: "object",
152
+ properties: {
153
+ parameters: {
154
+ oneOf: [
155
+ {
156
+ type: "integer",
157
+ minimum: 0,
158
+ },
159
+ {
160
+ enum: ["first"],
161
+ },
162
+ ],
163
+ },
164
+ },
165
+ },
166
+ ArrayExpression: {
167
+ oneOf: [
168
+ {
169
+ type: "integer",
170
+ minimum: 0,
171
+ },
172
+ {
173
+ enum: ["first"],
174
+ },
175
+ ],
176
+ },
177
+ ObjectExpression: {
178
+ oneOf: [
179
+ {
180
+ type: "integer",
181
+ minimum: 0,
182
+ },
183
+ {
184
+ enum: ["first"],
185
+ },
186
+ ],
187
+ },
188
+ },
189
+ additionalProperties: false,
190
+ },
191
+ ],
192
+ messages: {
193
+ expected:
194
+ "Expected indentation of {{expected}} but found {{actual}}.",
195
+ },
196
+ },
197
+
198
+ create(context) {
199
+ const DEFAULT_VARIABLE_INDENT = 1;
200
+ const DEFAULT_PARAMETER_INDENT = null; // For backwards compatibility, don't check parameter indentation unless specified in the config
201
+ const DEFAULT_FUNCTION_BODY_INDENT = 1;
202
+
203
+ let indentType = "space";
204
+ let indentSize = 4;
205
+ const options = {
206
+ SwitchCase: 0,
207
+ VariableDeclarator: {
208
+ var: DEFAULT_VARIABLE_INDENT,
209
+ let: DEFAULT_VARIABLE_INDENT,
210
+ const: DEFAULT_VARIABLE_INDENT,
211
+ },
212
+ outerIIFEBody: null,
213
+ FunctionDeclaration: {
214
+ parameters: DEFAULT_PARAMETER_INDENT,
215
+ body: DEFAULT_FUNCTION_BODY_INDENT,
216
+ },
217
+ FunctionExpression: {
218
+ parameters: DEFAULT_PARAMETER_INDENT,
219
+ body: DEFAULT_FUNCTION_BODY_INDENT,
220
+ },
221
+ CallExpression: {
222
+ arguments: DEFAULT_PARAMETER_INDENT,
223
+ },
224
+ ArrayExpression: 1,
225
+ ObjectExpression: 1,
226
+ };
227
+
228
+ const sourceCode = context.sourceCode;
229
+
230
+ if (context.options.length) {
231
+ if (context.options[0] === "tab") {
232
+ indentSize = 1;
233
+ indentType = "tab";
234
+ } /* c8 ignore start */ else if (
235
+ typeof context.options[0] === "number"
236
+ ) {
237
+ indentSize = context.options[0];
238
+ indentType = "space";
239
+ } /* c8 ignore stop */
240
+
241
+ if (context.options[1]) {
242
+ const opts = context.options[1];
243
+
244
+ options.SwitchCase = opts.SwitchCase || 0;
245
+ const variableDeclaratorRules = opts.VariableDeclarator;
246
+
247
+ if (typeof variableDeclaratorRules === "number") {
248
+ options.VariableDeclarator = {
249
+ var: variableDeclaratorRules,
250
+ let: variableDeclaratorRules,
251
+ const: variableDeclaratorRules,
252
+ };
253
+ } else if (typeof variableDeclaratorRules === "object") {
254
+ Object.assign(
255
+ options.VariableDeclarator,
256
+ variableDeclaratorRules,
257
+ );
258
+ }
259
+
260
+ if (typeof opts.outerIIFEBody === "number") {
261
+ options.outerIIFEBody = opts.outerIIFEBody;
262
+ }
263
+
264
+ if (typeof opts.MemberExpression === "number") {
265
+ options.MemberExpression = opts.MemberExpression;
266
+ }
267
+
268
+ if (typeof opts.FunctionDeclaration === "object") {
269
+ Object.assign(
270
+ options.FunctionDeclaration,
271
+ opts.FunctionDeclaration,
272
+ );
273
+ }
274
+
275
+ if (typeof opts.FunctionExpression === "object") {
276
+ Object.assign(
277
+ options.FunctionExpression,
278
+ opts.FunctionExpression,
279
+ );
280
+ }
281
+
282
+ if (typeof opts.CallExpression === "object") {
283
+ Object.assign(options.CallExpression, opts.CallExpression);
284
+ }
285
+
286
+ if (
287
+ typeof opts.ArrayExpression === "number" ||
288
+ typeof opts.ArrayExpression === "string"
289
+ ) {
290
+ options.ArrayExpression = opts.ArrayExpression;
291
+ }
292
+
293
+ if (
294
+ typeof opts.ObjectExpression === "number" ||
295
+ typeof opts.ObjectExpression === "string"
296
+ ) {
297
+ options.ObjectExpression = opts.ObjectExpression;
298
+ }
299
+ }
300
+ }
301
+
302
+ const caseIndentStore = {};
303
+
304
+ /**
305
+ * Creates an error message for a line, given the expected/actual indentation.
306
+ * @param {number} expectedAmount The expected amount of indentation characters for this line
307
+ * @param {number} actualSpaces The actual number of indentation spaces that were found on this line
308
+ * @param {number} actualTabs The actual number of indentation tabs that were found on this line
309
+ * @returns {string} An error message for this line
310
+ */
311
+ function createErrorMessageData(
312
+ expectedAmount,
313
+ actualSpaces,
314
+ actualTabs,
315
+ ) {
316
+ const expectedStatement = `${expectedAmount} ${indentType}${expectedAmount === 1 ? "" : "s"}`; // e.g. "2 tabs"
317
+ const foundSpacesWord = `space${actualSpaces === 1 ? "" : "s"}`; // e.g. "space"
318
+ const foundTabsWord = `tab${actualTabs === 1 ? "" : "s"}`; // e.g. "tabs"
319
+ let foundStatement;
320
+
321
+ if (actualSpaces > 0 && actualTabs > 0) {
322
+ foundStatement = `${actualSpaces} ${foundSpacesWord} and ${actualTabs} ${foundTabsWord}`; // e.g. "1 space and 2 tabs"
323
+ } else if (actualSpaces > 0) {
324
+ /*
325
+ * Abbreviate the message if the expected indentation is also spaces.
326
+ * e.g. 'Expected 4 spaces but found 2' rather than 'Expected 4 spaces but found 2 spaces'
327
+ */
328
+ foundStatement =
329
+ indentType === "space"
330
+ ? actualSpaces
331
+ : `${actualSpaces} ${foundSpacesWord}`;
332
+ } else if (actualTabs > 0) {
333
+ foundStatement =
334
+ indentType === "tab"
335
+ ? actualTabs
336
+ : `${actualTabs} ${foundTabsWord}`;
337
+ } else {
338
+ foundStatement = "0";
339
+ }
340
+ return {
341
+ expected: expectedStatement,
342
+ actual: foundStatement,
343
+ };
344
+ }
345
+
346
+ /**
347
+ * Reports a given indent violation
348
+ * @param {ASTNode} node Node violating the indent rule
349
+ * @param {number} needed Expected indentation character count
350
+ * @param {number} gottenSpaces Indentation space count in the actual node/code
351
+ * @param {number} gottenTabs Indentation tab count in the actual node/code
352
+ * @param {Object} [loc] Error line and column location
353
+ * @param {boolean} isLastNodeCheck Is the error for last node check
354
+ * @returns {void}
355
+ */
356
+ function report(
357
+ node,
358
+ needed,
359
+ gottenSpaces,
360
+ gottenTabs,
361
+ loc,
362
+ isLastNodeCheck,
363
+ ) {
364
+ if (gottenSpaces && gottenTabs) {
365
+ // To avoid conflicts with `no-mixed-spaces-and-tabs`, don't report lines that have both spaces and tabs.
366
+ return;
367
+ }
368
+
369
+ const desiredIndent = (indentType === "space" ? " " : "\t").repeat(
370
+ needed,
371
+ );
372
+
373
+ const textRange = isLastNodeCheck
374
+ ? [
375
+ node.range[1] - node.loc.end.column,
376
+ node.range[1] -
377
+ node.loc.end.column +
378
+ gottenSpaces +
379
+ gottenTabs,
380
+ ]
381
+ : [
382
+ node.range[0] - node.loc.start.column,
383
+ node.range[0] -
384
+ node.loc.start.column +
385
+ gottenSpaces +
386
+ gottenTabs,
387
+ ];
388
+
389
+ context.report({
390
+ node,
391
+ loc,
392
+ messageId: "expected",
393
+ data: createErrorMessageData(needed, gottenSpaces, gottenTabs),
394
+ fix: fixer => fixer.replaceTextRange(textRange, desiredIndent),
395
+ });
396
+ }
397
+
398
+ /**
399
+ * Get the actual indent of node
400
+ * @param {ASTNode|Token} node Node to examine
401
+ * @param {boolean} [byLastLine=false] get indent of node's last line
402
+ * @returns {Object} The node's indent. Contains keys `space` and `tab`, representing the indent of each character. Also
403
+ * contains keys `goodChar` and `badChar`, where `goodChar` is the amount of the user's desired indentation character, and
404
+ * `badChar` is the amount of the other indentation character.
405
+ */
406
+ function getNodeIndent(node, byLastLine) {
407
+ const token = byLastLine
408
+ ? sourceCode.getLastToken(node)
409
+ : sourceCode.getFirstToken(node);
410
+ const srcCharsBeforeNode = sourceCode
411
+ .getText(token, token.loc.start.column)
412
+ .split("");
413
+ const indentChars = srcCharsBeforeNode.slice(
414
+ 0,
415
+ srcCharsBeforeNode.findIndex(
416
+ char => char !== " " && char !== "\t",
417
+ ),
418
+ );
419
+ const spaces = indentChars.filter(char => char === " ").length;
420
+ const tabs = indentChars.filter(char => char === "\t").length;
421
+
422
+ return {
423
+ space: spaces,
424
+ tab: tabs,
425
+ goodChar: indentType === "space" ? spaces : tabs,
426
+ badChar: indentType === "space" ? tabs : spaces,
427
+ };
428
+ }
429
+
430
+ /**
431
+ * Checks node is the first in its own start line. By default it looks by start line.
432
+ * @param {ASTNode} node The node to check
433
+ * @param {boolean} [byEndLocation=false] Lookup based on start position or end
434
+ * @returns {boolean} true if its the first in the its start line
435
+ */
436
+ function isNodeFirstInLine(node, byEndLocation) {
437
+ const firstToken =
438
+ byEndLocation === true
439
+ ? sourceCode.getLastToken(node, 1)
440
+ : sourceCode.getTokenBefore(node),
441
+ startLine =
442
+ byEndLocation === true
443
+ ? node.loc.end.line
444
+ : node.loc.start.line,
445
+ endLine = firstToken ? firstToken.loc.end.line : -1;
446
+
447
+ return startLine !== endLine;
448
+ }
449
+
450
+ /**
451
+ * Check indent for node
452
+ * @param {ASTNode} node Node to check
453
+ * @param {number} neededIndent needed indent
454
+ * @returns {void}
455
+ */
456
+ function checkNodeIndent(node, neededIndent) {
457
+ const actualIndent = getNodeIndent(node, false);
458
+
459
+ if (
460
+ node.type !== "ArrayExpression" &&
461
+ node.type !== "ObjectExpression" &&
462
+ (actualIndent.goodChar !== neededIndent ||
463
+ actualIndent.badChar !== 0) &&
464
+ isNodeFirstInLine(node)
465
+ ) {
466
+ report(
467
+ node,
468
+ neededIndent,
469
+ actualIndent.space,
470
+ actualIndent.tab,
471
+ );
472
+ }
473
+
474
+ if (node.type === "IfStatement" && node.alternate) {
475
+ const elseToken = sourceCode.getTokenBefore(node.alternate);
476
+
477
+ checkNodeIndent(elseToken, neededIndent);
478
+
479
+ if (!isNodeFirstInLine(node.alternate)) {
480
+ checkNodeIndent(node.alternate, neededIndent);
481
+ }
482
+ }
483
+
484
+ if (node.type === "TryStatement" && node.handler) {
485
+ const catchToken = sourceCode.getFirstToken(node.handler);
486
+
487
+ checkNodeIndent(catchToken, neededIndent);
488
+ }
489
+
490
+ if (node.type === "TryStatement" && node.finalizer) {
491
+ const finallyToken = sourceCode.getTokenBefore(node.finalizer);
492
+
493
+ checkNodeIndent(finallyToken, neededIndent);
494
+ }
495
+
496
+ if (node.type === "DoWhileStatement") {
497
+ const whileToken = sourceCode.getTokenAfter(node.body);
498
+
499
+ checkNodeIndent(whileToken, neededIndent);
500
+ }
501
+ }
502
+
503
+ /**
504
+ * Check indent for nodes list
505
+ * @param {ASTNode[]} nodes list of node objects
506
+ * @param {number} indent needed indent
507
+ * @returns {void}
508
+ */
509
+ function checkNodesIndent(nodes, indent) {
510
+ nodes.forEach(node => checkNodeIndent(node, indent));
511
+ }
512
+
513
+ /**
514
+ * Check last node line indent this detects, that block closed correctly
515
+ * @param {ASTNode} node Node to examine
516
+ * @param {number} lastLineIndent needed indent
517
+ * @returns {void}
518
+ */
519
+ function checkLastNodeLineIndent(node, lastLineIndent) {
520
+ const lastToken = sourceCode.getLastToken(node);
521
+ const endIndent = getNodeIndent(lastToken, true);
522
+
523
+ if (
524
+ (endIndent.goodChar !== lastLineIndent ||
525
+ endIndent.badChar !== 0) &&
526
+ isNodeFirstInLine(node, true)
527
+ ) {
528
+ report(
529
+ node,
530
+ lastLineIndent,
531
+ endIndent.space,
532
+ endIndent.tab,
533
+ {
534
+ line: lastToken.loc.start.line,
535
+ column: lastToken.loc.start.column,
536
+ },
537
+ true,
538
+ );
539
+ }
540
+ }
541
+
542
+ /**
543
+ * Check last node line indent this detects, that block closed correctly
544
+ * This function for more complicated return statement case, where closing parenthesis may be followed by ';'
545
+ * @param {ASTNode} node Node to examine
546
+ * @param {number} firstLineIndent first line needed indent
547
+ * @returns {void}
548
+ */
549
+ function checkLastReturnStatementLineIndent(node, firstLineIndent) {
550
+ /*
551
+ * in case if return statement ends with ');' we have traverse back to ')'
552
+ * otherwise we'll measure indent for ';' and replace ')'
553
+ */
554
+ const lastToken = sourceCode.getLastToken(
555
+ node,
556
+ astUtils.isClosingParenToken,
557
+ );
558
+ const textBeforeClosingParenthesis = sourceCode
559
+ .getText(lastToken, lastToken.loc.start.column)
560
+ .slice(0, -1);
561
+
562
+ if (textBeforeClosingParenthesis.trim()) {
563
+ // There are tokens before the closing paren, don't report this case
564
+ return;
565
+ }
566
+
567
+ const endIndent = getNodeIndent(lastToken, true);
568
+
569
+ if (endIndent.goodChar !== firstLineIndent) {
570
+ report(
571
+ node,
572
+ firstLineIndent,
573
+ endIndent.space,
574
+ endIndent.tab,
575
+ {
576
+ line: lastToken.loc.start.line,
577
+ column: lastToken.loc.start.column,
578
+ },
579
+ true,
580
+ );
581
+ }
582
+ }
583
+
584
+ /**
585
+ * Check first node line indent is correct
586
+ * @param {ASTNode} node Node to examine
587
+ * @param {number} firstLineIndent needed indent
588
+ * @returns {void}
589
+ */
590
+ function checkFirstNodeLineIndent(node, firstLineIndent) {
591
+ const startIndent = getNodeIndent(node, false);
592
+
593
+ if (
594
+ (startIndent.goodChar !== firstLineIndent ||
595
+ startIndent.badChar !== 0) &&
596
+ isNodeFirstInLine(node)
597
+ ) {
598
+ report(
599
+ node,
600
+ firstLineIndent,
601
+ startIndent.space,
602
+ startIndent.tab,
603
+ {
604
+ line: node.loc.start.line,
605
+ column: node.loc.start.column,
606
+ },
607
+ );
608
+ }
609
+ }
610
+
611
+ /**
612
+ * Returns a parent node of given node based on a specified type
613
+ * if not present then return null
614
+ * @param {ASTNode} node node to examine
615
+ * @param {string} type type that is being looked for
616
+ * @param {string} stopAtList end points for the evaluating code
617
+ * @returns {ASTNode|void} if found then node otherwise null
618
+ */
619
+ function getParentNodeByType(node, type, stopAtList) {
620
+ let parent = node.parent;
621
+ const stopAtSet = new Set(stopAtList || ["Program"]);
622
+
623
+ while (
624
+ parent.type !== type &&
625
+ !stopAtSet.has(parent.type) &&
626
+ parent.type !== "Program"
627
+ ) {
628
+ parent = parent.parent;
629
+ }
630
+
631
+ return parent.type === type ? parent : null;
632
+ }
633
+
634
+ /**
635
+ * Returns the VariableDeclarator based on the current node
636
+ * if not present then return null
637
+ * @param {ASTNode} node node to examine
638
+ * @returns {ASTNode|void} if found then node otherwise null
639
+ */
640
+ function getVariableDeclaratorNode(node) {
641
+ return getParentNodeByType(node, "VariableDeclarator");
642
+ }
643
+
644
+ /**
645
+ * Check to see if the node is part of the multi-line variable declaration.
646
+ * Also if its on the same line as the varNode
647
+ * @param {ASTNode} node node to check
648
+ * @param {ASTNode} varNode variable declaration node to check against
649
+ * @returns {boolean} True if all the above condition satisfy
650
+ */
651
+ function isNodeInVarOnTop(node, varNode) {
652
+ return (
653
+ varNode &&
654
+ varNode.parent.loc.start.line === node.loc.start.line &&
655
+ varNode.parent.declarations.length > 1
656
+ );
657
+ }
658
+
659
+ /**
660
+ * Check to see if the argument before the callee node is multi-line and
661
+ * there should only be 1 argument before the callee node
662
+ * @param {ASTNode} node node to check
663
+ * @returns {boolean} True if arguments are multi-line
664
+ */
665
+ function isArgBeforeCalleeNodeMultiline(node) {
666
+ const parent = node.parent;
667
+
668
+ if (parent.arguments.length >= 2 && parent.arguments[1] === node) {
669
+ return (
670
+ parent.arguments[0].loc.end.line >
671
+ parent.arguments[0].loc.start.line
672
+ );
673
+ }
674
+
675
+ return false;
676
+ }
677
+
678
+ /**
679
+ * Check to see if the node is a file level IIFE
680
+ * @param {ASTNode} node The function node to check.
681
+ * @returns {boolean} True if the node is the outer IIFE
682
+ */
683
+ function isOuterIIFE(node) {
684
+ const parent = node.parent;
685
+ let stmt = parent.parent;
686
+
687
+ /*
688
+ * Verify that the node is an IIEF
689
+ */
690
+ if (parent.type !== "CallExpression" || parent.callee !== node) {
691
+ return false;
692
+ }
693
+
694
+ /*
695
+ * Navigate legal ancestors to determine whether this IIEF is outer
696
+ */
697
+ while (
698
+ (stmt.type === "UnaryExpression" &&
699
+ (stmt.operator === "!" ||
700
+ stmt.operator === "~" ||
701
+ stmt.operator === "+" ||
702
+ stmt.operator === "-")) ||
703
+ stmt.type === "AssignmentExpression" ||
704
+ stmt.type === "LogicalExpression" ||
705
+ stmt.type === "SequenceExpression" ||
706
+ stmt.type === "VariableDeclarator"
707
+ ) {
708
+ stmt = stmt.parent;
709
+ }
710
+
711
+ return (
712
+ (stmt.type === "ExpressionStatement" ||
713
+ stmt.type === "VariableDeclaration") &&
714
+ stmt.parent &&
715
+ stmt.parent.type === "Program"
716
+ );
717
+ }
718
+
719
+ /**
720
+ * Check indent for function block content
721
+ * @param {ASTNode} node A BlockStatement node that is inside of a function.
722
+ * @returns {void}
723
+ */
724
+ function checkIndentInFunctionBlock(node) {
725
+ /*
726
+ * Search first caller in chain.
727
+ * Ex.:
728
+ *
729
+ * Models <- Identifier
730
+ * .User
731
+ * .find()
732
+ * .exec(function() {
733
+ * // function body
734
+ * });
735
+ *
736
+ * Looks for 'Models'
737
+ */
738
+ const calleeNode = node.parent; // FunctionExpression
739
+ let indent;
740
+
741
+ if (
742
+ calleeNode.parent &&
743
+ (calleeNode.parent.type === "Property" ||
744
+ calleeNode.parent.type === "ArrayExpression")
745
+ ) {
746
+ // If function is part of array or object, comma can be put at left
747
+ indent = getNodeIndent(calleeNode, false).goodChar;
748
+ } else {
749
+ // If function is standalone, simple calculate indent
750
+ indent = getNodeIndent(calleeNode).goodChar;
751
+ }
752
+
753
+ if (calleeNode.parent.type === "CallExpression") {
754
+ const calleeParent = calleeNode.parent;
755
+
756
+ if (
757
+ calleeNode.type !== "FunctionExpression" &&
758
+ calleeNode.type !== "ArrowFunctionExpression"
759
+ ) {
760
+ if (
761
+ calleeParent &&
762
+ calleeParent.loc.start.line < node.loc.start.line
763
+ ) {
764
+ indent = getNodeIndent(calleeParent).goodChar;
765
+ }
766
+ } else {
767
+ if (
768
+ isArgBeforeCalleeNodeMultiline(calleeNode) &&
769
+ calleeParent.callee.loc.start.line ===
770
+ calleeParent.callee.loc.end.line &&
771
+ !isNodeFirstInLine(calleeNode)
772
+ ) {
773
+ indent = getNodeIndent(calleeParent).goodChar;
774
+ }
775
+ }
776
+ }
777
+
778
+ /*
779
+ * function body indent should be indent + indent size, unless this
780
+ * is a FunctionDeclaration, FunctionExpression, or outer IIFE and the corresponding options are enabled.
781
+ */
782
+ let functionOffset = indentSize;
783
+
784
+ if (options.outerIIFEBody !== null && isOuterIIFE(calleeNode)) {
785
+ functionOffset = options.outerIIFEBody * indentSize;
786
+ } else if (calleeNode.type === "FunctionExpression") {
787
+ functionOffset = options.FunctionExpression.body * indentSize;
788
+ } else if (calleeNode.type === "FunctionDeclaration") {
789
+ functionOffset = options.FunctionDeclaration.body * indentSize;
790
+ }
791
+ indent += functionOffset;
792
+
793
+ // check if the node is inside a variable
794
+ const parentVarNode = getVariableDeclaratorNode(node);
795
+
796
+ if (parentVarNode && isNodeInVarOnTop(node, parentVarNode)) {
797
+ indent +=
798
+ indentSize *
799
+ options.VariableDeclarator[parentVarNode.parent.kind];
800
+ }
801
+
802
+ if (node.body.length > 0) {
803
+ checkNodesIndent(node.body, indent);
804
+ }
805
+
806
+ checkLastNodeLineIndent(node, indent - functionOffset);
807
+ }
808
+
809
+ /**
810
+ * Checks if the given node starts and ends on the same line
811
+ * @param {ASTNode} node The node to check
812
+ * @returns {boolean} Whether or not the block starts and ends on the same line.
813
+ */
814
+ function isSingleLineNode(node) {
815
+ const lastToken = sourceCode.getLastToken(node),
816
+ startLine = node.loc.start.line,
817
+ endLine = lastToken.loc.end.line;
818
+
819
+ return startLine === endLine;
820
+ }
821
+
822
+ /**
823
+ * Check indent for array block content or object block content
824
+ * @param {ASTNode} node node to examine
825
+ * @returns {void}
826
+ */
827
+ function checkIndentInArrayOrObjectBlock(node) {
828
+ // Skip inline
829
+ if (isSingleLineNode(node)) {
830
+ return;
831
+ }
832
+
833
+ let elements =
834
+ node.type === "ArrayExpression"
835
+ ? node.elements
836
+ : node.properties;
837
+
838
+ // filter out empty elements example would be [ , 2] so remove first element as espree considers it as null
839
+ elements = elements.filter(elem => elem !== null);
840
+
841
+ let nodeIndent;
842
+ let elementsIndent;
843
+ const parentVarNode = getVariableDeclaratorNode(node);
844
+
845
+ // TODO - come up with a better strategy in future
846
+ if (isNodeFirstInLine(node)) {
847
+ const parent = node.parent;
848
+
849
+ nodeIndent = getNodeIndent(parent).goodChar;
850
+ if (
851
+ !parentVarNode ||
852
+ parentVarNode.loc.start.line !== node.loc.start.line
853
+ ) {
854
+ if (
855
+ parent.type !== "VariableDeclarator" ||
856
+ parentVarNode === parentVarNode.parent.declarations[0]
857
+ ) {
858
+ if (
859
+ parent.type === "VariableDeclarator" &&
860
+ parentVarNode.loc.start.line ===
861
+ parent.loc.start.line
862
+ ) {
863
+ nodeIndent +=
864
+ indentSize *
865
+ options.VariableDeclarator[
866
+ parentVarNode.parent.kind
867
+ ];
868
+ } else if (
869
+ parent.type === "ObjectExpression" ||
870
+ parent.type === "ArrayExpression"
871
+ ) {
872
+ const parentElements =
873
+ node.parent.type === "ObjectExpression"
874
+ ? node.parent.properties
875
+ : node.parent.elements;
876
+
877
+ if (
878
+ parentElements[0] &&
879
+ parentElements[0].loc.start.line ===
880
+ parent.loc.start.line &&
881
+ parentElements[0].loc.end.line !==
882
+ parent.loc.start.line
883
+ ) {
884
+ /*
885
+ * If the first element of the array spans multiple lines, don't increase the expected indentation of the rest.
886
+ * e.g. [{
887
+ * foo: 1
888
+ * },
889
+ * {
890
+ * bar: 1
891
+ * }]
892
+ * the second object is not indented.
893
+ */
894
+ } else if (
895
+ typeof options[parent.type] === "number"
896
+ ) {
897
+ nodeIndent += options[parent.type] * indentSize;
898
+ } else {
899
+ nodeIndent = parentElements[0].loc.start.column;
900
+ }
901
+ } else if (
902
+ parent.type === "CallExpression" ||
903
+ parent.type === "NewExpression"
904
+ ) {
905
+ if (
906
+ typeof options.CallExpression.arguments ===
907
+ "number"
908
+ ) {
909
+ nodeIndent +=
910
+ options.CallExpression.arguments *
911
+ indentSize;
912
+ } else if (
913
+ options.CallExpression.arguments === "first"
914
+ ) {
915
+ if (parent.arguments.includes(node)) {
916
+ nodeIndent =
917
+ parent.arguments[0].loc.start.column;
918
+ }
919
+ } else {
920
+ nodeIndent += indentSize;
921
+ }
922
+ } else if (
923
+ parent.type === "LogicalExpression" ||
924
+ parent.type === "ArrowFunctionExpression"
925
+ ) {
926
+ nodeIndent += indentSize;
927
+ }
928
+ }
929
+ }
930
+
931
+ checkFirstNodeLineIndent(node, nodeIndent);
932
+ } else {
933
+ nodeIndent = getNodeIndent(node).goodChar;
934
+ }
935
+
936
+ if (options[node.type] === "first") {
937
+ elementsIndent = elements.length
938
+ ? elements[0].loc.start.column
939
+ : 0; // If there are no elements, elementsIndent doesn't matter.
940
+ } else {
941
+ elementsIndent = nodeIndent + indentSize * options[node.type];
942
+ }
943
+
944
+ /*
945
+ * Check if the node is a multiple variable declaration; if so, then
946
+ * make sure indentation takes that into account.
947
+ */
948
+ if (isNodeInVarOnTop(node, parentVarNode)) {
949
+ elementsIndent +=
950
+ indentSize *
951
+ options.VariableDeclarator[parentVarNode.parent.kind];
952
+ }
953
+
954
+ checkNodesIndent(elements, elementsIndent);
955
+
956
+ if (elements.length > 0) {
957
+ // Skip last block line check if last item in same line
958
+ if (elements.at(-1).loc.end.line === node.loc.end.line) {
959
+ return;
960
+ }
961
+ }
962
+
963
+ checkLastNodeLineIndent(
964
+ node,
965
+ nodeIndent +
966
+ (isNodeInVarOnTop(node, parentVarNode)
967
+ ? options.VariableDeclarator[
968
+ parentVarNode.parent.kind
969
+ ] * indentSize
970
+ : 0),
971
+ );
972
+ }
973
+
974
+ /**
975
+ * Check if the node or node body is a BlockStatement or not
976
+ * @param {ASTNode} node node to test
977
+ * @returns {boolean} True if it or its body is a block statement
978
+ */
979
+ function isNodeBodyBlock(node) {
980
+ return (
981
+ node.type === "BlockStatement" ||
982
+ node.type === "ClassBody" ||
983
+ (node.body && node.body.type === "BlockStatement") ||
984
+ (node.consequent && node.consequent.type === "BlockStatement")
985
+ );
986
+ }
987
+
988
+ /**
989
+ * Check indentation for blocks
990
+ * @param {ASTNode} node node to check
991
+ * @returns {void}
992
+ */
993
+ function blockIndentationCheck(node) {
994
+ // Skip inline blocks
995
+ if (isSingleLineNode(node)) {
996
+ return;
997
+ }
998
+
999
+ if (
1000
+ node.parent &&
1001
+ (node.parent.type === "FunctionExpression" ||
1002
+ node.parent.type === "FunctionDeclaration" ||
1003
+ node.parent.type === "ArrowFunctionExpression")
1004
+ ) {
1005
+ checkIndentInFunctionBlock(node);
1006
+ return;
1007
+ }
1008
+
1009
+ let indent;
1010
+ let nodesToCheck;
1011
+
1012
+ /*
1013
+ * For this statements we should check indent from statement beginning,
1014
+ * not from the beginning of the block.
1015
+ */
1016
+ const statementsWithProperties = [
1017
+ "IfStatement",
1018
+ "WhileStatement",
1019
+ "ForStatement",
1020
+ "ForInStatement",
1021
+ "ForOfStatement",
1022
+ "DoWhileStatement",
1023
+ "ClassDeclaration",
1024
+ "TryStatement",
1025
+ ];
1026
+
1027
+ if (
1028
+ node.parent &&
1029
+ statementsWithProperties.includes(node.parent.type) &&
1030
+ isNodeBodyBlock(node)
1031
+ ) {
1032
+ indent = getNodeIndent(node.parent).goodChar;
1033
+ } else if (node.parent && node.parent.type === "CatchClause") {
1034
+ indent = getNodeIndent(node.parent.parent).goodChar;
1035
+ } else {
1036
+ indent = getNodeIndent(node).goodChar;
1037
+ }
1038
+
1039
+ if (
1040
+ node.type === "IfStatement" &&
1041
+ node.consequent.type !== "BlockStatement"
1042
+ ) {
1043
+ nodesToCheck = [node.consequent];
1044
+ } else if (Array.isArray(node.body)) {
1045
+ nodesToCheck = node.body;
1046
+ } else {
1047
+ nodesToCheck = [node.body];
1048
+ }
1049
+
1050
+ if (nodesToCheck.length > 0) {
1051
+ checkNodesIndent(nodesToCheck, indent + indentSize);
1052
+ }
1053
+
1054
+ if (node.type === "BlockStatement") {
1055
+ checkLastNodeLineIndent(node, indent);
1056
+ }
1057
+ }
1058
+
1059
+ /**
1060
+ * Filter out the elements which are on the same line of each other or the node.
1061
+ * basically have only 1 elements from each line except the variable declaration line.
1062
+ * @param {ASTNode} node Variable declaration node
1063
+ * @returns {ASTNode[]} Filtered elements
1064
+ */
1065
+ function filterOutSameLineVars(node) {
1066
+ return node.declarations.reduce((finalCollection, elem) => {
1067
+ const lastElem = finalCollection.at(-1);
1068
+
1069
+ if (
1070
+ (elem.loc.start.line !== node.loc.start.line &&
1071
+ !lastElem) ||
1072
+ (lastElem &&
1073
+ lastElem.loc.start.line !== elem.loc.start.line)
1074
+ ) {
1075
+ finalCollection.push(elem);
1076
+ }
1077
+
1078
+ return finalCollection;
1079
+ }, []);
1080
+ }
1081
+
1082
+ /**
1083
+ * Check indentation for variable declarations
1084
+ * @param {ASTNode} node node to examine
1085
+ * @returns {void}
1086
+ */
1087
+ function checkIndentInVariableDeclarations(node) {
1088
+ const elements = filterOutSameLineVars(node);
1089
+ const nodeIndent = getNodeIndent(node).goodChar;
1090
+ const lastElement = elements.at(-1);
1091
+
1092
+ const elementsIndent =
1093
+ nodeIndent + indentSize * options.VariableDeclarator[node.kind];
1094
+
1095
+ checkNodesIndent(elements, elementsIndent);
1096
+
1097
+ // Only check the last line if there is any token after the last item
1098
+ if (
1099
+ sourceCode.getLastToken(node).loc.end.line <=
1100
+ lastElement.loc.end.line
1101
+ ) {
1102
+ return;
1103
+ }
1104
+
1105
+ const tokenBeforeLastElement =
1106
+ sourceCode.getTokenBefore(lastElement);
1107
+
1108
+ if (tokenBeforeLastElement.value === ",") {
1109
+ // Special case for comma-first syntax where the semicolon is indented
1110
+ checkLastNodeLineIndent(
1111
+ node,
1112
+ getNodeIndent(tokenBeforeLastElement).goodChar,
1113
+ );
1114
+ } else {
1115
+ checkLastNodeLineIndent(node, elementsIndent - indentSize);
1116
+ }
1117
+ }
1118
+
1119
+ /**
1120
+ * Check and decide whether to check for indentation for blockless nodes
1121
+ * Scenarios are for or while statements without braces around them
1122
+ * @param {ASTNode} node node to examine
1123
+ * @returns {void}
1124
+ */
1125
+ function blockLessNodes(node) {
1126
+ if (node.body.type !== "BlockStatement") {
1127
+ blockIndentationCheck(node);
1128
+ }
1129
+ }
1130
+
1131
+ /**
1132
+ * Returns the expected indentation for the case statement
1133
+ * @param {ASTNode} node node to examine
1134
+ * @param {number} [providedSwitchIndent] indent for switch statement
1135
+ * @returns {number} indent size
1136
+ */
1137
+ function expectedCaseIndent(node, providedSwitchIndent) {
1138
+ const switchNode =
1139
+ node.type === "SwitchStatement" ? node : node.parent;
1140
+ const switchIndent =
1141
+ typeof providedSwitchIndent === "undefined"
1142
+ ? getNodeIndent(switchNode).goodChar
1143
+ : providedSwitchIndent;
1144
+ let caseIndent;
1145
+
1146
+ if (caseIndentStore[switchNode.loc.start.line]) {
1147
+ return caseIndentStore[switchNode.loc.start.line];
1148
+ }
1149
+
1150
+ if (switchNode.cases.length > 0 && options.SwitchCase === 0) {
1151
+ caseIndent = switchIndent;
1152
+ } else {
1153
+ caseIndent = switchIndent + indentSize * options.SwitchCase;
1154
+ }
1155
+
1156
+ caseIndentStore[switchNode.loc.start.line] = caseIndent;
1157
+ return caseIndent;
1158
+ }
1159
+
1160
+ /**
1161
+ * Checks whether a return statement is wrapped in ()
1162
+ * @param {ASTNode} node node to examine
1163
+ * @returns {boolean} the result
1164
+ */
1165
+ function isWrappedInParenthesis(node) {
1166
+ const regex = /^return\s*\(\s*\)/u;
1167
+
1168
+ const statementWithoutArgument = sourceCode
1169
+ .getText(node)
1170
+ .replace(sourceCode.getText(node.argument), "");
1171
+
1172
+ return regex.test(statementWithoutArgument);
1173
+ }
1174
+
1175
+ return {
1176
+ Program(node) {
1177
+ if (node.body.length > 0) {
1178
+ // Root nodes should have no indent
1179
+ checkNodesIndent(node.body, getNodeIndent(node).goodChar);
1180
+ }
1181
+ },
1182
+
1183
+ ClassBody: blockIndentationCheck,
1184
+
1185
+ BlockStatement: blockIndentationCheck,
1186
+
1187
+ WhileStatement: blockLessNodes,
1188
+
1189
+ ForStatement: blockLessNodes,
1190
+
1191
+ ForInStatement: blockLessNodes,
1192
+
1193
+ ForOfStatement: blockLessNodes,
1194
+
1195
+ DoWhileStatement: blockLessNodes,
1196
+
1197
+ IfStatement(node) {
1198
+ if (
1199
+ node.consequent.type !== "BlockStatement" &&
1200
+ node.consequent.loc.start.line > node.loc.start.line
1201
+ ) {
1202
+ blockIndentationCheck(node);
1203
+ }
1204
+ },
1205
+
1206
+ VariableDeclaration(node) {
1207
+ if (
1208
+ node.declarations.at(-1).loc.start.line >
1209
+ node.declarations[0].loc.start.line
1210
+ ) {
1211
+ checkIndentInVariableDeclarations(node);
1212
+ }
1213
+ },
1214
+
1215
+ ObjectExpression(node) {
1216
+ checkIndentInArrayOrObjectBlock(node);
1217
+ },
1218
+
1219
+ ArrayExpression(node) {
1220
+ checkIndentInArrayOrObjectBlock(node);
1221
+ },
1222
+
1223
+ MemberExpression(node) {
1224
+ if (typeof options.MemberExpression === "undefined") {
1225
+ return;
1226
+ }
1227
+
1228
+ if (isSingleLineNode(node)) {
1229
+ return;
1230
+ }
1231
+
1232
+ /*
1233
+ * The typical layout of variable declarations and assignments
1234
+ * alter the expectation of correct indentation. Skip them.
1235
+ * TODO: Add appropriate configuration options for variable
1236
+ * declarations and assignments.
1237
+ */
1238
+ if (
1239
+ getParentNodeByType(node, "VariableDeclarator", [
1240
+ "FunctionExpression",
1241
+ "ArrowFunctionExpression",
1242
+ ])
1243
+ ) {
1244
+ return;
1245
+ }
1246
+
1247
+ if (
1248
+ getParentNodeByType(node, "AssignmentExpression", [
1249
+ "FunctionExpression",
1250
+ ])
1251
+ ) {
1252
+ return;
1253
+ }
1254
+
1255
+ const propertyIndent =
1256
+ getNodeIndent(node).goodChar +
1257
+ indentSize * options.MemberExpression;
1258
+
1259
+ const checkNodes = [node.property];
1260
+
1261
+ const dot = sourceCode.getTokenBefore(node.property);
1262
+
1263
+ if (dot.type === "Punctuator" && dot.value === ".") {
1264
+ checkNodes.push(dot);
1265
+ }
1266
+
1267
+ checkNodesIndent(checkNodes, propertyIndent);
1268
+ },
1269
+
1270
+ SwitchStatement(node) {
1271
+ // Switch is not a 'BlockStatement'
1272
+ const switchIndent = getNodeIndent(node).goodChar;
1273
+ const caseIndent = expectedCaseIndent(node, switchIndent);
1274
+
1275
+ checkNodesIndent(node.cases, caseIndent);
1276
+
1277
+ checkLastNodeLineIndent(node, switchIndent);
1278
+ },
1279
+
1280
+ SwitchCase(node) {
1281
+ // Skip inline cases
1282
+ if (isSingleLineNode(node)) {
1283
+ return;
1284
+ }
1285
+ const caseIndent = expectedCaseIndent(node);
1286
+
1287
+ checkNodesIndent(node.consequent, caseIndent + indentSize);
1288
+ },
1289
+
1290
+ FunctionDeclaration(node) {
1291
+ if (isSingleLineNode(node)) {
1292
+ return;
1293
+ }
1294
+ if (
1295
+ options.FunctionDeclaration.parameters === "first" &&
1296
+ node.params.length
1297
+ ) {
1298
+ checkNodesIndent(
1299
+ node.params.slice(1),
1300
+ node.params[0].loc.start.column,
1301
+ );
1302
+ } else if (options.FunctionDeclaration.parameters !== null) {
1303
+ checkNodesIndent(
1304
+ node.params,
1305
+ getNodeIndent(node).goodChar +
1306
+ indentSize * options.FunctionDeclaration.parameters,
1307
+ );
1308
+ }
1309
+ },
1310
+
1311
+ FunctionExpression(node) {
1312
+ if (isSingleLineNode(node)) {
1313
+ return;
1314
+ }
1315
+ if (
1316
+ options.FunctionExpression.parameters === "first" &&
1317
+ node.params.length
1318
+ ) {
1319
+ checkNodesIndent(
1320
+ node.params.slice(1),
1321
+ node.params[0].loc.start.column,
1322
+ );
1323
+ } else if (options.FunctionExpression.parameters !== null) {
1324
+ checkNodesIndent(
1325
+ node.params,
1326
+ getNodeIndent(node).goodChar +
1327
+ indentSize * options.FunctionExpression.parameters,
1328
+ );
1329
+ }
1330
+ },
1331
+
1332
+ ReturnStatement(node) {
1333
+ if (isSingleLineNode(node)) {
1334
+ return;
1335
+ }
1336
+
1337
+ const firstLineIndent = getNodeIndent(node).goodChar;
1338
+
1339
+ // in case if return statement is wrapped in parenthesis
1340
+ if (isWrappedInParenthesis(node)) {
1341
+ checkLastReturnStatementLineIndent(node, firstLineIndent);
1342
+ } else {
1343
+ checkNodeIndent(node, firstLineIndent);
1344
+ }
1345
+ },
1346
+
1347
+ CallExpression(node) {
1348
+ if (isSingleLineNode(node)) {
1349
+ return;
1350
+ }
1351
+ if (
1352
+ options.CallExpression.arguments === "first" &&
1353
+ node.arguments.length
1354
+ ) {
1355
+ checkNodesIndent(
1356
+ node.arguments.slice(1),
1357
+ node.arguments[0].loc.start.column,
1358
+ );
1359
+ } else if (options.CallExpression.arguments !== null) {
1360
+ checkNodesIndent(
1361
+ node.arguments,
1362
+ getNodeIndent(node).goodChar +
1363
+ indentSize * options.CallExpression.arguments,
1364
+ );
1365
+ }
1366
+ },
1367
+ };
1368
+ },
1126
1369
  };