@arcgis/eslint-config 4.33.0-next.94 → 4.33.0-next.96

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.
@@ -42,11 +42,11 @@ const lumina = [
42
42
  ...luminaPlugin.configs.recommended,
43
43
  files: ["**/*.tsx"]
44
44
  },
45
- // Enable this rule in both .ts and .tsx files
46
45
  {
47
46
  files: ["**/*.ts", "**/*.tsx"],
48
47
  plugins: { lumina: luminaPlugin },
49
48
  rules: {
49
+ // Enable this rule in both .ts and .tsx files
50
50
  "lumina/component-placement-rules": "error"
51
51
  }
52
52
  },
@@ -56,6 +56,8 @@ const lumina = [
56
56
  // It's a good practice for @method() in components to be async even if
57
57
  // they don't have await
58
58
  "@typescript-eslint/require-await": "off",
59
+ // Too many offenders for now to report this as an error. Assigned issues to tackle this.
60
+ "lumina/no-create-element-component": "warn",
59
61
  /*
60
62
  * This rule emits false warnings when using Controllers due to
61
63
  * limitations of TypeScript when it comes to expressing the type of a
@@ -132,6 +134,14 @@ declare global {
132
134
  importNames: ["ContextProvider", "ContextConsumer", "provide", "consume"],
133
135
  message: `For lazy-loading compatibility, import the useContextProvider() and the useContextConsumer() controllers from @arcgis/lumina rather than directly calling the Lit's controllers/decorators.`,
134
136
  caseSensitive: true
137
+ },
138
+ {
139
+ group: ["/@arcgis/components-controllers/accessor"],
140
+ message: 'Import from "@arcgis/lumina/controllers/accessor" instead'
141
+ },
142
+ {
143
+ group: ["/@arcgis/components-controllers"],
144
+ message: 'Import from "@arcgis/lumina/controllers" instead'
135
145
  }
136
146
  ]
137
147
  }
@@ -1,9 +1,10 @@
1
1
  import { ESLintUtils } from "@typescript-eslint/utils";
2
- const version = "4.33.0-next.94";
2
+ const version = "4.33.0-next.96";
3
3
  function makeEslintPlugin(pluginName, urlCreator) {
4
4
  const rules = [];
5
5
  const creator = ESLintUtils.RuleCreator(urlCreator);
6
6
  return {
7
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
7
8
  createRule(rule) {
8
9
  const { meta, name, ...rest } = rule;
9
10
  const docs = { ...meta.docs, name };
@@ -11,6 +12,7 @@ function makeEslintPlugin(pluginName, urlCreator) {
11
12
  rules.push(ruleModule);
12
13
  return ruleModule;
13
14
  },
15
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
14
16
  finalize() {
15
17
  const config = {
16
18
  rules: Object.fromEntries(
@@ -1,4 +1,4 @@
1
- import { m as makeEslintPlugin } from "../../makePlugin-W-rZIyei.js";
1
+ import { m as makeEslintPlugin } from "../../makePlugin-khyXfCm4.js";
2
2
  import { AST_NODE_TYPES, ESLintUtils, AST_TOKEN_TYPES } from "@typescript-eslint/utils";
3
3
  import ts from "typescript";
4
4
  import { camelToKebab } from "@arcgis/components-utils";
@@ -95,16 +95,16 @@ function isBindThisCallee(callee) {
95
95
  callee.object.object.type === AST_NODE_TYPES.ThisExpression;
96
96
  }
97
97
  const importDeclaration = `import { ${luminaJsxExportName} } from "${luminaEntrypointName}";`;
98
- const description$i = `To use Lumina's JSX, you need to ${importDeclaration}`;
98
+ const description$j = `To use Lumina's JSX, you need to ${importDeclaration}`;
99
99
  plugin.createRule({
100
100
  name: "add-missing-jsx-import",
101
101
  meta: {
102
102
  docs: {
103
- description: description$i,
103
+ description: description$j,
104
104
  defaultLevel: "error"
105
105
  },
106
106
  messages: {
107
- addMissingJsxImport: description$i
107
+ addMissingJsxImport: description$j
108
108
  },
109
109
  type: "problem",
110
110
  schema: [],
@@ -161,12 +161,12 @@ plugin.createRule({
161
161
  };
162
162
  }
163
163
  });
164
- const description$h = "Auto add { type: Boolean } or { type: Number } where necessary";
164
+ const description$i = "Auto add { type: Boolean } or { type: Number } where necessary";
165
165
  plugin.createRule({
166
166
  name: "auto-add-type",
167
167
  meta: {
168
168
  docs: {
169
- description: description$h,
169
+ description: description$i,
170
170
  defaultLevel: "warn"
171
171
  },
172
172
  messages: {
@@ -333,7 +333,7 @@ plugin.createRule({
333
333
  meta: {
334
334
  docs: {
335
335
  description: "This rule helps ban or warn against listened event types",
336
- defaultLevel: "error"
336
+ defaultLevel: "off"
337
337
  },
338
338
  messages: {
339
339
  default: "{{message}}"
@@ -402,12 +402,12 @@ plugin.createRule({
402
402
  };
403
403
  }
404
404
  });
405
- const description$g = `Lumina component must be declared in a TSX file with a matching folder name located inside of src/components folder.`;
405
+ const description$h = `Lumina component must be declared in a TSX file with a matching folder name located inside of src/components folder.`;
406
406
  plugin.createRule({
407
407
  name: "component-placement-rules",
408
408
  meta: {
409
409
  docs: {
410
- description: description$g,
410
+ description: description$h,
411
411
  defaultLevel: "error"
412
412
  },
413
413
  messages: {
@@ -446,7 +446,7 @@ plugin.createRule({
446
446
  };
447
447
  }
448
448
  });
449
- const description$f = `Enforce consistent event naming.`;
449
+ const description$g = `Enforce consistent event naming.`;
450
450
  const defaultOptions$1 = [
451
451
  {
452
452
  eventNamespaces: ["arcgis"],
@@ -457,7 +457,7 @@ plugin.createRule({
457
457
  name: "consistent-event-naming",
458
458
  meta: {
459
459
  docs: {
460
- description: description$f,
460
+ description: description$g,
461
461
  defaultLevel: "warn"
462
462
  },
463
463
  messages: {
@@ -558,12 +558,12 @@ Discussion: https://devtopia.esri.com/WebGIS/arcgis-web-components/discussions/3
558
558
  }
559
559
  });
560
560
  const capitalAfterLower = /(?<=[a-z\d])[A-Z]/u;
561
- const description$e = `Enforce that @property(), @method() and createEvent() members are used in the correct context.`;
561
+ const description$f = `Enforce that @property(), @method() and createEvent() members are used in the correct context.`;
562
562
  plugin.createRule({
563
563
  name: "decorators-context",
564
564
  meta: {
565
565
  docs: {
566
- description: description$e,
566
+ description: description$f,
567
567
  defaultLevel: "error"
568
568
  },
569
569
  messages: {
@@ -657,7 +657,7 @@ function isLuminaJsxType(type) {
657
657
  }
658
658
  const hasTypeFlag = (type, flag) => type.flags & flag ? true : (type.flags & ts.TypeFlags.Union) !== 0 && type.types.some((t) => hasTypeFlag(t, flag));
659
659
  const literalTypeFlag = ts.TypeFlags.StringLike | ts.TypeFlags.NumberLike | ts.TypeFlags.BooleanLike;
660
- const description$d = `Need to add explicit type annotation: {{ setterType }}
660
+ const description$e = `Need to add explicit type annotation: {{ setterType }}
661
661
 
662
662
  Explanation:
663
663
  Lumina automatically creates an attribute for a property if property type includes a literal type (string|number|boolean).
@@ -668,11 +668,11 @@ plugin.createRule({
668
668
  name: "explicit-setter-type",
669
669
  meta: {
670
670
  docs: {
671
- description: description$d,
671
+ description: description$e,
672
672
  defaultLevel: "error"
673
673
  },
674
674
  messages: {
675
- explicitSetterType: description$d,
675
+ explicitSetterType: description$e,
676
676
  addExplicitSetterType: `Add {{ setterType }} type annotation`
677
677
  },
678
678
  type: "problem",
@@ -1169,6 +1169,9 @@ const blockListedCustomElementNames = /* @__PURE__ */ new Set([
1169
1169
  "font-face-name",
1170
1170
  "missing-glyph"
1171
1171
  ]);
1172
+ function isCustomElementTag(tag) {
1173
+ return tag.includes("-") && !blockListedCustomElementNames.has(tag);
1174
+ }
1172
1175
  plugin.createRule({
1173
1176
  name: "no-create-element-component",
1174
1177
  defaultOptions: [],
@@ -1215,19 +1218,16 @@ function isCreateElementComponent(node) {
1215
1218
  }
1216
1219
  return false;
1217
1220
  }
1218
- function isCustomElementTag(tag) {
1219
- return tag.includes("-") && !blockListedCustomElementNames.has(tag);
1220
- }
1221
- const description$c = `Use @internal or @private JSDoc tag over @ignore. See https://qawebgis.esri.com/components/lumina/documenting-components#excluding-api-from-public-documentation`;
1221
+ const description$d = `Use @internal or @private JSDoc tag over @ignore. See https://qawebgis.esri.com/components/lumina/documenting-components#excluding-api-from-public-documentation`;
1222
1222
  plugin.createRule({
1223
1223
  name: "no-ignore-jsdoc-tag",
1224
1224
  meta: {
1225
1225
  docs: {
1226
- description: description$c,
1226
+ description: description$d,
1227
1227
  defaultLevel: "error"
1228
1228
  },
1229
1229
  messages: {
1230
- noIgnoreJsDocTag: description$c
1230
+ noIgnoreJsDocTag: description$d
1231
1231
  },
1232
1232
  type: "problem",
1233
1233
  schema: []
@@ -1256,12 +1256,12 @@ plugin.createRule({
1256
1256
  }
1257
1257
  });
1258
1258
  const reIgnore = /\* @ignore/gu;
1259
- const description$b = `Detect incorrect usage of dynamic JSX tag name`;
1259
+ const description$c = `Detect incorrect usage of dynamic JSX tag name`;
1260
1260
  plugin.createRule({
1261
1261
  name: "no-incorrect-dynamic-tag-name",
1262
1262
  meta: {
1263
1263
  docs: {
1264
- description: description$b,
1264
+ description: description$c,
1265
1265
  defaultLevel: "error"
1266
1266
  },
1267
1267
  messages: {
@@ -1305,7 +1305,7 @@ plugin.createRule({
1305
1305
  }
1306
1306
  });
1307
1307
  const baseDescription$2 = `Do not pass an inline arrow function to a ref prop - such syntax creates a new function on each render, which makes Lit call ref callback again on each render.`;
1308
- const description$a = `${baseDescription$2}
1308
+ const description$b = `${baseDescription$2}
1309
1309
 
1310
1310
  Alternatives: https://qawebgis.esri.com/components/lumina/jsx#refs`;
1311
1311
  plugin.createRule({
@@ -1316,7 +1316,7 @@ plugin.createRule({
1316
1316
  defaultLevel: "error"
1317
1317
  },
1318
1318
  messages: {
1319
- errorInlineArrow: description$a
1319
+ errorInlineArrow: description$b
1320
1320
  },
1321
1321
  type: "problem",
1322
1322
  schema: []
@@ -1342,16 +1342,16 @@ plugin.createRule({
1342
1342
  };
1343
1343
  }
1344
1344
  });
1345
- const description$9 = `directives={} prop value must be an array literal. Documentation: https://qawebgis.esri.com/components/lumina/jsx#lit-directives`;
1345
+ const description$a = `directives={} prop value must be an array literal. Documentation: https://qawebgis.esri.com/components/lumina/jsx#lit-directives`;
1346
1346
  plugin.createRule({
1347
1347
  name: "no-invalid-directives-prop",
1348
1348
  meta: {
1349
1349
  docs: {
1350
- description: description$9,
1350
+ description: description$a,
1351
1351
  defaultLevel: "error"
1352
1352
  },
1353
1353
  messages: {
1354
- noInvalidDirectivesProp: description$9
1354
+ noInvalidDirectivesProp: description$a
1355
1355
  },
1356
1356
  type: "problem",
1357
1357
  schema: []
@@ -1388,16 +1388,16 @@ plugin.createRule({
1388
1388
  };
1389
1389
  }
1390
1390
  });
1391
- const description$8 = `This spread syntax is not supported. Alternatives: https://qawebgis.esri.com/components/lumina/jsx#spread-attributes`;
1391
+ const description$9 = `This spread syntax is not supported. Alternatives: https://qawebgis.esri.com/components/lumina/jsx#spread-attributes`;
1392
1392
  plugin.createRule({
1393
1393
  name: "no-jsx-spread",
1394
1394
  meta: {
1395
1395
  docs: {
1396
- description: description$8,
1396
+ description: description$9,
1397
1397
  defaultLevel: "error"
1398
1398
  },
1399
1399
  messages: {
1400
- noJsxSpread: description$8
1400
+ noJsxSpread: description$9
1401
1401
  },
1402
1402
  type: "problem",
1403
1403
  schema: []
@@ -1427,7 +1427,7 @@ plugin.createRule({
1427
1427
  }
1428
1428
  });
1429
1429
  const baseDescription$1 = `Do not call this.listen()/this.listenOn() in connectedCallback.`;
1430
- const description$7 = `${baseDescription$1}
1430
+ const description$8 = `${baseDescription$1}
1431
1431
 
1432
1432
  Instead, call this.listen()/this.listenOn() in constructor(), load() or loaded().
1433
1433
 
@@ -1442,7 +1442,7 @@ plugin.createRule({
1442
1442
  defaultLevel: "error"
1443
1443
  },
1444
1444
  messages: {
1445
- errorListenInConnectedCallback: description$7
1445
+ errorListenInConnectedCallback: description$8
1446
1446
  },
1447
1447
  type: "problem",
1448
1448
  schema: []
@@ -1477,18 +1477,18 @@ plugin.createRule({
1477
1477
  };
1478
1478
  }
1479
1479
  });
1480
- const description$6 = `To ensure Hot Module Replacement (HMR) works correctly, the file that defines the Lumina component must not export anything other than the component. Exceptions: type-only exports, and the \`exportsForTests\` object for exposing additional things for usages in tests only`;
1480
+ const description$7 = `To ensure Hot Module Replacement (HMR) works correctly, the file that defines the Lumina component must not export anything other than the component. Exceptions: type-only exports, and the \`exportsForTests\` object for exposing additional things for usages in tests only`;
1481
1481
  plugin.createRule({
1482
1482
  name: "no-non-component-exports",
1483
1483
  meta: {
1484
1484
  docs: {
1485
- description: description$6,
1485
+ description: description$7,
1486
1486
  defaultLevel: "warn"
1487
1487
  },
1488
1488
  messages: {
1489
- noNonComponentExports: description$6,
1490
- noDefaultExports: `Default exports are not allowed in files that export Lumina component - only named exports are allowed. ${description$6}`,
1491
- noExportAll: `\`export *\` exports are not allowed in files that export Lumina component - only named exports are allowed. ${description$6}`
1489
+ noNonComponentExports: description$7,
1490
+ noDefaultExports: `Default exports are not allowed in files that export Lumina component - only named exports are allowed. ${description$7}`,
1491
+ noExportAll: `\`export *\` exports are not allowed in files that export Lumina component - only named exports are allowed. ${description$7}`
1492
1492
  },
1493
1493
  type: "problem",
1494
1494
  schema: []
@@ -1574,16 +1574,16 @@ plugin.createRule({
1574
1574
  };
1575
1575
  }
1576
1576
  });
1577
- const description$5 = `Do not start public property names with "on" as that can confuse frameworks into thinking this property is an event`;
1577
+ const description$6 = `Do not start public property names with "on" as that can confuse frameworks into thinking this property is an event`;
1578
1578
  plugin.createRule({
1579
1579
  name: "no-property-name-start-with-on",
1580
1580
  meta: {
1581
1581
  docs: {
1582
- description: description$5,
1582
+ description: description$6,
1583
1583
  defaultLevel: "error"
1584
1584
  },
1585
1585
  messages: {
1586
- noPropertyNameStartWithOn: description$5
1586
+ noPropertyNameStartWithOn: description$6
1587
1587
  },
1588
1588
  type: "problem",
1589
1589
  schema: []
@@ -1617,7 +1617,7 @@ plugin.createRule({
1617
1617
  }
1618
1618
  });
1619
1619
  const baseDescription = `Avoid accidentally rendering "false" to the screen (Lit stringifies booleans, rather than drop them like React/Stencil).`;
1620
- const description$4 = `${baseDescription}
1620
+ const description$5 = `${baseDescription}
1621
1621
 
1622
1622
  Lumina automatically handles some cases where "false" may get rendered to the screen, but the pattern this code is using is not handled.`;
1623
1623
  plugin.createRule({
@@ -1628,7 +1628,7 @@ plugin.createRule({
1628
1628
  defaultLevel: "warn"
1629
1629
  },
1630
1630
  messages: {
1631
- errorFalseRendered: description$4
1631
+ errorFalseRendered: description$5
1632
1632
  },
1633
1633
  type: "problem",
1634
1634
  schema: [],
@@ -1670,6 +1670,96 @@ plugin.createRule({
1670
1670
  };
1671
1671
  }
1672
1672
  });
1673
+ const description$4 = `Avoid using type assertions like 'event.target as ...', 'event.currentTarget as ...', or 'event.detail as ...'. Instead, improve the event argument type to avoid the need for assertions.`;
1674
+ plugin.createRule({
1675
+ name: "no-unnecessary-assertion-on-event",
1676
+ meta: {
1677
+ docs: {
1678
+ description: description$4,
1679
+ defaultLevel: "warn"
1680
+ },
1681
+ messages: {
1682
+ needlessInlineJsxEventTypeAnnotation: "There is no need for this type annotation as JSX types already define event types.",
1683
+ redundantTypeAssertion: "This type assertion is likely unnecessary.",
1684
+ avoidTypeAssertion: "This type assertion might be avoidable if you improve the event argument type. See examples in https://qawebgis.esri.com/components/lumina/events#listening-to-events-on-children-components"
1685
+ },
1686
+ hasSuggestions: true,
1687
+ fixable: "code",
1688
+ type: "suggestion",
1689
+ schema: []
1690
+ },
1691
+ defaultOptions: [],
1692
+ create(context) {
1693
+ const luminaJsxCheck = checkForLuminaJsx();
1694
+ return {
1695
+ ImportDeclaration: luminaJsxCheck,
1696
+ TSAsExpression(node) {
1697
+ if (!luminaJsxCheck.isLuminaJsx || node.expression.type !== AST_NODE_TYPES.MemberExpression || node.expression.object.type !== AST_NODE_TYPES.Identifier || node.expression.property.type !== AST_NODE_TYPES.Identifier || !["target", "currentTarget", "detail"].includes(node.expression.property.name)) {
1698
+ return;
1699
+ }
1700
+ const eventVariableName = node.expression.object.name;
1701
+ let parent = node.parent;
1702
+ let functionParent = void 0;
1703
+ while (parent != null) {
1704
+ if (parent.type === AST_NODE_TYPES.FunctionExpression || parent.type === AST_NODE_TYPES.FunctionDeclaration || parent.type === AST_NODE_TYPES.ArrowFunctionExpression) {
1705
+ functionParent = parent;
1706
+ break;
1707
+ }
1708
+ parent = parent.parent;
1709
+ }
1710
+ if (functionParent === void 0) {
1711
+ return;
1712
+ }
1713
+ const argument = functionParent.params.at(0);
1714
+ if (argument === void 0 || argument.type !== AST_NODE_TYPES.Identifier || argument.name !== eventVariableName) {
1715
+ return;
1716
+ }
1717
+ const reportPossiblyUnnecessaryTypeAssertion = () => context.report({
1718
+ messageId: "redundantTypeAssertion",
1719
+ node,
1720
+ // If it is definitely unnecessary, it will be removed by
1721
+ // the `@typescript-eslint/no-unnecessary-type-assertion` rule.
1722
+ // So we only report as a suggestion for manual review.
1723
+ suggest: [
1724
+ {
1725
+ messageId: "redundantTypeAssertion",
1726
+ fix(fixer) {
1727
+ return fixer.replaceTextRange(
1728
+ [node.range[0], node.range[1]],
1729
+ context.sourceCode.getText(node.expression)
1730
+ );
1731
+ }
1732
+ }
1733
+ ]
1734
+ });
1735
+ const isInlineListener = functionParent.parent.type === AST_NODE_TYPES.JSXExpressionContainer;
1736
+ if (isInlineListener) {
1737
+ if (argument.typeAnnotation === void 0) {
1738
+ reportPossiblyUnnecessaryTypeAssertion();
1739
+ return;
1740
+ } else {
1741
+ const typeAnnotation = argument.typeAnnotation;
1742
+ context.report({
1743
+ messageId: "needlessInlineJsxEventTypeAnnotation",
1744
+ node: typeAnnotation,
1745
+ fix(fixer) {
1746
+ return fixer.remove(typeAnnotation);
1747
+ }
1748
+ });
1749
+ return;
1750
+ }
1751
+ }
1752
+ if (argument.typeAnnotation?.typeAnnotation.type === AST_NODE_TYPES.TSUnionType) {
1753
+ return;
1754
+ }
1755
+ context.report({
1756
+ messageId: "avoidTypeAssertion",
1757
+ node
1758
+ });
1759
+ }
1760
+ };
1761
+ }
1762
+ });
1673
1763
  const description$3 = 'There is no need for { attribute: "name" } in @property() when attribute name is trivially inferrable from the property name';
1674
1764
  plugin.createRule({
1675
1765
  name: "no-unnecessary-attribute-name",
@@ -0,0 +1,3 @@
1
+ import { TSESLint } from '@typescript-eslint/utils';
2
+ declare const _default: TSESLint.RuleModule<"needlessInlineJsxEventTypeAnnotation" | "redundantTypeAssertion" | "avoidTypeAssertion", [], import('../../utils/makePlugin').CommonDocs, TSESLint.RuleListener>;
3
+ export default _default;
@@ -6,3 +6,9 @@
6
6
  * @see https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define#valid_custom_element_names
7
7
  */
8
8
  export declare const blockListedCustomElementNames: Set<string>;
9
+ /**
10
+ * Simple check based on:
11
+ * - https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
12
+ * - https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define#valid_custom_element_names
13
+ */
14
+ export declare function isCustomElementTag(tag: string): boolean;
@@ -1,4 +1,4 @@
1
- import { m as makeEslintPlugin } from "../../makePlugin-W-rZIyei.js";
1
+ import { m as makeEslintPlugin } from "../../makePlugin-khyXfCm4.js";
2
2
  import { resolve } from "node:path/posix";
3
3
  import { AST_NODE_TYPES } from "@typescript-eslint/utils";
4
4
  const plugin = makeEslintPlugin(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcgis/eslint-config",
3
- "version": "4.33.0-next.94",
3
+ "version": "4.33.0-next.96",
4
4
  "description": "ESLint configuration for arcgis-web-components",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -19,7 +19,7 @@
19
19
  ],
20
20
  "license": "SEE LICENSE IN LICENSE.md",
21
21
  "dependencies": {
22
- "@arcgis/components-utils": "4.33.0-next.94",
22
+ "@arcgis/components-utils": "4.33.0-next.96",
23
23
  "@eslint/js": "^9.17.0",
24
24
  "@types/confusing-browser-globals": "^1.0.3",
25
25
  "confusing-browser-globals": "^1.0.11",