@api-extractor-tools/eslint-plugin 0.1.0-alpha.0 → 0.1.0-alpha.2

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.
Files changed (104) hide show
  1. package/ARCHITECTURE.md +204 -0
  2. package/CHANGELOG.md +32 -0
  3. package/README.md +306 -10
  4. package/api-extractor.json +1 -0
  5. package/dist/configs/recommended.d.ts +1 -1
  6. package/dist/configs/recommended.d.ts.map +1 -1
  7. package/dist/configs/recommended.js +7 -1
  8. package/dist/configs/recommended.js.map +1 -1
  9. package/dist/index.d.ts +9 -16
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +11 -15
  12. package/dist/index.js.map +1 -1
  13. package/dist/node.d.ts +28 -0
  14. package/dist/node.d.ts.map +1 -0
  15. package/dist/node.js +41 -0
  16. package/dist/node.js.map +1 -0
  17. package/dist/rules/extra-release-tag.d.ts +24 -0
  18. package/dist/rules/extra-release-tag.d.ts.map +1 -0
  19. package/dist/rules/extra-release-tag.js +141 -0
  20. package/dist/rules/extra-release-tag.js.map +1 -0
  21. package/dist/rules/forgotten-export.d.ts +24 -0
  22. package/dist/rules/forgotten-export.d.ts.map +1 -0
  23. package/dist/rules/forgotten-export.js +212 -0
  24. package/dist/rules/forgotten-export.js.map +1 -0
  25. package/dist/rules/incompatible-release-tags.d.ts +25 -0
  26. package/dist/rules/incompatible-release-tags.d.ts.map +1 -0
  27. package/dist/rules/incompatible-release-tags.js +237 -0
  28. package/dist/rules/incompatible-release-tags.js.map +1 -0
  29. package/dist/rules/index.d.ts +8 -2
  30. package/dist/rules/index.d.ts.map +1 -1
  31. package/dist/rules/index.js +13 -1
  32. package/dist/rules/index.js.map +1 -1
  33. package/dist/rules/missing-release-tag.d.ts +4 -0
  34. package/dist/rules/missing-release-tag.d.ts.map +1 -1
  35. package/dist/rules/missing-release-tag.js +14 -21
  36. package/dist/rules/missing-release-tag.js.map +1 -1
  37. package/dist/rules/override-keyword.d.ts +4 -0
  38. package/dist/rules/override-keyword.d.ts.map +1 -1
  39. package/dist/rules/override-keyword.js +9 -11
  40. package/dist/rules/override-keyword.js.map +1 -1
  41. package/dist/rules/package-documentation.d.ts +5 -2
  42. package/dist/rules/package-documentation.d.ts.map +1 -1
  43. package/dist/rules/package-documentation.js +45 -35
  44. package/dist/rules/package-documentation.js.map +1 -1
  45. package/dist/rules/public-on-non-exported.d.ts +24 -0
  46. package/dist/rules/public-on-non-exported.d.ts.map +1 -0
  47. package/dist/rules/public-on-non-exported.js +191 -0
  48. package/dist/rules/public-on-non-exported.js.map +1 -0
  49. package/dist/rules/public-on-private-member.d.ts +24 -0
  50. package/dist/rules/public-on-private-member.d.ts.map +1 -0
  51. package/dist/rules/public-on-private-member.js +111 -0
  52. package/dist/rules/public-on-private-member.js.map +1 -0
  53. package/dist/rules/valid-enum-type.d.ts +17 -0
  54. package/dist/rules/valid-enum-type.d.ts.map +1 -0
  55. package/dist/rules/valid-enum-type.js +206 -0
  56. package/dist/rules/valid-enum-type.js.map +1 -0
  57. package/dist/types.d.ts +66 -24
  58. package/dist/types.d.ts.map +1 -1
  59. package/dist/types.js +4 -1
  60. package/dist/types.js.map +1 -1
  61. package/dist/utils/tsdoc-parser.d.ts +37 -6
  62. package/dist/utils/tsdoc-parser.d.ts.map +1 -1
  63. package/dist/utils/tsdoc-parser.js +40 -0
  64. package/dist/utils/tsdoc-parser.js.map +1 -1
  65. package/docs/rules/valid-enum-type.md +153 -0
  66. package/package.json +22 -8
  67. package/src/configs/recommended.ts +7 -1
  68. package/src/index.ts +21 -15
  69. package/src/node.ts +50 -0
  70. package/src/rules/extra-release-tag.ts +201 -0
  71. package/src/rules/forgotten-export.ts +274 -0
  72. package/src/rules/incompatible-release-tags.ts +331 -0
  73. package/src/rules/index.ts +13 -1
  74. package/src/rules/missing-release-tag.ts +11 -26
  75. package/src/rules/override-keyword.ts +6 -8
  76. package/src/rules/package-documentation.ts +54 -40
  77. package/src/rules/public-on-non-exported.ts +265 -0
  78. package/src/rules/public-on-private-member.ts +157 -0
  79. package/src/rules/valid-enum-type.ts +252 -0
  80. package/src/types.ts +60 -17
  81. package/src/utils/config-loader.ts +1 -0
  82. package/src/utils/entry-point.ts +1 -0
  83. package/src/utils/tsdoc-parser.ts +67 -0
  84. package/temp/eslint-plugin.api.md +101 -32
  85. package/test/index.test.ts +1 -0
  86. package/test/rules/extra-release-tag.test.ts +276 -0
  87. package/test/rules/forgotten-export.test.ts +190 -0
  88. package/test/rules/incompatible-release-tags.test.ts +340 -0
  89. package/test/rules/missing-release-tag.test.ts +2 -1
  90. package/test/rules/override-keyword.test.ts +2 -1
  91. package/test/rules/package-documentation.test.ts +113 -64
  92. package/test/rules/public-on-non-exported.test.ts +201 -0
  93. package/test/rules/public-on-private-member.test.ts +207 -0
  94. package/test/rules/valid-enum-type.test.ts +409 -0
  95. package/test/types.test-d.ts +20 -0
  96. package/test/utils/config-loader.test.ts +1 -0
  97. package/test/utils/tsdoc-parser.test.ts +117 -9
  98. package/tsconfig.json +1 -0
  99. package/vitest.config.mts +1 -0
  100. package/dist/utils/index.d.ts +0 -8
  101. package/dist/utils/index.d.ts.map +0 -1
  102. package/dist/utils/index.js +0 -21
  103. package/dist/utils/index.js.map +0 -1
  104. package/src/utils/index.ts +0 -17
