@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.
Files changed (76) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/LICENSE +21 -0
  3. package/README.md +183 -0
  4. package/api-extractor.json +10 -0
  5. package/dist/configs/index.d.ts +6 -0
  6. package/dist/configs/index.d.ts.map +1 -0
  7. package/dist/configs/index.js +11 -0
  8. package/dist/configs/index.js.map +1 -0
  9. package/dist/configs/recommended.d.ts +31 -0
  10. package/dist/configs/recommended.d.ts.map +1 -0
  11. package/dist/configs/recommended.js +45 -0
  12. package/dist/configs/recommended.js.map +1 -0
  13. package/dist/index.d.ts +74 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +68 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/rules/index.d.ts +14 -0
  18. package/dist/rules/index.d.ts.map +1 -0
  19. package/dist/rules/index.js +20 -0
  20. package/dist/rules/index.js.map +1 -0
  21. package/dist/rules/missing-release-tag.d.ts +8 -0
  22. package/dist/rules/missing-release-tag.d.ts.map +1 -0
  23. package/dist/rules/missing-release-tag.js +148 -0
  24. package/dist/rules/missing-release-tag.js.map +1 -0
  25. package/dist/rules/override-keyword.d.ts +8 -0
  26. package/dist/rules/override-keyword.d.ts.map +1 -0
  27. package/dist/rules/override-keyword.js +106 -0
  28. package/dist/rules/override-keyword.js.map +1 -0
  29. package/dist/rules/package-documentation.d.ts +8 -0
  30. package/dist/rules/package-documentation.d.ts.map +1 -0
  31. package/dist/rules/package-documentation.js +70 -0
  32. package/dist/rules/package-documentation.js.map +1 -0
  33. package/dist/types.d.ts +90 -0
  34. package/dist/types.d.ts.map +1 -0
  35. package/dist/types.js +19 -0
  36. package/dist/types.js.map +1 -0
  37. package/dist/utils/config-loader.d.ts +47 -0
  38. package/dist/utils/config-loader.d.ts.map +1 -0
  39. package/dist/utils/config-loader.js +163 -0
  40. package/dist/utils/config-loader.js.map +1 -0
  41. package/dist/utils/entry-point.d.ts +56 -0
  42. package/dist/utils/entry-point.d.ts.map +1 -0
  43. package/dist/utils/entry-point.js +198 -0
  44. package/dist/utils/entry-point.js.map +1 -0
  45. package/dist/utils/index.d.ts +8 -0
  46. package/dist/utils/index.d.ts.map +1 -0
  47. package/dist/utils/index.js +21 -0
  48. package/dist/utils/index.js.map +1 -0
  49. package/dist/utils/tsdoc-parser.d.ts +58 -0
  50. package/dist/utils/tsdoc-parser.d.ts.map +1 -0
  51. package/dist/utils/tsdoc-parser.js +137 -0
  52. package/dist/utils/tsdoc-parser.js.map +1 -0
  53. package/package.json +44 -0
  54. package/src/configs/index.ts +6 -0
  55. package/src/configs/recommended.ts +46 -0
  56. package/src/index.ts +111 -0
  57. package/src/rules/index.ts +18 -0
  58. package/src/rules/missing-release-tag.ts +203 -0
  59. package/src/rules/override-keyword.ts +139 -0
  60. package/src/rules/package-documentation.ts +90 -0
  61. package/src/types.ts +104 -0
  62. package/src/utils/config-loader.ts +194 -0
  63. package/src/utils/entry-point.ts +247 -0
  64. package/src/utils/index.ts +17 -0
  65. package/src/utils/tsdoc-parser.ts +163 -0
  66. package/temp/eslint-plugin.api.md +118 -0
  67. package/test/index.test.ts +66 -0
  68. package/test/rules/missing-release-tag.test.ts +184 -0
  69. package/test/rules/override-keyword.test.ts +171 -0
  70. package/test/rules/package-documentation.test.ts +152 -0
  71. package/test/tsconfig.json +11 -0
  72. package/test/utils/config-loader.test.ts +199 -0
  73. package/test/utils/entry-point.test.ts +172 -0
  74. package/test/utils/tsdoc-parser.test.ts +113 -0
  75. package/tsconfig.json +12 -0
  76. 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
+ })