immosquare-cleaner 0.1.32 → 0.1.33

Sign up to get free protection for your applications and to get access to all the features.
Files changed (376) hide show
  1. checksums.yaml +4 -4
  2. data/lib/immosquare-cleaner/version.rb +1 -1
  3. data/node_modules/@eslint/eslintrc/README.md +15 -4
  4. data/node_modules/@eslint/eslintrc/dist/eslintrc-universal.cjs +57 -18
  5. data/node_modules/@eslint/eslintrc/dist/eslintrc-universal.cjs.map +1 -1
  6. data/node_modules/@eslint/eslintrc/dist/eslintrc.cjs +60 -34
  7. data/node_modules/@eslint/eslintrc/dist/eslintrc.cjs.map +1 -1
  8. data/node_modules/@eslint/eslintrc/lib/config-array/config-array.js +3 -16
  9. data/node_modules/@eslint/eslintrc/lib/shared/config-validator.js +63 -18
  10. data/node_modules/@eslint/eslintrc/package.json +4 -4
  11. data/node_modules/@eslint/js/package.json +2 -2
  12. data/node_modules/@eslint/js/src/configs/eslint-all.js +9 -1
  13. data/node_modules/@eslint/js/src/configs/eslint-recommended.js +12 -5
  14. data/node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys/LICENSE +201 -0
  15. data/node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys/README.md +105 -0
  16. data/node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys/dist/eslint-visitor-keys.cjs +384 -0
  17. data/node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys/dist/eslint-visitor-keys.d.cts +27 -0
  18. data/node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys/dist/index.d.ts +16 -0
  19. data/node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys/dist/visitor-keys.d.ts +12 -0
  20. data/node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys/lib/index.js +65 -0
  21. data/node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys/lib/visitor-keys.js +315 -0
  22. data/node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys/package.json +74 -0
  23. data/node_modules/@humanwhocodes/config-array/api.js +114 -16
  24. data/node_modules/@humanwhocodes/config-array/package.json +5 -3
  25. data/node_modules/@humanwhocodes/object-schema/CHANGELOG.md +7 -0
  26. data/node_modules/@humanwhocodes/object-schema/package.json +6 -1
  27. data/node_modules/@humanwhocodes/retry/LICENSE +201 -0
  28. data/node_modules/@humanwhocodes/retry/README.md +122 -0
  29. data/node_modules/@humanwhocodes/retry/dist/retrier.cjs +267 -0
  30. data/node_modules/@humanwhocodes/retry/dist/retrier.d.ts +24 -0
  31. data/node_modules/@humanwhocodes/retry/dist/retrier.js +265 -0
  32. data/node_modules/@humanwhocodes/retry/dist/retrier.min.js +1 -0
  33. data/node_modules/@humanwhocodes/retry/dist/retrier.mjs +265 -0
  34. data/node_modules/@humanwhocodes/retry/package.json +74 -0
  35. data/node_modules/@types/semver-utils/LICENSE +21 -0
  36. data/node_modules/@types/semver-utils/README.md +34 -0
  37. data/node_modules/@types/semver-utils/index.d.ts +15 -0
  38. data/node_modules/@types/semver-utils/package.json +25 -0
  39. data/node_modules/acorn/CHANGELOG.md +36 -0
  40. data/node_modules/acorn/README.md +8 -3
  41. data/node_modules/acorn/dist/acorn.d.mts +857 -26
  42. data/node_modules/acorn/dist/acorn.d.ts +833 -268
  43. data/node_modules/acorn/dist/acorn.js +33 -21
  44. data/node_modules/acorn/dist/acorn.mjs +33 -21
  45. data/node_modules/acorn/package.json +1 -1
  46. data/node_modules/cacheable-request/node_modules/keyv/README.md +429 -0
  47. data/node_modules/cacheable-request/node_modules/keyv/package.json +57 -0
  48. data/node_modules/cacheable-request/node_modules/keyv/src/index.d.ts +112 -0
  49. data/node_modules/cacheable-request/node_modules/keyv/src/index.js +264 -0
  50. data/node_modules/doctrine/CHANGELOG.md +0 -7
  51. data/node_modules/doctrine/lib/doctrine.js +1 -0
  52. data/node_modules/doctrine/package.json +8 -9
  53. data/node_modules/eslint/README.md +31 -28
  54. data/node_modules/eslint/bin/eslint.js +4 -3
  55. data/node_modules/eslint/conf/ecma-version.js +16 -0
  56. data/node_modules/eslint/conf/globals.js +1 -0
  57. data/node_modules/eslint/conf/rule-type-list.json +3 -1
  58. data/node_modules/eslint/lib/api.js +7 -11
  59. data/node_modules/eslint/lib/cli-engine/cli-engine.js +14 -3
  60. data/node_modules/eslint/lib/cli-engine/formatters/formatters-meta.json +1 -29
  61. data/node_modules/eslint/lib/cli-engine/lint-result-cache.js +2 -2
  62. data/node_modules/eslint/lib/cli.js +115 -36
  63. data/node_modules/eslint/lib/config/default-config.js +3 -0
  64. data/node_modules/eslint/lib/config/flat-config-array.js +110 -24
  65. data/node_modules/eslint/lib/config/flat-config-helpers.js +41 -20
  66. data/node_modules/eslint/lib/config/flat-config-schema.js +1 -7
  67. data/node_modules/eslint/lib/config/rule-validator.js +42 -6
  68. data/node_modules/eslint/lib/eslint/eslint-helpers.js +116 -58
  69. data/node_modules/eslint/lib/eslint/eslint.js +892 -377
  70. data/node_modules/eslint/lib/eslint/index.js +2 -2
  71. data/node_modules/eslint/lib/eslint/legacy-eslint.js +728 -0
  72. data/node_modules/eslint/lib/linter/apply-disable-directives.js +59 -31
  73. data/node_modules/eslint/lib/linter/code-path-analysis/code-path-analyzer.js +0 -1
  74. data/node_modules/eslint/lib/linter/code-path-analysis/code-path.js +32 -30
  75. data/node_modules/eslint/lib/linter/code-path-analysis/fork-context.js +1 -1
  76. data/node_modules/eslint/lib/linter/config-comment-parser.js +8 -11
  77. data/node_modules/eslint/lib/linter/index.js +1 -3
  78. data/node_modules/eslint/lib/linter/interpolate.js +24 -2
  79. data/node_modules/eslint/lib/linter/linter.js +428 -207
  80. data/node_modules/eslint/lib/linter/report-translator.js +3 -3
  81. data/node_modules/eslint/lib/linter/rules.js +6 -15
  82. data/node_modules/eslint/lib/linter/source-code-fixer.js +1 -1
  83. data/node_modules/eslint/lib/linter/timing.js +16 -8
  84. data/node_modules/eslint/lib/options.js +35 -3
  85. data/node_modules/eslint/lib/rule-tester/index.js +3 -1
  86. data/node_modules/eslint/lib/rule-tester/rule-tester.js +424 -347
  87. data/node_modules/eslint/lib/rules/array-bracket-newline.js +1 -1
  88. data/node_modules/eslint/lib/rules/array-bracket-spacing.js +1 -1
  89. data/node_modules/eslint/lib/rules/block-scoped-var.js +1 -1
  90. data/node_modules/eslint/lib/rules/callback-return.js +2 -2
  91. data/node_modules/eslint/lib/rules/camelcase.js +3 -5
  92. data/node_modules/eslint/lib/rules/capitalized-comments.js +10 -7
  93. data/node_modules/eslint/lib/rules/comma-dangle.js +1 -1
  94. data/node_modules/eslint/lib/rules/comma-style.js +2 -2
  95. data/node_modules/eslint/lib/rules/complexity.js +14 -1
  96. data/node_modules/eslint/lib/rules/constructor-super.js +99 -100
  97. data/node_modules/eslint/lib/rules/default-case.js +1 -1
  98. data/node_modules/eslint/lib/rules/eol-last.js +2 -2
  99. data/node_modules/eslint/lib/rules/function-paren-newline.js +2 -2
  100. data/node_modules/eslint/lib/rules/indent-legacy.js +5 -5
  101. data/node_modules/eslint/lib/rules/indent.js +5 -5
  102. data/node_modules/eslint/lib/rules/index.js +1 -2
  103. data/node_modules/eslint/lib/rules/key-spacing.js +2 -2
  104. data/node_modules/eslint/lib/rules/line-comment-position.js +1 -1
  105. data/node_modules/eslint/lib/rules/lines-around-directive.js +2 -2
  106. data/node_modules/eslint/lib/rules/max-depth.js +1 -1
  107. data/node_modules/eslint/lib/rules/max-len.js +3 -3
  108. data/node_modules/eslint/lib/rules/max-lines.js +3 -3
  109. data/node_modules/eslint/lib/rules/max-nested-callbacks.js +1 -1
  110. data/node_modules/eslint/lib/rules/max-params.js +1 -1
  111. data/node_modules/eslint/lib/rules/max-statements.js +1 -1
  112. data/node_modules/eslint/lib/rules/multiline-comment-style.js +7 -7
  113. data/node_modules/eslint/lib/rules/new-cap.js +1 -1
  114. data/node_modules/eslint/lib/rules/newline-after-var.js +1 -1
  115. data/node_modules/eslint/lib/rules/newline-before-return.js +1 -1
  116. data/node_modules/eslint/lib/rules/no-case-declarations.js +13 -1
  117. data/node_modules/eslint/lib/rules/no-constant-binary-expression.js +7 -8
  118. data/node_modules/eslint/lib/rules/no-constant-condition.js +18 -7
  119. data/node_modules/eslint/lib/rules/no-constructor-return.js +2 -2
  120. data/node_modules/eslint/lib/rules/no-dupe-class-members.js +2 -2
  121. data/node_modules/eslint/lib/rules/no-else-return.js +1 -1
  122. data/node_modules/eslint/lib/rules/no-empty-function.js +2 -2
  123. data/node_modules/eslint/lib/rules/no-empty-static-block.js +1 -1
  124. data/node_modules/eslint/lib/rules/no-extend-native.js +1 -2
  125. data/node_modules/eslint/lib/rules/no-extra-semi.js +1 -1
  126. data/node_modules/eslint/lib/rules/no-fallthrough.js +41 -16
  127. data/node_modules/eslint/lib/rules/no-implicit-coercion.js +66 -24
  128. data/node_modules/eslint/lib/rules/no-inner-declarations.js +23 -2
  129. data/node_modules/eslint/lib/rules/no-invalid-regexp.js +1 -1
  130. data/node_modules/eslint/lib/rules/no-invalid-this.js +1 -1
  131. data/node_modules/eslint/lib/rules/no-lone-blocks.js +3 -3
  132. data/node_modules/eslint/lib/rules/no-loss-of-precision.js +1 -1
  133. data/node_modules/eslint/lib/rules/no-misleading-character-class.js +225 -69
  134. data/node_modules/eslint/lib/rules/no-mixed-spaces-and-tabs.js +1 -1
  135. data/node_modules/eslint/lib/rules/no-multiple-empty-lines.js +1 -1
  136. data/node_modules/eslint/lib/rules/no-new-native-nonconstructor.js +1 -1
  137. data/node_modules/eslint/lib/rules/no-new-symbol.js +8 -1
  138. data/node_modules/eslint/lib/rules/no-restricted-globals.js +1 -1
  139. data/node_modules/eslint/lib/rules/no-restricted-imports.js +186 -40
  140. data/node_modules/eslint/lib/rules/no-restricted-modules.js +2 -2
  141. data/node_modules/eslint/lib/rules/no-return-await.js +1 -1
  142. data/node_modules/eslint/lib/rules/no-sequences.js +1 -0
  143. data/node_modules/eslint/lib/rules/no-this-before-super.js +45 -13
  144. data/node_modules/eslint/lib/rules/no-trailing-spaces.js +2 -3
  145. data/node_modules/eslint/lib/rules/no-unneeded-ternary.js +1 -1
  146. data/node_modules/eslint/lib/rules/no-unsafe-optional-chaining.js +1 -1
  147. data/node_modules/eslint/lib/rules/no-unused-private-class-members.js +1 -1
  148. data/node_modules/eslint/lib/rules/no-unused-vars.js +197 -36
  149. data/node_modules/eslint/lib/rules/no-useless-assignment.js +566 -0
  150. data/node_modules/eslint/lib/rules/no-useless-backreference.js +1 -1
  151. data/node_modules/eslint/lib/rules/no-useless-computed-key.js +2 -2
  152. data/node_modules/eslint/lib/rules/no-useless-return.js +7 -2
  153. data/node_modules/eslint/lib/rules/object-curly-spacing.js +3 -3
  154. data/node_modules/eslint/lib/rules/object-property-newline.js +1 -1
  155. data/node_modules/eslint/lib/rules/one-var.js +5 -5
  156. data/node_modules/eslint/lib/rules/padded-blocks.js +7 -7
  157. data/node_modules/eslint/lib/rules/prefer-arrow-callback.js +3 -3
  158. data/node_modules/eslint/lib/rules/prefer-reflect.js +1 -1
  159. data/node_modules/eslint/lib/rules/prefer-regex-literals.js +1 -1
  160. data/node_modules/eslint/lib/rules/prefer-template.js +1 -1
  161. data/node_modules/eslint/lib/rules/radix.js +2 -2
  162. data/node_modules/eslint/lib/rules/semi-style.js +1 -1
  163. data/node_modules/eslint/lib/rules/sort-imports.js +1 -1
  164. data/node_modules/eslint/lib/rules/sort-keys.js +1 -1
  165. data/node_modules/eslint/lib/rules/sort-vars.js +1 -1
  166. data/node_modules/eslint/lib/rules/space-unary-ops.js +1 -1
  167. data/node_modules/eslint/lib/rules/strict.js +1 -1
  168. data/node_modules/eslint/lib/rules/use-isnan.js +101 -7
  169. data/node_modules/eslint/lib/rules/utils/ast-utils.js +16 -7
  170. data/node_modules/eslint/lib/rules/utils/char-source.js +240 -0
  171. data/node_modules/eslint/lib/rules/utils/lazy-loading-rule-map.js +1 -1
  172. data/node_modules/eslint/lib/rules/utils/unicode/index.js +9 -4
  173. data/node_modules/eslint/lib/rules/yield-star-spacing.js +1 -1
  174. data/node_modules/eslint/lib/shared/runtime-info.js +1 -0
  175. data/node_modules/eslint/lib/shared/serialization.js +55 -0
  176. data/node_modules/eslint/lib/shared/stats.js +30 -0
  177. data/node_modules/eslint/lib/shared/string-utils.js +9 -11
  178. data/node_modules/eslint/lib/shared/types.js +35 -1
  179. data/node_modules/eslint/lib/source-code/index.js +3 -1
  180. data/node_modules/eslint/lib/source-code/source-code.js +299 -85
  181. data/node_modules/eslint/lib/source-code/token-store/backward-token-cursor.js +3 -3
  182. data/node_modules/eslint/lib/source-code/token-store/cursors.js +4 -2
  183. data/node_modules/eslint/lib/source-code/token-store/forward-token-comment-cursor.js +3 -3
  184. data/node_modules/eslint/lib/source-code/token-store/forward-token-cursor.js +3 -3
  185. data/node_modules/eslint/lib/source-code/token-store/index.js +2 -2
  186. data/node_modules/eslint/lib/unsupported-api.js +3 -5
  187. data/node_modules/eslint/messages/no-config-found.js +1 -1
  188. data/node_modules/eslint/messages/plugin-conflict.js +1 -1
  189. data/node_modules/eslint/messages/plugin-invalid.js +1 -1
  190. data/node_modules/eslint/messages/plugin-missing.js +1 -1
  191. data/node_modules/eslint/package.json +32 -29
  192. data/node_modules/eslint-scope/README.md +23 -2
  193. data/node_modules/eslint-scope/dist/eslint-scope.cjs +84 -58
  194. data/node_modules/eslint-scope/lib/index.js +0 -3
  195. data/node_modules/eslint-scope/lib/pattern-visitor.js +4 -3
  196. data/node_modules/eslint-scope/lib/referencer.js +13 -11
  197. data/node_modules/eslint-scope/lib/scope-manager.js +2 -8
  198. data/node_modules/eslint-scope/lib/scope.js +64 -43
  199. data/node_modules/eslint-scope/lib/version.js +1 -1
  200. data/node_modules/eslint-scope/package.json +21 -19
  201. data/node_modules/eslint-visitor-keys/README.md +1 -1
  202. data/node_modules/eslint-visitor-keys/package.json +2 -2
  203. data/node_modules/espree/README.md +1 -1
  204. data/node_modules/espree/dist/espree.cjs +1 -1
  205. data/node_modules/espree/lib/version.js +1 -1
  206. data/node_modules/espree/package.json +18 -20
  207. data/node_modules/file-entry-cache/LICENSE +1 -1
  208. data/node_modules/file-entry-cache/README.md +6 -3
  209. data/node_modules/file-entry-cache/package.json +19 -43
  210. data/node_modules/flat-cache/README.md +8 -6
  211. data/node_modules/flat-cache/changelog.md +155 -205
  212. data/node_modules/flat-cache/package.json +16 -39
  213. data/node_modules/flat-cache/src/cache.js +22 -26
  214. data/node_modules/flat-cache/src/del.js +28 -11
  215. data/node_modules/flat-cache/src/utils.js +39 -41
  216. data/node_modules/globals/globals.json +25 -0
  217. data/node_modules/globals/index.d.ts +2074 -3
  218. data/node_modules/globals/package.json +10 -7
  219. data/node_modules/globals/readme.md +2 -14
  220. data/node_modules/keyv/package.json +4 -4
  221. data/node_modules/keyv/src/index.js +6 -11
  222. data/node_modules/npm-check-updates/build/cli.js +22 -21
  223. data/node_modules/npm-check-updates/build/cli.js.map +1 -1
  224. data/node_modules/npm-check-updates/build/index-5sFb3Tvv.js +66 -0
  225. data/node_modules/npm-check-updates/build/index-5sFb3Tvv.js.map +1 -0
  226. data/node_modules/npm-check-updates/build/index-BmUFwMVL.js +6 -0
  227. data/node_modules/npm-check-updates/build/index-BmUFwMVL.js.map +1 -0
  228. data/node_modules/npm-check-updates/build/index.d.ts +5 -2
  229. data/node_modules/npm-check-updates/build/index.js +560 -1
  230. data/node_modules/npm-check-updates/build/index.js.map +1 -1
  231. data/node_modules/npm-check-updates/build/package.json +4 -3
  232. data/node_modules/npm-check-updates/build/src/index.js +1 -1
  233. data/node_modules/npm-check-updates/build/src/index.js.map +1 -1
  234. data/node_modules/npm-check-updates/build/src/lib/libnpmconfig/index.js +2 -2
  235. data/node_modules/npm-check-updates/build/src/lib/libnpmconfig/index.js.map +1 -1
  236. data/node_modules/npm-check-updates/build/src/package-managers/bun.js +0 -6
  237. data/node_modules/npm-check-updates/build/src/package-managers/bun.js.map +1 -1
  238. data/node_modules/npm-check-updates/package.json +4 -3
  239. data/node_modules/type-fest/index.d.ts +95 -2
  240. data/node_modules/type-fest/package.json +12 -18
  241. data/node_modules/type-fest/readme.md +299 -52
  242. data/node_modules/type-fest/source/async-return-type.d.ts +4 -2
  243. data/node_modules/type-fest/source/asyncify.d.ts +5 -3
  244. data/node_modules/type-fest/source/basic.d.ts +21 -43
  245. data/node_modules/type-fest/{ts41 → source}/camel-case.d.ts +18 -17
  246. data/node_modules/type-fest/source/camel-cased-properties-deep.d.ts +54 -0
  247. data/node_modules/type-fest/source/camel-cased-properties.d.ts +36 -0
  248. data/node_modules/type-fest/source/conditional-except.d.ts +6 -4
  249. data/node_modules/type-fest/source/conditional-keys.d.ts +5 -1
  250. data/node_modules/type-fest/source/conditional-pick.d.ts +5 -3
  251. data/node_modules/type-fest/{ts41 → source}/delimiter-case.d.ts +16 -8
  252. data/node_modules/type-fest/source/delimiter-cased-properties-deep.d.ts +60 -0
  253. data/node_modules/type-fest/source/delimiter-cased-properties.d.ts +37 -0
  254. data/node_modules/type-fest/source/entries.d.ts +8 -3
  255. data/node_modules/type-fest/source/entry.d.ts +8 -3
  256. data/node_modules/type-fest/source/exact.d.ts +73 -0
  257. data/node_modules/type-fest/source/except.d.ts +38 -3
  258. data/node_modules/type-fest/source/fixed-length-array.d.ts +6 -1
  259. data/node_modules/type-fest/source/get.d.ts +184 -0
  260. data/node_modules/type-fest/source/has-optional-keys.d.ts +21 -0
  261. data/node_modules/type-fest/source/has-required-keys.d.ts +59 -0
  262. data/node_modules/type-fest/source/includes.d.ts +22 -0
  263. data/node_modules/type-fest/source/internal.d.ts +59 -0
  264. data/node_modules/type-fest/source/invariant-of.d.ts +76 -0
  265. data/node_modules/type-fest/source/iterable-element.d.ts +8 -0
  266. data/node_modules/type-fest/source/join.d.ts +30 -0
  267. data/node_modules/type-fest/source/jsonify.d.ts +90 -0
  268. data/node_modules/type-fest/{ts41 → source}/kebab-case.d.ts +7 -5
  269. data/node_modules/type-fest/source/kebab-cased-properties-deep.d.ts +47 -0
  270. data/node_modules/type-fest/source/kebab-cased-properties.d.ts +30 -0
  271. data/node_modules/type-fest/source/last-array-element.d.ts +28 -0
  272. data/node_modules/type-fest/source/literal-to-primitive.d.ts +36 -0
  273. data/node_modules/type-fest/source/literal-union.d.ts +7 -5
  274. data/node_modules/type-fest/source/merge-exclusive.d.ts +3 -1
  275. data/node_modules/type-fest/source/merge.d.ts +8 -3
  276. data/node_modules/type-fest/source/multidimensional-array.d.ts +43 -0
  277. data/node_modules/type-fest/source/multidimensional-readonly-array.d.ts +47 -0
  278. data/node_modules/type-fest/source/mutable.d.ts +4 -21
  279. data/node_modules/type-fest/source/numeric.d.ts +170 -0
  280. data/node_modules/type-fest/source/observable-like.d.ts +62 -0
  281. data/node_modules/type-fest/source/opaque.d.ts +45 -3
  282. data/node_modules/type-fest/source/optional-keys-of.d.ts +38 -0
  283. data/node_modules/type-fest/source/package-json.d.ts +64 -12
  284. data/node_modules/type-fest/source/partial-deep.d.ts +57 -16
  285. data/node_modules/type-fest/source/partial-on-undefined-deep.d.ts +70 -0
  286. data/node_modules/type-fest/{ts41 → source}/pascal-case.d.ts +6 -4
  287. data/node_modules/type-fest/source/pascal-cased-properties-deep.d.ts +54 -0
  288. data/node_modules/type-fest/source/pascal-cased-properties.d.ts +34 -0
  289. data/node_modules/type-fest/source/primitive.d.ts +13 -0
  290. data/node_modules/type-fest/source/promisable.d.ts +5 -3
  291. data/node_modules/type-fest/source/promise-value.d.ts +9 -7
  292. data/node_modules/type-fest/source/readonly-deep.d.ts +33 -7
  293. data/node_modules/type-fest/source/readonly-tuple.d.ts +41 -0
  294. data/node_modules/type-fest/source/remove-index-signature.d.ts +104 -0
  295. data/node_modules/type-fest/source/replace.d.ts +67 -0
  296. data/node_modules/type-fest/source/require-all-or-none.d.ts +36 -0
  297. data/node_modules/type-fest/source/require-at-least-one.d.ts +5 -3
  298. data/node_modules/type-fest/source/require-exactly-one.d.ts +4 -5
  299. data/node_modules/type-fest/source/required-keys-of.d.ts +29 -0
  300. data/node_modules/type-fest/source/schema.d.ts +72 -0
  301. data/node_modules/type-fest/source/screaming-snake-case.d.ts +33 -0
  302. data/node_modules/type-fest/source/set-non-nullable.d.ts +35 -0
  303. data/node_modules/type-fest/source/set-optional.d.ts +12 -11
  304. data/node_modules/type-fest/source/set-required.d.ts +12 -11
  305. data/node_modules/type-fest/source/set-return-type.d.ts +3 -1
  306. data/node_modules/type-fest/source/simplify.d.ts +83 -0
  307. data/node_modules/type-fest/{ts41 → source}/snake-case.d.ts +7 -4
  308. data/node_modules/type-fest/source/snake-cased-properties-deep.d.ts +47 -0
  309. data/node_modules/type-fest/source/snake-cased-properties.d.ts +30 -0
  310. data/node_modules/type-fest/source/split.d.ts +29 -0
  311. data/node_modules/type-fest/source/spread.d.ts +85 -0
  312. data/node_modules/type-fest/source/string-key-of.d.ts +25 -0
  313. data/node_modules/type-fest/source/stringified.d.ts +3 -1
  314. data/node_modules/type-fest/source/trim.d.ts +25 -0
  315. data/node_modules/type-fest/source/tsconfig-json.d.ts +316 -14
  316. data/node_modules/type-fest/source/typed-array.d.ts +17 -0
  317. data/node_modules/type-fest/source/union-to-intersection.d.ts +4 -2
  318. data/node_modules/type-fest/source/value-of.d.ts +3 -1
  319. data/node_modules/type-fest/source/writable.d.ts +40 -0
  320. data/package.json +2 -2
  321. metadata +86 -63
  322. data/linters/rubocop-2.7.6.yml +0 -86
  323. data/node_modules/@humanwhocodes/object-schema/tests/merge-strategy.js +0 -66
  324. data/node_modules/@humanwhocodes/object-schema/tests/object-schema.js +0 -659
  325. data/node_modules/@humanwhocodes/object-schema/tests/validation-strategy.js +0 -186
  326. data/node_modules/eslint/conf/config-schema.js +0 -93
  327. data/node_modules/eslint/lib/cli-engine/formatters/checkstyle.js +0 -60
  328. data/node_modules/eslint/lib/cli-engine/formatters/compact.js +0 -60
  329. data/node_modules/eslint/lib/cli-engine/formatters/jslint-xml.js +0 -41
  330. data/node_modules/eslint/lib/cli-engine/formatters/junit.js +0 -82
  331. data/node_modules/eslint/lib/cli-engine/formatters/tap.js +0 -95
  332. data/node_modules/eslint/lib/cli-engine/formatters/unix.js +0 -58
  333. data/node_modules/eslint/lib/cli-engine/formatters/visualstudio.js +0 -63
  334. data/node_modules/eslint/lib/cli-engine/xml-escape.js +0 -34
  335. data/node_modules/eslint/lib/eslint/flat-eslint.js +0 -1155
  336. data/node_modules/eslint/lib/rule-tester/flat-rule-tester.js +0 -1131
  337. data/node_modules/eslint/lib/rules/require-jsdoc.js +0 -122
  338. data/node_modules/eslint/lib/rules/utils/patterns/letters.js +0 -36
  339. data/node_modules/eslint/lib/rules/valid-jsdoc.js +0 -516
  340. data/node_modules/eslint/lib/shared/config-validator.js +0 -347
  341. data/node_modules/eslint/lib/shared/deprecation-warnings.js +0 -58
  342. data/node_modules/eslint/lib/shared/relative-module-resolver.js +0 -50
  343. data/node_modules/file-entry-cache/changelog.md +0 -163
  344. data/node_modules/flat-cache/node_modules/rimraf/CHANGELOG.md +0 -65
  345. data/node_modules/flat-cache/node_modules/rimraf/LICENSE +0 -15
  346. data/node_modules/flat-cache/node_modules/rimraf/README.md +0 -101
  347. data/node_modules/flat-cache/node_modules/rimraf/bin.js +0 -68
  348. data/node_modules/flat-cache/node_modules/rimraf/node_modules/glob/LICENSE +0 -21
  349. data/node_modules/flat-cache/node_modules/rimraf/node_modules/glob/README.md +0 -378
  350. data/node_modules/flat-cache/node_modules/rimraf/node_modules/glob/common.js +0 -238
  351. data/node_modules/flat-cache/node_modules/rimraf/node_modules/glob/glob.js +0 -790
  352. data/node_modules/flat-cache/node_modules/rimraf/node_modules/glob/node_modules/minimatch/LICENSE +0 -15
  353. data/node_modules/flat-cache/node_modules/rimraf/node_modules/glob/node_modules/minimatch/README.md +0 -230
  354. data/node_modules/flat-cache/node_modules/rimraf/node_modules/glob/node_modules/minimatch/minimatch.js +0 -947
  355. data/node_modules/flat-cache/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/LICENSE +0 -21
  356. data/node_modules/flat-cache/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/README.md +0 -129
  357. data/node_modules/flat-cache/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/index.js +0 -201
  358. data/node_modules/flat-cache/node_modules/rimraf/node_modules/glob/node_modules/minimatch/node_modules/brace-expansion/package.json +0 -47
  359. data/node_modules/flat-cache/node_modules/rimraf/node_modules/glob/node_modules/minimatch/package.json +0 -33
  360. data/node_modules/flat-cache/node_modules/rimraf/node_modules/glob/package.json +0 -55
  361. data/node_modules/flat-cache/node_modules/rimraf/node_modules/glob/sync.js +0 -486
  362. data/node_modules/flat-cache/node_modules/rimraf/package.json +0 -32
  363. data/node_modules/flat-cache/node_modules/rimraf/rimraf.js +0 -360
  364. data/node_modules/npm-check-updates/build/index-CI_x-D21.js +0 -593
  365. data/node_modules/npm-check-updates/build/index-CI_x-D21.js.map +0 -1
  366. data/node_modules/npm-check-updates/build/index-Ci8A2QXv.js +0 -6
  367. data/node_modules/npm-check-updates/build/index-Ci8A2QXv.js.map +0 -1
  368. data/node_modules/npm-check-updates/build/index-DgVn3Gax.js +0 -36
  369. data/node_modules/npm-check-updates/build/index-DgVn3Gax.js.map +0 -1
  370. data/node_modules/npm-check-updates/build/src/types/SpawnPleaseOptions.d.ts +0 -6
  371. data/node_modules/npm-check-updates/build/src/types/SpawnPleaseOptions.js +0 -3
  372. data/node_modules/npm-check-updates/build/src/types/SpawnPleaseOptions.js.map +0 -1
  373. data/node_modules/type-fest/base.d.ts +0 -38
  374. data/node_modules/type-fest/license +0 -9
  375. data/node_modules/type-fest/source/utilities.d.ts +0 -3
  376. data/node_modules/type-fest/ts41/index.d.ts +0 -9