@@ -0,0 +1,153 @@
1
+ # valid-enum-type
2
+
3
+ Validates the usage of the `@enumType` TSDoc tag on enum declarations and string literal union type aliases.
4
+
5
+ ## Rule Details
6
+
7
+ The `@enumType` tag is used to specify whether an enum or string literal union is "open" (new members may be added) or "closed" (the set of members is fixed). This information is used by the change detector to properly classify API changes.
8
+
9
+ This rule ensures that:
10
+
11
+ - `@enumType` tags have a valid value (`open` or `closed`)
12
+ - `@enumType` tags are not duplicated
13
+ - `@enumType` tags are only used on valid constructs (enums and string literal unions)
14
+ - Optionally, exported enums and string literal unions have an `@enumType` tag
15
+
16
+ ## Options
17
+
18
+ ```json
19
+ {
20
+ "@api-extractor-tools/valid-enum-type": [
21
+ "warn",
22
+ {
23
+ "requireOnExported": false
24
+ }
25
+ ]
26
+ }
27
+ ```
28
+
29
+ ### `requireOnExported`
30
+
31
+ Type: `boolean`
32
+ Default: `false`
33
+
34
+ When `true`, requires all exported enums and string literal union type aliases to have an `@enumType` tag.
35
+
36
+ ## Examples
37
+
38
+ ### ❌ Incorrect
39
+
40
+ ```ts
41
+ // Missing value
42
+ /**
43
+ * @enumType
44
+ */
45
+ export enum Status {
46
+ Active,
47
+ Inactive,
48
+ }
49
+
50
+ // Invalid value
51
+ /**
52
+ * @enumType invalid
53
+ */
54
+ export enum Status {
55
+ Active,
56
+ Inactive,
57
+ }
58
+
59
+ // Multiple @enumType tags
60
+ /**
61
+ * @enumType open
62
+ * @enumType closed
63
+ */
64
+ export enum Status {
65
+ Active,
66
+ Inactive,
67
+ }
68
+
69
+ // @enumType on invalid construct
70
+ /**
71
+ * @enumType open
72
+ */
73
+ export function myFunction(): void {}
74
+
75
+ // @enumType on non-string-literal union
76
+ /**
77
+ * @enumType open
78
+ */
79
+ export type NumberUnion = 1 | 2 | 3
80
+
81
+ // With requireOnExported: true - missing @enumType
82
+ export enum Status {
83
+ Active,
84
+ Inactive,
85
+ }
86
+ ```
87
+
88
+ ### ✅ Correct
89
+
90
+ ```ts
91
+ // Open enum - new members may be added in future versions
92
+ /**
93
+ * Status values for a resource.
94
+ * @enumType open
95
+ * @public
96
+ */
97
+ export enum Status {
98
+ Active = 'active',
99
+ Inactive = 'inactive',
100
+ }
101
+
102
+ // Closed enum - the set of members is fixed
103
+ /**
104
+ * Boolean-like values.
105
+ * @enumType closed
106
+ * @public
107
+ */
108
+ export enum YesNo {
109
+ Yes = 'yes',
110
+ No = 'no',
111
+ }
112
+
113
+ // String literal union with @enumType
114
+ /**
115
+ * Supported color values.
116
+ * @enumType open
117
+ * @public
118
+ */
119
+ export type Color = 'red' | 'green' | 'blue'
120
+
121
+ // Single string literal (also valid)
122
+ /**
123
+ * The only supported format.
124
+ * @enumType closed
125
+ * @public
126
+ */
127
+ export type Format = 'json'
128
+
129
+ // Without requireOnExported, missing @enumType is allowed
130
+ export enum InternalStatus {
131
+ Pending,
132
+ Complete,
133
+ }
134
+ ```
135
+
136
+ ## When to Use
137
+
138
+ Use this rule when:
139
+
140
+ - You use the `@enumType` tag to document enum semantics
141
+ - You want to ensure consistent and valid `@enumType` usage
142
+ - You want to require `@enumType` on all exported enums (with `requireOnExported: true`)
143
+
144
+ ## When Not to Use
145
+
146
+ You might want to disable this rule if:
147
+
148
+ - You don't use the `@enumType` tag in your codebase
149
+ - You're not using the change detector that processes this tag
150
+
151
+ ## Related
152
+
153
+ - [Open/Closed Enum Semantics](https://github.com/mike-north/api-extractor-tools/issues/127) - Epic describing the enum type feature
package/package.json CHANGED
@@ -1,9 +1,19 @@
1
1
  {
2
2
  "name": "@api-extractor-tools/eslint-plugin",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.2",
4
4
  "description": "ESLint plugin providing authoring-time feedback for API Extractor",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/eslint-plugin-public.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/eslint-plugin-public.d.ts",
10
+ "default": "./dist/index.js"
11
+ },
12
+ "./node": {
13
+ "types": "./dist/node.d.ts",
14
+ "default": "./dist/node.js"
15
+ }
16
+ },
7
17
  "keywords": [
8
18
  "eslint",
9
19
  "eslint-plugin",
@@ -18,26 +28,30 @@
18
28
  "typescript": ">=5.5.0"
19
29
  },
