@arcgis/eslint-config 5.0.0-next.98 → 5.0.0

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/LICENSE.md CHANGED
@@ -4,10 +4,14 @@ COPYRIGHT © Esri
4
4
 
5
5
  All rights reserved under the copyright laws of the United States and applicable international laws, treaties, and conventions.
6
6
 
7
- This material is licensed for use under the Esri Master License Agreement (MLA), and is bound by the terms of that agreement. You may redistribute and use this code without modification, provided you adhere to the terms of the MLA and include this copyright notice.
7
+ This material is licensed for use under the [Esri Master License Agreement (MLA)](https://www.esri.com/content/dam/esrisites/en-us/media/legal/ma-full/ma-full.pdf), and is bound by the terms of that agreement.
8
+ You may redistribute and use this code without modification, provided you adhere to the terms of the MLA and include this copyright notice.
8
9
 
9
- See use restrictions at http://www.esri.com/legal/pdfs/mla_e204_e300/english
10
+ For additional information, contact:
11
+ Environmental Systems Research Institute, Inc.
12
+ Attn: Contracts and Legal Services Department
13
+ 380 New York Street
14
+ Redlands, California, USA 92373
15
+ USA
10
16
 
11
- For additional information, contact: Environmental Systems Research Institute, Inc. Attn: Contracts and Legal Services Department 380 New York Street Redlands, California, USA 92373 USA
12
-
13
- email: contracts@esri.com
17
+ email: legal@esri.com
package/README.md CHANGED
@@ -8,6 +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
- COPYRIGHT © Esri
12
-
13
- This package is licensed under the terms described in the `LICENSE.md` file, located in the root of the package.
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.0/LICENSE.txt.
12
+ For third party notices, see https://js.arcgis.com/5.0/third-party-notices.txt.
@@ -5,6 +5,10 @@ import globals from "globals";
5
5
  import { webgisPlugin } from "../plugins/webgis/index.js";
6
6
  import { globalIgnores } from "eslint/config";
7
7
  import markdown from "@eslint/markdown";
8
+ import packageJson from "eslint-plugin-package-json";
9
+ const packageJsonSortableCollections = packageJson.rules["sort-collections"].meta.defaultOptions[0].filter(
10
+ (collection) => !["scripts", "exports"].includes(collection)
11
+ ) ?? [];
8
12
  const defaultConfig = [
9
13
  globalIgnores(["**/www", "**/dist", "**/assets", "**/coverage", "**/.docs"]),
10
14
  {
@@ -280,7 +284,7 @@ const defaultConfig = [
280
284
  "no-loss-of-precision": "error",
281
285
  "@typescript-eslint/switch-exhaustiveness-check": "error",
282
286
  "default-case-last": "error",
283
- "func-style": ["error", "declaration", { allowArrowFunctions: true }],
287
+ "func-style": ["error", "declaration", { allowArrowFunctions: true, allowTypeAnnotation: true }],
284
288
  "no-new-func": "error",
285
289
  "no-new-wrappers": "error",
286
290
  "@typescript-eslint/no-misused-promises": [
@@ -570,6 +574,52 @@ const defaultConfig = [
570
574
  // Don't do type-aware linting for untyped files
571
575
  files: ["**/*.js"],
572
576
  ...tsEslint.configs.disableTypeChecked
577
+ },
578
+ packageJson.configs.recommended,
579
+ {
580
+ files: ["**/package.json"],
581
+ rules: {
582
+ // We don't want to sort scripts and exports
583
+ "package-json/sort-collections": ["warn", packageJsonSortableCollections],
584
+ // Making a warning for now to avoid disrupting current work but we should go to error later
585
+ "package-json/require-description": "warn",
586
+ "package-json/bin-name-casing": "warn",
587
+ // Allow empty "files" field to explicitly indicate that no files
588
+ // should be included in the package
589
+ "package-json/no-empty-fields": ["error", { ignoreProperties: ["files"] }],
590
+ // Enforce a specific property order for better readability and consistency
591
+ "package-json/order-properties": [
592
+ "warn",
593
+ {
594
+ order: [
595
+ "name",
596
+ "version",
597
+ "description",
598
+ "keywords",
599
+ "homepage",
600
+ "private",
601
+ "sideEffects",
602
+ "workspaces",
603
+ "type",
604
+ "main",
605
+ "module",
606
+ "types",
607
+ "exports",
608
+ "files",
609
+ "bin",
610
+ "publishConfig",
611
+ "license",
612
+ "scripts",
613
+ "acme:scripts",
614
+ "dependencies",
615
+ "devDependencies",
616
+ "peerDependencies",
617
+ "peerDependenciesMeta",
618
+ "packageManager"
619
+ ]
620
+ }
621
+ ]
622
+ }
573
623
  }
574
624
  ];
575
625
  export {
@@ -0,0 +1,159 @@
1
+ import { ESLintUtils, AST_NODE_TYPES } from "@typescript-eslint/utils";
2
+ const version = "5.0.0";
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
+ function isCreateEvent(node) {
85
+ return node.value?.type === AST_NODE_TYPES.CallExpression && node.value.callee.type === AST_NODE_TYPES.Identifier && node.value.callee.name === "createEvent" && !node.static;
86
+ }
87
+ const getProperty = (properties, name) => properties?.find(
88
+ (option) => option.type === AST_NODE_TYPES.Property && option.key.type === AST_NODE_TYPES.Identifier && option.key.name === name
89
+ )?.value;
90
+ function isGetterWithoutSetter(node) {
91
+ const isGetter = node.type === AST_NODE_TYPES.MethodDefinition && node.kind === "get";
92
+ if (!isGetter) {
93
+ return false;
94
+ }
95
+ const index = node.parent.body.indexOf(node);
96
+ const previousNode = node.parent.body.at(index - 1);
97
+ const nextNode = node.parent.body.at(index + 1);
98
+ const name = getName(node);
99
+ if (name === void 0) {
100
+ return false;
101
+ }
102
+ const previousIsSetter = previousNode?.type === AST_NODE_TYPES.MethodDefinition && previousNode.kind === "set" && getName(previousNode) === name;
103
+ if (previousIsSetter) {
104
+ return false;
105
+ }
106
+ const nextIsSetter = nextNode?.type === AST_NODE_TYPES.MethodDefinition && nextNode.kind === "set" && getName(nextNode) === name;
107
+ return !nextIsSetter;
108
+ }
109
+ function getName(node) {
110
+ if (node.key.type === AST_NODE_TYPES.Identifier) {
111
+ return node.key.name;
112
+ } else if (node.key.type === AST_NODE_TYPES.Literal && typeof node.key.value === "string") {
113
+ return node.key.value;
114
+ } else {
115
+ return void 0;
116
+ }
117
+ }
118
+ function parsePropertyDecorator(decorator) {
119
+ const isPropertyDecorator = decorator.expression.type === AST_NODE_TYPES.CallExpression && decorator.expression.callee.type === AST_NODE_TYPES.Identifier && decorator.expression.callee.name === "property";
120
+ if (!isPropertyDecorator) {
121
+ return;
122
+ }
123
+ const callExpression = decorator?.expression.type === AST_NODE_TYPES.CallExpression ? decorator.expression : void 0;
124
+ if (callExpression === void 0) {
125
+ return;
126
+ }
127
+ const options = callExpression.arguments[0]?.type === AST_NODE_TYPES.ObjectExpression ? callExpression.arguments[0] : void 0;
128
+ const properties = options?.properties;
129
+ return {
130
+ callExpression,
131
+ options,
132
+ properties
133
+ };
134
+ }
135
+ function isBindThisCallee(callee) {
136
+ return callee.type === AST_NODE_TYPES.MemberExpression && // expression.identifier(this)
137
+ callee.property.type === AST_NODE_TYPES.Identifier && // expression.bind(this)
138
+ callee.property.name === "bind" && // expression.expression.bind(this)
139
+ callee.object.type === AST_NODE_TYPES.MemberExpression && // expression.identifier.bind(this)
140
+ callee.object.property.type === AST_NODE_TYPES.Identifier && // this.identifier.bind(this)
141
+ callee.object.object.type === AST_NODE_TYPES.ThisExpression;
142
+ }
143
+ export {
144
+ luminaEntrypointName as a,
145
+ luminaTestEntrypointName as b,
146
+ getProperty as c,
147
+ checkForLuminaJsx as d,
148
+ extractDeclareElementsInterface as e,
149
+ isCreateEvent as f,
150
+ getComponentDeclaration as g,
151
+ hasDecorator as h,
152
+ isGetterWithoutSetter as i,
153
+ getName as j,
154
+ isBindThisCallee as k,
155
+ luminaJsxExportName as l,
156
+ makeEslintPlugin as m,
157
+ parsePropertyDecorator as p,
158
+ unwrapExpression as u
159
+ };
@@ -1,4 +1,4 @@
1
- import { m as makeEslintPlugin } from "../../makePlugin-DvrrzfxD.js";
1
+ import { m as makeEslintPlugin, l as luminaJsxExportName, a as luminaEntrypointName, b as luminaTestEntrypointName, p as parsePropertyDecorator, c as getProperty, i as isGetterWithoutSetter, d as checkForLuminaJsx, e as extractDeclareElementsInterface, f as isCreateEvent, h as hasDecorator, j as getName, k as isBindThisCallee, g as getComponentDeclaration, u as unwrapExpression } from "../../estree-Can6TC4w.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$k = `To use Lumina's JSX, you need to ${importDeclaration}`;
99
11
  plugin.createRule({
@@ -1575,7 +1487,7 @@ plugin.createRule({
1575
1487
  },
1576
1488
  defaultOptions: [],
1577
1489
  create(context) {
1578
- const declaredComponents = /* @__PURE__ */ new Set();
1490
+ let className;
1579
1491
  const hasLuminaDeclarations = context.sourceCode.text.includes("interface DeclareElements");
1580
1492
  return {
1581
1493
  TSModuleDeclaration(node) {
@@ -1583,20 +1495,10 @@ plugin.createRule({
1583
1495
  if (luminaDeclarationInterface === void 0) {
1584
1496
  return;
1585
1497
  }
1586
- luminaDeclarationInterface.body.body.forEach((member) => {
1587
- if (member.type !== AST_NODE_TYPES.TSPropertySignature || member.computed) {
1588
- return;
1589
- }
1590
- const type = member.typeAnnotation?.typeAnnotation;
1591
- if (type?.type !== AST_NODE_TYPES.TSTypeReference || type.typeName.type !== AST_NODE_TYPES.Identifier) {
1592
- return;
1593
- }
1594
- const className = type.typeName.name;
1595
- declaredComponents.add(className);
1596
- });
1498
+ className = getComponentDeclaration(luminaDeclarationInterface)?.typeAnnotation?.typeAnnotation.typeName.name;
1597
1499
  },
1598
1500
  ExportNamedDeclaration(node) {
1599
- if (!hasLuminaDeclarations && declaredComponents.size === 0) {
1501
+ if (!hasLuminaDeclarations && className === void 0) {
1600
1502
  return;
1601
1503
  }
1602
1504
  if (node.exportKind === "type") {
@@ -1620,7 +1522,7 @@ plugin.createRule({
1620
1522
  ) {
1621
1523
  return;
1622
1524
  } else if (node.declaration?.type === AST_NODE_TYPES.ClassDeclaration) {
1623
- const isComponent = declaredComponents.has(node.declaration.id?.name ?? "");
1525
+ const isComponent = node.declaration.id?.name === className;
1624
1526
  if (isComponent) {
1625
1527
  return;
1626
1528
  }
@@ -1631,7 +1533,7 @@ plugin.createRule({
1631
1533
  });
1632
1534
  },
1633
1535
  ExportDefaultDeclaration(node) {
1634
- if (!hasLuminaDeclarations && declaredComponents.size === 0) {
1536
+ if (!hasLuminaDeclarations && className === void 0) {
1635
1537
  return;
1636
1538
  }
1637
1539
  context.report({
@@ -1640,7 +1542,7 @@ plugin.createRule({
1640
1542
  });
1641
1543
  },
1642
1544
  ExportAllDeclaration(node) {
1643
- if (!hasLuminaDeclarations && declaredComponents.size === 0) {
1545
+ if (!hasLuminaDeclarations && className === void 0) {
1644
1546
  return;
1645
1547
  }
1646
1548
  if (node.exportKind === "type") {
@@ -9,6 +9,14 @@ 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;
12
20
  export declare function isCreateEvent(node: TSESTree.PropertyDefinition): boolean;
13
21
  export declare const getProperty: (properties: TSESTree.ObjectLiteralElement[] | undefined, name: string) => TSESTree.Property["value"] | undefined;
14
22
  export declare function isGetterWithoutSetter(node: TSESTree.MethodDefinition | TSESTree.PropertyDefinition): boolean;
@@ -1,4 +1,4 @@
1
- import { m as makeEslintPlugin } from "../../makePlugin-DvrrzfxD.js";
1
+ import { m as makeEslintPlugin, e as extractDeclareElementsInterface, g as getComponentDeclaration } from "../../estree-Can6TC4w.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",
@@ -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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcgis/eslint-config",
3
- "version": "5.0.0-next.98",
3
+ "version": "5.0.0",
4
4
  "description": "ESLint configuration for arcgis-web-components",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -20,16 +20,19 @@
20
20
  ],
21
21
  "license": "SEE LICENSE IN LICENSE.md",
22
22
  "dependencies": {
23
- "@arcgis/toolkit": "5.0.0-next.98",
24
23
  "@eslint/js": "^9.39.1",
25
24
  "@eslint/markdown": "^7.5.1",
26
25
  "@types/confusing-browser-globals": "^1.0.3",
26
+ "@typescript-eslint/utils": "^8.46.4",
27
27
  "confusing-browser-globals": "^1.0.11",
28
+ "eslint-plugin-package-json": "^0.88.1",
28
29
  "eslint-plugin-storybook": "^0.12.0",
29
30
  "globals": "^16.5.0",
31
+ "jsonc-eslint-parser": "^2.0.0",
30
32
  "tslib": "^2.8.1",
31
33
  "typescript": "~5.9.3",
32
- "typescript-eslint": "^8.46.3"
34
+ "typescript-eslint": "^8.46.3",
35
+ "@arcgis/toolkit": "5.0.0"
33
36
  },
34
37
  "peerDependencies": {
35
38
  "eslint": "^9.39.1"
@@ -1,41 +0,0 @@
1
- import { ESLintUtils } from "@typescript-eslint/utils";
2
- const version = "5.0.0-next.98";
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
- };