@arcgis/eslint-config 5.2.0-next.50 → 5.2.0-next.52

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.
package/README.md CHANGED
@@ -8,5 +8,5 @@ It is not intended to be used directly, but rather used as a dependency by other
8
8
 
9
9
  ## License
10
10
 
11
- This package is licensed under the terms described in the `LICENSE.md` file, located in the root of the package, and at https://js.arcgis.com/5.1/LICENSE.txt.
12
- For third party notices, see https://js.arcgis.com/5.1/third-party-notices.txt.
11
+ This package is licensed under the terms described in the `LICENSE.md` file, located in the root of the package, and at https://js.arcgis.com/5.2/LICENSE.txt.
12
+ For third party notices, see https://js.arcgis.com/5.2/third-party-notices.txt.
@@ -28,8 +28,6 @@ const config = [
28
28
  "func-names": "off",
29
29
  // This rule is for consistency between library developers - less important for monolith apps.
30
30
  "id-denylist": "off",
31
- // Some cases actually require sequential async loops
32
- "no-await-in-loop": "off",
33
31
  // More important for library-exposed symbols than apps
34
32
  "symbol-description": "off"
35
33
  }
@@ -0,0 +1,621 @@
1
+ import markdown from "@eslint/markdown";
2
+ import jsdocPlugin from "eslint-plugin-jsdoc";
3
+ import perfectionistPlugin from "eslint-plugin-perfectionist";
4
+ import reactJsxPlugin from "eslint-plugin-react-jsx";
5
+ import regexpPlugin from "eslint-plugin-regexp";
6
+ import unicornPlugin from "eslint-plugin-unicorn";
7
+ import { join } from "node:path";
8
+ import { corePlugin } from "../plugins/core/index.js";
9
+ const noRestrictedSyntax = [
10
+ "error",
11
+ "AccessorProperty",
12
+ "PrivateIdentifier",
13
+ {
14
+ selector: "CallExpression[callee.property.name='concat'][arguments.length=0]",
15
+ message: "Use slice() instead of concat()."
16
+ },
17
+ {
18
+ selector: "CallExpression[callee.property.name='slice'][arguments.length=1] > .arguments[value=0]",
19
+ message: "Use slice() instead of slice(0)."
20
+ },
21
+ {
22
+ selector: "TSAbstractPropertyDefinition[decorators.length>0]",
23
+ message: "Decorators are not supported on abstract properties."
24
+ },
25
+ {
26
+ selector: "TSEnumDeclaration:not([const=true])",
27
+ message: "Don't declare non-const enums. Use `const enum` instead, or an object with `as const`. See https://www.typescriptlang.org/docs/handbook/enums.html#objects-vs-enums"
28
+ },
29
+ {
30
+ selector: "VariableDeclarator > CallExpression > MemberExpression[property.name='getLogger']",
31
+ message: "Get a logger as needed instead of assigning it for future use."
32
+ },
33
+ {
34
+ selector: "TSModuleDeclaration[id.type='Identifier']",
35
+ message: "Use const objects or static classes instead. See https://devtopia.esri.com/WebGIS/arcgis-js-api/discussions/72194"
36
+ }
37
+ ];
38
+ const disabledMarkdownRules = Object.fromEntries(
39
+ Object.keys(markdown.rules).map((ruleName) => [`markdown/${ruleName}`, "off"])
40
+ );
41
+ const eslintConfigCore = [
42
+ {
43
+ files: ["packages/core-packages/**/*.{ts,tsx}"],
44
+ linterOptions: {
45
+ reportUnusedDisableDirectives: "off"
46
+ },
47
+ plugins: {
48
+ core: corePlugin,
49
+ jsdoc: jsdocPlugin,
50
+ regexp: regexpPlugin,
51
+ unicorn: unicornPlugin
52
+ },
53
+ settings: {
54
+ jsdoc: {
55
+ tagNamePreference: {
56
+ // Our tags
57
+ "public": "public",
58
+ "copyDoc": "copyDoc",
59
+ // Flags
60
+ "beta": "beta",
61
+ "experimental": "experimental",
62
+ "readonly": "readonly",
63
+ // Metadata
64
+ "deprecated": "deprecated",
65
+ "default": "default",
66
+ "since": "since",
67
+ "param": "param",
68
+ "returns": "returns",
69
+ "see": "see",
70
+ "esriCompatibilityName": "esriCompatibilityName",
71
+ // Long descriptions
72
+ "example": "example",
73
+ "privateRemarks": "privateRemarks",
74
+ // Legacy replacements
75
+ "const": "readonly",
76
+ "constant": "readonly",
77
+ "defaultvalue": "default",
78
+ "return": "returns",
79
+ // Obsolete tags. Do not use in new code. These are retained for
80
+ // compatibility with existing non-public Core JSDoc.
81
+ "amdalias": "amdalias",
82
+ "autocast": "autocast",
83
+ "noautocast": "noautocast",
84
+ "constructonly": "constructonly",
85
+ "tag constructor": "constructor",
86
+ "deprecatedParameters": "deprecatedParameters",
87
+ "deprecatedProperties": "deprecatedProperties",
88
+ "esmimport": "esmimport",
89
+ "extends": "extends",
90
+ "method": "method",
91
+ "moduletype": "moduletype",
92
+ "ignoreNilOnly": "ignoreNilOnly"
93
+ }
94
+ }
95
+ },
96
+ rules: {
97
+ //#region Temporary Disable monorepo-exclusive rules
98
+ "@typescript-eslint/adjacent-overload-signatures": "off",
99
+ "@typescript-eslint/array-type": "off",
100
+ "@typescript-eslint/await-thenable": "off",
101
+ "@typescript-eslint/consistent-generic-constructors": "off",
102
+ "@typescript-eslint/consistent-indexed-object-style": "off",
103
+ "@typescript-eslint/consistent-type-assertions": "off",
104
+ "@typescript-eslint/consistent-type-exports": "off",
105
+ "@typescript-eslint/dot-notation": "off",
106
+ "@typescript-eslint/max-params": "off",
107
+ "@typescript-eslint/method-signature-style": "off",
108
+ "@typescript-eslint/no-confusing-non-null-assertion": "off",
109
+ "@typescript-eslint/no-deprecated": "off",
110
+ "@typescript-eslint/no-duplicate-type-constituents": "off",
111
+ "@typescript-eslint/no-dynamic-delete": "off",
112
+ "@typescript-eslint/no-empty-function": "off",
113
+ "@typescript-eslint/no-empty-object-type": "off",
114
+ "@typescript-eslint/no-explicit-any": "off",
115
+ "@typescript-eslint/no-extra-non-null-assertion": "off",
116
+ "@typescript-eslint/no-extraneous-class": "off",
117
+ "@typescript-eslint/no-implied-eval": "off",
118
+ "@typescript-eslint/no-inferrable-types": "off",
119
+ "@typescript-eslint/no-misused-new": "off",
120
+ "@typescript-eslint/no-misused-spread": "off",
121
+ "@typescript-eslint/no-non-null-asserted-nullish-coalescing": "off",
122
+ "@typescript-eslint/no-non-null-asserted-optional-chain": "off",
123
+ "@typescript-eslint/no-redundant-type-constituents": "off",
124
+ "@typescript-eslint/no-unnecessary-boolean-literal-compare": "off",
125
+ "@typescript-eslint/no-unnecessary-condition": "off",
126
+ "@typescript-eslint/no-unnecessary-template-expression": "off",
127
+ "@typescript-eslint/no-unnecessary-type-assertion": "off",
128
+ "@typescript-eslint/no-unnecessary-type-constraint": "off",
129
+ "@typescript-eslint/no-unnecessary-type-conversion": "off",
130
+ "@typescript-eslint/no-unsafe-argument": "off",
131
+ "@typescript-eslint/no-unsafe-assignment": "off",
132
+ "@typescript-eslint/no-unsafe-call": "off",
133
+ "@typescript-eslint/no-unsafe-enum-comparison": "off",
134
+ "@typescript-eslint/no-unsafe-function-type": "off",
135
+ "@typescript-eslint/no-unsafe-member-access": "off",
136
+ "@typescript-eslint/no-useless-empty-export": "off",
137
+ "@typescript-eslint/no-wrapper-object-types": "off",
138
+ "@typescript-eslint/non-nullable-type-assertion-style": "off",
139
+ "@typescript-eslint/only-throw-error": "off",
140
+ "@typescript-eslint/prefer-as-const": "off",
141
+ "@typescript-eslint/prefer-function-type": "off",
142
+ "@typescript-eslint/prefer-promise-reject-errors": "off",
143
+ "@typescript-eslint/prefer-reduce-type-parameter": "off",
144
+ "@typescript-eslint/prefer-return-this-type": "off",
145
+ "@typescript-eslint/promise-function-async": "off",
146
+ "@typescript-eslint/related-getter-setter-pairs": "off",
147
+ "@typescript-eslint/require-array-sort-compare": "off",
148
+ "@typescript-eslint/require-await": "off",
149
+ "@typescript-eslint/restrict-plus-operands": "off",
150
+ "@typescript-eslint/restrict-template-expressions": "off",
151
+ "@typescript-eslint/sort-type-constituents": "off",
152
+ "@typescript-eslint/switch-exhaustiveness-check": "off",
153
+ "@typescript-eslint/triple-slash-reference": "off",
154
+ "@typescript-eslint/unified-signatures": "off",
155
+ "array-callback-return": "off",
156
+ "default-case-last": "off",
157
+ "func-names": "off",
158
+ "func-style": "off",
159
+ "grouped-accessor-pairs": "off",
160
+ "id-denylist": "off",
161
+ "lumina/add-missing-jsx-import": "off",
162
+ "lumina/auto-add-type": "off",
163
+ "lumina/component-placement-rules": "off",
164
+ "lumina/consistent-event-naming": "off",
165
+ "lumina/consistent-nullability": "off",
166
+ "lumina/decorators-context": "off",
167
+ "lumina/explicit-setter-type": "off",
168
+ "lumina/member-ordering": "off",
169
+ "lumina/no-create-element-component": "off",
170
+ "lumina/no-ignore-jsdoc-tag": "off",
171
+ "lumina/no-incorrect-dynamic-tag-name": "off",
172
+ "lumina/no-inline-arrow-in-ref": "off",
173
+ "lumina/no-inline-exposure-jsdoc-tag": "off",
174
+ "lumina/no-invalid-directives-prop": "off",
175
+ "lumina/no-jsdoc-xref-links": "off",
176
+ "lumina/no-jsx-spread": "off",
177
+ "lumina/no-listen-in-connected-callback": "off",
178
+ "lumina/no-non-component-exports": "off",
179
+ "lumina/no-property-name-start-with-on": "off",
180
+ "lumina/no-render-false": "off",
181
+ "lumina/no-unnecessary-assertion-on-event": "off",
182
+ "lumina/no-unnecessary-attribute-name": "off",
183
+ "lumina/no-unnecessary-bind-this": "off",
184
+ "lumina/no-unnecessary-key": "off",
185
+ "lumina/tag-name-rules": "off",
186
+ "no-alert": "off",
187
+ "no-await-in-loop": "off",
188
+ "no-constructor-return": "off",
189
+ "no-extend-native": "off",
190
+ "no-extra-bind": "off",
191
+ "no-loss-of-precision": "off",
192
+ "no-multi-assign": "off",
193
+ "no-object-constructor": "off",
194
+ "no-restricted-syntax": "off",
195
+ "no-return-assign": "off",
196
+ "no-script-url": "off",
197
+ "no-self-compare": "off",
198
+ "no-sequences": "off",
199
+ "no-unneeded-ternary": "off",
200
+ "no-unreachable-loop": "off",
201
+ "no-unsafe-negation": "off",
202
+ "no-useless-call": "off",
203
+ "no-useless-computed-key": "off",
204
+ "no-useless-concat": "off",
205
+ "no-warning-comments": "off",
206
+ "prefer-arrow-callback": "off",
207
+ "prefer-numeric-literals": "off",
208
+ "prefer-object-has-own": "off",
209
+ "prefer-object-spread": "off",
210
+ "prefer-regex-literals": "off",
211
+ "prefer-rest-params": "off",
212
+ "prefer-spread": "off",
213
+ "prefer-template": "off",
214
+ "require-atomic-updates": "off",
215
+ "require-unicode-regexp": "off",
216
+ "strict": "off",
217
+ "symbol-description": "off",
218
+ "unicode-bom": "off",
219
+ "unicorn/number-literal-case": "off",
220
+ "webgis/consistent-logging": "off",
221
+ "webgis/no-dts-files": "off",
222
+ "webgis/no-import-outside-src": "off",
223
+ "webgis/no-touching-jsdoc": "off",
224
+ "webgis/no-unsafe-hash-links": "off",
225
+ "webgis/require-await-for-async-screenshot-assert": "off",
226
+ "webgis/require-js-in-imports": "off",
227
+ "yoda": "off",
228
+ //#endregion
229
+ //#region Temporary enable core-exclusive rules
230
+ "@typescript-eslint/ban-ts-comment": ["error", { "ts-expect-error": false, "ts-nocheck": false }],
231
+ "@typescript-eslint/consistent-type-definitions": ["error", "interface"],
232
+ "@typescript-eslint/explicit-function-return-type": ["error", { allowExpressions: true }],
233
+ "@typescript-eslint/explicit-member-accessibility": ["error", { accessibility: "no-public" }],
234
+ "@typescript-eslint/naming-convention": [
235
+ "error",
236
+ { selector: ["typeLike"], format: ["PascalCase"] },
237
+ {
238
+ selector: ["function", "classMethod", "classProperty", "typeMethod"],
239
+ format: ["camelCase"],
240
+ leadingUnderscore: "allow"
241
+ },
242
+ {
243
+ selector: ["memberLike", "classProperty"],
244
+ modifiers: ["private"],
245
+ format: ["camelCase"],
246
+ leadingUnderscore: "require"
247
+ },
248
+ {
249
+ selector: ["classProperty"],
250
+ modifiers: ["static"],
251
+ format: ["UPPER_CASE", "PascalCase", "camelCase"],
252
+ leadingUnderscore: "allow"
253
+ },
254
+ {
255
+ selector: ["classProperty"],
256
+ modifiers: ["private", "static"],
257
+ format: ["UPPER_CASE", "PascalCase", "camelCase"],
258
+ leadingUnderscore: "allow"
259
+ }
260
+ ],
261
+ "@typescript-eslint/no-array-constructor": "error",
262
+ "@typescript-eslint/no-import-type-side-effects": "error",
263
+ "@typescript-eslint/no-redeclare": "off",
264
+ // turn on when this doesn't error on mixins
265
+ "@typescript-eslint/no-require-imports": "error",
266
+ "@typescript-eslint/no-restricted-imports": [
267
+ "error",
268
+ {
269
+ paths: [
270
+ { name: "esri/identity/IdentityManager", message: "Use `esri/kernel.id` or the `authMode` option." },
271
+ {
272
+ name: "esri/layers/support/rasterFunctionUtils",
273
+ message: "Import raster function creators directly instead."
274
+ },
275
+ {
276
+ name: "esri/TimeExtent",
277
+ message: "Use `esri/time/TimeExtent` instead. See https://devtopia.esri.com/WebGIS/arcgis-js-api/issues/49223."
278
+ },
279
+ {
280
+ name: "esri/TimeInterval",
281
+ message: "Use `esri/time/TimeInterval` instead. See https://devtopia.esri.com/WebGIS/arcgis-js-api/issues/49223."
282
+ },
283
+ { name: "esri/rest/geometryService", message: "Import geometry services directly instead" },
284
+ { name: "esri/rest/geoprocessor", message: "Import geoprocessor exports directly instead" },
285
+ { name: "esri/rest/locator", message: "Import locator exports directly instead" },
286
+ { name: "esri/rest/query", message: "use individual files instead" }
287
+ ],
288
+ patterns: [
289
+ {
290
+ group: ["@esri/calcite-components/dist/components/*"],
291
+ message: "Pass a dynamic import to `loadCalciteComponents` inside `loadDependencies` instead.",
292
+ allowTypeImports: true
293
+ }
294
+ ]
295
+ }
296
+ ],
297
+ "@typescript-eslint/no-unnecessary-parameter-property-assignment": "error",
298
+ "@typescript-eslint/no-unused-expressions": ["error", { allowShortCircuit: true, allowTernary: true }],
299
+ "@typescript-eslint/no-unused-vars": [
300
+ "error",
301
+ {
302
+ args: "all",
303
+ argsIgnorePattern: "^_",
304
+ caughtErrors: "none",
305
+ destructuredArrayIgnorePattern: "^_",
306
+ varsIgnorePattern: "^_|^tsx",
307
+ ignoreRestSiblings: true,
308
+ enableAutofixRemoval: { imports: true }
309
+ }
310
+ ],
311
+ "@typescript-eslint/prefer-namespace-keyword": "error",
312
+ "arrow-body-style": "error",
313
+ "core/glsl-whitespace": "error",
314
+ "core/imports-format": "error",
315
+ "core/no-duplicate-import-comments": "error",
316
+ "core/no-empty-jsdoc-todo": "error",
317
+ "core/no-redundant-checked-in-files": "error",
318
+ "core/prefer-relative-imports": "error",
319
+ "core/require-calcite-import": "error",
320
+ "curly": "error",
321
+ "eqeqeq": ["error", "smart"],
322
+ "import-x/no-duplicates": "error",
323
+ "import-x/no-dynamic-require": ["error", { esmodule: true }],
324
+ "import-x/no-restricted-paths": [
325
+ "error",
326
+ {
327
+ // This is temporary. Once most of this config is merged with the
328
+ // root @arcgis/eslint-config, the core-specific rules can be moved
329
+ // into @webgis/monorepo-config
330
+ basePath: join(import.meta.dirname, "../../../../core-packages/core"),
331
+ zones: [
332
+ {
333
+ target: "src",
334
+ from: "src/identity/IdentityManager.ts",
335
+ message: "Use `kernel.id` or the `authMode` request option."
336
+ },
337
+ {
338
+ target: "src",
339
+ from: "src/layers/support/rasterFunctionUtils.ts",
340
+ message: "Import raster function creators directly instead."
341
+ },
342
+ {
343
+ target: "src",
344
+ from: "src/rest/geometryService.ts",
345
+ message: "Import the `geometryService` exports directly instead."
346
+ },
347
+ {
348
+ target: "src",
349
+ from: "src/rest/geoprocessor.ts",
350
+ message: "Import the geoprocessor exports directly instead."
351
+ },
352
+ {
353
+ target: "src",
354
+ from: "src/rest/locator.ts",
355
+ message: "Import the locator exports directly instead."
356
+ },
357
+ {
358
+ target: "src",
359
+ from: "src/rest/query.ts",
360
+ message: "Import the query exports directly instead"
361
+ },
362
+ {
363
+ target: "src",
364
+ from: "tests",
365
+ message: "Modules in /src should not import modules in /tests."
366
+ }
367
+ ]
368
+ }
369
+ ],
370
+ "import-x/no-useless-path-segments": "error",
371
+ "jsdoc/check-tag-names": [
372
+ "error",
373
+ { definedTags: ["tstype", "ignoreNilOnly", "noautocast", "collectionItemType"] }
374
+ ],
375
+ "jsdoc/check-types": "error",
376
+ // Ensure @readonly and @public is used without any content
377
+ "jsdoc/empty-tags": "error",
378
+ // Avoid /**\n ** something */
379
+ "jsdoc/no-multi-asterisks": ["error", { allowWhitespace: true }],
380
+ // Enforce each JSDoc line starts with *
381
+ "jsdoc/require-asterisk-prefix": "error",
382
+ // Enforce @param has param name
383
+ "jsdoc/require-param-name": "error",
384
+ "no-array-constructor": "off",
385
+ "no-async-promise-executor": "error",
386
+ "no-caller": "error",
387
+ "no-case-declarations": "error",
388
+ "no-cond-assign": "error",
389
+ "no-constant-binary-expression": "error",
390
+ "no-control-regex": "error",
391
+ "no-debugger": "error",
392
+ "no-duplicate-case": "error",
393
+ "no-else-return": "error",
394
+ "no-eval": "error",
395
+ "no-fallthrough": ["error", { reportUnusedFallthroughComment: false }],
396
+ "no-implied-eval": "error",
397
+ "no-inner-declarations": "error",
398
+ "no-labels": ["error", { allowLoop: true, allowSwitch: true }],
399
+ "no-new-func": "error",
400
+ "no-new-wrappers": "error",
401
+ "no-redeclare": "off",
402
+ "no-regex-spaces": "error",
403
+ "no-restricted-globals": ["error", "event", "name", "origin"],
404
+ "no-shadow-restricted-names": "error",
405
+ "no-throw-literal": "error",
406
+ "no-unsafe-finally": "error",
407
+ "no-unsafe-optional-chaining": "error",
408
+ "no-unused-expressions": "off",
409
+ "no-unused-vars": "off",
410
+ "no-useless-escape": "error",
411
+ "no-useless-rename": "error",
412
+ "no-var": "error",
413
+ "object-shorthand": "error",
414
+ "one-var": ["error", { initialized: "never" }],
415
+ "prefer-const": ["error", { destructuring: "all" }],
416
+ "prefer-exponentiation-operator": "error",
417
+ "radix": "error",
418
+ "regexp/control-character-escape": "error",
419
+ "regexp/no-dupe-characters-character-class": "error",
420
+ "regexp/no-dupe-disjunctions": "error",
421
+ "regexp/no-empty-alternative": "error",
422
+ "regexp/no-empty-capturing-group": "error",
423
+ "regexp/no-empty-character-class": "error",
424
+ "regexp/no-empty-group": "error",
425
+ "regexp/no-empty-lookarounds-assertion": "error",
426
+ "regexp/no-escape-backspace": "error",
427
+ "regexp/no-extra-lookaround-assertions": "error",
428
+ "regexp/no-invalid-regexp": "error",
429
+ "regexp/no-invisible-character": "error",
430
+ "regexp/no-lazy-ends": "error",
431
+ "regexp/no-misleading-unicode-character": "error",
432
+ "regexp/no-missing-g-flag": "error",
433
+ "regexp/no-obscure-range": "error",
434
+ "regexp/no-optional-assertion": "error",
435
+ "regexp/no-trivially-nested-assertion": "error",
436
+ "regexp/no-trivially-nested-quantifier": "error",
437
+ "regexp/no-useless-assertions": "error",
438
+ "regexp/no-useless-backreference": "error",
439
+ "regexp/no-useless-character-class": "error",
440
+ "regexp/no-useless-dollar-replacements": "error",
441
+ "regexp/no-useless-escape": "error",
442
+ // adds auto-fix and additional checks compared to `no-useless-escape`
443
+ "regexp/no-useless-lazy": "error",
444
+ "regexp/no-useless-quantifier": "error",
445
+ "regexp/no-useless-range": "error",
446
+ "regexp/no-useless-set-operand": "error",
447
+ "regexp/no-useless-string-literal": "error",
448
+ "regexp/no-useless-two-nums-quantifier": "error",
449
+ "regexp/optimal-quantifier-concatenation": "error",
450
+ "regexp/prefer-predefined-assertion": "error",
451
+ "regexp/prefer-range": "error",
452
+ "regexp/prefer-set-operation": "error",
453
+ "unicorn/consistent-date-clone": "error",
454
+ "unicorn/consistent-empty-array-spread": "error",
455
+ "unicorn/no-abusive-eslint-disable": "error",
456
+ "unicorn/no-accessor-recursion": "error",
457
+ "unicorn/no-await-in-promise-methods": "error",
458
+ "unicorn/no-instanceof-builtins": "error",
459
+ "unicorn/no-invalid-remove-event-listener": "error",
460
+ "unicorn/no-negation-in-equality-check": "error",
461
+ "unicorn/no-single-promise-in-promise-methods": "error",
462
+ "unicorn/no-typeof-undefined": "error",
463
+ "unicorn/no-unnecessary-array-flat-depth": "error",
464
+ "unicorn/no-unnecessary-array-splice-count": "error",
465
+ "unicorn/no-unnecessary-slice-end": "error",
466
+ "unicorn/no-useless-fallback-in-spread": "error",
467
+ "unicorn/no-useless-length-check": "error",
468
+ "unicorn/no-useless-promise-resolve-reject": "error",
469
+ "unicorn/no-useless-spread": "error",
470
+ "unicorn/no-useless-undefined": ["error", { checkArguments: false, checkArrowFunctionBody: false }],
471
+ "unicorn/prefer-array-find": "error",
472
+ "unicorn/prefer-array-flat": "error",
473
+ "unicorn/prefer-array-flat-map": "error",
474
+ "unicorn/prefer-array-index-of": "error",
475
+ "unicorn/prefer-array-some": "error",
476
+ "unicorn/prefer-blob-reading-methods": "error",
477
+ "unicorn/prefer-date-now": "error",
478
+ "unicorn/prefer-default-parameters": "error",
479
+ "unicorn/prefer-includes": "error",
480
+ "unicorn/prefer-logical-operator-over-ternary": "error",
481
+ "unicorn/prefer-negative-index": "error",
482
+ "unicorn/prefer-node-protocol": "error",
483
+ "unicorn/prefer-regexp-test": "error",
484
+ "unicorn/prefer-set-has": "error",
485
+ "unicorn/prefer-set-size": "error",
486
+ "unicorn/prefer-string-replace-all": "error",
487
+ "unicorn/prefer-string-slice": "error",
488
+ "unicorn/prefer-string-starts-ends-with": "error",
489
+ "unicorn/throw-new-error": "error",
490
+ "webgis/consistent-constructor-arguments": "error",
491
+ "webgis/consistent-mixin-syntax": "error",
492
+ "webgis/no-empty-catch": "error",
493
+ "webgis/no-kebab-case-props": "error",
494
+ "webgis/require-button-type": "error"
495
+ //#endregion
496
+ }
497
+ },
498
+ {
499
+ files: ["packages/core-packages/**/*.md"],
500
+ rules: disabledMarkdownRules
501
+ },
502
+ {
503
+ files: ["packages/core-packages/core/package.json"],
504
+ rules: {
505
+ // @typescript/native-preview temporarily uses the `beta` tag rather than a semver range.
506
+ "package-json/valid-devDependencies": "off"
507
+ }
508
+ },
509
+ {
510
+ files: ["packages/core-packages/**/*.d.ts"],
511
+ rules: {
512
+ "@typescript-eslint/naming-convention": "off"
513
+ // we can't enforce names in libraries
514
+ }
515
+ },
516
+ {
517
+ // Type-aware rules
518
+ files: ["packages/core-packages/**/*.{ts,tsx}"],
519
+ // This folder runs in node, so uses a separate tsconfig.json
520
+ ignores: ["packages/core-packages/core/tests/support/vitest/node/**/*.ts"],
521
+ rules: {
522
+ // Disabled until https://github.com/typescript-eslint/typescript-eslint/pull/11267 is available.
523
+ "@typescript-eslint/await-thenable": "off",
524
+ "@typescript-eslint/no-array-delete": "error",
525
+ "@typescript-eslint/no-floating-promises": "error",
526
+ "@typescript-eslint/no-for-in-array": "error",
527
+ "@typescript-eslint/no-meaningless-void-operator": "error",
528
+ "@typescript-eslint/no-misused-promises": ["error", { checksVoidReturn: false }],
529
+ "@typescript-eslint/no-unsafe-unary-minus": "error",
530
+ "@typescript-eslint/prefer-find": "error",
531
+ "@typescript-eslint/prefer-includes": "error",
532
+ "@typescript-eslint/prefer-optional-chain": ["error", { requireNullish: true }],
533
+ "@typescript-eslint/prefer-string-starts-ends-with": "error",
534
+ "@typescript-eslint/return-await": ["error", "error-handling-correctness-only"]
535
+ }
536
+ },
537
+ {
538
+ files: ["packages/core-packages/**/*.tsx"],
539
+ plugins: {
540
+ "perfectionist": perfectionistPlugin,
541
+ "react-jsx": reactJsxPlugin
542
+ },
543
+ rules: {
544
+ "perfectionist/sort-jsx-props": [
545
+ "error",
546
+ {
547
+ groups: ["unknown", "callback"],
548
+ customGroups: [{ groupName: "callback", elementNamePattern: "^on.+" }]
549
+ }
550
+ ],
551
+ "react-jsx/no-children-prop": "error",
552
+ "react-jsx/no-comment-textnodes": "error",
553
+ "react-jsx/no-useless-fragment": "error"
554
+ }
555
+ },
556
+ {
557
+ files: ["packages/core-packages/core/src/**/*.{ts,tsx}"],
558
+ rules: {
559
+ "core/no-invalid-declared-class": "error"
560
+ }
561
+ },
562
+ {
563
+ files: ["packages/core-packages/core/src/**/*.{ts,tsx}"],
564
+ ignores: ["packages/core-packages/core/src/**/*.d.ts"],
565
+ rules: {
566
+ "no-restricted-syntax": noRestrictedSyntax
567
+ }
568
+ },
569
+ {
570
+ files: ["packages/core-packages/core/src/**/*.{ts,tsx}"],
571
+ ignores: ["packages/core-packages/core/src/arcade/**", "packages/core-packages/core/src/widgets/**"],
572
+ rules: {
573
+ "webgis/no-instanceof": "error"
574
+ }
575
+ },
576
+ {
577
+ files: ["packages/core-packages/core/tests/**/*.{ts,tsx}"],
578
+ rules: {
579
+ "webgis/no-instanceof": "off",
580
+ "@typescript-eslint/no-restricted-imports": [
581
+ "error",
582
+ {
583
+ patterns: [
584
+ {
585
+ group: ["*.spec", "*.spec.*", "**/*.spec", "**/*.spec.*"],
586
+ message: "Do not import test suites (.spec) from other tests. Move shared helpers to tests/support (use the test-support/* alias)."
587
+ }
588
+ ]
589
+ }
590
+ ],
591
+ "core/spec-suite-filename": "error",
592
+ "no-restricted-syntax": [
593
+ ...noRestrictedSyntax,
594
+ {
595
+ message: "The { only: true } test option may be used during local testing, but can not be committed.",
596
+ selector: "CallExpression ObjectExpression Property[key.name='only'][value.value='true']"
597
+ },
598
+ {
599
+ message: "test.only may be used during local testing, but can not be committed.",
600
+ selector: "CallExpression[callee.type='MemberExpression'][callee.object.name='test'] > MemberExpression.callee > Identifier.property[name='only']"
601
+ },
602
+ {
603
+ message: "The { repeatUntilFailure: true } test option may be used during local testing, but can not be committed.",
604
+ selector: "CallExpression ObjectExpression Property[key.name='repeatUntilFailure'][value.value='true']"
605
+ },
606
+ {
607
+ message: "test.repeatUntilFailure may be used during local testing, but can not be committed.",
608
+ selector: "CallExpression[callee.type='MemberExpression'][callee.object.name='test'] > MemberExpression.callee > Identifier.property[name='repeatUntilFailure']"
609
+ },
610
+ {
611
+ selector: "CallExpression MemberExpression[object.name='has'][property.name='add']",
612
+ message: "Avoid adding has flags manually. Instead use the suite/test-level options to set has flags."
613
+ }
614
+ ],
615
+ "webgis/require-await-for-async-screenshot-assert": "error"
616
+ }
617
+ }
618
+ ];
619
+ export {
620
+ eslintConfigCore as default
621
+ };
@@ -7,9 +7,12 @@ import { globalIgnores } from "eslint/config";
7
7
  import markdown from "@eslint/markdown";
