@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,152 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest'
2
+ import * as path from 'path'
3
+ import * as fs from 'fs'
4
+ import * as os from 'os'
5
+ import { clearPackageJsonCache } from '../../src/utils/entry-point.js'
6
+ import {
7
+ findPackageJson,
8
+ isEntryPoint,
9
+ hasPackageDocumentation,
10
+ parseTSDocComment,
11
+ } from '../../src/utils'
12
+
13
+ describe('package-documentation', () => {
14
+ let tempDir: string
15
+
16
+ beforeEach(() => {
17
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'eslint-plugin-test-'))
18
+ clearPackageJsonCache()
19
+ })
20
+
21
+ afterEach(() => {
22
+ fs.rmSync(tempDir, { recursive: true, force: true })
23
+ clearPackageJsonCache()
24
+ })
25
+
26
+ function createPackageJson(main: string): void {
27
+ const pkgPath = path.join(tempDir, 'package.json')
28
+ fs.writeFileSync(
29
+ pkgPath,
30
+ JSON.stringify({ name: 'test-package', main }, null, 2),
31
+ )
32
+ }
33
+
34
+ function createSourceDir(): string {
35
+ const srcDir = path.join(tempDir, 'src')
36
+ fs.mkdirSync(srcDir, { recursive: true })
37
+ return srcDir
38
+ }
39
+
40
+ describe('entry point detection', () => {
41
+ it('should correctly identify entry points from package.json', () => {
42
+ createPackageJson('./src/index.ts')
43
+ const srcDir = createSourceDir()
44
+ const indexPath = path.join(srcDir, 'index.ts')
45
+ fs.writeFileSync(indexPath, 'export {}')
46
+
47
+ const pkgPath = findPackageJson(srcDir)
48
+ expect(pkgPath).toBeDefined()
49
+ expect(isEntryPoint(indexPath, pkgPath!)).toBe(true)
50
+ })
51
+
52
+ it('should not identify non-entry points', () => {
53
+ createPackageJson('./src/index.ts')
54
+ const srcDir = createSourceDir()
55
+
56
+ const indexPath = path.join(srcDir, 'index.ts')
57
+ fs.writeFileSync(indexPath, 'export {}')
58
+
59
+ const helperPath = path.join(srcDir, 'helper.ts')
60
+ fs.writeFileSync(helperPath, 'export {}')
61
+
62
+ const pkgPath = findPackageJson(srcDir)
63
+ expect(isEntryPoint(helperPath, pkgPath!)).toBe(false)
64
+ })
65
+ })
66
+
67
+ describe('@packageDocumentation detection', () => {
68
+ it('should detect @packageDocumentation in a comment', () => {
69
+ const comment = `/**
70
+ * This is the main entry point.
71
+ * @packageDocumentation
72
+ */`
73
+ const parsed = parseTSDocComment(comment)
74
+ expect(parsed.docComment).toBeDefined()
75
+ expect(hasPackageDocumentation(parsed.docComment!)).toBe(true)
76
+ })
77
+
78
+ it('should return false when @packageDocumentation is missing', () => {
79
+ const comment = `/**
80
+ * This is just a comment.
81
+ */`
82
+ const parsed = parseTSDocComment(comment)
83
+ expect(parsed.docComment).toBeDefined()
84
+ expect(hasPackageDocumentation(parsed.docComment!)).toBe(false)
85
+ })
86
+ })
87
+
88
+ describe('rule logic simulation', () => {
89
+ it('should pass when entry point has @packageDocumentation', () => {
90
+ createPackageJson('./src/index.ts')
91
+ const srcDir = createSourceDir()
92
+ const indexPath = path.join(srcDir, 'index.ts')
93
+
94
+ const code = `/**
95
+ * This is the main entry point.
96
+ * @packageDocumentation
97
+ */
98
+
99
+ export function foo() {}`
100
+
101
+ fs.writeFileSync(indexPath, code)
102
+
103
+ const pkgPath = findPackageJson(srcDir)
104
+ expect(isEntryPoint(indexPath, pkgPath!)).toBe(true)
105
+
106
+ // Simulate checking for @packageDocumentation
107
+ const parsed = parseTSDocComment(`/**
108
+ * This is the main entry point.
109
+ * @packageDocumentation
110
+ */`)
111
+ expect(hasPackageDocumentation(parsed.docComment!)).toBe(true)
112
+ })
113
+
114
+ it('should fail when entry point lacks @packageDocumentation', () => {
115
+ createPackageJson('./src/index.ts')
116
+ const srcDir = createSourceDir()
117
+ const indexPath = path.join(srcDir, 'index.ts')
118
+
119
+ const code = `/**
120
+ * This is the main entry point.
121
+ */
122
+
123
+ export function foo() {}`
124
+
125
+ fs.writeFileSync(indexPath, code)
126
+
127
+ const pkgPath = findPackageJson(srcDir)
128
+ expect(isEntryPoint(indexPath, pkgPath!)).toBe(true)
129
+
130
+ // Simulate checking for @packageDocumentation
131
+ const parsed = parseTSDocComment(`/**
132
+ * This is the main entry point.
133
+ */`)
134
+ expect(hasPackageDocumentation(parsed.docComment!)).toBe(false)
135
+ })
136
+
137
+ it('should not require @packageDocumentation for non-entry points', () => {
138
+ createPackageJson('./src/index.ts')
139
+ const srcDir = createSourceDir()
140
+
141
+ const indexPath = path.join(srcDir, 'index.ts')
142
+ fs.writeFileSync(indexPath, '/** @packageDocumentation */ export {}')
143
+
144
+ const helperPath = path.join(srcDir, 'helper.ts')
145
+ fs.writeFileSync(helperPath, '// No package documentation needed')
146
+
147
+ const pkgPath = findPackageJson(srcDir)
148
+ // Helper is not an entry point, so no check needed
149
+ expect(isEntryPoint(helperPath, pkgPath!)).toBe(false)
150
+ })
151
+ })
152
+ })
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "../tsconfig.json",
3
+ "compilerOptions": {
4
+ "noEmit": true,
5
+ "rootDir": "..",
6
+ "baseUrl": "..",
7
+ "module": "ESNext",
8
+ "moduleResolution": "bundler"
9
+ },
10
+ "include": ["./**/*.ts", "../src/**/*.ts"]
11
+ }
@@ -0,0 +1,199 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest'
2
+ import * as path from 'path'
3
+ import * as fs from 'fs'
4
+ import * as os from 'os'
5
+ import {
6
+ findApiExtractorConfig,
7
+ loadApiExtractorConfig,
8
+ getMessageLogLevel,
9
+ resolveConfig,
10
+ logLevelToSeverity,
11
+ clearConfigCache,
12
+ } from '../../src/utils/config-loader'
13
+
14
+ describe('config-loader', () => {
15
+ let tempDir: string
16
+
17
+ beforeEach(() => {
18
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'config-loader-test-'))
19
+ clearConfigCache()
20
+ })
21
+
22
+ afterEach(() => {
23
+ fs.rmSync(tempDir, { recursive: true, force: true })
24
+ clearConfigCache()
25
+ })
26
+
27
+ describe('findApiExtractorConfig', () => {
28
+ it('should find api-extractor.json in the same directory', () => {
29
+ const configPath = path.join(tempDir, 'api-extractor.json')
30
+ fs.writeFileSync(configPath, '{}')
31
+
32
+ const found = findApiExtractorConfig(tempDir)
33
+ expect(found).toBe(configPath)
34
+ })
35
+
36
+ it('should find api-extractor.json in parent directory', () => {
37
+ const subDir = path.join(tempDir, 'src')
38
+ fs.mkdirSync(subDir)
39
+ const configPath = path.join(tempDir, 'api-extractor.json')
40
+ fs.writeFileSync(configPath, '{}')
41
+
42
+ const found = findApiExtractorConfig(subDir)
43
+ expect(found).toBe(configPath)
44
+ })
45
+
46
+ it('should return undefined when no config found', () => {
47
+ const found = findApiExtractorConfig(tempDir)
48
+ expect(found).toBeUndefined()
49
+ })
50
+ })
51
+
52
+ describe('loadApiExtractorConfig', () => {
53
+ it('should load and parse api-extractor.json', () => {
54
+ const configPath = path.join(tempDir, 'api-extractor.json')
55
+ const config = {
56
+ mainEntryPointFilePath: './dist/index.d.ts',
57
+ messages: {
58
+ extractorMessageReporting: {
59
+ 'ae-missing-release-tag': { logLevel: 'error' },
60
+ },
61
+ },
62
+ }
63
+ fs.writeFileSync(configPath, JSON.stringify(config))
64
+
65
+ const loaded = loadApiExtractorConfig(configPath)
66
+ expect(loaded).toEqual(config)
67
+ })
68
+
69
+ it('should handle JSON with comments', () => {
70
+ const configPath = path.join(tempDir, 'api-extractor.json')
71
+ const content = `{
72
+ // This is a comment
73
+ "mainEntryPointFilePath": "./dist/index.d.ts"
74
+ /* Block comment */
75
+ }`
76
+ fs.writeFileSync(configPath, content)
77
+
78
+ const loaded = loadApiExtractorConfig(configPath)
79
+ expect(loaded?.mainEntryPointFilePath).toBe('./dist/index.d.ts')
80
+ })
81
+
82
+ it('should handle extends', () => {
83
+ const baseConfigPath = path.join(tempDir, 'base.json')
84
+ const baseConfig = {
85
+ messages: {
86
+ extractorMessageReporting: {
87
+ default: { logLevel: 'warning' },
88
+ },
89
+ },
90
+ }
91
+ fs.writeFileSync(baseConfigPath, JSON.stringify(baseConfig))
92
+
93
+ const configPath = path.join(tempDir, 'api-extractor.json')
94
+ const config = {
95
+ extends: './base.json',
96
+ mainEntryPointFilePath: './dist/index.d.ts',
97
+ }
98
+ fs.writeFileSync(configPath, JSON.stringify(config))
99
+
100
+ const loaded = loadApiExtractorConfig(configPath)
101
+ expect(loaded?.mainEntryPointFilePath).toBe('./dist/index.d.ts')
102
+ expect(
103
+ loaded?.messages?.extractorMessageReporting?.default?.logLevel,
104
+ ).toBe('warning')
105
+ })
106
+
107
+ it('should return null for invalid JSON', () => {
108
+ const configPath = path.join(tempDir, 'api-extractor.json')
109
+ fs.writeFileSync(configPath, 'invalid json')
110
+
111
+ const loaded = loadApiExtractorConfig(configPath)
112
+ expect(loaded).toBeNull()
113
+ })
114
+
115
+ it('should return null for non-existent file', () => {
116
+ const configPath = path.join(tempDir, 'non-existent.json')
117
+ const loaded = loadApiExtractorConfig(configPath)
118
+ expect(loaded).toBeNull()
119
+ })
120
+ })
121
+
122
+ describe('getMessageLogLevel', () => {
123
+ it('should return specific message log level', () => {
124
+ const config = {
125
+ messages: {
126
+ extractorMessageReporting: {
127
+ 'ae-missing-release-tag': { logLevel: 'error' as const },
128
+ },
129
+ },
130
+ }
131
+
132
+ const level = getMessageLogLevel(config, 'ae-missing-release-tag')
133
+ expect(level).toBe('error')
134
+ })
135
+
136
+ it('should return default log level when message not configured', () => {
137
+ const config = {
138
+ messages: {
139
+ extractorMessageReporting: {
140
+ default: { logLevel: 'warning' as const },
141
+ },
142
+ },
143
+ }
144
+
145
+ const level = getMessageLogLevel(config, 'ae-missing-release-tag')
146
+ expect(level).toBe('warning')
147
+ })
148
+
149
+ it('should return warning for null config', () => {
150
+ const level = getMessageLogLevel(null, 'ae-missing-release-tag')
151
+ expect(level).toBe('warning')
152
+ })
153
+ })
154
+
155
+ describe('logLevelToSeverity', () => {
156
+ it('should map error to 2', () => {
157
+ expect(logLevelToSeverity('error')).toBe(2)
158
+ })
159
+
160
+ it('should map warning to 1', () => {
161
+ expect(logLevelToSeverity('warning')).toBe(1)
162
+ })
163
+
164
+ it('should map none to 0', () => {
165
+ expect(logLevelToSeverity('none')).toBe(0)
166
+ })
167
+ })
168
+
169
+ describe('resolveConfig', () => {
170
+ it('should use explicit config path when provided', () => {
171
+ const configPath = path.join(tempDir, 'custom-config.json')
172
+ const config = { mainEntryPointFilePath: './custom.d.ts' }
173
+ fs.writeFileSync(configPath, JSON.stringify(config))
174
+
175
+ const filePath = path.join(tempDir, 'src', 'index.ts')
176
+ const resolved = resolveConfig(filePath, configPath)
177
+ expect(resolved?.mainEntryPointFilePath).toBe('./custom.d.ts')
178
+ })
179
+
180
+ it('should auto-discover config when no explicit path', () => {
181
+ const configPath = path.join(tempDir, 'api-extractor.json')
182
+ const config = { mainEntryPointFilePath: './dist/index.d.ts' }
183
+ fs.writeFileSync(configPath, JSON.stringify(config))
184
+
185
+ const srcDir = path.join(tempDir, 'src')
186
+ fs.mkdirSync(srcDir)
187
+ const filePath = path.join(srcDir, 'index.ts')
188
+
189
+ const resolved = resolveConfig(filePath)
190
+ expect(resolved?.mainEntryPointFilePath).toBe('./dist/index.d.ts')
191
+ })
192
+
193
+ it('should return null when no config found', () => {
194
+ const filePath = path.join(tempDir, 'index.ts')
195
+ const resolved = resolveConfig(filePath)
196
+ expect(resolved).toBeNull()
197
+ })
198
+ })
199
+ })
@@ -0,0 +1,172 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest'
2
+ import * as path from 'path'
3
+ import * as fs from 'fs'
4
+ import * as os from 'os'
5
+ import {
6
+ findPackageJson,
7
+ loadPackageJson,
8
+ resolveEntryPoints,
9
+ isEntryPoint,
10
+ clearPackageJsonCache,
11
+ } from '../../src/utils/entry-point'
12
+
13
+ describe('entry-point', () => {
14
+ let tempDir: string
15
+
16
+ beforeEach(() => {
17
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'entry-point-test-'))
18
+ clearPackageJsonCache()
19
+ })
20
+
21
+ afterEach(() => {
22
+ fs.rmSync(tempDir, { recursive: true, force: true })
23
+ clearPackageJsonCache()
24
+ })
25
+
26
+ describe('findPackageJson', () => {
27
+ it('should find package.json in the same directory', () => {
28
+ const pkgPath = path.join(tempDir, 'package.json')
29
+ fs.writeFileSync(pkgPath, '{}')
30
+
31
+ const found = findPackageJson(tempDir)
32
+ expect(found).toBe(pkgPath)
33
+ })
34
+
35
+ it('should find package.json in parent directory', () => {
36
+ const subDir = path.join(tempDir, 'src')
37
+ fs.mkdirSync(subDir)
38
+ const pkgPath = path.join(tempDir, 'package.json')
39
+ fs.writeFileSync(pkgPath, '{}')
40
+
41
+ const found = findPackageJson(subDir)
42
+ expect(found).toBe(pkgPath)
43
+ })
44
+
45
+ it('should return undefined when no package.json found', () => {
46
+ const found = findPackageJson(tempDir)
47
+ expect(found).toBeUndefined()
48
+ })
49
+ })
50
+
51
+ describe('loadPackageJson', () => {
52
+ it('should load and parse package.json', () => {
53
+ const pkgPath = path.join(tempDir, 'package.json')
54
+ const pkg = { name: 'test-package', main: './dist/index.js' }
55
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg))
56
+
57
+ const loaded = loadPackageJson(pkgPath)
58
+ expect(loaded).toEqual(pkg)
59
+ })
60
+
61
+ it('should return null for invalid JSON', () => {
62
+ const pkgPath = path.join(tempDir, 'package.json')
63
+ fs.writeFileSync(pkgPath, 'invalid json')
64
+
65
+ const loaded = loadPackageJson(pkgPath)
66
+ expect(loaded).toBeNull()
67
+ })
68
+ })
69
+
70
+ describe('resolveEntryPoints', () => {
71
+ it('should resolve main entry point', () => {
72
+ const pkgPath = path.join(tempDir, 'package.json')
73
+ fs.writeFileSync(pkgPath, JSON.stringify({ main: './dist/index.js' }))
74
+
75
+ const entryPoints = resolveEntryPoints(pkgPath)
76
+ expect(entryPoints.main).toBe(path.join(tempDir, 'dist/index.js'))
77
+ })
78
+
79
+ it('should resolve types entry point', () => {
80
+ const pkgPath = path.join(tempDir, 'package.json')
81
+ fs.writeFileSync(pkgPath, JSON.stringify({ types: './dist/index.d.ts' }))
82
+
83
+ const entryPoints = resolveEntryPoints(pkgPath)
84
+ expect(entryPoints.types).toBe(path.join(tempDir, 'dist/index.d.ts'))
85
+ })
86
+
87
+ it('should resolve typings entry point', () => {
88
+ const pkgPath = path.join(tempDir, 'package.json')
89
+ fs.writeFileSync(
90
+ pkgPath,
91
+ JSON.stringify({ typings: './dist/index.d.ts' }),
92
+ )
93
+
94
+ const entryPoints = resolveEntryPoints(pkgPath)
95
+ expect(entryPoints.types).toBe(path.join(tempDir, 'dist/index.d.ts'))
96
+ })
97
+
98
+ it('should resolve simple exports', () => {
99
+ const pkgPath = path.join(tempDir, 'package.json')
100
+ fs.writeFileSync(
101
+ pkgPath,
102
+ JSON.stringify({
103
+ exports: {
104
+ '.': './dist/index.js',
105
+ './utils': './dist/utils.js',
106
+ },
107
+ }),
108
+ )
109
+
110
+ const entryPoints = resolveEntryPoints(pkgPath)
111
+ expect(entryPoints.exports).toContain(path.join(tempDir, 'dist/index.js'))
112
+ expect(entryPoints.exports).toContain(path.join(tempDir, 'dist/utils.js'))
113
+ })
114
+
115
+ it('should resolve nested exports', () => {
116
+ const pkgPath = path.join(tempDir, 'package.json')
117
+ fs.writeFileSync(
118
+ pkgPath,
119
+ JSON.stringify({
120
+ exports: {
121
+ '.': {
122
+ import: './dist/index.mjs',
123
+ require: './dist/index.cjs',
124
+ },
125
+ },
126
+ }),
127
+ )
128
+
129
+ const entryPoints = resolveEntryPoints(pkgPath)
130
+ expect(entryPoints.exports).toContain(
131
+ path.join(tempDir, 'dist/index.mjs'),
132
+ )
133
+ expect(entryPoints.exports).toContain(
134
+ path.join(tempDir, 'dist/index.cjs'),
135
+ )
136
+ })
137
+ })
138
+
139
+ describe('isEntryPoint', () => {
140
+ it('should identify main entry point', () => {
141
+ const pkgPath = path.join(tempDir, 'package.json')
142
+ fs.writeFileSync(pkgPath, JSON.stringify({ main: './dist/index.js' }))
143
+
144
+ const filePath = path.join(tempDir, 'dist/index.js')
145
+ expect(isEntryPoint(filePath, pkgPath)).toBe(true)
146
+ })
147
+
148
+ it('should identify source file when main points to source', () => {
149
+ const pkgPath = path.join(tempDir, 'package.json')
150
+ fs.writeFileSync(pkgPath, JSON.stringify({ main: './src/index.ts' }))
151
+
152
+ const filePath = path.join(tempDir, 'src/index.ts')
153
+ expect(isEntryPoint(filePath, pkgPath)).toBe(true)
154
+ })
155
+
156
+ it('should not identify non-entry point files', () => {
157
+ const pkgPath = path.join(tempDir, 'package.json')
158
+ fs.writeFileSync(pkgPath, JSON.stringify({ main: './dist/index.js' }))
159
+
160
+ const filePath = path.join(tempDir, 'src/utils.ts')
161
+ expect(isEntryPoint(filePath, pkgPath)).toBe(false)
162
+ })
163
+
164
+ it('should handle relative paths in package.json', () => {
165
+ const pkgPath = path.join(tempDir, 'package.json')
166
+ fs.writeFileSync(pkgPath, JSON.stringify({ main: 'dist/index.js' }))
167
+
168
+ const filePath = path.join(tempDir, 'dist/index.js')
169
+ expect(isEntryPoint(filePath, pkgPath)).toBe(true)
170
+ })
171
+ })
172
+ })
@@ -0,0 +1,113 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import {
3
+ parseTSDocComment,
4
+ extractReleaseTag,
5
+ hasOverrideTag,
6
+ hasPackageDocumentation,
7
+ } from '../../src/utils/tsdoc-parser'
8
+
9
+ describe('tsdoc-parser', () => {
10
+ describe('parseTSDocComment', () => {
11
+ it('should parse a simple TSDoc comment', () => {
12
+ const comment = `/**
13
+ * A simple comment.
14
+ */`
15
+ const result = parseTSDocComment(comment)
16
+ expect(result.docComment).toBeDefined()
17
+ })
18
+
19
+ it('should handle comments with modifier tags', () => {
20
+ const comment = `/**
21
+ * A public API.
22
+ * @public
23
+ */`
24
+ const result = parseTSDocComment(comment)
25
+ expect(result.docComment).toBeDefined()
26
+ expect(result.docComment?.modifierTagSet.isPublic()).toBe(true)
27
+ })
28
+ })
29
+
30
+ describe('extractReleaseTag', () => {
31
+ it('should extract @public tag', () => {
32
+ const comment = `/**
33
+ * @public
34
+ */`
35
+ const result = parseTSDocComment(comment)
36
+ const tag = extractReleaseTag(result.docComment!)
37
+ expect(tag).toBe('public')
38
+ })
39
+
40
+ it('should extract @beta tag', () => {
41
+ const comment = `/**
42
+ * @beta
43
+ */`
44
+ const result = parseTSDocComment(comment)
45
+ const tag = extractReleaseTag(result.docComment!)
46
+ expect(tag).toBe('beta')
47
+ })
48
+
49
+ it('should extract @alpha tag', () => {
50
+ const comment = `/**
51
+ * @alpha
52
+ */`
53
+ const result = parseTSDocComment(comment)
54
+ const tag = extractReleaseTag(result.docComment!)
55
+ expect(tag).toBe('alpha')
56
+ })
57
+
58
+ it('should extract @internal tag', () => {
59
+ const comment = `/**
60
+ * @internal
61
+ */`
62
+ const result = parseTSDocComment(comment)
63
+ const tag = extractReleaseTag(result.docComment!)
64
+ expect(tag).toBe('internal')
65
+ })
66
+
67
+ it('should return undefined when no release tag', () => {
68
+ const comment = `/**
69
+ * Just a description.
70
+ */`
71
+ const result = parseTSDocComment(comment)
72
+ const tag = extractReleaseTag(result.docComment!)
73
+ expect(tag).toBeUndefined()
74
+ })
75
+ })
76
+
77
+ describe('hasOverrideTag', () => {
78
+ it('should detect @override tag', () => {
79
+ const comment = `/**
80
+ * @override
81
+ */`
82
+ const result = parseTSDocComment(comment)
83
+ expect(hasOverrideTag(result.docComment!)).toBe(true)
84
+ })
85
+
86
+ it('should return false when no @override tag', () => {
87
+ const comment = `/**
88
+ * Just a comment.
89
+ */`
90
+ const result = parseTSDocComment(comment)
91
+ expect(hasOverrideTag(result.docComment!)).toBe(false)
92
+ })
93
+ })
94
+
95
+ describe('hasPackageDocumentation', () => {
96
+ it('should detect @packageDocumentation tag', () => {
97
+ const comment = `/**
98
+ * Package description.
99
+ * @packageDocumentation
100
+ */`
101
+ const result = parseTSDocComment(comment)
102
+ expect(hasPackageDocumentation(result.docComment!)).toBe(true)
103
+ })
104
+
105
+ it('should return false when no @packageDocumentation tag', () => {
106
+ const comment = `/**
107
+ * Just a comment.
108
+ */`
109
+ const result = parseTSDocComment(comment)
110
+ expect(hasPackageDocumentation(result.docComment!)).toBe(false)
111
+ })
112
+ })
113
+ })
package/tsconfig.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "noEmit": false,
5
+ "outDir": "dist",
6
+ "rootDir": "src",
7
+ "baseUrl": "src",
8
+ "module": "CommonJS",
9
+ "moduleResolution": "node"
10
+ },
11
+ "include": ["src/**/*.ts"]
12
+ }
@@ -0,0 +1,25 @@
1
+ import { defineConfig } from 'vitest/config'
2
+ import * as path from 'path'
3
+
4
+ export default defineConfig({
5
+ resolve: {
6
+ alias: {
7
+ '@': path.resolve(__dirname, 'src/index.ts'),
8
+ },
9
+ },
10
+ test: {
11
+ testTimeout: 30000,
12
+ server: {
13
+ deps: {
14
+ inline: ['fast-glob'],
15
+ },
16
+ },
17
+ coverage: {
18
+ provider: 'v8',
19
+ reporter: ['text', 'json', 'html'],
20
+ reportsDirectory: './coverage',
21
+ include: ['src/**/*.ts'],
22
+ exclude: ['src/**/*.d.ts', 'src/**/index.ts'],
23
+ },
24
+ },
25
+ })