@@ -1,68 +1,42 @@
1
1
  /**
2
- * @fileoverview Mocha test wrapper
2
+ * @fileoverview Mocha/Jest test wrapper
3
3
  * @author Ilya Volodin
4
4
  */
5
5
  "use strict";
6
6
 
7
7
  /* globals describe, it -- Mocha globals */
8
8
 
9
- /*
10
- * This is a wrapper around mocha to allow for DRY unittests for eslint
11
- * Format:
12
- * RuleTester.run("{ruleName}", {
13
- * valid: [
14
- * "{code}",
15
- * { code: "{code}", options: {options}, globals: {globals}, parser: "{parser}", settings: {settings} }
16
- * ],
17
- * invalid: [
18
- * { code: "{code}", errors: {numErrors} },
19
- * { code: "{code}", errors: ["{errorMessage}"] },
20
- * { code: "{code}", options: {options}, globals: {globals}, parser: "{parser}", settings: {settings}, errors: [{ message: "{errorMessage}", type: "{errorNodeType}"}] }
21
- * ]
22
- * });
23
- *
24
- * Variables:
25
- * {code} - String that represents the code to be tested
26
- * {options} - Arguments that are passed to the configurable rules.
27
- * {globals} - An object representing a list of variables that are
28
- * registered as globals
29
- * {parser} - String representing the parser to use
30
- * {settings} - An object representing global settings for all rules
31
- * {numErrors} - If failing case doesn't need to check error message,
32
- * this integer will specify how many errors should be
33
- * received
34
- * {errorMessage} - Message that is returned by the rule on failure
35
- * {errorNodeType} - AST node type that is returned by they rule as
36
- * a cause of the failure.
37
- */
38
-
39
9
  //------------------------------------------------------------------------------