8
8
  import packageJson from "eslint-plugin-package-json";
9
9
  import { importX } from "eslint-plugin-import-x";
10
+ import { createTypeScriptImportResolver } from "eslint-import-resolver-typescript";
10
11
  const packageJsonSortableCollections = packageJson.rules["sort-collections"].meta.defaultOptions[0].filter(
11
12
  (collection) => !["scripts", "exports"].includes(collection)
12
13
  ) ?? [];
14
+ const importXTypeScriptSettings = { ...importX.flatConfigs.typescript.settings };
15
+ delete importXTypeScriptSettings["import-x/resolver"];
13
16
  const config = [
14
17
  globalIgnores(["**/www", "**/dist", "**/assets", "**/coverage", "**/.docs"]),
15
18
  {
@@ -24,7 +27,13 @@ const config = [
24
27
  ...webgisPlugin.configs.recommended,
25
28
  files: ["**/*.ts", "**/*.tsx"]
26
29
  },
27
- importX.flatConfigs.typescript,
30
+ {
31
+ ...importX.flatConfigs.typescript,
32
+ settings: {
33
+ ...importXTypeScriptSettings,
34
+ "import-x/resolver-next": [createTypeScriptImportResolver()]
35
+ }
36
+ },
28
37
  {
29
38
  files: ["**/*.ts", "**/*.tsx"],
30
39
  linterOptions: {
@@ -145,8 +154,10 @@ const config = [
145
154
  * consider refactoring to less confusing syntax instead
146
155
  *
147
156
  */
148
- // Might be intentional, so only a warning
149
- "no-await-in-loop": "warn",
157
+ // Too many false positives. Some cases require sequential execution of
158
+ // async code for correctness, cleaner output, or to avoid performance
159
+ // constraints
160
+ "no-await-in-loop": "off",
150
161
  "no-constructor-return": "warn",
151
162
  "no-fallthrough": ["error", { reportUnusedFallthroughComment: true }],
152
163
  // This mis-fires for anonymous functions when not wrapped in {}
@@ -464,7 +475,10 @@ const config = [
464
475
  // rule - give it more time to catch the bug cases. Also, this rule
465
476
  // changes runtime behavior, and may lead to breaking changes.
466
477
  "@typescript-eslint/no-useless-default-assignment": "off",
467
- "@typescript-eslint/no-unnecessary-type-assertion": ["warn"],
478
+ // Since https://devtopia.esri.com/WebGIS/arcgis-web-components/pull/17821,
479
+ // this rule has more true positives, but also many more false positives.
480
+ // The autofix often causes TypeScript errors, and new ESLint errors
481
+ "@typescript-eslint/no-unnecessary-type-assertion": "off",
468
482
  "@typescript-eslint/prefer-includes": ["warn"],
469
483
  "@typescript-eslint/prefer-reduce-type-parameter": ["warn"],
470
484
  "@typescript-eslint/prefer-return-this-type": ["warn"],
@@ -1,5 +1,5 @@
1
1
  import { ESLintUtils } from "@typescript-eslint/utils";
2
- const version = "5.2.0-next.50";
2
+ const version = "5.2.0-next.52";
3
3
  const packageJson = {
4
4
  version
5
5
  };
@@ -1,7 +1,9 @@
1
- import { m as makeEslintPlugin } from "../../makePlugin-CI0e_JK0.js";
1
+ import { m as makeEslintPlugin } from "../../makePlugin-CErhRqFB.js";
2
2
  import { AST_NODE_TYPES, AST_TOKEN_TYPES } from "@typescript-eslint/utils";
3
3
  import { generateDifferences, showInvisibles } from "prettier-linter-helpers";
4
4
  import path, { relative, win32, posix, normalize } from "path";
5
+ import { dirname, join } from "node:path";
6
+ import { existsSync } from "node:fs";
5
7
  import { execSync } from "child_process";
6
8
  const plugin = makeEslintPlugin(
7
9
  "core",
@@ -96,7 +98,7 @@ function removeTrailingBlankLines(text) {
96
98
  function findProjectRoot(filename) {
97
99
  const normalized = filename.replaceAll("\\", "/");
98
100
  let minimumIndex = Number.POSITIVE_INFINITY;
99
- for (const marker of ["src/", "tests/", "test-apps/", "esri/"]) {
101
+ for (const marker of ["src/", "tests/", "test-apps/", "esri/", ".github/"]) {
100
102
  const index = normalized.indexOf(marker);
101
103
  if (index === 0) {
102
104
  return ".";
@@ -104,7 +106,17 @@ function findProjectRoot(filename) {
104
106
  minimumIndex = Math.min(minimumIndex, index);
105
107
  }
106
108
  }
107
- return minimumIndex === Number.POSITIVE_INFINITY ? void 0 : normalized.slice(0, minimumIndex);
109
+ if (minimumIndex !== Number.POSITIVE_INFINITY) {
110
+ return normalized.slice(0, minimumIndex);
111
+ }
112
+ const fileToFind = "package.json";
113
+ for (let root = dirname(filename), previousRoot = filename; root !== previousRoot; previousRoot = root, root = dirname(root)) {
114
+ const filepath = join(root, fileToFind);
115
+ if (existsSync(filepath)) {
116
+ return root;
117
+ }
118
+ }
119
+ return;
108
120
  }
109
121
  const slashCharCode = 47;
110
122
  const isWindows = process.platform === "win32";
@@ -577,8 +589,15 @@ plugin.createRule({
577
589
  }
578
590
  });
579
591
  function pathRoot(p) {
580
- const dirName = path.dirname(p);
581
- return dirName === "." ? p : pathRoot(dirName);
592
+ const pathImplementation = win32.isAbsolute(p) && !posix.isAbsolute(p) ? win32 : path;
593
+ let root = p;
594
+ while (true) {
595
+ const parent = pathImplementation.dirname(root);
596
+ if (parent === "." || parent === root) {
597
+ return root;
598
+ }
599
+ root = parent;
600
+ }
582
601
  }
583
602
  function verifyRelativeImport(context, node, absImportPath, basePath, importRoot) {
584
603
  const modulePath = node.value;
@@ -1,4 +1,4 @@
1
- import { m as makeEslintPlugin } from "../../makePlugin-CI0e_JK0.js";
1
+ import { m as makeEslintPlugin } from "../../makePlugin-CErhRqFB.js";
2
2
  import { AST_NODE_TYPES, ESLintUtils, AST_TOKEN_TYPES } from "@typescript-eslint/utils";
3
3
  import { l as luminaJsxExportName, a as luminaEntrypointName, b as luminaTestEntrypointName, s as sourceCodeDeclaresComponent, p as parsePropertyDecorator, c as getProperty, i as isGetterWithoutSetter, e as extractDeclareElementsInterface, d as isCreateEvent, h as hasDecorator, f as getName, j as checkForLuminaJsx, k as isBindThisCallee, g as getComponentDeclaration, u as unwrapExpression } from "../../estree-CDc-die8.js";
4
4
  import ts from "typescript";
@@ -1,4 +1,4 @@
1
- import { m as makeEslintPlugin } from "../../makePlugin-CI0e_JK0.js";
1
+ import { m as makeEslintPlugin } from "../../makePlugin-CErhRqFB.js";
2
2
  import { TSESTree, AST_TOKEN_TYPES, AST_NODE_TYPES, TSESLint } from "@typescript-eslint/utils";
3
3
  import path from "node:path";
4
4
  import { resolve } from "path/posix";
@@ -4,7 +4,8 @@ import type { TSESLint } from "@typescript-eslint/utils";
4
4
  * A utility for temporary running ESLint with only specific rules enabled.
5
5
  *
6
6
  * @param config - flat config
7
- * @param enabledRules - exceptions
7
+ * @param enabledRules - exceptions. Set a rule to `true` to preserve existing
8
+ * configuration
8
9
  * @param disableTypeChecking - make ESLint much faster by temporary disabling
9
10
  * type-aware linting.
10
11
  * @returns a new flat config array where every rule is disabled,
@@ -21,4 +22,4 @@ import type { TSESLint } from "@typescript-eslint/utils";
21
22
  * true,
22
23
  * );
23
24
  */
24
- export function disableAllRulesExcept(config: TSESLint.FlatConfig.ConfigArray, enabledRules: Record<string, TSESLint.FlatConfig.RuleLevel>, disableTypeChecking: boolean): TSESLint.FlatConfig.ConfigArray;
25
+ export function disableAllRulesExcept(config: TSESLint.FlatConfig.ConfigArray, enabledRules: Partial<Record<string, TSESLint.FlatConfig.RuleEntry | true>>, disableTypeChecking: boolean): TSESLint.FlatConfig.ConfigArray;
@@ -10,7 +10,7 @@ function applyRulesAndOptions(entry, enabledRules, disableTypeChecking) {
10
10
  rules: Object.fromEntries(
11
11
  Object.keys(entry.rules).map((ruleName) => {
12
12
  const override = enabledRules[ruleName];
13
- return [ruleName, override ?? "off"];
13
+ return [ruleName, override === true ? entry.rules[ruleName] : override ?? "off"];
14
14
  })
15
15
  )
16
16
  } : entry;
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@arcgis/eslint-config",
3
- "version": "5.2.0-next.50",
3
+ "version": "5.2.0-next.52",
4
4
  "description": "ESLint configuration for WebGIS SDK",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": "./dist/config/index.js",
8
8
  "./application": "./dist/config/applications.js",
9
+ "./core": "./dist/config/core.js",
9
10
  "./extra": "./dist/config/extra.js",
10
11
  "./lumina": "./dist/config/lumina.js",
11
12
  "./plugins/core": "./dist/plugins/core/index.js",
@@ -27,15 +28,20 @@
27
28
  "confusing-browser-globals": "^1.0.11",
28
29
  "eslint-import-resolver-typescript": "^4.4.5",
29
30
  "eslint-plugin-import-x": "^4.17.1",
31
+ "eslint-plugin-jsdoc": "~63.0.13",
30
32
  "eslint-plugin-package-json": "~0.91.1",
33
+ "eslint-plugin-perfectionist": "~5.10.0",
34
+ "eslint-plugin-react-jsx": "~5.11.2",
35
+ "eslint-plugin-regexp": "~3.1.1",
31
36
  "eslint-plugin-storybook": "^10.3.4",
37
+ "eslint-plugin-unicorn": "~64.0.0",
32
38
  "globals": "^16.5.0",
33
39
  "jsonc-eslint-parser": "^3.1.0",
34
40
  "prettier-linter-helpers": "^1.0.1",
35
41
  "tslib": "^2.8.1",
36
42
  "typescript": "~6.0.3",
37
43
  "typescript-eslint": "^8.63.0",
38
- "@arcgis/toolkit": "5.2.0-next.50"
44
+ "@arcgis/toolkit": "5.2.0-next.52"
39
45
  },
40
46
  "peerDependencies": {
41
47
  "eslint": "^10.6.0"