@api-extractor-tools/eslint-plugin 0.1.0-alpha.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/CHANGELOG.md +18 -0
- package/LICENSE +21 -0
- package/README.md +183 -0
- package/api-extractor.json +10 -0
- package/dist/configs/index.d.ts +6 -0
- package/dist/configs/index.d.ts.map +1 -0
- package/dist/configs/index.js +11 -0
- package/dist/configs/index.js.map +1 -0
- package/dist/configs/recommended.d.ts +31 -0
- package/dist/configs/recommended.d.ts.map +1 -0
- package/dist/configs/recommended.js +45 -0
- package/dist/configs/recommended.js.map +1 -0
- package/dist/index.d.ts +74 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +68 -0
- package/dist/index.js.map +1 -0
- package/dist/rules/index.d.ts +14 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +20 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/missing-release-tag.d.ts +8 -0
- package/dist/rules/missing-release-tag.d.ts.map +1 -0
- package/dist/rules/missing-release-tag.js +148 -0
- package/dist/rules/missing-release-tag.js.map +1 -0
- package/dist/rules/override-keyword.d.ts +8 -0
- package/dist/rules/override-keyword.d.ts.map +1 -0
- package/dist/rules/override-keyword.js +106 -0
- package/dist/rules/override-keyword.js.map +1 -0
- package/dist/rules/package-documentation.d.ts +8 -0
- package/dist/rules/package-documentation.d.ts.map +1 -0
- package/dist/rules/package-documentation.js +70 -0
- package/dist/rules/package-documentation.js.map +1 -0
- package/dist/types.d.ts +90 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +19 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/config-loader.d.ts +47 -0
- package/dist/utils/config-loader.d.ts.map +1 -0
- package/dist/utils/config-loader.js +163 -0
- package/dist/utils/config-loader.js.map +1 -0
- package/dist/utils/entry-point.d.ts +56 -0
- package/dist/utils/entry-point.d.ts.map +1 -0
- package/dist/utils/entry-point.js +198 -0
- package/dist/utils/entry-point.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +21 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/tsdoc-parser.d.ts +58 -0
- package/dist/utils/tsdoc-parser.d.ts.map +1 -0
- package/dist/utils/tsdoc-parser.js +137 -0
- package/dist/utils/tsdoc-parser.js.map +1 -0
- package/package.json +44 -0
- package/src/configs/index.ts +6 -0
- package/src/configs/recommended.ts +46 -0
- package/src/index.ts +111 -0
- package/src/rules/index.ts +18 -0
- package/src/rules/missing-release-tag.ts +203 -0
- package/src/rules/override-keyword.ts +139 -0
- package/src/rules/package-documentation.ts +90 -0
- package/src/types.ts +104 -0
- package/src/utils/config-loader.ts +194 -0
- package/src/utils/entry-point.ts +247 -0
- package/src/utils/index.ts +17 -0
- package/src/utils/tsdoc-parser.ts +163 -0
- package/temp/eslint-plugin.api.md +118 -0
- package/test/index.test.ts +66 -0
- package/test/rules/missing-release-tag.test.ts +184 -0
- package/test/rules/override-keyword.test.ts +171 -0
- package/test/rules/package-documentation.test.ts +152 -0
- package/test/tsconfig.json +11 -0
- package/test/utils/config-loader.test.ts +199 -0
- package/test/utils/entry-point.test.ts +172 -0
- package/test/utils/tsdoc-parser.test.ts +113 -0
- package/tsconfig.json +12 -0
- package/vitest.config.mts +25 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for parsing TSDoc comments.
|
|
3
|
+
* @internal
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
TSDocParser,
|
|
8
|
+
TSDocConfiguration,
|
|
9
|
+
TextRange,
|
|
10
|
+
ParserContext,
|
|
11
|
+
DocComment,
|
|
12
|
+
} from '@microsoft/tsdoc'
|
|
13
|
+
import type { TSESTree } from '@typescript-eslint/utils'
|
|
14
|
+
import type { ReleaseTag } from '../types'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* TSDoc parser instance configured for API Extractor compatibility.
|
|
18
|
+
*/
|
|
19
|
+
let parserInstance: TSDocParser | undefined
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Gets or creates a TSDoc parser instance.
|
|
23
|
+
*/
|
|
24
|
+
function getParser(): TSDocParser {
|
|
25
|
+
if (!parserInstance) {
|
|
26
|
+
const config = new TSDocConfiguration()
|
|
27
|
+
// API Extractor's custom tags are defined via tsdoc.json extends
|
|
28
|
+
// For our purposes, the default configuration suffices as we're
|
|
29
|
+
// checking for standard modifier tags
|
|
30
|
+
parserInstance = new TSDocParser(config)
|
|
31
|
+
}
|
|
32
|
+
return parserInstance
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Parses a TSDoc comment string.
|
|
37
|
+
*
|
|
38
|
+
* @param commentText - The full comment text including delimiters
|
|
39
|
+
* @returns Parser context with the parsed doc comment
|
|
40
|
+
*/
|
|
41
|
+
export function parseTSDocComment(commentText: string): ParserContext {
|
|
42
|
+
const parser = getParser()
|
|
43
|
+
const textRange = TextRange.fromString(commentText)
|
|
44
|
+
return parser.parseRange(textRange)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Extracts a release tag from a parsed TSDoc comment.
|
|
49
|
+
*
|
|
50
|
+
* @param docComment - The parsed doc comment
|
|
51
|
+
* @returns The release tag if found, undefined otherwise
|
|
52
|
+
*/
|
|
53
|
+
export function extractReleaseTag(
|
|
54
|
+
docComment: DocComment,
|
|
55
|
+
): ReleaseTag | undefined {
|
|
56
|
+
// Check for modifier tags
|
|
57
|
+
if (docComment.modifierTagSet.isPublic()) {
|
|
58
|
+
return 'public'
|
|
59
|
+
}
|
|
60
|
+
if (docComment.modifierTagSet.isBeta()) {
|
|
61
|
+
return 'beta'
|
|
62
|
+
}
|
|
63
|
+
if (docComment.modifierTagSet.isAlpha()) {
|
|
64
|
+
return 'alpha'
|
|
65
|
+
}
|
|
66
|
+
if (docComment.modifierTagSet.isInternal()) {
|
|
67
|
+
return 'internal'
|
|
68
|
+
}
|
|
69
|
+
return undefined
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Checks if a TSDoc comment has the @override tag.
|
|
74
|
+
*
|
|
75
|
+
* @param docComment - The parsed doc comment
|
|
76
|
+
* @returns True if @override tag is present
|
|
77
|
+
*/
|
|
78
|
+
export function hasOverrideTag(docComment: DocComment): boolean {
|
|
79
|
+
return docComment.modifierTagSet.isOverride()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Checks if a TSDoc comment has the @packageDocumentation tag.
|
|
84
|
+
*
|
|
85
|
+
* @param docComment - The parsed doc comment
|
|
86
|
+
* @returns True if @packageDocumentation tag is present
|
|
87
|
+
*/
|
|
88
|
+
export function hasPackageDocumentation(docComment: DocComment): boolean {
|
|
89
|
+
return docComment.modifierTagSet.isPackageDocumentation()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Checks if a comment is a block comment (TSDoc style).
|
|
94
|
+
*/
|
|
95
|
+
function isBlockComment(comment: TSESTree.Comment): boolean {
|
|
96
|
+
// TSESTree.Comment.type is 'Line' | 'Block' - comparing to string literal
|
|
97
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison
|
|
98
|
+
return comment.type === 'Block'
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Gets the leading comment for a node, if it's a TSDoc comment.
|
|
103
|
+
*
|
|
104
|
+
* @param sourceCode - ESLint source code object
|
|
105
|
+
* @param node - The AST node to check
|
|
106
|
+
* @returns The comment text if a TSDoc comment exists, undefined otherwise
|
|
107
|
+
*/
|
|
108
|
+
export function getLeadingTSDocComment(
|
|
109
|
+
sourceCode: {
|
|
110
|
+
getCommentsBefore: (node: TSESTree.Node) => TSESTree.Comment[]
|
|
111
|
+
},
|
|
112
|
+
node: TSESTree.Node,
|
|
113
|
+
): string | undefined {
|
|
114
|
+
const comments = sourceCode.getCommentsBefore(node)
|
|
115
|
+
if (comments.length === 0) {
|
|
116
|
+
return undefined
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Get the last comment before the node (closest to it)
|
|
120
|
+
const lastComment = comments[comments.length - 1]
|
|
121
|
+
if (!lastComment) {
|
|
122
|
+
return undefined
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// TSDoc comments must be block comments starting with /**
|
|
126
|
+
if (!isBlockComment(lastComment)) {
|
|
127
|
+
return undefined
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Check if it's a TSDoc comment (starts with *)
|
|
131
|
+
const value = lastComment.value
|
|
132
|
+
if (!value.startsWith('*')) {
|
|
133
|
+
return undefined
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Reconstruct the full comment
|
|
137
|
+
return `/*${value}*/`
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Finds all TSDoc comments in a source file.
|
|
142
|
+
*
|
|
143
|
+
* @param sourceCode - ESLint source code object
|
|
144
|
+
* @returns Array of comment objects with their parsed content
|
|
145
|
+
*/
|
|
146
|
+
export function findAllTSDocComments(sourceCode: {
|
|
147
|
+
getAllComments: () => TSESTree.Comment[]
|
|
148
|
+
}): Array<{ comment: TSESTree.Comment; parsed: ParserContext }> {
|
|
149
|
+
const results: Array<{ comment: TSESTree.Comment; parsed: ParserContext }> =
|
|
150
|
+
[]
|
|
151
|
+
|
|
152
|
+
for (const comment of sourceCode.getAllComments()) {
|
|
153
|
+
if (!isBlockComment(comment) || !comment.value.startsWith('*')) {
|
|
154
|
+
continue
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const commentText = `/*${comment.value}*/`
|
|
158
|
+
const parsed = parseTSDocComment(commentText)
|
|
159
|
+
results.push({ comment, parsed })
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return results
|
|
163
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
## API Report File for "@api-extractor-tools/eslint-plugin"
|
|
2
|
+
|
|
3
|
+
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
|
|
7
|
+
import { RuleListener } from '@typescript-eslint/utils/dist/ts-eslint';
|
|
8
|
+
import { RuleModule } from '@typescript-eslint/utils/dist/ts-eslint';
|
|
9
|
+
import type { TSESLint } from '@typescript-eslint/utils';
|
|
10
|
+
|
|
11
|
+
// @public
|
|
12
|
+
export interface ApiExtractorConfig {
|
|
13
|
+
// (undocumented)
|
|
14
|
+
extends?: string;
|
|
15
|
+
// (undocumented)
|
|
16
|
+
mainEntryPointFilePath?: string;
|
|
17
|
+
// (undocumented)
|
|
18
|
+
messages?: ApiExtractorMessagesConfig;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// @public
|
|
22
|
+
export interface ApiExtractorEslintPlugin {
|
|
23
|
+
// Warning: (ae-forgotten-export) The symbol "PluginConfigs" needs to be exported by the entry point index.d.ts
|
|
24
|
+
//
|
|
25
|
+
// (undocumented)
|
|
26
|
+
configs: PluginConfigs;
|
|
27
|
+
// (undocumented)
|
|
28
|
+
meta: {
|
|
29
|
+
name: string;
|
|
30
|
+
version: string;
|
|
31
|
+
};
|
|
32
|
+
// (undocumented)
|
|
33
|
+
rules: typeof rules;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// @public
|
|
37
|
+
export type ApiExtractorLogLevel = 'error' | 'warning' | 'none';
|
|
38
|
+
|
|
39
|
+
// @public
|
|
40
|
+
export interface ApiExtractorMessagesConfig {
|
|
41
|
+
// (undocumented)
|
|
42
|
+
compilerMessageReporting?: {
|
|
43
|
+
default?: MessageConfig;
|
|
44
|
+
[messageId: string]: MessageConfig | undefined;
|
|
45
|
+
};
|
|
46
|
+
// (undocumented)
|
|
47
|
+
extractorMessageReporting?: {
|
|
48
|
+
default?: MessageConfig;
|
|
49
|
+
'ae-missing-release-tag'?: MessageConfig;
|
|
50
|
+
'ae-forgotten-export'?: MessageConfig;
|
|
51
|
+
'ae-internal-missing-underscore'?: MessageConfig;
|
|
52
|
+
'ae-incompatible-release-tags'?: MessageConfig;
|
|
53
|
+
[messageId: string]: MessageConfig | undefined;
|
|
54
|
+
};
|
|
55
|
+
// (undocumented)
|
|
56
|
+
tsdocMessageReporting?: {
|
|
57
|
+
default?: MessageConfig;
|
|
58
|
+
[messageId: string]: MessageConfig | undefined;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// @public
|
|
63
|
+
export interface MessageConfig {
|
|
64
|
+
// (undocumented)
|
|
65
|
+
addToApiReportFile?: boolean;
|
|
66
|
+
// (undocumented)
|
|
67
|
+
logLevel: ApiExtractorLogLevel;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// @public
|
|
71
|
+
export interface MissingReleaseTagRuleOptions {
|
|
72
|
+
// (undocumented)
|
|
73
|
+
configPath?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// @public
|
|
77
|
+
export interface OverrideKeywordRuleOptions {
|
|
78
|
+
// (undocumented)
|
|
79
|
+
configPath?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// @public
|
|
83
|
+
export interface PackageDocumentationRuleOptions {
|
|
84
|
+
// (undocumented)
|
|
85
|
+
configPath?: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// @public
|
|
89
|
+
const plugin: ApiExtractorEslintPlugin;
|
|
90
|
+
export default plugin;
|
|
91
|
+
|
|
92
|
+
// @public
|
|
93
|
+
export const recommendedRules: TSESLint.Linter.RulesRecord;
|
|
94
|
+
|
|
95
|
+
// @public
|
|
96
|
+
export const RELEASE_TAGS: readonly ReleaseTag[];
|
|
97
|
+
|
|
98
|
+
// @public
|
|
99
|
+
export type ReleaseTag = 'public' | 'beta' | 'alpha' | 'internal';
|
|
100
|
+
|
|
101
|
+
// @public
|
|
102
|
+
export interface ResolvedEntryPoints {
|
|
103
|
+
// (undocumented)
|
|
104
|
+
exports: string[];
|
|
105
|
+
// (undocumented)
|
|
106
|
+
main?: string;
|
|
107
|
+
// (undocumented)
|
|
108
|
+
types?: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// @public
|
|
112
|
+
export const rules: {
|
|
113
|
+
readonly 'missing-release-tag': RuleModule<"missingReleaseTag", [MissingReleaseTagRuleOptions], unknown, RuleListener>;
|
|
114
|
+
readonly 'override-keyword': RuleModule<"missingOverrideKeyword", [OverrideKeywordRuleOptions], unknown, RuleListener>;
|
|
115
|
+
readonly 'package-documentation': RuleModule<"missingPackageDocumentation", [PackageDocumentationRuleOptions], unknown, RuleListener>;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
```
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import plugin, { rules, recommendedRules, RELEASE_TAGS } from '../src/index'
|
|
3
|
+
|
|
4
|
+
describe('eslint-plugin', () => {
|
|
5
|
+
describe('plugin exports', () => {
|
|
6
|
+
it('should export plugin meta', () => {
|
|
7
|
+
expect(plugin.meta).toBeDefined()
|
|
8
|
+
expect(plugin.meta.name).toBe('@api-extractor-tools/eslint-plugin')
|
|
9
|
+
expect(plugin.meta.version).toBeDefined()
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('should export all rules', () => {
|
|
13
|
+
expect(plugin.rules).toBeDefined()
|
|
14
|
+
expect(plugin.rules['missing-release-tag']).toBeDefined()
|
|
15
|
+
expect(plugin.rules['override-keyword']).toBeDefined()
|
|
16
|
+
expect(plugin.rules['package-documentation']).toBeDefined()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('should export configs', () => {
|
|
20
|
+
expect(plugin.configs).toBeDefined()
|
|
21
|
+
expect(plugin.configs.recommended).toBeDefined()
|
|
22
|
+
expect(plugin.configs['recommended-legacy']).toBeDefined()
|
|
23
|
+
})
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
describe('named exports', () => {
|
|
27
|
+
it('should export rules object', () => {
|
|
28
|
+
expect(rules).toBeDefined()
|
|
29
|
+
expect(rules['missing-release-tag']).toBeDefined()
|
|
30
|
+
expect(rules['override-keyword']).toBeDefined()
|
|
31
|
+
expect(rules['package-documentation']).toBeDefined()
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('should export recommendedRules', () => {
|
|
35
|
+
expect(recommendedRules).toBeDefined()
|
|
36
|
+
expect(recommendedRules['@api-extractor-tools/missing-release-tag']).toBe(
|
|
37
|
+
'warn',
|
|
38
|
+
)
|
|
39
|
+
expect(recommendedRules['@api-extractor-tools/override-keyword']).toBe(
|
|
40
|
+
'error',
|
|
41
|
+
)
|
|
42
|
+
expect(
|
|
43
|
+
recommendedRules['@api-extractor-tools/package-documentation'],
|
|
44
|
+
).toBe('warn')
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('should export RELEASE_TAGS', () => {
|
|
48
|
+
expect(RELEASE_TAGS).toEqual(['public', 'beta', 'alpha', 'internal'])
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
describe('recommended config', () => {
|
|
53
|
+
it('should have correct structure for flat config', () => {
|
|
54
|
+
const config = plugin.configs.recommended
|
|
55
|
+
expect(config.plugins).toBeDefined()
|
|
56
|
+
expect(config.plugins?.['@api-extractor-tools']).toBe(plugin)
|
|
57
|
+
expect(config.rules).toEqual(recommendedRules)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('should have correct structure for legacy config', () => {
|
|
61
|
+
const config = plugin.configs['recommended-legacy']
|
|
62
|
+
expect(config.plugins).toEqual(['@api-extractor-tools'])
|
|
63
|
+
expect(config.rules).toEqual(recommendedRules)
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
})
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { RuleTester } from '@typescript-eslint/rule-tester'
|
|
2
|
+
import { describe, it, afterAll } from 'vitest'
|
|
3
|
+
import { missingReleaseTag } from '../../src/rules/missing-release-tag'
|
|
4
|
+
|
|
5
|
+
RuleTester.afterAll = afterAll
|
|
6
|
+
RuleTester.describe = describe
|
|
7
|
+
RuleTester.it = it
|
|
8
|
+
|
|
9
|
+
const ruleTester = new RuleTester({
|
|
10
|
+
languageOptions: {
|
|
11
|
+
parser: require('@typescript-eslint/parser'),
|
|
12
|
+
parserOptions: {
|
|
13
|
+
ecmaVersion: 2022,
|
|
14
|
+
sourceType: 'module',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
describe('missing-release-tag', () => {
|
|
20
|
+
ruleTester.run('missing-release-tag', missingReleaseTag, {
|
|
21
|
+
valid: [
|
|
22
|
+
// Exported function with @public tag
|
|
23
|
+
{
|
|
24
|
+
code: `
|
|
25
|
+
/**
|
|
26
|
+
* A public function.
|
|
27
|
+
* @public
|
|
28
|
+
*/
|
|
29
|
+
export function myFunction() {}
|
|
30
|
+
`,
|
|
31
|
+
},
|
|
32
|
+
// Exported function with @beta tag
|
|
33
|
+
{
|
|
34
|
+
code: `
|
|
35
|
+
/**
|
|
36
|
+
* A beta function.
|
|
37
|
+
* @beta
|
|
38
|
+
*/
|
|
39
|
+
export function myBetaFunction() {}
|
|
40
|
+
`,
|
|
41
|
+
},
|
|
42
|
+
// Exported function with @alpha tag
|
|
43
|
+
{
|
|
44
|
+
code: `
|
|
45
|
+
/**
|
|
46
|
+
* An alpha function.
|
|
47
|
+
* @alpha
|
|
48
|
+
*/
|
|
49
|
+
export function myAlphaFunction() {}
|
|
50
|
+
`,
|
|
51
|
+
},
|
|
52
|
+
// Exported function with @internal tag
|
|
53
|
+
{
|
|
54
|
+
code: `
|
|
55
|
+
/**
|
|
56
|
+
* An internal function.
|
|
57
|
+
* @internal
|
|
58
|
+
*/
|
|
59
|
+
export function myInternalFunction() {}
|
|
60
|
+
`,
|
|
61
|
+
},
|
|
62
|
+
// Exported class with release tag
|
|
63
|
+
{
|
|
64
|
+
code: `
|
|
65
|
+
/**
|
|
66
|
+
* A public class.
|
|
67
|
+
* @public
|
|
68
|
+
*/
|
|
69
|
+
export class MyClass {}
|
|
70
|
+
`,
|
|
71
|
+
},
|
|
72
|
+
// Exported interface with release tag
|
|
73
|
+
{
|
|
74
|
+
code: `
|
|
75
|
+
/**
|
|
76
|
+
* A public interface.
|
|
77
|
+
* @public
|
|
78
|
+
*/
|
|
79
|
+
export interface MyInterface {}
|
|
80
|
+
`,
|
|
81
|
+
},
|
|
82
|
+
// Exported type with release tag
|
|
83
|
+
{
|
|
84
|
+
code: `
|
|
85
|
+
/**
|
|
86
|
+
* A public type.
|
|
87
|
+
* @public
|
|
88
|
+
*/
|
|
89
|
+
export type MyType = string;
|
|
90
|
+
`,
|
|
91
|
+
},
|
|
92
|
+
// Exported enum with release tag
|
|
93
|
+
{
|
|
94
|
+
code: `
|
|
95
|
+
/**
|
|
96
|
+
* A public enum.
|
|
97
|
+
* @public
|
|
98
|
+
*/
|
|
99
|
+
export enum MyEnum { A, B }
|
|
100
|
+
`,
|
|
101
|
+
},
|
|
102
|
+
// Exported const with release tag
|
|
103
|
+
{
|
|
104
|
+
code: `
|
|
105
|
+
/**
|
|
106
|
+
* A public constant.
|
|
107
|
+
* @public
|
|
108
|
+
*/
|
|
109
|
+
export const MY_CONST = 42;
|
|
110
|
+
`,
|
|
111
|
+
},
|
|
112
|
+
// Non-exported function (no release tag needed)
|
|
113
|
+
{
|
|
114
|
+
code: `
|
|
115
|
+
function privateFunction() {}
|
|
116
|
+
`,
|
|
117
|
+
},
|
|
118
|
+
// Non-exported class
|
|
119
|
+
{
|
|
120
|
+
code: `
|
|
121
|
+
class PrivateClass {}
|
|
122
|
+
`,
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
invalid: [
|
|
126
|
+
// Exported function without release tag
|
|
127
|
+
{
|
|
128
|
+
code: `
|
|
129
|
+
/**
|
|
130
|
+
* A function without release tag.
|
|
131
|
+
*/
|
|
132
|
+
export function myFunction() {}
|
|
133
|
+
`,
|
|
134
|
+
errors: [{ messageId: 'missingReleaseTag' }],
|
|
135
|
+
},
|
|
136
|
+
// Exported class without release tag
|
|
137
|
+
{
|
|
138
|
+
code: `
|
|
139
|
+
/**
|
|
140
|
+
* A class without release tag.
|
|
141
|
+
*/
|
|
142
|
+
export class MyClass {}
|
|
143
|
+
`,
|
|
144
|
+
errors: [{ messageId: 'missingReleaseTag' }],
|
|
145
|
+
},
|
|
146
|
+
// Exported function without any comment
|
|
147
|
+
{
|
|
148
|
+
code: `export function myFunction() {}`,
|
|
149
|
+
errors: [{ messageId: 'missingReleaseTag' }],
|
|
150
|
+
},
|
|
151
|
+
// Exported interface without release tag
|
|
152
|
+
{
|
|
153
|
+
code: `
|
|
154
|
+
/**
|
|
155
|
+
* An interface without release tag.
|
|
156
|
+
*/
|
|
157
|
+
export interface MyInterface {}
|
|
158
|
+
`,
|
|
159
|
+
errors: [{ messageId: 'missingReleaseTag' }],
|
|
160
|
+
},
|
|
161
|
+
// Exported type without release tag
|
|
162
|
+
{
|
|
163
|
+
code: `
|
|
164
|
+
export type MyType = string;
|
|
165
|
+
`,
|
|
166
|
+
errors: [{ messageId: 'missingReleaseTag' }],
|
|
167
|
+
},
|
|
168
|
+
// Exported enum without release tag
|
|
169
|
+
{
|
|
170
|
+
code: `
|
|
171
|
+
export enum MyEnum { A, B }
|
|
172
|
+
`,
|
|
173
|
+
errors: [{ messageId: 'missingReleaseTag' }],
|
|
174
|
+
},
|
|
175
|
+
// Exported const without release tag
|
|
176
|
+
{
|
|
177
|
+
code: `
|
|
178
|
+
export const MY_CONST = 42;
|
|
179
|
+
`,
|
|
180
|
+
errors: [{ messageId: 'missingReleaseTag' }],
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
})
|
|
184
|
+
})
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { RuleTester } from '@typescript-eslint/rule-tester'
|
|
2
|
+
import { describe, it, afterAll } from 'vitest'
|
|
3
|
+
import { overrideKeyword } from '../../src/rules/override-keyword'
|
|
4
|
+
|
|
5
|
+
RuleTester.afterAll = afterAll
|
|
6
|
+
RuleTester.describe = describe
|
|
7
|
+
RuleTester.it = it
|
|
8
|
+
|
|
9
|
+
const ruleTester = new RuleTester({
|
|
10
|
+
languageOptions: {
|
|
11
|
+
parser: require('@typescript-eslint/parser'),
|
|
12
|
+
parserOptions: {
|
|
13
|
+
ecmaVersion: 2022,
|
|
14
|
+
sourceType: 'module',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
describe('override-keyword', () => {
|
|
20
|
+
ruleTester.run('override-keyword', overrideKeyword, {
|
|
21
|
+
valid: [
|
|
22
|
+
// Method with @override and override keyword
|
|
23
|
+
{
|
|
24
|
+
code: `
|
|
25
|
+
class Child extends Parent {
|
|
26
|
+
/**
|
|
27
|
+
* @override
|
|
28
|
+
*/
|
|
29
|
+
override doSomething() {}
|
|
30
|
+
}
|
|
31
|
+
`,
|
|
32
|
+
},
|
|
33
|
+
// Property with @override and override keyword
|
|
34
|
+
{
|
|
35
|
+
code: `
|
|
36
|
+
class Child extends Parent {
|
|
37
|
+
/**
|
|
38
|
+
* @override
|
|
39
|
+
*/
|
|
40
|
+
override myProperty = 42;
|
|
41
|
+
}
|
|
42
|
+
`,
|
|
43
|
+
},
|
|
44
|
+
// Method without @override tag (no override keyword needed)
|
|
45
|
+
{
|
|
46
|
+
code: `
|
|
47
|
+
class Child extends Parent {
|
|
48
|
+
/**
|
|
49
|
+
* A method without override tag.
|
|
50
|
+
*/
|
|
51
|
+
doSomething() {}
|
|
52
|
+
}
|
|
53
|
+
`,
|
|
54
|
+
},
|
|
55
|
+
// Method with override keyword but no @override tag (allowed)
|
|
56
|
+
{
|
|
57
|
+
code: `
|
|
58
|
+
class Child extends Parent {
|
|
59
|
+
override doSomething() {}
|
|
60
|
+
}
|
|
61
|
+
`,
|
|
62
|
+
},
|
|
63
|
+
// Static method with @override and override keyword
|
|
64
|
+
{
|
|
65
|
+
code: `
|
|
66
|
+
class Child extends Parent {
|
|
67
|
+
/**
|
|
68
|
+
* @override
|
|
69
|
+
*/
|
|
70
|
+
static override staticMethod() {}
|
|
71
|
+
}
|
|
72
|
+
`,
|
|
73
|
+
},
|
|
74
|
+
// Constructor (should be skipped)
|
|
75
|
+
{
|
|
76
|
+
code: `
|
|
77
|
+
class Child extends Parent {
|
|
78
|
+
/**
|
|
79
|
+
* Constructor.
|
|
80
|
+
*/
|
|
81
|
+
constructor() {
|
|
82
|
+
super();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
`,
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
invalid: [
|
|
89
|
+
// Method with @override but missing override keyword
|
|
90
|
+
{
|
|
91
|
+
code: `
|
|
92
|
+
class Child extends Parent {
|
|
93
|
+
/**
|
|
94
|
+
* @override
|
|
95
|
+
*/
|
|
96
|
+
doSomething() {}
|
|
97
|
+
}
|
|
98
|
+
`,
|
|
99
|
+
output: `
|
|
100
|
+
class Child extends Parent {
|
|
101
|
+
/**
|
|
102
|
+
* @override
|
|
103
|
+
*/
|
|
104
|
+
override doSomething() {}
|
|
105
|
+
}
|
|
106
|
+
`,
|
|
107
|
+
errors: [{ messageId: 'missingOverrideKeyword' }],
|
|
108
|
+
},
|
|
109
|
+
// Property with @override but missing override keyword
|
|
110
|
+
{
|
|
111
|
+
code: `
|
|
112
|
+
class Child extends Parent {
|
|
113
|
+
/**
|
|
114
|
+
* @override
|
|
115
|
+
*/
|
|
116
|
+
myProperty = 42;
|
|
117
|
+
}
|
|
118
|
+
`,
|
|
119
|
+
output: `
|
|
120
|
+
class Child extends Parent {
|
|
121
|
+
/**
|
|
122
|
+
* @override
|
|
123
|
+
*/
|
|
124
|
+
override myProperty = 42;
|
|
125
|
+
}
|
|
126
|
+
`,
|
|
127
|
+
errors: [{ messageId: 'missingOverrideKeyword' }],
|
|
128
|
+
},
|
|
129
|
+
// Getter with @override but missing override keyword
|
|
130
|
+
{
|
|
131
|
+
code: `
|
|
132
|
+
class Child extends Parent {
|
|
133
|
+
/**
|
|
134
|
+
* @override
|
|
135
|
+
*/
|
|
136
|
+
get myGetter() { return 42; }
|
|
137
|
+
}
|
|
138
|
+
`,
|
|
139
|
+
output: `
|
|
140
|
+
class Child extends Parent {
|
|
141
|
+
/**
|
|
142
|
+
* @override
|
|
143
|
+
*/
|
|
144
|
+
override get myGetter() { return 42; }
|
|
145
|
+
}
|
|
146
|
+
`,
|
|
147
|
+
errors: [{ messageId: 'missingOverrideKeyword' }],
|
|
148
|
+
},
|
|
149
|
+
// Setter with @override but missing override keyword
|
|
150
|
+
{
|
|
151
|
+
code: `
|
|
152
|
+
class Child extends Parent {
|
|
153
|
+
/**
|
|
154
|
+
* @override
|
|
155
|
+
*/
|
|
156
|
+
set mySetter(value: number) {}
|
|
157
|
+
}
|
|
158
|
+
`,
|
|
159
|
+
output: `
|
|
160
|
+
class Child extends Parent {
|
|
161
|
+
/**
|
|
162
|
+
* @override
|
|
163
|
+
*/
|
|
164
|
+
override set mySetter(value: number) {}
|
|
165
|
+
}
|
|
166
|
+
`,
|
|
167
|
+
errors: [{ messageId: 'missingOverrideKeyword' }],
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
})
|
|
171
|
+
})
|