40
10
  // Requirements
41
11
  //------------------------------------------------------------------------------
42
12
 
43
13
  const
44
14
  assert = require("assert"),
45
- path = require("path"),
46
15
  util = require("util"),
47
- merge = require("lodash.merge"),
16
+ path = require("path"),
48
17
  equal = require("fast-deep-equal"),
49
- Traverser = require("../../lib/shared/traverser"),
50
- { getRuleOptionsSchema, validate } = require("../shared/config-validator"),
51
- { Linter, SourceCodeFixer, interpolate } = require("../linter"),
52
- CodePath = require("../linter/code-path-analysis/code-path");
18
+ Traverser = require("../shared/traverser"),
19
+ { getRuleOptionsSchema } = require("../config/flat-config-helpers"),
20
+ { Linter, SourceCodeFixer } = require("../linter"),
21
+ { interpolate, getPlaceholderMatcher } = require("../linter/interpolate"),
22
+ stringify = require("json-stable-stringify-without-jsonify");
23
+
24
+ const { FlatConfigArray } = require("../config/flat-config-array");
25
+ const { defaultConfig } = require("../config/default-config");
53
26
 
54
27
  const ajv = require("../shared/ajv")({ strictDefaults: true });
55
28
 
56
- const espreePath = require.resolve("espree");
57
29
  const parserSymbol = Symbol.for("eslint.RuleTester.parser");