20
30
  "dependencies": {
21
- "@microsoft/tsdoc": "^0.15.1",
22
- "@typescript-eslint/utils": "^8.48.1"
31
+ "@microsoft/tsdoc": "^0.15.1"
23
32
  },
24
33
  "devDependencies": {
25
34
  "@microsoft/api-extractor": "^7.55.1",
26
35
  "@types/node": "^22.10.2",
27
36
  "@typescript-eslint/parser": "^8.48.1",
28
37
  "@typescript-eslint/rule-tester": "^8.48.1",
38
+ "@typescript-eslint/utils": "^8.48.1",
39
+ "@vitest/coverage-v8": "^4.0.15",
29
40
  "eslint": "^9.39.1",
41
+ "tsd": "^0.33.0",
30
42
  "typescript": "5.8.3",
31
- "@vitest/coverage-v8": "^4.0.15",
32
- "vitest": "^4.0.15"
43
+ "vitest": "^4.0.15",
44
+ "@api-extractor-tools/declaration-file-normalizer": "0.1.0-alpha.6"
33
45
  },
34
46
  "scripts": {
35
- "build": "tsc",
47
+ "clean": "rm -rf dist",
48
+ "build": "tsc && node ../declaration-file-normalizer/dist/cli.js dist/index.d.ts",
36
49
  "generate:api-report": "api-extractor run --local --verbose",
37
- "test": "vitest run",
50
+ "test": "vitest run && pnpm test:types",
51
+ "test:types": "tsd --files 'test/**/*.test-d.ts' --typings src/index.ts",
38
52
  "test:coverage": "vitest run --coverage",
39
53
  "check": "pnpm check:eslint && pnpm check:typecheck-tests && pnpm check:api-report",
40
- "check:eslint": "eslint src",
54
+ "check:eslint": "eslint src test",
41
55
  "check:typecheck-tests": "tsc -p test/tsconfig.json",
42
56
  "check:api-report": "api-extractor run"
43
57
  }
