@arcgis/eslint-config 5.1.0-next.8 → 5.1.0-next.80

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.
@@ -213,6 +213,18 @@ const defaultConfig = [
213
213
  "@typescript-eslint/restrict-plus-operands": ["warn", { allowNumberAndString: true }],
214
214
  // We often actually have control characters in our regexes
215
215
  "no-control-regex": "off",
216
+ "@typescript-eslint/no-restricted-imports": [
217
+ "error",
218
+ {
219
+ paths: [
220
+ {
221
+ name: "commander",
222
+ importNames: ["Command"],
223
+ message: "Import from @commander-js/extra-typings instead."
224
+ }
225
+ ]
226
+ }
227
+ ],
216
228
  // Functions that deal with JSON.stringify/localStorage may have a type
217
229
  // parameter as a more readable alternative to a type assertion.
218
230
  // Also, this rule is quite complicated/expensive.
@@ -446,7 +458,13 @@ const defaultConfig = [
446
458
  "@typescript-eslint/no-duplicate-type-constituents": ["warn"],
447
459
  "@typescript-eslint/no-unnecessary-boolean-literal-compare": ["warn"],
448
460
  "@typescript-eslint/no-extra-non-null-assertion": ["warn"],
449
- "@typescript-eslint/no-unnecessary-type-arguments": ["warn"],
461
+ // False positive for the following:
462
+ // this.listen<CustomEvent>("arcgisChartsJSDataProcessComplete", this.handleDataProcessComplete);
463
+ "@typescript-eslint/no-unnecessary-type-arguments": "off",
464
+ // The autofix creates TS errors in makeT9nController(). This is a new
465
+ // rule - give it more time to catch the bug cases. Also, this rule
466
+ // changes runtime behavior, and may lead to breaking changes.
467
+ "@typescript-eslint/no-useless-default-assignment": "off",
450
468
  "@typescript-eslint/no-unnecessary-type-assertion": ["warn"],
451
469
  "@typescript-eslint/prefer-includes": ["warn"],
452
470
  "@typescript-eslint/prefer-reduce-type-parameter": ["warn"],
@@ -587,6 +605,18 @@ const defaultConfig = [
587
605
  // Allow empty "files" field to explicitly indicate that no files
588
606
  // should be included in the package
589
607
  "package-json/no-empty-fields": ["error", { ignoreProperties: ["files"] }],
608
+ // We use license instead
609
+ "package-json/require-attribution": "off",
610
+ // We are not open source
611
+ "package-json/require-repository": "off",
612
+ // This option is tricky to set right, especially for web component
613
+ // libraries that have many side effect entrypoints. It can also get out
614
+ // of date easily. If we don't explicitly indicate sideEffects, bundlers
615
+ // have good default AST-based heuristics.
616
+ "package-json/require-sideEffects": "off",
617
+ // Can't address these till 6.0 without breaking changes.
618
+ // Most errors are in react wrappers which will be dropped in 6.0.
619
+ "package-json/require-exports": "off",
590
620
  // Enforce a specific property order for better readability and consistency
591
621
  "package-json/order-properties": [
592
622
  "warn",
@@ -29,6 +29,8 @@ const storybookConfig = [
29
29
  "storybook/no-title-property-in-meta": "off",
30
30
  // Not applicable as we have a central Storybook config (@arcgis/storybook-utils)
31
31
  "storybook/no-uninstalled-addons": "off",
32
+ // Not that helpful as we have a central Storybook config (@arcgis/storybook-utils)
33
+ "storybook/no-renderer-packages": "off",
32
34
  // We never used storiesOf, and it's no longer included in Storybook
33
35
  "storybook/no-stories-of": "off",
34
36
  // Redundant with TypeScript
@@ -0,0 +1,161 @@
1
+ import { ESLintUtils, AST_NODE_TYPES } from "@typescript-eslint/utils";
2
+ const version = "5.1.0-next.80";
3
+ const packageJson = {
4
+ version
5
+ };
6
+ function makeEslintPlugin(pluginName, urlCreator) {
7
+ const rules = [];
8
+ const creator = ESLintUtils.RuleCreator(urlCreator);
9
+ return {
10
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
11
+ createRule(rule) {
12
+ const { meta, name, ...rest } = rule;
13
+ const docs = { ...meta.docs, name };
14
+ const ruleModule = creator({ ...rest, meta: { ...meta, docs }, name });
15
+ rules.push(ruleModule);
16
+ return ruleModule;
17
+ },
18
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
19
+ finalize() {
20
+ const config = {
21
+ rules: Object.fromEntries(
22
+ rules.map((rule) => [`${pluginName}/${rule.meta.docs.name}`, rule.meta.docs.defaultLevel])
23
+ )
24
+ };
25
+ const plugin = {
26
+ meta: { name: `@arcgis/eslint-plugin-${pluginName}`, version: packageJson.version },
27
+ configs: {
28
+ recommended: config
29
+ },
30
+ rules: Object.fromEntries(rules.map((rule) => [rule.meta.docs.name, rule]))
31
+ };
32
+ config.plugins = {
33
+ [pluginName]: plugin
34
+ };
35
+ return plugin;
36
+ }
37
+ };
38
+ }
39
+ const unwrapExpression = (expression) => expression.type === AST_NODE_TYPES.TSAsExpression || expression.type === AST_NODE_TYPES.TSNonNullExpression || expression.type === AST_NODE_TYPES.TSSatisfiesExpression ? unwrapExpression(expression.expression) : expression;
40
+ const luminaEntrypointName = "@arcgis/lumina";
41
+ const luminaTestEntrypointName = "@arcgis/lumina-compiler/testing";
42
+ const luminaJsxExportName = "h";
43
+ function checkForLuminaJsx() {
44
+ const ImportDeclaration = (node) => {
45
+ if (node.source.value !== luminaEntrypointName) {
46
+ return;
47
+ }
48
+ for (const specifier of node.specifiers) {
49
+ if (specifier.type === AST_NODE_TYPES.ImportSpecifier && (specifier.imported.type === AST_NODE_TYPES.Identifier && specifier.imported.name === luminaJsxExportName || specifier.imported.type === AST_NODE_TYPES.Literal && specifier.imported.value === luminaJsxExportName)) {
50
+ withProperty.isLuminaJsx = true;
51
+ return;
52
+ }
53
+ }
54
+ };
55
+ const withProperty = ImportDeclaration;
56
+ return withProperty;
57
+ }
58
+ function hasDecorator(node, decoratorName) {
59
+ return node.decorators.some(
60
+ (decorator) => decorator.expression.type === AST_NODE_TYPES.CallExpression && decorator.expression.callee.type === AST_NODE_TYPES.Identifier && decorator.expression.callee.name === decoratorName
61
+ ) ?? false;
62
+ }
63
+ function extractDeclareElementsInterface(node) {
64
+ return node.kind === "global" ? node.body.body.find(
65
+ (node2) => node2.type === AST_NODE_TYPES.TSInterfaceDeclaration && node2.id.name === "DeclareElements"
66
+ ) : void 0;
67
+ }
68
+ function getComponentDeclaration(node) {
69
+ for (const member of node.body.body) {
70
+ if (member.type !== AST_NODE_TYPES.TSPropertySignature) {
71
+ continue;
72
+ }
73
+ const type = member.typeAnnotation?.typeAnnotation;
74
+ if (type?.type !== AST_NODE_TYPES.TSTypeReference || type.typeName.type !== AST_NODE_TYPES.Identifier) {
75
+ continue;
76
+ }
77
+ if (member.key.type !== AST_NODE_TYPES.Literal) {
78
+ continue;
79
+ }
80
+ return member;
81
+ }
82
+ return;
83
+ }
84
+ const sourceCodeDeclaresComponent = (sourceCode) => sourceCode.text.includes("interface DeclareElements");
85
+ function isCreateEvent(node) {
86
+ return node.value?.type === AST_NODE_TYPES.CallExpression && node.value.callee.type === AST_NODE_TYPES.Identifier && node.value.callee.name === "createEvent" && !node.static;
87
+ }
88
+ const getProperty = (properties, name) => properties?.find(
89
+ (option) => option.type === AST_NODE_TYPES.Property && option.key.type === AST_NODE_TYPES.Identifier && option.key.name === name
90
+ )?.value;
91
+ function isGetterWithoutSetter(node) {
92
+ const isGetter = node.type === AST_NODE_TYPES.MethodDefinition && node.kind === "get";
93
+ if (!isGetter) {
94
+ return false;
95
+ }
96
+ const index = node.parent.body.indexOf(node);
97
+ const previousNode = node.parent.body.at(index - 1);
98
+ const nextNode = node.parent.body.at(index + 1);
99
+ const name = getName(node);
100
+ if (name === void 0) {
101
+ return false;
102
+ }
103
+ const previousIsSetter = previousNode?.type === AST_NODE_TYPES.MethodDefinition && previousNode.kind === "set" && getName(previousNode) === name;
104
+ if (previousIsSetter) {
105
+ return false;
106
+ }
107
+ const nextIsSetter = nextNode?.type === AST_NODE_TYPES.MethodDefinition && nextNode.kind === "set" && getName(nextNode) === name;
108
+ return !nextIsSetter;
109
+ }
110
+ function getName(node) {
111
+ if (node.key.type === AST_NODE_TYPES.Identifier) {
112
+ return node.key.name;
113
+ } else if (node.key.type === AST_NODE_TYPES.Literal && typeof node.key.value === "string") {
114
+ return node.key.value;
115
+ } else {
116
+ return void 0;
117
+ }
118
+ }
119
+ function parsePropertyDecorator(decorator) {
120
+ const isPropertyDecorator = decorator.expression.type === AST_NODE_TYPES.CallExpression && decorator.expression.callee.type === AST_NODE_TYPES.Identifier && decorator.expression.callee.name === "property";
121
+ if (!isPropertyDecorator) {
122
+ return;
123
+ }
124
+ const callExpression = decorator?.expression.type === AST_NODE_TYPES.CallExpression ? decorator.expression : void 0;
125
+ if (callExpression === void 0) {
126
+ return;
127
+ }
128
+ const options = callExpression.arguments[0]?.type === AST_NODE_TYPES.ObjectExpression ? callExpression.arguments[0] : void 0;
129
+ const properties = options?.properties;
130
+ return {
131
+ callExpression,
132
+ options,
133
+ properties
134
+ };
135
+ }
136
+ function isBindThisCallee(callee) {
137
+ return callee.type === AST_NODE_TYPES.MemberExpression && // expression.identifier(this)
138
+ callee.property.type === AST_NODE_TYPES.Identifier && // expression.bind(this)
139
+ callee.property.name === "bind" && // expression.expression.bind(this)
140
+ callee.object.type === AST_NODE_TYPES.MemberExpression && // expression.identifier.bind(this)
141
+ callee.object.property.type === AST_NODE_TYPES.Identifier && // this.identifier.bind(this)
142
+ callee.object.object.type === AST_NODE_TYPES.ThisExpression;
143
+ }
144
+ export {
145
+ luminaEntrypointName as a,
146
+ luminaTestEntrypointName as b,
147
+ getProperty as c,
148
+ isCreateEvent as d,
149
+ extractDeclareElementsInterface as e,
150
+ getName as f,
151
+ getComponentDeclaration as g,
152
+ hasDecorator as h,
153
+ isGetterWithoutSetter as i,
154
+ checkForLuminaJsx as j,
155
+ isBindThisCallee as k,
156
+ luminaJsxExportName as l,
157
+ makeEslintPlugin as m,
158
+ parsePropertyDecorator as p,
159
+ sourceCodeDeclaresComponent as s,
160
+ unwrapExpression as u
161
+ };
@@ -1,4 +1,4 @@
1
- import { m as makeEslintPlugin } from "../../makePlugin-N9H9kNYE.js";
1
+ import { m as makeEslintPlugin, 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-B6kiRVur.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/toolkit/string";
@@ -6,94 +6,6 @@ const plugin = makeEslintPlugin(
6
6
  "lumina",
7
7
  (rule) => `https://devtopia.esri.com/WebGIS/arcgis-web-components/tree/main/packages/support-packages/eslint-config/src/plugins/lumina/rules/${rule}.ts`
8
8
  );
9
- const unwrapExpression = (expression) => expression.type === AST_NODE_TYPES.TSAsExpression || expression.type === AST_NODE_TYPES.TSNonNullExpression || expression.type === AST_NODE_TYPES.TSSatisfiesExpression ? unwrapExpression(expression.expression) : expression;
10
- const luminaEntrypointName = "@arcgis/lumina";
11
- const luminaTestEntrypointName = "@arcgis/lumina-compiler/testing";
12
- const luminaJsxExportName = "h";
13
- function checkForLuminaJsx() {
14
- const ImportDeclaration = (node) => {
15
- if (node.source.value !== luminaEntrypointName) {
16
- return;
17
- }
18
- for (const specifier of node.specifiers) {
19
- if (specifier.type === AST_NODE_TYPES.ImportSpecifier && (specifier.imported.type === AST_NODE_TYPES.Identifier && specifier.imported.name === luminaJsxExportName || specifier.imported.type === AST_NODE_TYPES.Literal && specifier.imported.value === luminaJsxExportName)) {
20
- withProperty.isLuminaJsx = true;
21
- return;
22
- }
23
- }
24
- };
25
- const withProperty = ImportDeclaration;
26
- return withProperty;
27
- }
28
- function hasDecorator(node, decoratorName) {
29
- return node.decorators.some(
30
- (decorator) => decorator.expression.type === AST_NODE_TYPES.CallExpression && decorator.expression.callee.type === AST_NODE_TYPES.Identifier && decorator.expression.callee.name === decoratorName
31
- ) ?? false;
32
- }
33
- function extractDeclareElementsInterface(node) {
34
- return node.kind === "global" ? node.body.body.find(
35
- (node2) => node2.type === AST_NODE_TYPES.TSInterfaceDeclaration && node2.id.name === "DeclareElements"
36
- ) : void 0;
37
- }
38
- function isCreateEvent(node) {
39
- return node.value?.type === AST_NODE_TYPES.CallExpression && node.value.callee.type === AST_NODE_TYPES.Identifier && node.value.callee.name === "createEvent" && !node.static;
40
- }
41
- const getProperty = (properties, name) => properties?.find(
42
- (option) => option.type === AST_NODE_TYPES.Property && option.key.type === AST_NODE_TYPES.Identifier && option.key.name === name
43
- )?.value;
44
- function isGetterWithoutSetter(node) {
45
- const isGetter = node.type === AST_NODE_TYPES.MethodDefinition && node.kind === "get";
46
- if (!isGetter) {
47
- return false;
48
- }
49
- const index = node.parent.body.indexOf(node);
50
- const previousNode = node.parent.body.at(index - 1);
51
- const nextNode = node.parent.body.at(index + 1);
52
- const name = getName(node);
53
- if (name === void 0) {
54
- return false;
55
- }
56
- const previousIsSetter = previousNode?.type === AST_NODE_TYPES.MethodDefinition && previousNode.kind === "set" && getName(previousNode) === name;
57
- if (previousIsSetter) {
58
- return false;
59
- }
60
- const nextIsSetter = nextNode?.type === AST_NODE_TYPES.MethodDefinition && nextNode.kind === "set" && getName(nextNode) === name;
61
- return !nextIsSetter;
62
- }
63
- function getName(node) {
64
- if (node.key.type === AST_NODE_TYPES.Identifier) {
65
- return node.key.name;
66
- } else if (node.key.type === AST_NODE_TYPES.Literal && typeof node.key.value === "string") {
67
- return node.key.value;
68
- } else {
69
- return void 0;
70
- }
71
- }
72
- function parsePropertyDecorator(decorator) {
73
- const isPropertyDecorator = decorator.expression.type === AST_NODE_TYPES.CallExpression && decorator.expression.callee.type === AST_NODE_TYPES.Identifier && decorator.expression.callee.name === "property";
74
- if (!isPropertyDecorator) {
75
- return;
76
- }
77
- const callExpression = decorator?.expression.type === AST_NODE_TYPES.CallExpression ? decorator.expression : void 0;
78
- if (callExpression === void 0) {
79
- return;
80
- }
81
- const options = callExpression.arguments[0]?.type === AST_NODE_TYPES.ObjectExpression ? callExpression.arguments[0] : void 0;
82
- const properties = options?.properties;
83
- return {
84
- callExpression,
85
- options,
86
- properties
87
- };
88
- }
89
- function isBindThisCallee(callee) {
90
- return callee.type === AST_NODE_TYPES.MemberExpression && // expression.identifier(this)
91
- callee.property.type === AST_NODE_TYPES.Identifier && // expression.bind(this)
92
- callee.property.name === "bind" && // expression.expression.bind(this)
93
- callee.object.type === AST_NODE_TYPES.MemberExpression && // expression.identifier.bind(this)
94
- callee.object.property.type === AST_NODE_TYPES.Identifier && // this.identifier.bind(this)
95
- callee.object.object.type === AST_NODE_TYPES.ThisExpression;
96
- }
97
9
  const importDeclaration = `import { ${luminaJsxExportName} } from "${luminaEntrypointName}";`;
98
10
  const description$l = `To use Lumina's JSX, you need to ${importDeclaration}`;
99
11
  plugin.createRule({
@@ -184,6 +96,9 @@ More information: https://devtopia.esri.com/WebGIS/arcgis-web-components/issues/
184
96
  },
185
97
  defaultOptions: [],
186
98
  create(context) {
99
+ if (!sourceCodeDeclaresComponent(context.sourceCode)) {
100
+ return {};
101
+ }
187
102
  const services = ESLintUtils.getParserServices(context);
188
103
  return {
189
104
  Decorator(decorator) {
@@ -362,6 +277,9 @@ plugin.createRule({
362
277
  type: "problem"
363
278
  },
364
279
  create(context) {
280
+ if (!sourceCodeDeclaresComponent(context.sourceCode)) {
281
+ return {};
282
+ }
365
283
  const bannedEventToMessageLookup = /* @__PURE__ */ new Map();
366
284
  context.options.forEach((option) => {
367
285
  const event = typeof option === "string" ? option : option.event;
@@ -382,20 +300,12 @@ plugin.createRule({
382
300
  });
383
301
  }
384
302
  }
385
- const luminaJsxCheck = checkForLuminaJsx();
386
303
  return {
387
- "ImportDeclaration": luminaJsxCheck,
388
304
  "CallExpression:matches([callee.property.name=addEventListener], [callee.property.name=removeEventListener]), CallExpression[callee.object.type=ThisExpression][callee.property.name=listen]"(node) {
389
- if (!luminaJsxCheck.isLuminaJsx) {
390
- return;
391
- }
392
305
  const eventName = node.arguments[0].value;
393
306
  checkEvent(node, eventName);
394
307
  },
395
308
  "CallExpression[callee.object.type=ThisExpression][callee.property.name=listenOn]"(node) {
396
- if (!luminaJsxCheck.isLuminaJsx) {
397
- return;
398
- }
399
309
  const eventName = node.arguments[1].value;
400
310
  checkEvent(node, eventName);
401
311
  }
@@ -662,6 +572,9 @@ If you wish to hide this member from public documentation, use @private or @prot
662
572
  },
663
573
  defaultOptions: [],
664
574
  create(context) {
575
+ if (!sourceCodeDeclaresComponent(context.sourceCode)) {
576
+ return {};
577
+ }
665
578
  return {
666
579
  PropertyDefinition(node) {
667
580
  const hasPropertyDecorator = hasDecorator(node, "property");
@@ -1441,20 +1354,26 @@ plugin.createRule({
1441
1354
  return {
1442
1355
  "Program:exit"() {
1443
1356
  const source = context.sourceCode.text;
1444
- for (const match of source.matchAll(/@(?:internal|private)\b/gu)) {
1445
- const idx = match.index;
1446
- const lineStart = source.lastIndexOf("\n", idx - 1) + 1;
1447
- const before = source.slice(lineStart, idx).trim();
1448
- if (before === "*" || before === "/**") {
1357
+ for (const comment of context.sourceCode.getAllComments()) {
1358
+ const commentText = source.slice(comment.range[0], comment.range[1]);
1359
+ if (!commentText.startsWith("/**")) {
1449
1360
  continue;
1450
1361
  }
1451
- context.report({
1452
- messageId: "inlineExposure",
1453
- loc: {
1454
- start: context.sourceCode.getLocFromIndex(idx - 1),
1455
- end: context.sourceCode.getLocFromIndex(idx + match[0].length)
1362
+ for (const match of comment.value.matchAll(/@(?:internal|private)\b/gu)) {
1363
+ const idx = comment.range[0] + 2 + match.index;
1364
+ const lineStart = source.lastIndexOf("\n", idx - 1) + 1;
1365
+ const before = source.slice(lineStart, idx).trim();
1366
+ if (before === "*" || before === "/**") {
1367
+ continue;
1456
1368
  }
1457
- });
1369
+ context.report({
1370
+ messageId: "inlineExposure",
1371
+ loc: {
1372
+ start: context.sourceCode.getLocFromIndex(idx - 1),
1373
+ end: context.sourceCode.getLocFromIndex(idx + match[0].length)
1374
+ }
1375
+ });
1376
+ }
1458
1377
  }
1459
1378
  }
1460
1379
  };
@@ -1613,28 +1532,20 @@ plugin.createRule({
1613
1532
  },
1614
1533
  defaultOptions: [],
1615
1534
  create(context) {
1616
- const declaredComponents = /* @__PURE__ */ new Set();
1617
- const hasLuminaDeclarations = context.sourceCode.text.includes("interface DeclareElements");
1535
+ if (!sourceCodeDeclaresComponent(context.sourceCode)) {
1536
+ return {};
1537
+ }
1538
+ let className;
1618
1539
  return {
1619
1540
  TSModuleDeclaration(node) {
1620
1541
  const luminaDeclarationInterface = extractDeclareElementsInterface(node);
1621
1542
  if (luminaDeclarationInterface === void 0) {
1622
1543
  return;
1623
1544
  }
1624
- luminaDeclarationInterface.body.body.forEach((member) => {
1625
- if (member.type !== AST_NODE_TYPES.TSPropertySignature || member.computed) {
1626
- return;
1627
- }
1628
- const type = member.typeAnnotation?.typeAnnotation;
1629
- if (type?.type !== AST_NODE_TYPES.TSTypeReference || type.typeName.type !== AST_NODE_TYPES.Identifier) {
1630
- return;
1631
- }
1632
- const className = type.typeName.name;
1633
- declaredComponents.add(className);
1634
- });
1545
+ className = getComponentDeclaration(luminaDeclarationInterface)?.typeAnnotation?.typeAnnotation.typeName.name;
1635
1546
  },
1636
1547
  ExportNamedDeclaration(node) {
1637
- if (!hasLuminaDeclarations && declaredComponents.size === 0) {
1548
+ if (className === void 0) {
1638
1549
  return;
1639
1550
  }
1640
1551
  if (node.exportKind === "type") {
@@ -1658,7 +1569,7 @@ plugin.createRule({
1658
1569
  ) {
1659
1570
  return;
1660
1571
  } else if (node.declaration?.type === AST_NODE_TYPES.ClassDeclaration) {
1661
- const isComponent = declaredComponents.has(node.declaration.id?.name ?? "");
1572
+ const isComponent = node.declaration.id?.name === className;
1662
1573
  if (isComponent) {
1663
1574
  return;
1664
1575
  }
@@ -1669,7 +1580,7 @@ plugin.createRule({
1669
1580
  });
1670
1581
  },
1671
1582
  ExportDefaultDeclaration(node) {
1672
- if (!hasLuminaDeclarations && declaredComponents.size === 0) {
1583
+ if (className === void 0) {
1673
1584
  return;
1674
1585
  }
1675
1586
  context.report({
@@ -1678,7 +1589,7 @@ plugin.createRule({
1678
1589
  });
1679
1590
  },
1680
1591
  ExportAllDeclaration(node) {
1681
- if (!hasLuminaDeclarations && declaredComponents.size === 0) {
1592
+ if (className === void 0) {
1682
1593
  return;
1683
1594
  }
1684
1595
  if (node.exportKind === "type") {
@@ -9,6 +9,17 @@ export declare function checkForLuminaJsx(): LuminaJsxCheck & {
9
9
  };
10
10
  export declare function hasDecorator(node: Pick<TSESTree.PropertyDefinitionNonComputedName, "decorators">, decoratorName: string): boolean;
11
11
  export declare function extractDeclareElementsInterface(node: TSESTree.TSModuleDeclaration): TSESTree.TSInterfaceDeclaration | undefined;
12
+ export declare function getComponentDeclaration(node: TSESTree.TSInterfaceDeclaration): {
13
+ key: TSESTree.Literal;
14
+ typeAnnotation: {
15
+ typeAnnotation: {
16
+ typeName: TSESTree.Identifier;
17
+ };
18
+ };
19
+ } | undefined;
20
+ export declare const sourceCodeDeclaresComponent: (sourceCode: {
21
+ readonly text: string;
22
+ }) => boolean;
12
23
  export declare function isCreateEvent(node: TSESTree.PropertyDefinition): boolean;
13
24
  export declare const getProperty: (properties: TSESTree.ObjectLiteralElement[] | undefined, name: string) => TSESTree.Property["value"] | undefined;
14
25
  export declare function isGetterWithoutSetter(node: TSESTree.MethodDefinition | TSESTree.PropertyDefinition): boolean;
@@ -1,4 +1,4 @@
1
- import { m as makeEslintPlugin } from "../../makePlugin-N9H9kNYE.js";
1
+ import { m as makeEslintPlugin, e as extractDeclareElementsInterface, g as getComponentDeclaration } from "../../estree-B6kiRVur.js";
2
2
  import { resolve } from "path/posix";
3
3
  import { AST_NODE_TYPES } from "@typescript-eslint/utils";
4
4
  const plugin = makeEslintPlugin(
@@ -7,16 +7,16 @@ const plugin = makeEslintPlugin(
7
7
  );
8
8
  const isTestFile = (filePath) => filePath.includes("/test") || filePath.includes(".test") || filePath.includes(".spec") || filePath.includes("e2e") || filePath.includes("__") || filePath.includes("/.");
9
9
  const isStorybookFile = (filePath) => filePath.includes(".stories");
10
- const description$4 = `Imports of files outside the src/ folder are not-portable and likely to break for consumers of this package.`;
10
+ const description$5 = `Imports of files outside the src/ folder are not-portable and likely to break for consumers of this package.`;
11
11
  plugin.createRule({
12
12
  name: "no-import-outside-src",
13
13
  meta: {
14
14
  docs: {
15
- description: description$4,
15
+ description: description$5,
16
16
  defaultLevel: "error"
17
17
  },
18
18
  messages: {
19
- noImportOutsideSrc: description$4
19
+ noImportOutsideSrc: description$5
20
20
  },
21
21
  type: "problem",
22
22
  schema: []
@@ -50,16 +50,16 @@ plugin.createRule({
50
50
  };
51
51
  }
52
52
  });
53
- const description$3 = `Having two JSDoc comments next to each other is most likely a mistake - consider combining them into one, or separating them for clarity.`;
53
+ const description$4 = `Having two JSDoc comments next to each other is most likely a mistake - consider combining them into one, or separating them for clarity.`;
54
54
  plugin.createRule({
55
55
  name: "no-touching-jsdoc",
56
56
  meta: {
57
57
  docs: {
58
- description: description$3,
58
+ description: description$4,
59
59
  defaultLevel: "warn"
60
60
  },
61
61
  messages: {
62
- noTouchingJsDoc: description$3
62
+ noTouchingJsDoc: description$4
63
63
  },
64
64
  type: "problem",
65
65
  schema: []
@@ -95,17 +95,17 @@ plugin.createRule({
95
95
  }
96
96
  });
97
97
  const reTouchingJsDoc = /\*\/\s+\/\*\*/gu;
98
- const description$2 = `@arcgis/core imports need to end with .js for better compatibility with ESM CDN builds for @arcgis/core and other packages.`;
98
+ const description$3 = `@arcgis/core imports need to end with .js for better compatibility with ESM CDN builds for @arcgis/core and other packages.`;
99
99
  const packagesToEnforce = ["@arcgis/core/", "@amcharts/amcharts4/", "@amcharts/amcharts5/"];
100
100
  plugin.createRule({
101
101
  name: "require-js-in-imports",
102
102
  meta: {
103
103
  docs: {
104
- description: description$2,
104
+ description: description$3,
105
105
  defaultLevel: "warn"
106
106
  },
107
107
  messages: {
108
- requireJsInCoreImport: description$2
108
+ requireJsInCoreImport: description$3
109
109
  },
110
110
  type: "problem",
111
111
  fixable: "code",
@@ -117,7 +117,7 @@ plugin.createRule({
117
117
  return {};
118
118
  }
119
119
  function updateSpecifier(node) {
120
- if (node.source.type !== AST_NODE_TYPES.Literal || node.importKind === "type") {
120
+ if (node.source.type !== AST_NODE_TYPES.Literal) {
121
121
  return;
122
122
  }
123
123
  const specifier = node.source.value;
@@ -139,17 +139,17 @@ plugin.createRule({
139
139
  };
140
140
  }
141
141
  });
142
- const description$1 = "Using .d.ts files is discouraged. Prefer .ts files instead, as they are type-checked and not in global scope.";
142
+ const description$2 = "Using .d.ts files is discouraged. Prefer .ts files instead, as they are type-checked and not in global scope.";
143
143
  const allowedNames = /* @__PURE__ */ new Set(["vite-env.d.ts", "components.d.ts"]);
144
144
  plugin.createRule({
145
145
  name: "no-dts-files",
146
146
  meta: {
147
147
  docs: {
148
- description: description$1,
148
+ description: description$2,
149
149
  defaultLevel: "warn"
150
150
  },
151
151
  messages: {
152
- avoidDtsFiles: description$1
152
+ avoidDtsFiles: description$2
153
153
  },
154
154
  type: "suggestion",
155
155
  schema: []
@@ -179,12 +179,12 @@ plugin.createRule({
179
179
  };
180
180
  }
181
181
  });
182
- const description = `Enforce consistent logging so that ArcGIS developers can easily debug errors or warnings logged by our web components, which may lack a meaningful context in compiled code. See [our documentation on @arcgis/toolkit/log](https://webgis.esri.com/references/toolkit/log).`;
182
+ const description$1 = `Enforce consistent logging so that ArcGIS developers can easily debug errors or warnings logged by our web components, which may lack a meaningful context in compiled code. See [our documentation on @arcgis/toolkit/log](https://webgis.esri.com/references/toolkit/log).`;
183
183
  plugin.createRule({
184
184
  name: "consistent-logging",
185
185
  meta: {
186
186
  docs: {
187
- description,
187
+ description: description$1,
188
188
  defaultLevel: "off"
189
189
  // NOTE: this is turned on conditionally in root eslint config
190
190
  },
@@ -217,6 +217,61 @@ plugin.createRule({
217
217
  };
218
218
  }
219
219
  });
220
+ const description = `Do not use links like [](#something) as they are not portable across .d.ts, and are not validated at build time. Use {@link } syntax, or absolute links. See https://webgis.esri.com/webgis/core/core/documenting-api#link`;
221
+ plugin.createRule({
222
+ name: "no-unsafe-hash-links",
223
+ meta: {
224
+ docs: {
225
+ description,
226
+ defaultLevel: "warn"
227
+ },
228
+ messages: {
229
+ error: description
230
+ },
231
+ type: "problem",
232
+ schema: [],
233
+ fixable: "code"
234
+ },
235
+ defaultOptions: [],
236
+ create(context) {
237
+ let declareElementsInterface;
238
+ return {
239
+ "TSModuleDeclaration"(node) {
240
+ declareElementsInterface = extractDeclareElementsInterface(node);
241
+ },
242
+ "Program:exit"() {
243
+ if (declareElementsInterface === void 0 && !context.sourceCode.text.includes("@public") && !context.sourceCode.text.includes("@internal")) {
244
+ return;
245
+ }
246
+ for (const match of context.sourceCode.text.matchAll(reHashLink)) {
247
+ const fullMatch = match[0];
248
+ const anchor = match[2];
249
+ const replacement = computeReplacement(declareElementsInterface, anchor);
250
+ context.report({
251
+ messageId: "error",
252
+ loc: {
253
+ start: context.sourceCode.getLocFromIndex(match.index),
254
+ end: context.sourceCode.getLocFromIndex(match.index + fullMatch.length)
255
+ },
256
+ fix: replacement === void 0 ? void 0 : (fixer) => fixer.replaceTextRange([match.index, match.index + fullMatch.length], `{@link ${replacement}}`)
257
+ });
258
+ }
259
+ }
260
+ };
261
+ }
262
+ });
263
+ const reHashLink = /\[([^\]\n]+)\]\(#([^)]+)\)/gu;
264
+ function computeReplacement(declareElementsInterface, anchor) {
265
+ if (declareElementsInterface === void 0) {
266
+ return;
267
+ }
268
+ const declaration = getComponentDeclaration(declareElementsInterface);
269
+ if (declaration === void 0) {
270
+ return;
271
+ }
272
+ const tagName = declaration.key.value;
273
+ return `components/${tagName}#${anchor}`;
274
+ }
220
275
  const webgisPlugin = plugin.finalize();
221
276
  export {
222
277
  webgisPlugin
@@ -0,0 +1,2 @@
1
+ declare const _default: import('@typescript-eslint/utils/ts-eslint').RuleModule<"error", [], import('../../utils/makePlugin.ts').CommonDocs, import('@typescript-eslint/utils/ts-eslint').RuleListener>;
2
+ export default _default;
@@ -0,0 +1,24 @@
1
+ import { TSESLint } from '@typescript-eslint/utils';
2
+ /**
3
+ * A utility for temporary running ESLint with only specific rules enabled.
4
+ *
5
+ * @param config flat config
6
+ * @param enabledRules exceptions
7
+ * @param disableTypeChecking make ESLint much faster by temporary disabling
8
+ * type-aware linting.
9
+ * @returns a new flat config array where every rule is disabled,
10
+ * except for rules explicitly listed in `enabledRules`.
11
+ *
12
+ * @example
13
+ * const config = [
14
+ * // Your ESLint flat config
15
+ * ];
16
+ * export default disableAllRulesExcept(
17
+ * config,
18
+ * {
19
+ * "rule-name-1": "error",
20
+ * },
21
+ * true,
22
+ * );
23
+ */
24
+ export declare function disableAllRulesExcept(config: TSESLint.FlatConfig.ConfigArray, enabledRules: Record<string, TSESLint.FlatConfig.RuleLevel>, disableTypeChecking: boolean): TSESLint.FlatConfig.ConfigArray;
@@ -0,0 +1,47 @@
1
+ function disableAllRulesExcept(config, enabledRules, disableTypeChecking) {
2
+ return config.map((entry) => applyRulesAndOptions(entry, enabledRules, disableTypeChecking));
3
+ }
4
+ function applyRulesAndOptions(entry, enabledRules, disableTypeChecking) {
5
+ if (!entry || typeof entry !== "object") {
6
+ return entry;
7
+ }
8
+ const nextEntry = entry.rules ? {
9
+ ...entry,
10
+ rules: Object.fromEntries(
11
+ Object.keys(entry.rules).map((ruleName) => {
12
+ const override = enabledRules[ruleName];
13
+ return [ruleName, override ?? "off"];
14
+ })
15
+ )
16
+ } : entry;
17
+ return tweakOptions(nextEntry, disableTypeChecking);
18
+ }
19
+ function tweakOptions(entry, disableTypeChecking) {
20
+ if (entry.linterOptions !== void 0) {
21
+ entry = {
22
+ ...entry,
23
+ linterOptions: {
24
+ ...entry.linterOptions,
25
+ reportUnusedDisableDirectives: "off",
26
+ reportUnusedInlineConfigs: "off"
27
+ }
28
+ };
29
+ }
30
+ if (disableTypeChecking && entry.languageOptions?.parserOptions) {
31
+ entry = {
32
+ ...entry,
33
+ languageOptions: {
34
+ ...entry.languageOptions,
35
+ parserOptions: {
36
+ ...entry.languageOptions.parserOptions,
37
+ project: void 0,
38
+ projectService: void 0
39
+ }
40
+ }
41
+ };
42
+ }
43
+ return entry;
44
+ }
45
+ export {
46
+ disableAllRulesExcept
47
+ };
package/package.json CHANGED
@@ -1,18 +1,21 @@
1
1
  {
2
2
  "name": "@arcgis/eslint-config",
3
- "version": "5.1.0-next.8",
4
- "description": "ESLint configuration for arcgis-web-components",
3
+ "version": "5.1.0-next.80",
4
+ "description": "ESLint configuration for WebGIS SDK",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "module": "dist/index.js",
8
8
  "types": "dist/index.d.ts",
9
9
  "exports": {
10
10
  ".": "./dist/config/index.js",
11
+ "./ts": "./src/config/index.ts",
11
12
  "./application": "./dist/config/applications.js",
12
13
  "./extra": "./dist/config/extra.js",
13
14
  "./lumina": "./dist/config/lumina.js",
15
+ "./lumina/ts": "./src/config/lumina.ts",
14
16
  "./plugins/webgis": "./dist/plugins/webgis/index.js",
15
17
  "./plugins/lumina": "./dist/plugins/lumina/index.js",
18
+ "./utils/disable-rules": "./dist/utils/disable-rules.js",
16
19
  "./package.json": "./package.json"
17
20
  },
18
21
  "files": [
@@ -20,21 +23,21 @@
20
23
  ],
21
24
  "license": "SEE LICENSE IN LICENSE.md",
22
25
  "dependencies": {
23
- "@eslint/js": "^9.39.1",
24
- "@eslint/markdown": "^7.5.1",
26
+ "@eslint/js": "^10.0.1",
27
+ "@eslint/markdown": "^8.0.1",
25
28
  "@types/confusing-browser-globals": "^1.0.3",
26
- "@typescript-eslint/utils": "^8.46.4",
29
+ "@typescript-eslint/utils": "^8.58.0",
27
30
  "confusing-browser-globals": "^1.0.11",
28
- "eslint-plugin-package-json": "^0.88.1",
29
- "eslint-plugin-storybook": "^0.12.0",
31
+ "eslint-plugin-package-json": "~0.91.1",
32
+ "eslint-plugin-storybook": "^10.3.4",
30
33
  "globals": "^16.5.0",
31
- "jsonc-eslint-parser": "^2.0.0",
34
+ "jsonc-eslint-parser": "^3.1.0",
32
35
  "tslib": "^2.8.1",
33
- "typescript": "~5.9.3",
34
- "typescript-eslint": "^8.46.3",
35
- "@arcgis/toolkit": "5.1.0-next.8"
36
+ "typescript": "~6.0.2",
37
+ "typescript-eslint": "^8.58.0",
38
+ "@arcgis/toolkit": "5.1.0-next.80"
36
39
  },
37
40
  "peerDependencies": {
38
- "eslint": "^9.39.1"
41
+ "eslint": "^10.2.0"
39
42
  }
40
43
  }
@@ -1,41 +0,0 @@
1
- import { ESLintUtils } from "@typescript-eslint/utils";
2
- const version = "5.1.0-next.8";
3
- const packageJson = {
4
- version
5
- };
6
- function makeEslintPlugin(pluginName, urlCreator) {
7
- const rules = [];
8
- const creator = ESLintUtils.RuleCreator(urlCreator);
9
- return {
10
- // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
11
- createRule(rule) {
12
- const { meta, name, ...rest } = rule;
13
- const docs = { ...meta.docs, name };
14
- const ruleModule = creator({ ...rest, meta: { ...meta, docs }, name });
15
- rules.push(ruleModule);
16
- return ruleModule;
17
- },
18
- // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
19
- finalize() {
20
- const config = {
21
- rules: Object.fromEntries(
22
- rules.map((rule) => [`${pluginName}/${rule.meta.docs.name}`, rule.meta.docs.defaultLevel])
23
- )
24
- };
25
- const plugin = {
26
- meta: { name: `@arcgis/eslint-plugin-${pluginName}`, version: packageJson.version },
27
- configs: {
28
- recommended: config
29
- },
30
- rules: Object.fromEntries(rules.map((rule) => [rule.meta.docs.name, rule]))
31
- };
32
- config.plugins = {
33
- [pluginName]: plugin
34
- };
35
- return plugin;
36
- }
37
- };
38
- }
39
- export {
40
- makeEslintPlugin as m
41
- };