@api-extractor-tools/eslint-plugin 0.1.0-alpha.0 → 0.1.0-alpha.1
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/ARCHITECTURE.md +201 -0
- package/CHANGELOG.md +24 -0
- package/README.md +306 -10
- package/api-extractor.json +1 -0
- package/dist/configs/recommended.d.ts +1 -1
- package/dist/configs/recommended.d.ts.map +1 -1
- package/dist/configs/recommended.js +7 -1
- package/dist/configs/recommended.js.map +1 -1
- package/dist/index.d.ts +9 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -15
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +28 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +41 -0
- package/dist/node.js.map +1 -0
- package/dist/rules/extra-release-tag.d.ts +24 -0
- package/dist/rules/extra-release-tag.d.ts.map +1 -0
- package/dist/rules/extra-release-tag.js +141 -0
- package/dist/rules/extra-release-tag.js.map +1 -0
- package/dist/rules/forgotten-export.d.ts +24 -0
- package/dist/rules/forgotten-export.d.ts.map +1 -0
- package/dist/rules/forgotten-export.js +212 -0
- package/dist/rules/forgotten-export.js.map +1 -0
- package/dist/rules/incompatible-release-tags.d.ts +25 -0
- package/dist/rules/incompatible-release-tags.d.ts.map +1 -0
- package/dist/rules/incompatible-release-tags.js +237 -0
- package/dist/rules/incompatible-release-tags.js.map +1 -0
- package/dist/rules/index.d.ts +2 -6
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +13 -1
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/missing-release-tag.d.ts +4 -0
- package/dist/rules/missing-release-tag.d.ts.map +1 -1
- package/dist/rules/missing-release-tag.js +14 -21
- package/dist/rules/missing-release-tag.js.map +1 -1
- package/dist/rules/override-keyword.d.ts +4 -0
- package/dist/rules/override-keyword.d.ts.map +1 -1
- package/dist/rules/override-keyword.js +9 -11
- package/dist/rules/override-keyword.js.map +1 -1
- package/dist/rules/package-documentation.d.ts +1 -1
- package/dist/rules/package-documentation.d.ts.map +1 -1
- package/dist/rules/package-documentation.js +7 -28
- package/dist/rules/package-documentation.js.map +1 -1
- package/dist/rules/public-on-non-exported.d.ts +24 -0
- package/dist/rules/public-on-non-exported.d.ts.map +1 -0
- package/dist/rules/public-on-non-exported.js +191 -0
- package/dist/rules/public-on-non-exported.js.map +1 -0
- package/dist/rules/public-on-private-member.d.ts +24 -0
- package/dist/rules/public-on-private-member.d.ts.map +1 -0
- package/dist/rules/public-on-private-member.js +111 -0
- package/dist/rules/public-on-private-member.js.map +1 -0
- package/dist/rules/valid-enum-type.d.ts +17 -0
- package/dist/rules/valid-enum-type.d.ts.map +1 -0
- package/dist/rules/valid-enum-type.js +206 -0
- package/dist/rules/valid-enum-type.js.map +1 -0
- package/dist/types.d.ts +63 -35
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/tsdoc-parser.d.ts +35 -0
- package/dist/utils/tsdoc-parser.d.ts.map +1 -1
- package/dist/utils/tsdoc-parser.js +40 -0
- package/dist/utils/tsdoc-parser.js.map +1 -1
- package/docs/rules/valid-enum-type.md +153 -0
- package/package.json +22 -8
- package/src/configs/recommended.ts +7 -1
- package/src/index.ts +21 -15
- package/src/node.ts +50 -0
- package/src/rules/extra-release-tag.ts +201 -0
- package/src/rules/forgotten-export.ts +274 -0
- package/src/rules/incompatible-release-tags.ts +331 -0
- package/src/rules/index.ts +13 -1
- package/src/rules/missing-release-tag.ts +11 -26
- package/src/rules/override-keyword.ts +6 -8
- package/src/rules/package-documentation.ts +5 -31
- package/src/rules/public-on-non-exported.ts +265 -0
- package/src/rules/public-on-private-member.ts +157 -0
- package/src/rules/valid-enum-type.ts +252 -0
- package/src/types.ts +60 -17
- package/src/utils/config-loader.ts +1 -0
- package/src/utils/entry-point.ts +1 -0
- package/src/utils/tsdoc-parser.ts +67 -0
- package/temp/eslint-plugin.api.md +96 -47
- package/test/index.test.ts +1 -0
- package/test/rules/extra-release-tag.test.ts +276 -0
- package/test/rules/forgotten-export.test.ts +190 -0
- package/test/rules/incompatible-release-tags.test.ts +340 -0
- package/test/rules/missing-release-tag.test.ts +2 -1
- package/test/rules/override-keyword.test.ts +2 -1
- package/test/rules/package-documentation.test.ts +8 -6
- package/test/rules/public-on-non-exported.test.ts +201 -0
- package/test/rules/public-on-private-member.test.ts +207 -0
- package/test/rules/valid-enum-type.test.ts +409 -0
- package/test/types.test-d.ts +20 -0
- package/test/utils/config-loader.test.ts +1 -0
- package/test/utils/tsdoc-parser.test.ts +117 -9
- package/tsconfig.json +1 -0
- package/vitest.config.mts +1 -0
- package/dist/utils/index.d.ts +0 -8
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -21
- package/dist/utils/index.js.map +0 -1
- package/src/utils/index.ts +0 -17
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint rule for detecting incompatible release tags.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* This rule detects when an exported API with a specific release tag references
|
|
6
|
+
* another symbol with a less visible release tag. For example, a @public API
|
|
7
|
+
* should not reference an @internal type.
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { AST_NODE_TYPES, ESLintUtils, TSESTree } from '@typescript-eslint/utils'
|
|
13
|
+
import {
|
|
14
|
+
getLeadingTSDocComment,
|
|
15
|
+
parseTSDocComment,
|
|
16
|
+
extractReleaseTag,
|
|
17
|
+
} from '../utils/tsdoc-parser'
|
|
18
|
+
import type { ApiExtractorLogLevel, ReleaseTag } from '../types'
|
|
19
|
+
|
|
20
|
+
const createRule = ESLintUtils.RuleCreator(
|
|
21
|
+
(name) =>
|
|
22
|
+
`https://github.com/mike-north/api-extractor-tools/blob/main/tools/eslint-plugin/docs/rules/${name}.md`,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
type MessageIds = 'incompatibleReleaseTags'
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Options for the incompatible-release-tags rule.
|
|
29
|
+
* @alpha
|
|
30
|
+
*/
|
|
31
|
+
export interface IncompatibleReleaseTagsRuleOptions {
|
|
32
|
+
/**
|
|
33
|
+
* Severity level for incompatible release tags.
|
|
34
|
+
* @defaultValue 'warning'
|
|
35
|
+
*/
|
|
36
|
+
severity?: ApiExtractorLogLevel
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Release tag visibility levels (higher = more visible)
|
|
40
|
+
const RELEASE_TAG_LEVELS: Record<ReleaseTag, number> = {
|
|
41
|
+
internal: 0,
|
|
42
|
+
alpha: 1,
|
|
43
|
+
beta: 2,
|
|
44
|
+
public: 3,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const incompatibleReleaseTags = createRule<
|
|
48
|
+
[IncompatibleReleaseTagsRuleOptions],
|
|
49
|
+
MessageIds
|
|
50
|
+
>({
|
|
51
|
+
name: 'incompatible-release-tags',
|
|
52
|
+
meta: {
|
|
53
|
+
type: 'problem',
|
|
54
|
+
docs: {
|
|
55
|
+
description:
|
|
56
|
+
'Require that exported APIs do not reference symbols with less visible release tags',
|
|
57
|
+
},
|
|
58
|
+
messages: {
|
|
59
|
+
incompatibleReleaseTags:
|
|
60
|
+
'The {{exportedTag}} API "{{exportedName}}" references the {{referencedTag}} symbol "{{referencedName}}". Referenced symbols must be at least as visible as the API that uses them.',
|
|
61
|
+
},
|
|
62
|
+
schema: [
|
|
63
|
+
{
|
|
64
|
+
type: 'object',
|
|
65
|
+
properties: {
|
|
66
|
+
severity: {
|
|
67
|
+
type: 'string',
|
|
68
|
+
enum: ['error', 'warning', 'none'],
|
|
69
|
+
description: 'Severity level for incompatible release tags',
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
additionalProperties: false,
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
defaultOptions: [{}],
|
|
77
|
+
create(context) {
|
|
78
|
+
const options = context.options[0] ?? {}
|
|
79
|
+
const severity = options.severity ?? 'warning'
|
|
80
|
+
|
|
81
|
+
// If severity is 'none', disable the rule
|
|
82
|
+
if (severity === 'none') {
|
|
83
|
+
return {}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const sourceCode = context.sourceCode
|
|
87
|
+
const symbolReleaseTags = new Map<string, ReleaseTag>()
|
|
88
|
+
const exportedSymbols = new Map<
|
|
89
|
+
string,
|
|
90
|
+
{ tag: ReleaseTag; node: TSESTree.Node }
|
|
91
|
+
>()
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Gets the release tag for a node.
|
|
95
|
+
*/
|
|
96
|
+
function getReleaseTag(node: TSESTree.Node): ReleaseTag | undefined {
|
|
97
|
+
const commentText = getLeadingTSDocComment(sourceCode, node)
|
|
98
|
+
if (!commentText) {
|
|
99
|
+
return undefined
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const parsed = parseTSDocComment(commentText)
|
|
103
|
+
if (parsed.docComment) {
|
|
104
|
+
return extractReleaseTag(parsed.docComment)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return undefined
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Gets the name of a declaration.
|
|
112
|
+
*/
|
|
113
|
+
function getDeclarationName(
|
|
114
|
+
node:
|
|
115
|
+
| TSESTree.FunctionDeclaration
|
|
116
|
+
| TSESTree.ClassDeclaration
|
|
117
|
+
| TSESTree.TSInterfaceDeclaration
|
|
118
|
+
| TSESTree.TSTypeAliasDeclaration
|
|
119
|
+
| TSESTree.TSEnumDeclaration
|
|
120
|
+
| TSESTree.VariableDeclaration,
|
|
121
|
+
): string | undefined {
|
|
122
|
+
if (node.type === AST_NODE_TYPES.VariableDeclaration) {
|
|
123
|
+
const firstDeclarator = node.declarations[0]
|
|
124
|
+
if (firstDeclarator?.id.type === AST_NODE_TYPES.Identifier) {
|
|
125
|
+
return firstDeclarator.id.name
|
|
126
|
+
}
|
|
127
|
+
return undefined
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if ('id' in node && node.id) {
|
|
131
|
+
return node.id.name
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return undefined
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Checks if a release tag is compatible.
|
|
139
|
+
*/
|
|
140
|
+
function isCompatible(
|
|
141
|
+
exportedTag: ReleaseTag,
|
|
142
|
+
referencedTag: ReleaseTag,
|
|
143
|
+
): boolean {
|
|
144
|
+
return (
|
|
145
|
+
RELEASE_TAG_LEVELS[referencedTag] >= RELEASE_TAG_LEVELS[exportedTag]
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Collects release tags for all declarations.
|
|
151
|
+
*/
|
|
152
|
+
function collectSymbolReleaseTag(
|
|
153
|
+
node:
|
|
154
|
+
| TSESTree.FunctionDeclaration
|
|
155
|
+
| TSESTree.ClassDeclaration
|
|
156
|
+
| TSESTree.TSInterfaceDeclaration
|
|
157
|
+
| TSESTree.TSTypeAliasDeclaration
|
|
158
|
+
| TSESTree.TSEnumDeclaration
|
|
159
|
+
| TSESTree.VariableDeclaration,
|
|
160
|
+
): void {
|
|
161
|
+
const name = getDeclarationName(node)
|
|
162
|
+
if (!name) return
|
|
163
|
+
|
|
164
|
+
const tag = getReleaseTag(node)
|
|
165
|
+
if (tag) {
|
|
166
|
+
symbolReleaseTags.set(name, tag)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Checks if a node is exported.
|
|
172
|
+
*/
|
|
173
|
+
function isExported(node: TSESTree.Node): boolean {
|
|
174
|
+
const parent = node.parent
|
|
175
|
+
return (
|
|
176
|
+
parent?.type === AST_NODE_TYPES.ExportNamedDeclaration ||
|
|
177
|
+
parent?.type === AST_NODE_TYPES.ExportDefaultDeclaration
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Checks type references for incompatible release tags.
|
|
183
|
+
*/
|
|
184
|
+
function checkTypeReferences(
|
|
185
|
+
node: TSESTree.Node,
|
|
186
|
+
exportedName: string,
|
|
187
|
+
exportedTag: ReleaseTag,
|
|
188
|
+
): void {
|
|
189
|
+
const visited = new WeakSet<object>()
|
|
190
|
+
|
|
191
|
+
// Helper to recursively find type references
|
|
192
|
+
function findTypeReferences(n: TSESTree.Node): void {
|
|
193
|
+
// Avoid infinite recursion
|
|
194
|
+
if (visited.has(n)) {
|
|
195
|
+
return
|
|
196
|
+
}
|
|
197
|
+
visited.add(n)
|
|
198
|
+
|
|
199
|
+
if (n.type === AST_NODE_TYPES.TSTypeReference) {
|
|
200
|
+
const typeName = n.typeName
|
|
201
|
+
if (typeName.type === AST_NODE_TYPES.Identifier) {
|
|
202
|
+
const referencedName = typeName.name
|
|
203
|
+
const referencedTag = symbolReleaseTags.get(referencedName)
|
|
204
|
+
|
|
205
|
+
if (referencedTag && !isCompatible(exportedTag, referencedTag)) {
|
|
206
|
+
context.report({
|
|
207
|
+
node: n,
|
|
208
|
+
messageId: 'incompatibleReleaseTags',
|
|
209
|
+
data: {
|
|
210
|
+
exportedName,
|
|
211
|
+
exportedTag: `@${exportedTag}`,
|
|
212
|
+
referencedName,
|
|
213
|
+
referencedTag: `@${referencedTag}`,
|
|
214
|
+
},
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Recursively check child nodes, but skip 'parent' property to avoid cycles
|
|
221
|
+
for (const key in n) {
|
|
222
|
+
if (key === 'parent') {
|
|
223
|
+
continue
|
|
224
|
+
}
|
|
225
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
|
|
226
|
+
const child = (n as any)[key]
|
|
227
|
+
if (child && typeof child === 'object') {
|
|
228
|
+
if (Array.isArray(child)) {
|
|
229
|
+
for (const item of child) {
|
|
230
|
+
if (item && typeof item === 'object' && 'type' in item) {
|
|
231
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
232
|
+
findTypeReferences(item)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
} else if ('type' in child) {
|
|
236
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
237
|
+
findTypeReferences(child)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
findTypeReferences(node)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Checks a declaration for incompatible release tags.
|
|
248
|
+
*/
|
|
249
|
+
function checkDeclaration(
|
|
250
|
+
node:
|
|
251
|
+
| TSESTree.FunctionDeclaration
|
|
252
|
+
| TSESTree.ClassDeclaration
|
|
253
|
+
| TSESTree.TSInterfaceDeclaration
|
|
254
|
+
| TSESTree.TSTypeAliasDeclaration
|
|
255
|
+
| TSESTree.TSEnumDeclaration
|
|
256
|
+
| TSESTree.VariableDeclaration,
|
|
257
|
+
): void {
|
|
258
|
+
if (!isExported(node)) {
|
|
259
|
+
return
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const name = getDeclarationName(node)
|
|
263
|
+
if (!name) return
|
|
264
|
+
|
|
265
|
+
// Check both the export statement and the declaration for the comment
|
|
266
|
+
const exportNode = node.parent
|
|
267
|
+
const nodeToCheck = exportNode ?? node
|
|
268
|
+
|
|
269
|
+
const tag = getReleaseTag(nodeToCheck) ?? getReleaseTag(node)
|
|
270
|
+
if (tag) {
|
|
271
|
+
exportedSymbols.set(name, { tag, node })
|
|
272
|
+
checkTypeReferences(node, name, tag)
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const declarationsToCheck: Array<{
|
|
277
|
+
node:
|
|
278
|
+
| TSESTree.FunctionDeclaration
|
|
279
|
+
| TSESTree.ClassDeclaration
|
|
280
|
+
| TSESTree.TSInterfaceDeclaration
|
|
281
|
+
| TSESTree.TSTypeAliasDeclaration
|
|
282
|
+
| TSESTree.TSEnumDeclaration
|
|
283
|
+
| TSESTree.VariableDeclaration
|
|
284
|
+
}> = []
|
|
285
|
+
|
|
286
|
+
return {
|
|
287
|
+
FunctionDeclaration(node): void {
|
|
288
|
+
collectSymbolReleaseTag(node)
|
|
289
|
+
if (isExported(node)) {
|
|
290
|
+
declarationsToCheck.push({ node })
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
ClassDeclaration(node): void {
|
|
294
|
+
collectSymbolReleaseTag(node)
|
|
295
|
+
if (isExported(node)) {
|
|
296
|
+
declarationsToCheck.push({ node })
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
TSInterfaceDeclaration(node): void {
|
|
300
|
+
collectSymbolReleaseTag(node)
|
|
301
|
+
if (isExported(node)) {
|
|
302
|
+
declarationsToCheck.push({ node })
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
TSTypeAliasDeclaration(node): void {
|
|
306
|
+
collectSymbolReleaseTag(node)
|
|
307
|
+
if (isExported(node)) {
|
|
308
|
+
declarationsToCheck.push({ node })
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
TSEnumDeclaration(node): void {
|
|
312
|
+
collectSymbolReleaseTag(node)
|
|
313
|
+
if (isExported(node)) {
|
|
314
|
+
declarationsToCheck.push({ node })
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
VariableDeclaration(node): void {
|
|
318
|
+
collectSymbolReleaseTag(node)
|
|
319
|
+
if (isExported(node)) {
|
|
320
|
+
declarationsToCheck.push({ node })
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
'Program:exit'(): void {
|
|
324
|
+
// Check all exported declarations after collecting all tags
|
|
325
|
+
for (const { node } of declarationsToCheck) {
|
|
326
|
+
checkDeclaration(node)
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
})
|
package/src/rules/index.ts
CHANGED
|
@@ -6,13 +6,25 @@
|
|
|
6
6
|
import { missingReleaseTag } from './missing-release-tag'
|
|
7
7
|
import { overrideKeyword } from './override-keyword'
|
|
8
8
|
import { packageDocumentation } from './package-documentation'
|
|
9
|
+
import { forgottenExport } from './forgotten-export'
|
|
10
|
+
import { incompatibleReleaseTags } from './incompatible-release-tags'
|
|
11
|
+
import { extraReleaseTag } from './extra-release-tag'
|
|
12
|
+
import { publicOnPrivateMember } from './public-on-private-member'
|
|
13
|
+
import { publicOnNonExported } from './public-on-non-exported'
|
|
14
|
+
import { validEnumType } from './valid-enum-type'
|
|
9
15
|
|
|
10
16
|
/**
|
|
11
17
|
* All available ESLint rules.
|
|
12
|
-
* @
|
|
18
|
+
* @alpha
|
|
13
19
|
*/
|
|
14
20
|
export const rules = {
|
|
15
21
|
'missing-release-tag': missingReleaseTag,
|
|
16
22
|
'override-keyword': overrideKeyword,
|
|
17
23
|
'package-documentation': packageDocumentation,
|
|
24
|
+
'forgotten-export': forgottenExport,
|
|
25
|
+
'incompatible-release-tags': incompatibleReleaseTags,
|
|
26
|
+
'extra-release-tag': extraReleaseTag,
|
|
27
|
+
'public-on-private-member': publicOnPrivateMember,
|
|
28
|
+
'public-on-non-exported': publicOnNonExported,
|
|
29
|
+
'valid-enum-type': validEnumType,
|
|
18
30
|
} as const
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ESLint rule for detecting missing release tags on exported symbols.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Configuration is provided explicitly via rule options.
|
|
6
|
+
*
|
|
3
7
|
* @internal
|
|
4
8
|
*/
|
|
5
9
|
|
|
6
10
|
import { AST_NODE_TYPES, ESLintUtils, TSESTree } from '@typescript-eslint/utils'
|
|
7
11
|
import {
|
|
8
|
-
resolveConfig,
|
|
9
|
-
getMessageLogLevel,
|
|
10
12
|
getLeadingTSDocComment,
|
|
11
13
|
parseTSDocComment,
|
|
12
14
|
extractReleaseTag,
|
|
13
|
-
} from '../utils'
|
|
15
|
+
} from '../utils/tsdoc-parser'
|
|
14
16
|
import type { MissingReleaseTagRuleOptions } from '../types'
|
|
15
17
|
|
|
16
18
|
const createRule = ESLintUtils.RuleCreator(
|
|
@@ -39,9 +41,10 @@ export const missingReleaseTag = createRule<
|
|
|
39
41
|
{
|
|
40
42
|
type: 'object',
|
|
41
43
|
properties: {
|
|
42
|
-
|
|
44
|
+
severity: {
|
|
43
45
|
type: 'string',
|
|
44
|
-
|
|
46
|
+
enum: ['error', 'warning', 'none'],
|
|
47
|
+
description: 'Severity level for missing release tags',
|
|
45
48
|
},
|
|
46
49
|
},
|
|
47
50
|
additionalProperties: false,
|
|
@@ -51,12 +54,10 @@ export const missingReleaseTag = createRule<
|
|
|
51
54
|
defaultOptions: [{}],
|
|
52
55
|
create(context) {
|
|
53
56
|
const options = context.options[0] ?? {}
|
|
54
|
-
const
|
|
55
|
-
const config = resolveConfig(filename, options.configPath)
|
|
56
|
-
const logLevel = getMessageLogLevel(config, 'ae-missing-release-tag')
|
|
57
|
+
const severity = options.severity ?? 'warning'
|
|
57
58
|
|
|
58
|
-
// If
|
|
59
|
-
if (
|
|
59
|
+
// If severity is 'none', disable the rule
|
|
60
|
+
if (severity === 'none') {
|
|
60
61
|
return {}
|
|
61
62
|
}
|
|
62
63
|
|
|
@@ -164,40 +165,24 @@ export const missingReleaseTag = createRule<
|
|
|
164
165
|
}
|
|
165
166
|
|
|
166
167
|
return {
|
|
167
|
-
// Check exported function declarations
|
|
168
168
|
FunctionDeclaration(node): void {
|
|
169
169
|
checkDeclaration(node)
|
|
170
170
|
},
|
|
171
|
-
|
|
172
|
-
// Check exported class declarations
|
|
173
171
|
ClassDeclaration(node): void {
|
|
174
172
|
checkDeclaration(node)
|
|
175
173
|
},
|
|
176
|
-
|
|
177
|
-
// Check exported interface declarations
|
|
178
174
|
TSInterfaceDeclaration(node): void {
|
|
179
175
|
checkDeclaration(node)
|
|
180
176
|
},
|
|
181
|
-
|
|
182
|
-
// Check exported type alias declarations
|
|
183
177
|
TSTypeAliasDeclaration(node): void {
|
|
184
178
|
checkDeclaration(node)
|
|
185
179
|
},
|
|
186
|
-
|
|
187
|
-
// Check exported enum declarations
|
|
188
180
|
TSEnumDeclaration(node): void {
|
|
189
181
|
checkDeclaration(node)
|
|
190
182
|
},
|
|
191
|
-
|
|
192
|
-
// Check exported variable declarations
|
|
193
183
|
VariableDeclaration(node): void {
|
|
194
184
|
checkDeclaration(node)
|
|
195
185
|
},
|
|
196
|
-
|
|
197
|
-
// Note: We intentionally don't handle ExportNamedDeclaration with specifiers
|
|
198
|
-
// (e.g., `export { foo }` or `export { foo } from './bar'`) because:
|
|
199
|
-
// - Re-exports should have release tags on the original declaration
|
|
200
|
-
// - Named exports reference declarations that are checked elsewhere
|
|
201
186
|
}
|
|
202
187
|
},
|
|
203
188
|
})
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ESLint rule requiring the TypeScript `override` keyword when `@override` TSDoc tag is used.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* It is purely syntactic and requires no configuration.
|
|
6
|
+
*
|
|
3
7
|
* @internal
|
|
4
8
|
*/
|
|
5
9
|
|
|
@@ -8,7 +12,7 @@ import {
|
|
|
8
12
|
getLeadingTSDocComment,
|
|
9
13
|
parseTSDocComment,
|
|
10
14
|
hasOverrideTag,
|
|
11
|
-
} from '../utils'
|
|
15
|
+
} from '../utils/tsdoc-parser'
|
|
12
16
|
import type { OverrideKeywordRuleOptions } from '../types'
|
|
13
17
|
|
|
14
18
|
const createRule = ESLintUtils.RuleCreator(
|
|
@@ -37,12 +41,7 @@ export const overrideKeyword = createRule<
|
|
|
37
41
|
schema: [
|
|
38
42
|
{
|
|
39
43
|
type: 'object',
|
|
40
|
-
properties: {
|
|
41
|
-
configPath: {
|
|
42
|
-
type: 'string',
|
|
43
|
-
description: 'Path to api-extractor.json configuration file',
|
|
44
|
-
},
|
|
45
|
-
},
|
|
44
|
+
properties: {},
|
|
46
45
|
additionalProperties: false,
|
|
47
46
|
},
|
|
48
47
|
],
|
|
@@ -121,7 +120,6 @@ export const overrideKeyword = createRule<
|
|
|
121
120
|
messageId: 'missingOverrideKeyword',
|
|
122
121
|
data: { name },
|
|
123
122
|
fix(fixer) {
|
|
124
|
-
// Insert override keyword at the start of the member declaration
|
|
125
123
|
return fixer.insertTextBefore(node, 'override ')
|
|
126
124
|
},
|
|
127
125
|
})
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ESLint rule requiring @packageDocumentation in
|
|
2
|
+
* ESLint rule requiring @packageDocumentation in files.
|
|
3
3
|
* @internal
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { ESLintUtils } from '@typescript-eslint/utils'
|
|
7
7
|
import {
|
|
8
|
-
findPackageJson,
|
|
9
|
-
isEntryPoint,
|
|
10
8
|
findAllTSDocComments,
|
|
11
9
|
hasPackageDocumentation,
|
|
12
|
-
} from '../utils'
|
|
10
|
+
} from '../utils/tsdoc-parser'
|
|
13
11
|
import type { PackageDocumentationRuleOptions } from '../types'
|
|
14
12
|
|
|
15
13
|
const createRule = ESLintUtils.RuleCreator(
|
|
@@ -27,58 +25,34 @@ export const packageDocumentation = createRule<
|
|
|
27
25
|
meta: {
|
|
28
26
|
type: 'suggestion',
|
|
29
27
|
docs: {
|
|
30
|
-
description:
|
|
31
|
-
'Require @packageDocumentation tag in package entry point files',
|
|
28
|
+
description: 'Require @packageDocumentation tag in files',
|
|
32
29
|
},
|
|
33
30
|
messages: {
|
|
34
31
|
missingPackageDocumentation:
|
|
35
|
-
'
|
|
32
|
+
'File is missing a @packageDocumentation comment. Add a TSDoc comment with @packageDocumentation at the top of the file.',
|
|
36
33
|
},
|
|
37
34
|
schema: [
|
|
38
35
|
{
|
|
39
36
|
type: 'object',
|
|
40
|
-
properties: {
|
|
41
|
-
configPath: {
|
|
42
|
-
type: 'string',
|
|
43
|
-
description: 'Path to api-extractor.json configuration file',
|
|
44
|
-
},
|
|
45
|
-
},
|
|
37
|
+
properties: {},
|
|
46
38
|
additionalProperties: false,
|
|
47
39
|
},
|
|
48
40
|
],
|
|
49
41
|
},
|
|
50
42
|
defaultOptions: [{}],
|
|
51
43
|
create(context) {
|
|
52
|
-
const filename = context.filename
|
|
53
44
|
const sourceCode = context.sourceCode
|
|
54
45
|
|
|
55
|
-
// Find package.json
|
|
56
|
-
const pkgPath = findPackageJson(filename)
|
|
57
|
-
if (!pkgPath) {
|
|
58
|
-
// No package.json found, nothing to check
|
|
59
|
-
return {}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Check if this file is an entry point
|
|
63
|
-
if (!isEntryPoint(filename, pkgPath)) {
|
|
64
|
-
// Not an entry point, skip
|
|
65
|
-
return {}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
46
|
return {
|
|
69
47
|
Program(node): void {
|
|
70
|
-
// Look for @packageDocumentation in any TSDoc comment in the file
|
|
71
48
|
const tsdocComments = findAllTSDocComments(sourceCode)
|
|
72
49
|
|
|
73
50
|
for (const { parsed } of tsdocComments) {
|
|
74
51
|
if (parsed.docComment && hasPackageDocumentation(parsed.docComment)) {
|
|
75
|
-
// Found @packageDocumentation, all good
|
|
76
52
|
return
|
|
77
53
|
}
|
|
78
54
|
}
|
|
79
55
|
|
|
80
|
-
// No @packageDocumentation found
|
|
81
|
-
// Report at the first line of the file
|
|
82
56
|
context.report({
|
|
83
57
|
node,
|
|
84
58
|
loc: { line: 1, column: 0 },
|