@@ -13,12 +13,18 @@ import type { TSESLint } from '@typescript-eslint/utils'
13
13
  /**
14
14
  * Recommended rule configuration.
15
15
  * These are the rules enabled by default with appropriate severity.
16
- * @public
16
+ * @alpha
17
17
  */
18
18
  export const recommendedRules: TSESLint.Linter.RulesRecord = {
19
19
  '@api-extractor-tools/missing-release-tag': 'warn',
20
20
  '@api-extractor-tools/override-keyword': 'error',
21
21
  '@api-extractor-tools/package-documentation': 'warn',
22
+ '@api-extractor-tools/forgotten-export': 'warn',
23
+ '@api-extractor-tools/incompatible-release-tags': 'warn',
24
+ '@api-extractor-tools/extra-release-tag': 'error',
25
+ '@api-extractor-tools/public-on-private-member': 'error',
26
+ '@api-extractor-tools/public-on-non-exported': 'error',
27
+ '@api-extractor-tools/valid-enum-type': 'warn',
22
28
  }
23
29
 
24
30
  /**
package/src/index.ts CHANGED
@@ -1,10 +1,6 @@
1
1
  /**
2
2
  * ESLint plugin providing authoring-time feedback for API Extractor.
3
3
  *
4
- * @remarks
5
- * This plugin provides ESLint rules that mirror API Extractor's validations,
6
- * enabling developers to catch issues during development rather than at build time.
7
- *
8
4
  * @example
9
5
  * Using with flat config (eslint.config.js):
10
6
  * ```js
@@ -12,15 +8,6 @@
12
8
  *
13
9
  * export default [
14
10
  * apiExtractorPlugin.configs.recommended,
15
- * // Or configure rules individually:
16
- * {
17
- * plugins: {
18
- * '@api-extractor-tools': apiExtractorPlugin,
19
- * },
20
- * rules: {
21
- * '@api-extractor-tools/missing-release-tag': 'error',
22
- * },
23
- * },
24
11
  * ];
25
12
  * ```
26
13
  *
@@ -51,10 +38,29 @@ export type {
51
38
  OverrideKeywordRuleOptions,
52
39
  PackageDocumentationRuleOptions,
53
40
  ResolvedEntryPoints,
41
+ ValidEnumTypeRuleOptions,
54
42
  } from './types'
43
+ export type { ForgottenExportRuleOptions } from './rules/forgotten-export'
44
+ export type { IncompatibleReleaseTagsRuleOptions } from './rules/incompatible-release-tags'
45
+ export type { ExtraReleaseTagRuleOptions } from './rules/extra-release-tag'
46
+ export type { PublicOnPrivateMemberRuleOptions } from './rules/public-on-private-member'
47
+ export type { PublicOnNonExportedRuleOptions } from './rules/public-on-non-exported'
55
48
 
56
49
  export { RELEASE_TAGS } from './types'
57
50
 
51
+ // Re-export TSDoc utilities (browser-safe)
52
+ export {
53
+ parseTSDocComment,
54
+ extractReleaseTag,
55
+ hasOverrideTag,
56
+ hasPackageDocumentation,
57
+ getLeadingTSDocComment,
58
+ findAllTSDocComments,
59
+ extractEnumType,
60
+ type EnumTypeValue,
61
+ type EnumTypeExtraction,
62
+ } from './utils/tsdoc-parser'
63
+
58
64
  /**
59
65
  * Plugin configuration type.
60
66
  * @internal
@@ -69,7 +75,7 @@ interface PluginConfigs {
69
75
 
70
76
  /**
71
77
  * The ESLint plugin type.
72
- * @public
78
+ * @alpha
73
79
  */