58
-
59
30
  const { SourceCode } = require("../source-code");
31
+ const { ConfigArraySymbol } = require("@humanwhocodes/config-array");
32
+ const { isSerializable } = require("../shared/serialization");
60
33
 
61
34
  //------------------------------------------------------------------------------
62
35
  // Typedefs
63
36
  //------------------------------------------------------------------------------
64
37
 
65
38
  /** @typedef {import("../shared/types").Parser} Parser */
39
+ /** @typedef {import("../shared/types").LanguageOptions} LanguageOptions */
66
40
  /** @typedef {import("../shared/types").Rule} Rule */
67
41
 
68
42
 
@@ -72,12 +46,9 @@ const { SourceCode } = require("../source-code");
72
46
  * @property {string} [name] Name for the test case.
73
47
  * @property {string} code Code for the test case.
74
48
  * @property {any[]} [options] Options for the test case.
49
+ * @property {LanguageOptions} [languageOptions] The language options to use in the test case.
75
50
  * @property {{ [name: string]: any }} [settings] Settings for the test case.
76
51
  * @property {string} [filename] The fake filename for the test case. Useful for rules that make assertion about filenames.
77
- * @property {string} [parser] The absolute path for the parser.
78
- * @property {{ [name: string]: any }} [parserOptions] Options for the parser.
79
- * @property {{ [name: string]: "readonly" | "writable" | "off" }} [globals] The additional global variables.
80
- * @property {{ [name: string]: boolean }} [env] Environments for the test case.
81
52
  * @property {boolean} [only] Run only this test case or the subset of test cases with this property.
82
53
  */
83
54
 
@@ -91,10 +62,7 @@ const { SourceCode } = require("../source-code");
91
62
  * @property {any[]} [options] Options for the test case.
92
63
  * @property {{ [name: string]: any }} [settings] Settings for the test case.
93
64
  * @property {string} [filename] The fake filename for the test case. Useful for rules that make assertion about filenames.
94
- * @property {string} [parser] The absolute path for the parser.
95
- * @property {{ [name: string]: any }} [parserOptions] Options for the parser.
96
- * @property {{ [name: string]: "readonly" | "writable" | "off" }} [globals] The additional global variables.
97
- * @property {{ [name: string]: boolean }} [env] Environments for the test case.
65
+ * @property {LanguageOptions} [languageOptions] The language options to use in the test case.
98
66
  * @property {boolean} [only] Run only this test case or the subset of test cases with this property.
99
67
  */
100
68
 
@@ -120,7 +88,12 @@ const { SourceCode } = require("../source-code");
120
88
  * the initial default configuration
121
89
  */
122
90
  const testerDefaultConfig = { rules: {} };
123
- let defaultConfig = { rules: {} };
91
+
92
+ /*
93
+ * RuleTester uses this config as its default. This can be overwritten via
94
+ * setDefaultConfig().
95
+ */
96
+ let sharedDefaultConfig = { rules: {} };
124
97
 
125
98
  /*
126
99
  * List every parameters possible on a test case that are not related to eslint
@@ -163,42 +136,25 @@ const suggestionObjectParameters = new Set([
163
136
  ]);
164
137
  const friendlySuggestionObjectParameterList = `[${[...suggestionObjectParameters].map(key => `'${key}'`).join(", ")}]`;
165
138
 
139
+ /*
140
+ * Ignored test case properties when checking for test case duplicates.
141
+ */
142
+ const duplicationIgnoredParameters = new Set([
143
+ "name",
144
+ "errors",
145
+ "output"
146
+ ]);
147
+
166
148
  const forbiddenMethods = [
167
149
  "applyInlineConfig",
168
150
  "applyLanguageOptions",
169
151
  "finalize"
170
152
  ];
171
153
 
172
- const hasOwnProperty = Function.call.bind(Object.hasOwnProperty);
154
+ /** @type {Map<string,WeakSet>} */
155
+ const forbiddenMethodCalls = new Map(forbiddenMethods.map(methodName => ([methodName, new WeakSet()])));
173
156
 
174
- const DEPRECATED_SOURCECODE_PASSTHROUGHS = {
175
- getSource: "getText",
176
- getSourceLines: "getLines",
177
- getAllComments: "getAllComments",
178
- getNodeByRangeIndex: "getNodeByRangeIndex",
179
-
180
- // getComments: "getComments", -- already handled by a separate error
181
- getCommentsBefore: "getCommentsBefore",
182
- getCommentsAfter: "getCommentsAfter",
183
- getCommentsInside: "getCommentsInside",
184
- getJSDocComment: "getJSDocComment",
185
- getFirstToken: "getFirstToken",
186
- getFirstTokens: "getFirstTokens",
187
- getLastToken: "getLastToken",
188
- getLastTokens: "getLastTokens",
189
- getTokenAfter: "getTokenAfter",
190
- getTokenBefore: "getTokenBefore",
191
- getTokenByRangeStart: "getTokenByRangeStart",
192
- getTokens: "getTokens",
193
- getTokensAfter: "getTokensAfter",
194
- getTokensBefore: "getTokensBefore",
195
- getTokensBetween: "getTokensBetween",
196
-
197
- getScope: "getScope",
198
- getAncestors: "getAncestors",
199
- getDeclaredVariables: "getDeclaredVariables",
200
- markVariableAsUsed: "markVariableAsUsed"
201
- };
157
+ const hasOwnProperty = Function.call.bind(Object.hasOwnProperty);
202
158
 
203
159
  /**
204
160
  * Clones a given value deeply.
@@ -331,23 +287,27 @@ function wrapParser(parser) {
331
287
  }
332
288
 
333
289
  /**
334
- * Function to replace `SourceCode.prototype.getComments`.
335
- * @returns {void}
336
- * @throws {Error} Deprecation message.
337
- */
338
- function getCommentsDeprecation() {
339
- throw new Error(
340
- "`SourceCode#getComments()` is deprecated and will be removed in a future major version. Use `getCommentsBefore()`, `getCommentsAfter()`, and `getCommentsInside()` instead."
341
- );
342
- }
343
-
344
- /**
345
- * Function to replace forbidden `SourceCode` methods.
290
+ * Function to replace forbidden `SourceCode` methods. Allows just one call per method.
346
291
  * @param {string} methodName The name of the method to forbid.
292
+ * @param {Function} prototype The prototype with the original method to call.
347
293
  * @returns {Function} The function that throws the error.
348
294
  */