74
80
  export interface ApiExtractorEslintPlugin {
75
81
  meta: {
@@ -82,7 +88,7 @@ export interface ApiExtractorEslintPlugin {
82
88
 
83
89
  /**
84
90
  * The ESLint plugin object.
85
- * @public
91
+ * @alpha
86
92
  */
87
93
  const plugin: ApiExtractorEslintPlugin = {
88
94
  meta: {
package/src/node.ts ADDED
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Node.js-specific utilities for the ESLint plugin.
3
+ *
4
+ * @remarks
5
+ * These utilities require Node.js file system access. Use them to:
6
+ * - Load api-extractor.json configuration
7
+ * - Discover configuration file locations
8
+ * - Determine package entry points
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import {
13
+ * findApiExtractorConfig,
14
+ * loadApiExtractorConfig,
15
+ * getMessageLogLevel,
16
+ * } from '@api-extractor-tools/eslint-plugin/node';
17
+ *
18
+ * const configPath = findApiExtractorConfig(__dirname);
19
+ * const config = configPath ? loadApiExtractorConfig(configPath) : null;
20
+ * const severity = getMessageLogLevel(config, 'ae-missing-release-tag');
21
+ * ```
22
+ *
23
+ * @packageDocumentation
24
+ */
25
+
26
+ export {
27
+ findApiExtractorConfig,
28
+ loadApiExtractorConfig,
29
+ resolveConfig,
30
+ getMessageLogLevel,
31
+ logLevelToSeverity,
32
+ clearConfigCache,
33
+ } from './utils/config-loader'
34
+
35
+ export {
36
+ findPackageJson,
37
+ loadPackageJson,
38
+ resolveEntryPoints,
39
+ isEntryPoint,
40
+ clearPackageJsonCache,
41
+ } from './utils/entry-point'
42
+
43
+ // Re-export types that are useful for Node.js consumers
44
+ export type {
45
+ ApiExtractorConfig,
46
+ ApiExtractorLogLevel,
47
+ ApiExtractorMessagesConfig,
48
+ MessageConfig,
49
+ ResolvedEntryPoints,
50
+ } from './types'
@@ -0,0 +1,201 @@
1
+ /**
2
+ * ESLint rule for detecting multiple release tags on a single symbol.
3
+ *
4
+ * @remarks
5
+ * This rule detects when a symbol has more than one release tag
6
+ * (e.g., both `@public` and `@beta`). Each symbol should have exactly one release tag.
7
+ *
8
+ * @internal
9
+ */
10
+
11
+ import { AST_NODE_TYPES, ESLintUtils, TSESTree } from '@typescript-eslint/utils'
12
+ import {
13
+ getLeadingTSDocComment,
14
+ parseTSDocComment,
15
+ } from '../utils/tsdoc-parser'
16
+ import type { ApiExtractorLogLevel } from '../types'
17
+
18
+ const createRule = ESLintUtils.RuleCreator(
19
+ (name) =>
20
+ `https://github.com/mike-north/api-extractor-tools/blob/main/tools/eslint-plugin/docs/rules/${name}.md`,
21
+ )
22
+
23
+ type MessageIds = 'extraReleaseTag'
24
+
25
+ /**
26
+ * Options for the extra-release-tag rule.
27
+ * @alpha
28
+ */
29
+ export interface ExtraReleaseTagRuleOptions {
30
+ /**
31
+ * Severity level for extra release tags.
32
+ * @defaultValue 'error'
33
+ */
34
+ severity?: ApiExtractorLogLevel
35
+ }
36
+
37
+ export const extraReleaseTag = createRule<
38
+ [ExtraReleaseTagRuleOptions],
39
+ MessageIds
40
+ >({
41
+ name: 'extra-release-tag',
42
+ meta: {
43
+ type: 'problem',
44
+ docs: {
45
+ description:
46
+ 'Require that symbols have at most one release tag (@public, @beta, @alpha, or @internal)',
47
+ },
48
+ messages: {
49
+ extraReleaseTag:
50
+ 'Symbol "{{name}}" has multiple release tags: {{tags}}. Each symbol should have exactly one release tag.',
51
+ },
52
+ schema: [
53
+ {
54
+ type: 'object',
55
+ properties: {
56
+ severity: {
57
+ type: 'string',
58
+ enum: ['error', 'warning', 'none'],
59
+ description: 'Severity level for extra release tags',
60
+ },
61
+ },
62
+ additionalProperties: false,
63
+ },
64
+ ],
65
+ },
66
+ defaultOptions: [{}],
67
+ create(context) {
68
+ const options = context.options[0] ?? {}
69
+ const severity = options.severity ?? 'error'
70
+
71
+ // If severity is 'none', disable the rule
72
+ if (severity === 'none') {
73
+ return {}
74
+ }
75
+
76
+ const sourceCode = context.sourceCode
77
+
78
+ /**
79
+ * Counts release tags in a TSDoc comment.
80
+ */
81
+ function countReleaseTags(node: TSESTree.Node): {
82
+ count: number
83
+ tags: string[]
84
+ } {
85
+ const commentText = getLeadingTSDocComment(sourceCode, node)
86
+ if (!commentText) {
87
+ return { count: 0, tags: [] }
88
+ }
89
+
90
+ const parsed = parseTSDocComment(commentText)
91
+ if (!parsed.docComment) {
92
+ return { count: 0, tags: [] }
93
+ }
94
+
95
+ const modifierTagSet = parsed.docComment.modifierTagSet
96
+ const tags: string[] = []
97
+
98
+ if (modifierTagSet.isPublic()) {
99
+ tags.push('@public')
100
+ }
101
+ if (modifierTagSet.isBeta()) {
102
+ tags.push('@beta')
103
+ }
104
+ if (modifierTagSet.isAlpha()) {
105
+ tags.push('@alpha')
106
+ }
107
+ if (modifierTagSet.isInternal()) {
108
+ tags.push('@internal')
109
+ }
110
+
111
+ return { count: tags.length, tags }
112
+ }
113
+
114
+ /**
115
+ * Gets the name of a declaration.
116
+ */
117
+ function getDeclarationName(
118
+ node:
119
+ | TSESTree.FunctionDeclaration
120
+ | TSESTree.ClassDeclaration
121
+ | TSESTree.TSInterfaceDeclaration
122
+ | TSESTree.TSTypeAliasDeclaration
123
+ | TSESTree.TSEnumDeclaration
124
+ | TSESTree.VariableDeclaration,
125
+ ): string {
126
+ if (node.type === AST_NODE_TYPES.VariableDeclaration) {
127
+ const firstDeclarator = node.declarations[0]
128
+ if (firstDeclarator?.id.type === AST_NODE_TYPES.Identifier) {
129
+ return firstDeclarator.id.name
130
+ }
131
+ return '<unknown>'
132
+ }
133
+
134
+ if ('id' in node && node.id) {
135
+ return node.id.name
136
+ }
137
+
138
+ return '<anonymous>'
139
+ }
140
+
141
+ /**
142
+ * Checks a declaration for multiple release tags.
143
+ */
144
+ function checkDeclaration(
145
+ node:
146
+ | TSESTree.FunctionDeclaration
147
+ | TSESTree.ClassDeclaration
148
+ | TSESTree.TSInterfaceDeclaration
149
+ | TSESTree.TSTypeAliasDeclaration
150
+ | TSESTree.TSEnumDeclaration
151
+ | TSESTree.VariableDeclaration,
152
+ ): void {
153
+ // Check both the export statement and the declaration for the comment
154
+ const exportNode = node.parent
155
+ let releaseTagInfo = countReleaseTags(node)
156
+
157
+ // If no tags on the declaration, check the export statement
158
+ if (
159
+ releaseTagInfo.count === 0 &&
160
+ exportNode &&
161
+ (exportNode.type === AST_NODE_TYPES.ExportNamedDeclaration ||
162
+ exportNode.type === AST_NODE_TYPES.ExportDefaultDeclaration)
163
+ ) {
164
+ releaseTagInfo = countReleaseTags(exportNode)
165
+ }
166
+
167
+ if (releaseTagInfo.count > 1) {
168
+ const name = getDeclarationName(node)
169
+ context.report({
170
+ node,
171
+ messageId: 'extraReleaseTag',
172
+ data: {
173
+ name,
174
+ tags: releaseTagInfo.tags.join(', '),
175
+ },
176
+ })
177
+ }
178
+ }
179
+
180
+ return {
181
+ FunctionDeclaration(node): void {
182
+ checkDeclaration(node)
183
+ },
184
+ ClassDeclaration(node): void {
185
+ checkDeclaration(node)
186
+ },
187
+ TSInterfaceDeclaration(node): void {
188
+ checkDeclaration(node)
189
+ },
190
+ TSTypeAliasDeclaration(node): void {
191
+ checkDeclaration(node)
192
+ },
193
+ TSEnumDeclaration(node): void {
194
+ checkDeclaration(node)
195
+ },
196
+ VariableDeclaration(node): void {
197
+ checkDeclaration(node)
198
+ },
199
+ }
200
+ },
201
+ })