349
- function throwForbiddenMethodError(methodName) {
350
- return () => {
295
+ function throwForbiddenMethodError(methodName, prototype) {
296
+
297
+ const original = prototype[methodName];
298
+
299
+ return function(...args) {
300
+
301
+ const called = forbiddenMethodCalls.get(methodName);
302
+
303
+ /* eslint-disable no-invalid-this -- needed to operate as a method. */
304
+ if (!called.has(this)) {
305
+ called.add(this);
306
+
307
+ return original.apply(this, args);
308
+ }
309
+ /* eslint-enable no-invalid-this -- not needed past this point */
310
+
351
311
  throw new Error(
352
312
  `\`SourceCode#${methodName}()\` cannot be called inside a rule.`
353
313
  );
@@ -355,81 +315,45 @@ function throwForbiddenMethodError(methodName) {
355
315
  }
356
316
 
357
317
  /**
358
- * Emit a deprecation warning if function-style format is being used.
359
- * @param {string} ruleName Name of the rule.
360
- * @returns {void}
318
+ * Extracts names of {{ placeholders }} from the reported message.
319
+ * @param {string} message Reported message
320
+ * @returns {string[]} Array of placeholder names
361
321
  */
362
- function emitLegacyRuleAPIWarning(ruleName) {
363
- if (!emitLegacyRuleAPIWarning[`warned-${ruleName}`]) {
364
- emitLegacyRuleAPIWarning[`warned-${ruleName}`] = true;
365
- process.emitWarning(
366
- `"${ruleName}" rule is using the deprecated function-style format and will stop working in ESLint v9. Please use object-style format: https://eslint.org/docs/latest/extend/custom-rules`,
367
- "DeprecationWarning"
368
- );
369
- }
370
- }
322
+ function getMessagePlaceholders(message) {
323
+ const matcher = getPlaceholderMatcher();
371
324
 
372
- /**
373
- * Emit a deprecation warning if rule has options but is missing the "meta.schema" property
374
- * @param {string} ruleName Name of the rule.
375
- * @returns {void}
376
- */
377
- function emitMissingSchemaWarning(ruleName) {
378
- if (!emitMissingSchemaWarning[`warned-${ruleName}`]) {
379
- emitMissingSchemaWarning[`warned-${ruleName}`] = true;
380
- process.emitWarning(
381
- `"${ruleName}" rule has options but is missing the "meta.schema" property and will stop working in ESLint v9. Please add a schema: https://eslint.org/docs/latest/extend/custom-rules#options-schemas`,
382
- "DeprecationWarning"
383
- );
384
- }
325
+ return Array.from(message.matchAll(matcher), ([, name]) => name.trim());
385
326
  }
386
327
 
387
328
  /**
388
- * Emit a deprecation warning if a rule uses a deprecated `context` method.
389
- * @param {string} ruleName Name of the rule.
390
- * @param {string} methodName The name of the method on `context` that was used.
391
- * @returns {void}
329
+ * Returns the placeholders in the reported messages but
330
+ * only includes the placeholders available in the raw message and not in the provided data.
331
+ * @param {string} message The reported message
332
+ * @param {string} raw The raw message specified in the rule meta.messages
333
+ * @param {undefined|Record<unknown, unknown>} data The passed
334
+ * @returns {string[]} Missing placeholder names
392
335
  */
393
- function emitDeprecatedContextMethodWarning(ruleName, methodName) {
394
- if (!emitDeprecatedContextMethodWarning[`warned-${ruleName}-${methodName}`]) {
395
- emitDeprecatedContextMethodWarning[`warned-${ruleName}-${methodName}`] = true;
396
- process.emitWarning(
397
- `"${ruleName}" rule is using \`context.${methodName}()\`, which is deprecated and will be removed in ESLint v9. Please use \`sourceCode.${DEPRECATED_SOURCECODE_PASSTHROUGHS[methodName]}()\` instead.`,
398
- "DeprecationWarning"
399
- );
400
- }
401
- }
336
+ function getUnsubstitutedMessagePlaceholders(message, raw, data = {}) {
337
+ const unsubstituted = getMessagePlaceholders(message);
402
338
 
403
- /**
404
- * Emit a deprecation warning if rule uses CodePath#currentSegments.
405
- * @param {string} ruleName Name of the rule.
406
- * @returns {void}
407
- */
408
- function emitCodePathCurrentSegmentsWarning(ruleName) {
409
- if (!emitCodePathCurrentSegmentsWarning[`warned-${ruleName}`]) {
410
- emitCodePathCurrentSegmentsWarning[`warned-${ruleName}`] = true;
411
- process.emitWarning(
412
- `"${ruleName}" rule uses CodePath#currentSegments and will stop working in ESLint v9. Please read the documentation for how to update your code: https://eslint.org/docs/latest/extend/code-path-analysis#usage-examples`,
413
- "DeprecationWarning"
414
- );
339
+ if (unsubstituted.length === 0) {
340
+ return [];
415
341
  }
416
- }
417
342
 
418
- /**
419
- * Emit a deprecation warning if `context.parserServices` is used.
420
- * @param {string} ruleName Name of the rule.
421
- * @returns {void}
422
- */
423
- function emitParserServicesWarning(ruleName) {
424
- if (!emitParserServicesWarning[`warned-${ruleName}`]) {
425
- emitParserServicesWarning[`warned-${ruleName}`] = true;
426
- process.emitWarning(
427
- `"${ruleName}" rule is using \`context.parserServices\`, which is deprecated and will be removed in ESLint v9. Please use \`sourceCode.parserServices\` instead.`,
428
- "DeprecationWarning"
429
- );
430
- }
343
+ // Remove false positives by only counting placeholders in the raw message, which were not provided in the data matcher or added with a data property
344
+ const known = getMessagePlaceholders(raw);
345
+ const provided = Object.keys(data);
346
+
347
+ return unsubstituted.filter(name => known.includes(name) && !provided.includes(name));
431
348
  }
432
349
 
350
+ const metaSchemaDescription = `
351
+ \t- If the rule has options, set \`meta.schema\` to an array or non-empty object to enable options validation.
352
+ \t- If the rule doesn't have options, omit \`meta.schema\` to enforce that no options can be passed to the rule.
353
+ \t- You can also set \`meta.schema\` to \`false\` to opt-out of options validation (not recommended).
354
+
355
+ \thttps://eslint.org/docs/latest/extend/custom-rules#options-schemas
356
+ `;
433
357
 
434
358
  //------------------------------------------------------------------------------
435
359
  // Public Interface
@@ -479,26 +403,20 @@ class RuleTester {
479
403
  * Creates a new instance of RuleTester.
480
404
  * @param {Object} [testerConfig] Optional, extra configuration for the tester
481
405
  */
482
- constructor(testerConfig) {
406
+ constructor(testerConfig = {}) {
483
407
 
484
408
  /**
485
409
  * The configuration to use for this tester. Combination of the tester
486
410
  * configuration and the default configuration.
487
411
  * @type {Object}
488
412
  */
489
- this.testerConfig = merge(
490
- {},
491
- defaultConfig,
413
+ this.testerConfig = [
414
+ sharedDefaultConfig,
492
415
  testerConfig,
493
416
  { rules: { "rule-tester/validate-ast": "error" } }
494
- );
417
+ ];
495
418
 
496
- /**
497
- * Rule definitions to define before tests.
498
- * @type {Object}
499
- */
500
- this.rules = {};
501
- this.linter = new Linter();
419
+ this.linter = new Linter({ configType: "flat" });
502
420
  }
503
421
 
504
422
  /**
@@ -511,10 +429,10 @@ class RuleTester {
511
429
  if (typeof config !== "object" || config === null) {
512
430
  throw new TypeError("RuleTester.setDefaultConfig: config must be an object");
513
431
  }
514
- defaultConfig = config;
432
+ sharedDefaultConfig = config;
515
433
 
516
434
  // Make sure the rules object exists since it is assumed to exist later
517
- defaultConfig.rules = defaultConfig.rules || {};
435
+ sharedDefaultConfig.rules = sharedDefaultConfig.rules || {};
518
436
  }
519
437
 
520
438
  /**
@@ -522,7 +440,7 @@ class RuleTester {
522
440
  * @returns {Object} the current configuration
523
441
  */
524
442
  static getDefaultConfig() {
525
- return defaultConfig;
443
+ return sharedDefaultConfig;
526
444
  }
527
445
 
528
446
  /**
@@ -531,7 +449,11 @@ class RuleTester {
531
449
  * @returns {void}
532
450
  */
533
451
  static resetDefaultConfig() {
534
- defaultConfig = merge({}, testerDefaultConfig);
452
+ sharedDefaultConfig = {
453
+ rules: {
454
+ ...testerDefaultConfig.rules
455
+ }
456
+ };
535
457
  }
536
458
 
537
459
 
@@ -602,29 +524,17 @@ class RuleTester {
602
524
  this[IT_ONLY] = value;
603
525
  }
604
526
 
605
- /**
606
- * Define a rule for one particular run of tests.
607
- * @param {string} name The name of the rule to define.
608
- * @param {Function | Rule} rule The rule definition.
609
- * @returns {void}
610
- */
611
- defineRule(name, rule) {
612
- if (typeof rule === "function") {
613
- emitLegacyRuleAPIWarning(name);
614
- }
615
- this.rules[name] = rule;
616
- }
617
527
 
618
528
  /**
619
529
  * Adds a new rule test to execute.
620
530
  * @param {string} ruleName The name of the rule to run.
621
- * @param {Function | Rule} rule The rule to test.
531
+ * @param {Rule} rule The rule to test.
622
532
  * @param {{
623
533
  * valid: (ValidTestCase | string)[],
624
534
  * invalid: InvalidTestCase[]
625
535
  * }} test The collection of tests to run.
626
- * @throws {TypeError|Error} If non-object `test`, or if a required
627
- * scenario of the given type is missing.
536
+ * @throws {TypeError|Error} If `rule` is not an object with a `create` method,
537
+ * or if non-object `test`, or if a required scenario of the given type is missing.
628
538
  * @returns {void}
629
539
  */
630
540
  run(ruleName, rule, test) {
@@ -632,7 +542,15 @@ class RuleTester {
632
542
  const testerConfig = this.testerConfig,
633
543
  requiredScenarios = ["valid", "invalid"],
634
544
  scenarioErrors = [],
635
- linter = this.linter;
545
+ linter = this.linter,
546
+ ruleId = `rule-to-test/${ruleName}`;
547
+
548
+ const seenValidTestCases = new Set();
549
+ const seenInvalidTestCases = new Set();
550
+
551
+ if (!rule || typeof rule !== "object" || typeof rule.create !== "function") {
552
+ throw new TypeError("Rule must be an object with a `create` method");
553
+ }
636
554
 
637
555
  if (!test || typeof test !== "object") {
638
556
  throw new TypeError(`Test Scenarios for rule ${ruleName} : Could not find test scenario object`);
@@ -650,54 +568,55 @@ class RuleTester {
650
568
  ].concat(scenarioErrors).join("\n"));
651
569
  }
652
570
 
653
- if (typeof rule === "function") {
654
- emitLegacyRuleAPIWarning(ruleName);
655
- }
571
+ const baseConfig = [
572
+ { files: ["**"] }, // Make sure the default config matches for all files
573
+ {
574
+ plugins: {
656
575
 
657
- linter.defineRule(ruleName, Object.assign({}, rule, {
658
-
659
- // Create a wrapper rule that freezes the `context` properties.
660
- create(context) {
661
- freezeDeeply(context.options);
662
- freezeDeeply(context.settings);
663
- freezeDeeply(context.parserOptions);
664
-
665
- // wrap all deprecated methods
666
- const newContext = Object.create(
667
- context,
668
- Object.fromEntries(Object.keys(DEPRECATED_SOURCECODE_PASSTHROUGHS).map(methodName => [
669
- methodName,
670
- {
671
- value(...args) {
672
-
673
- // emit deprecation warning
674
- emitDeprecatedContextMethodWarning(ruleName, methodName);
675
-
676
- // call the original method
677
- return context[methodName].call(this, ...args);
678
- },
679
- enumerable: true
680
- }
681
- ]))
682
- );
576
+ // copy root plugin over
577
+ "@": {
683
578
 
684
- // emit warning about context.parserServices
685
- const parserServices = context.parserServices;
579
+ /*
580
+ * Parsers are wrapped to detect more errors, so this needs
581
+ * to be a new object for each call to run(), otherwise the
582
+ * parsers will be wrapped multiple times.
583
+ */
584
+ parsers: {
585
+ ...defaultConfig[0].plugins["@"].parsers
586
+ },
686
587
 
687
- Object.defineProperty(newContext, "parserServices", {
688
- get() {
689
- emitParserServicesWarning(ruleName);
690
- return parserServices;
691
- }
692
- });
588
+ /*
589
+ * The rules key on the default plugin is a proxy to lazy-load
590
+ * just the rules that are needed. So, don't create a new object
591
+ * here, just use the default one to keep that performance
592
+ * enhancement.
593
+ */
594
+ rules: defaultConfig[0].plugins["@"].rules
595
+ },
596
+ "rule-to-test": {
597
+ rules: {
598
+ [ruleName]: Object.assign({}, rule, {
693
599
 
694
- Object.freeze(newContext);
600
+ // Create a wrapper rule that freezes the `context` properties.
601
+ create(context) {
602
+ freezeDeeply(context.options);
603
+ freezeDeeply(context.settings);
604
+ freezeDeeply(context.parserOptions);
695
605
 
696
- return (typeof rule === "function" ? rule : rule.create)(newContext);
697
- }
698
- }));
606
+ // freezeDeeply(context.languageOptions);
699
607
 
700
- linter.defineRules(this.rules);
608
+ return rule.create(context);
609
+ }
610
+ })
611
+ }
612
+ }
613
+ },
614
+ languageOptions: {
615
+ ...defaultConfig[0].languageOptions
616
+ }
617
+ },
618
+ ...defaultConfig.slice(1)
619
+ ];
701
620
 
702
621
  /**
703
622
  * Run the rule for the given item
@@ -707,8 +626,34 @@ class RuleTester {
707
626
  * @private
708
627
  */
709
628
  function runRuleForItem(item) {
710
- let config = merge({}, testerConfig),
711
- code, filename, output, beforeAST, afterAST;
629
+ const flatConfigArrayOptions = {
630
+ baseConfig
631
+ };
632
+
633
+ if (item.filename) {
634
+ flatConfigArrayOptions.basePath = path.parse(item.filename).root;
635
+ }
636
+
637
+ const configs = new FlatConfigArray(testerConfig, flatConfigArrayOptions);
638
+
639
+ /*
640
+ * Modify the returned config so that the parser is wrapped to catch
641
+ * access of the start/end properties. This method is called just
642
+ * once per code snippet being tested, so each test case gets a clean
643
+ * parser.
644
+ */
645
+ configs[ConfigArraySymbol.finalizeConfig] = function(...args) {
646
+
647
+ // can't do super here :(
648
+ const proto = Object.getPrototypeOf(this);
649
+ const calculatedConfig = proto[ConfigArraySymbol.finalizeConfig].apply(this, args);
650
+
651
+ // wrap the parser to catch start/end property access
652
+ calculatedConfig.languageOptions.parser = wrapParser(calculatedConfig.languageOptions.parser);
653
+ return calculatedConfig;
654
+ };
655
+
656
+ let code, filename, output, beforeAST, afterAST;
712
657
 
713
658
  if (typeof item === "string") {
714
659
  code = item;
@@ -725,64 +670,97 @@ class RuleTester {
725
670
  delete itemConfig[parameter];
726
671
  }
727
672
 
673
+ // wrap any parsers
674
+ if (itemConfig.languageOptions && itemConfig.languageOptions.parser) {
675
+
676
+ const parser = itemConfig.languageOptions.parser;
677
+
678
+ if (parser && typeof parser !== "object") {
679
+ throw new Error("Parser must be an object with a parse() or parseForESLint() method.");
680
+ }
681
+
682
+ }
683
+
728
684
  /*
729
685
  * Create the config object from the tester config and this item
730
686
  * specific configurations.
731
687
  */
732
- config = merge(
733
- config,
734
- itemConfig
735
- );
688
+ configs.push(itemConfig);
736
689
  }
737
690
 
738
- if (item.filename) {
691
+ if (hasOwnProperty(item, "only")) {
692
+ assert.ok(typeof item.only === "boolean", "Optional test case property 'only' must be a boolean");
693
+ }
694
+ if (hasOwnProperty(item, "filename")) {
695
+ assert.ok(typeof item.filename === "string", "Optional test case property 'filename' must be a string");
739
696
  filename = item.filename;
740
697
  }
741
698
 
699
+ let ruleConfig = 1;
700
+
742
701
  if (hasOwnProperty(item, "options")) {
743
702
  assert(Array.isArray(item.options), "options must be an array");
744
- if (
745
- item.options.length > 0 &&
746
- typeof rule === "object" &&
747
- (
748
- !rule.meta || (rule.meta && (typeof rule.meta.schema === "undefined" || rule.meta.schema === null))
749
- )
750
- ) {
751
- emitMissingSchemaWarning(ruleName);
703
+ ruleConfig = [1, ...item.options];
704
+ }
705
+
706
+ configs.push({
707
+ rules: {
708
+ [ruleId]: ruleConfig
752
709
  }
753
- config.rules[ruleName] = [1].concat(item.options);
754
- } else {
755
- config.rules[ruleName] = 1;
710
+ });
711
+
712
+ let schema;
713
+
714
+ try {
715
+ schema = getRuleOptionsSchema(rule);
716
+ } catch (err) {
717
+ err.message += metaSchemaDescription;
718
+ throw err;
756
719
  }
757
720
 
758
- const schema = getRuleOptionsSchema(rule);
721
+ /*
722
+ * Check and throw an error if the schema is an empty object (`schema:{}`), because such schema
723
+ * doesn't validate or enforce anything and is therefore considered a possible error. If the intent
724
+ * was to skip options validation, `schema:false` should be set instead (explicit opt-out).
725
+ *
726
+ * For this purpose, a schema object is considered empty if it doesn't have any own enumerable string-keyed
727
+ * properties. While `ajv.compile()` does use enumerable properties from the prototype chain as well,
728
+ * it caches compiled schemas by serializing only own enumerable properties, so it's generally not a good idea
729
+ * to use inherited properties in schemas because schemas that differ only in inherited properties would end up
730
+ * having the same cache entry that would be correct for only one of them.
731
+ *
732
+ * At this point, `schema` can only be an object or `null`.
733
+ */
734
+ if (schema && Object.keys(schema).length === 0) {
735
+ throw new Error(`\`schema: {}\` is a no-op${metaSchemaDescription}`);
736
+ }
759
737
 
760
738
  /*
761
739
  * Setup AST getters.
762
740
  * The goal is to check whether or not AST was modified when
763
741
  * running the rule under test.
764
742
  */
765
- linter.defineRule("rule-tester/validate-ast", {
766
- create() {
767
- return {
768
- Program(node) {
769
- beforeAST = cloneDeeplyExcludesParent(node);
770
- },
771
- "Program:exit"(node) {
772
- afterAST = node;
743
+ configs.push({
744
+ plugins: {
745
+ "rule-tester": {
746
+ rules: {
747
+ "validate-ast": {
748
+ create() {
749
+ return {
750
+ Program(node) {
751
+ beforeAST = cloneDeeplyExcludesParent(node);
752
+ },
753
+ "Program:exit"(node) {
754
+ afterAST = node;
755
+ }
756
+ };
757
+ }
758
+ }
773
759
  }
774
- };
760
+ }
775
761
  }
776
762
  });
777
763
 
778
- if (typeof config.parser === "string") {
779
- assert(path.isAbsolute(config.parser), "Parsers provided as strings to RuleTester must be absolute paths");
780
- } else {
781
- config.parser = espreePath;
782
- }
783
-
784
- linter.defineParser(config.parser, wrapParser(require(config.parser)));
785
-
786
764
  if (schema) {
787
765
  ajv.validateSchema(schema);
788
766
 
@@ -809,35 +787,32 @@ class RuleTester {
809
787
  }
810
788
  }
811
789
 
812
- validate(config, "rule-tester", id => (id === ruleName ? rule : null));
790
+ // check for validation errors
791
+ try {
792
+ configs.normalizeSync();
793
+ configs.getConfig("test.js");
794
+ } catch (error) {
795
+ error.message = `ESLint configuration in rule-tester is invalid: ${error.message}`;
796
+ throw error;
797
+ }
813
798
 
814
799
  // Verify the code.
815
- const { getComments, applyLanguageOptions, applyInlineConfig, finalize } = SourceCode.prototype;
816
- const originalCurrentSegments = Object.getOwnPropertyDescriptor(CodePath.prototype, "currentSegments");
800
+ const { applyLanguageOptions, applyInlineConfig, finalize } = SourceCode.prototype;
817
801
  let messages;
818
802
 
819
803
  try {
820
- SourceCode.prototype.getComments = getCommentsDeprecation;
821
- Object.defineProperty(CodePath.prototype, "currentSegments", {
822
- get() {
823
- emitCodePathCurrentSegmentsWarning(ruleName);
824
- return originalCurrentSegments.get.call(this);
825
- }
826
- });
827
-
828
804
  forbiddenMethods.forEach(methodName => {
829
- SourceCode.prototype[methodName] = throwForbiddenMethodError(methodName);
805
+ SourceCode.prototype[methodName] = throwForbiddenMethodError(methodName, SourceCode.prototype);
830
806
  });
831
807
 
832
- messages = linter.verify(code, config, filename);
808
+ messages = linter.verify(code, configs, filename);
833
809
  } finally {
834
- SourceCode.prototype.getComments = getComments;
835
- Object.defineProperty(CodePath.prototype, "currentSegments", originalCurrentSegments);
836
810
  SourceCode.prototype.applyInlineConfig = applyInlineConfig;
837
811
  SourceCode.prototype.applyLanguageOptions = applyLanguageOptions;
838
812
  SourceCode.prototype.finalize = finalize;
839
813
  }
840
814
 
815
+
841
816
  const fatalErrorMessage = messages.find(m => m.fatal);
842
817
 
843
818
  assert(!fatalErrorMessage, `A fatal parsing error occurred: ${fatalErrorMessage && fatalErrorMessage.message}`);
@@ -845,7 +820,7 @@ class RuleTester {
845
820
  // Verify if autofix makes a syntax error or not.
846
821
  if (messages.some(m => m.fix)) {
847
822
  output = SourceCodeFixer.applyFixes(code, messages).output;
848
- const errorMessageInFix = linter.verify(output, config, filename).find(m => m.fatal);
823
+ const errorMessageInFix = linter.verify(output, configs, filename).find(m => m.fatal);
849
824
 
850
825
  assert(!errorMessageInFix, [
851
826
  "A fatal parsing error occurred in autofix.",
@@ -861,7 +836,9 @@ class RuleTester {
861
836
  messages,
862
837
  output,
863
838
  beforeAST,
864
- afterAST: cloneDeeplyExcludesParent(afterAST)
839
+ afterAST: cloneDeeplyExcludesParent(afterAST),
840
+ configs,
841
+ filename
865
842
  };
866
843
  }
867
844
 
@@ -878,6 +855,39 @@ class RuleTester {
878
855
  }
879
856
  }
880
857
 
858
+ /**
859
+ * Check if this test case is a duplicate of one we have seen before.
860
+ * @param {string|Object} item test case object
861
+ * @param {Set<string>} seenTestCases set of serialized test cases we have seen so far (managed by this function)
862
+ * @returns {void}
863
+ * @private
864
+ */
865
+ function checkDuplicateTestCase(item, seenTestCases) {
866
+ if (!isSerializable(item)) {
867
+
868
+ /*
869
+ * If we can't serialize a test case (because it contains a function, RegExp, etc), skip the check.
870
+ * This might happen with properties like: options, plugins, settings, languageOptions.parser, languageOptions.parserOptions.
871
+ */
872
+ return;
873
+ }
874
+
875
+ const normalizedItem = typeof item === "string" ? { code: item } : item;
876
+ const serializedTestCase = stringify(normalizedItem, {
877
+ replacer(key, value) {
878
+
879
+ // "this" is the currently stringified object --> only ignore top-level properties
880
+ return (normalizedItem !== this || !duplicationIgnoredParameters.has(key)) ? value : void 0;
881
+ }
882
+ });
883
+
884
+ assert(
885
+ !seenTestCases.has(serializedTestCase),
886
+ "detected duplicate test case"
887
+ );
888
+ seenTestCases.add(serializedTestCase);
889
+ }
890
+
881
891
  /**
882
892
  * Check if the template is valid or not
883
893
  * all valid cases go through this
@@ -893,6 +903,8 @@ class RuleTester {
893
903
  assert.ok(typeof item.name === "string", "Optional test case property 'name' must be a string");
894
904
  }
895
905
 
906
+ checkDuplicateTestCase(item, seenValidTestCases);
907
+
896
908
  const result = runRuleForItem(item);
897
909
  const messages = result.messages;
898
910
 
@@ -944,12 +956,30 @@ class RuleTester {
944
956
  assert.fail("Invalid cases must have at least one error");
945
957
  }
946
958
 
959
+ checkDuplicateTestCase(item, seenInvalidTestCases);
960
+
947
961
  const ruleHasMetaMessages = hasOwnProperty(rule, "meta") && hasOwnProperty(rule.meta, "messages");
948
962
  const friendlyIDList = ruleHasMetaMessages ? `[${Object.keys(rule.meta.messages).map(key => `'${key}'`).join(", ")}]` : null;
949
963
 
950
964
  const result = runRuleForItem(item);
951
965
  const messages = result.messages;
952
966
 
967
+ for (const message of messages) {
968
+ if (hasOwnProperty(message, "suggestions")) {
969
+
970
+ /** @type {Map<string, number>} */
971
+ const seenMessageIndices = new Map();
972
+
973
+ for (let i = 0; i < message.suggestions.length; i += 1) {
974
+ const suggestionMessage = message.suggestions[i].desc;
975
+ const previous = seenMessageIndices.get(suggestionMessage);
976
+
977
+ assert.ok(!seenMessageIndices.has(suggestionMessage), `Suggestion message '${suggestionMessage}' reported from suggestion ${i} was previously reported by suggestion ${previous}. Suggestion messages should be unique within an error.`);
978
+ seenMessageIndices.set(suggestionMessage, i);
979
+ }
980
+ }
981
+ }
982
+
953
983
  if (typeof item.errors === "number") {
954
984
 
955
985
  if (item.errors === 0) {
@@ -972,7 +1002,7 @@ class RuleTester {
972
1002
  )
973
1003
  );
974
1004
 
975
- const hasMessageOfThisRule = messages.some(m => m.ruleId === ruleName);
1005
+ const hasMessageOfThisRule = messages.some(m => m.ruleId === ruleId);
976
1006
 
977
1007
  for (let i = 0, l = item.errors.length; i < l; i++) {
978
1008
  const error = item.errors[i];
@@ -984,6 +1014,7 @@ class RuleTester {
984
1014
 
985
1015
  // Just an error message.
986
1016
  assertMessageMatches(message.message, error);
1017
+ assert.ok(message.suggestions === void 0, `Error at index ${i} has suggestions. Please convert the test error into an object and specify 'suggestions' property on it to test suggestions.`);
987
1018
  } else if (typeof error === "object" && error !== null) {
988
1019
 
989
1020
  /*
@@ -1016,6 +1047,18 @@ class RuleTester {
1016
1047
  error.messageId,
1017
1048
  `messageId '${message.messageId}' does not match expected messageId '${error.messageId}'.`
1018
1049
  );
1050
+
1051
+ const unsubstitutedPlaceholders = getUnsubstitutedMessagePlaceholders(
1052
+ message.message,
1053
+ rule.meta.messages[message.messageId],
1054
+ error.data
1055
+ );
1056
+
1057
+ assert.ok(
1058
+ unsubstitutedPlaceholders.length === 0,
1059
+ `The reported message has ${unsubstitutedPlaceholders.length > 1 ? `unsubstituted placeholders: ${unsubstitutedPlaceholders.map(name => `'${name}'`).join(", ")}` : `an unsubstituted placeholder '${unsubstitutedPlaceholders[0]}'`}. Please provide the missing ${unsubstitutedPlaceholders.length > 1 ? "values" : "value"} via the 'data' property in the context.report() call.`
1060
+ );
1061
+
1019
1062
  if (hasOwnProperty(error, "data")) {
1020
1063
 
1021
1064
  /*
@@ -1032,13 +1075,10 @@ class RuleTester {
1032
1075
  `Hydrated message "${rehydratedMessage}" does not match "${message.message}"`
1033
1076
  );
1034
1077
  }
1078
+ } else {
1079
+ assert.fail("Test error must specify either a 'messageId' or 'message'.");
1035
1080
  }
1036
1081
 
1037
- assert.ok(
1038
- hasOwnProperty(error, "data") ? hasOwnProperty(error, "messageId") : true,
1039
- "Error must specify 'messageId' if 'data' is used."
1040
- );
1041
-
1042
1082
  if (error.type) {
1043
1083
  assert.strictEqual(message.nodeType, error.type, `Error type should be ${error.type}, found ${message.nodeType}`);
1044
1084
  }
@@ -1059,81 +1099,117 @@ class RuleTester {
1059
1099
  assert.strictEqual(message.endColumn, error.endColumn, `Error endColumn should be ${error.endColumn}`);
1060
1100
  }
1061
1101
 
1102
+ assert.ok(!message.suggestions || hasOwnProperty(error, "suggestions"), `Error at index ${i} has suggestions. Please specify 'suggestions' property on the test error object.`);
1062
1103
  if (hasOwnProperty(error, "suggestions")) {
1063
1104
 
1064
1105
  // Support asserting there are no suggestions
1065
- if (!error.suggestions || (Array.isArray(error.suggestions) && error.suggestions.length === 0)) {
1066
- if (Array.isArray(message.suggestions) && message.suggestions.length > 0) {
1067
- assert.fail(`Error should have no suggestions on error with message: "${message.message}"`);
1068
- }
1069
- } else {
1070
- assert.strictEqual(Array.isArray(message.suggestions), true, `Error should have an array of suggestions. Instead received "${message.suggestions}" on error with message: "${message.message}"`);
1071
- assert.strictEqual(message.suggestions.length, error.suggestions.length, `Error should have ${error.suggestions.length} suggestions. Instead found ${message.suggestions.length} suggestions`);
1072
-
1073
- error.suggestions.forEach((expectedSuggestion, index) => {
1074
- assert.ok(
1075
- typeof expectedSuggestion === "object" && expectedSuggestion !== null,
1076
- "Test suggestion in 'suggestions' array must be an object."
1077
- );
1078
- Object.keys(expectedSuggestion).forEach(propertyName => {
1079
- assert.ok(
1080
- suggestionObjectParameters.has(propertyName),
1081
- `Invalid suggestion property name '${propertyName}'. Expected one of ${friendlySuggestionObjectParameterList}.`
1082
- );
1083
- });
1084
-
1085
- const actualSuggestion = message.suggestions[index];
1086
- const suggestionPrefix = `Error Suggestion at index ${index} :`;
1087
-
1088
- if (hasOwnProperty(expectedSuggestion, "desc")) {
1106
+ const expectsSuggestions = Array.isArray(error.suggestions) ? error.suggestions.length > 0 : Boolean(error.suggestions);
1107
+ const hasSuggestions = message.suggestions !== void 0;
1108
+
1109
+ if (!hasSuggestions && expectsSuggestions) {
1110
+ assert.ok(!error.suggestions, `Error should have suggestions on error with message: "${message.message}"`);
1111
+ } else if (hasSuggestions) {
1112
+ assert.ok(expectsSuggestions, `Error should have no suggestions on error with message: "${message.message}"`);
1113
+ if (typeof error.suggestions === "number") {
1114
+ assert.strictEqual(message.suggestions.length, error.suggestions, `Error should have ${error.suggestions} suggestions. Instead found ${message.suggestions.length} suggestions`);
1115
+ } else if (Array.isArray(error.suggestions)) {
1116
+ assert.strictEqual(message.suggestions.length, error.suggestions.length, `Error should have ${error.suggestions.length} suggestions. Instead found ${message.suggestions.length} suggestions`);
1117
+
1118
+ error.suggestions.forEach((expectedSuggestion, index) => {
1089
1119
  assert.ok(
1090
- !hasOwnProperty(expectedSuggestion, "data"),
1091
- `${suggestionPrefix} Test should not specify both 'desc' and 'data'.`
1092
- );
1093
- assert.strictEqual(
1094
- actualSuggestion.desc,
1095
- expectedSuggestion.desc,
1096
- `${suggestionPrefix} desc should be "${expectedSuggestion.desc}" but got "${actualSuggestion.desc}" instead.`
1120
+ typeof expectedSuggestion === "object" && expectedSuggestion !== null,
1121
+ "Test suggestion in 'suggestions' array must be an object."
1097
1122
  );
1098
- }
1123
+ Object.keys(expectedSuggestion).forEach(propertyName => {
1124
+ assert.ok(
1125
+ suggestionObjectParameters.has(propertyName),
1126
+ `Invalid suggestion property name '${propertyName}'. Expected one of ${friendlySuggestionObjectParameterList}.`
1127
+ );
1128
+ });
1099
1129
 
1100
- if (hasOwnProperty(expectedSuggestion, "messageId")) {
1101
- assert.ok(
1102
- ruleHasMetaMessages,
1103
- `${suggestionPrefix} Test can not use 'messageId' if rule under test doesn't define 'meta.messages'.`
1104
- );
1105
- assert.ok(
1106
- hasOwnProperty(rule.meta.messages, expectedSuggestion.messageId),
1107
- `${suggestionPrefix} Test has invalid messageId '${expectedSuggestion.messageId}', the rule under test allows only one of ${friendlyIDList}.`
1108
- );
1109
- assert.strictEqual(
1110
- actualSuggestion.messageId,
1111
- expectedSuggestion.messageId,
1112
- `${suggestionPrefix} messageId should be '${expectedSuggestion.messageId}' but got '${actualSuggestion.messageId}' instead.`
1113
- );
1114
- if (hasOwnProperty(expectedSuggestion, "data")) {
1115
- const unformattedMetaMessage = rule.meta.messages[expectedSuggestion.messageId];
1116
- const rehydratedDesc = interpolate(unformattedMetaMessage, expectedSuggestion.data);
1130
+ const actualSuggestion = message.suggestions[index];
1131
+ const suggestionPrefix = `Error Suggestion at index ${index}:`;
1117
1132
 
1133
+ if (hasOwnProperty(expectedSuggestion, "desc")) {
1134
+ assert.ok(
1135
+ !hasOwnProperty(expectedSuggestion, "data"),
1136
+ `${suggestionPrefix} Test should not specify both 'desc' and 'data'.`
1137
+ );
1138
+ assert.ok(
1139
+ !hasOwnProperty(expectedSuggestion, "messageId"),
1140
+ `${suggestionPrefix} Test should not specify both 'desc' and 'messageId'.`
1141
+ );
1118
1142
  assert.strictEqual(
1119
1143
  actualSuggestion.desc,
1120
- rehydratedDesc,
1121
- `${suggestionPrefix} Hydrated test desc "${rehydratedDesc}" does not match received desc "${actualSuggestion.desc}".`
1144
+ expectedSuggestion.desc,
1145
+ `${suggestionPrefix} desc should be "${expectedSuggestion.desc}" but got "${actualSuggestion.desc}" instead.`
1146
+ );
1147
+ } else if (hasOwnProperty(expectedSuggestion, "messageId")) {
1148
+ assert.ok(
1149
+ ruleHasMetaMessages,
1150
+ `${suggestionPrefix} Test can not use 'messageId' if rule under test doesn't define 'meta.messages'.`
1151
+ );
1152
+ assert.ok(
1153
+ hasOwnProperty(rule.meta.messages, expectedSuggestion.messageId),
1154
+ `${suggestionPrefix} Test has invalid messageId '${expectedSuggestion.messageId}', the rule under test allows only one of ${friendlyIDList}.`
1155
+ );
1156
+ assert.strictEqual(
1157
+ actualSuggestion.messageId,
1158
+ expectedSuggestion.messageId,
1159
+ `${suggestionPrefix} messageId should be '${expectedSuggestion.messageId}' but got '${actualSuggestion.messageId}' instead.`
1160
+ );
1161
+
1162
+ const unsubstitutedPlaceholders = getUnsubstitutedMessagePlaceholders(
1163
+ actualSuggestion.desc,
1164
+ rule.meta.messages[expectedSuggestion.messageId],
1165
+ expectedSuggestion.data
1166
+ );
1167
+
1168
+ assert.ok(
1169
+ unsubstitutedPlaceholders.length === 0,
1170
+ `The message of the suggestion has ${unsubstitutedPlaceholders.length > 1 ? `unsubstituted placeholders: ${unsubstitutedPlaceholders.map(name => `'${name}'`).join(", ")}` : `an unsubstituted placeholder '${unsubstitutedPlaceholders[0]}'`}. Please provide the missing ${unsubstitutedPlaceholders.length > 1 ? "values" : "value"} via the 'data' property for the suggestion in the context.report() call.`
1171
+ );
1172
+
1173
+ if (hasOwnProperty(expectedSuggestion, "data")) {
1174
+ const unformattedMetaMessage = rule.meta.messages[expectedSuggestion.messageId];
1175
+ const rehydratedDesc = interpolate(unformattedMetaMessage, expectedSuggestion.data);
1176
+
1177
+ assert.strictEqual(
1178
+ actualSuggestion.desc,
1179
+ rehydratedDesc,
1180
+ `${suggestionPrefix} Hydrated test desc "${rehydratedDesc}" does not match received desc "${actualSuggestion.desc}".`
1181
+ );
1182
+ }
1183
+ } else if (hasOwnProperty(expectedSuggestion, "data")) {
1184
+ assert.fail(
1185
+ `${suggestionPrefix} Test must specify 'messageId' if 'data' is used.`
1186
+ );
1187
+ } else {
1188
+ assert.fail(
1189
+ `${suggestionPrefix} Test must specify either 'messageId' or 'desc'.`
1122
1190
  );
1123
1191
  }
1124
- } else {
1125
- assert.ok(
1126
- !hasOwnProperty(expectedSuggestion, "data"),
1127
- `${suggestionPrefix} Test must specify 'messageId' if 'data' is used.`
1128
- );
1129
- }
1130
1192
 
1131
- if (hasOwnProperty(expectedSuggestion, "output")) {
1193
+ assert.ok(hasOwnProperty(expectedSuggestion, "output"), `${suggestionPrefix} The "output" property is required.`);
1132
1194
  const codeWithAppliedSuggestion = SourceCodeFixer.applyFixes(item.code, [actualSuggestion]).output;
1133
1195
 
1196
+ // Verify if suggestion fix makes a syntax error or not.
1197
+ const errorMessageInSuggestion =
1198
+ linter.verify(codeWithAppliedSuggestion, result.configs, result.filename).find(m => m.fatal);
1199
+
1200
+ assert(!errorMessageInSuggestion, [
1201
+ "A fatal parsing error occurred in suggestion fix.",
1202
+ `Error: ${errorMessageInSuggestion && errorMessageInSuggestion.message}`,
1203
+ "Suggestion output:",
1204
+ codeWithAppliedSuggestion
1205
+ ].join("\n"));
1206
+
1134
1207
  assert.strictEqual(codeWithAppliedSuggestion, expectedSuggestion.output, `Expected the applied suggestion fix to match the test suggestion output for suggestion at index: ${index} on error with message: "${message.message}"`);
1135
- }
1136
- });
1208
+ assert.notStrictEqual(expectedSuggestion.output, item.code, `The output of a suggestion should differ from the original source code for suggestion at index: ${index} on error with message: "${message.message}"`);
1209
+ });
1210
+ } else {
1211
+ assert.fail("Test error object property 'suggestions' should be an array or a number");
1212
+ }
1137
1213
  }
1138
1214
  }
1139
1215
  } else {
@@ -1153,6 +1229,7 @@ class RuleTester {
1153
1229
  );
1154
1230
  } else {
1155
1231
  assert.strictEqual(result.output, item.output, "Output is incorrect.");
1232
+ assert.notStrictEqual(item.code, item.output, "Test property 'output' matches 'code'. If no autofix is expected, then omit the 'output' property or set it to null.");
1156
1233
  }
1157
1234
  } else {
1158
1235
  assert.strictEqual(