skrypt-ai 0.7.0 → 0.8.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 (110) hide show
  1. package/dist/auth/index.js +3 -3
  2. package/dist/cli.js +1 -1
  3. package/dist/commands/cron.js +0 -4
  4. package/dist/commands/generate/index.d.ts +3 -0
  5. package/dist/commands/generate/index.js +393 -0
  6. package/dist/commands/generate/scan.d.ts +41 -0
  7. package/dist/commands/generate/scan.js +256 -0
  8. package/dist/commands/generate/verify.d.ts +14 -0
  9. package/dist/commands/generate/verify.js +122 -0
  10. package/dist/commands/generate/write.d.ts +25 -0
  11. package/dist/commands/generate/write.js +120 -0
  12. package/dist/commands/import.js +4 -1
  13. package/dist/commands/llms-txt.js +6 -4
  14. package/dist/config/loader.d.ts +0 -1
  15. package/dist/config/loader.js +1 -1
  16. package/dist/generator/agents-md.d.ts +25 -0
  17. package/dist/generator/agents-md.js +122 -0
  18. package/dist/generator/index.d.ts +2 -0
  19. package/dist/generator/index.js +2 -0
  20. package/dist/generator/mdx-serializer.d.ts +11 -0
  21. package/dist/generator/mdx-serializer.js +135 -0
  22. package/dist/generator/organizer.d.ts +1 -16
  23. package/dist/generator/organizer.js +0 -38
  24. package/dist/generator/writer.js +5 -4
  25. package/dist/llm/proxy-client.d.ts +32 -0
  26. package/dist/llm/proxy-client.js +103 -0
  27. package/dist/scanner/csharp.d.ts +0 -4
  28. package/dist/scanner/csharp.js +9 -49
  29. package/dist/scanner/go.d.ts +0 -3
  30. package/dist/scanner/go.js +8 -35
  31. package/dist/scanner/java.d.ts +0 -4
  32. package/dist/scanner/java.js +9 -49
  33. package/dist/scanner/kotlin.d.ts +0 -3
  34. package/dist/scanner/kotlin.js +6 -33
  35. package/dist/scanner/php.d.ts +0 -10
  36. package/dist/scanner/php.js +11 -55
  37. package/dist/scanner/ruby.d.ts +0 -3
  38. package/dist/scanner/ruby.js +8 -38
  39. package/dist/scanner/rust.d.ts +0 -3
  40. package/dist/scanner/rust.js +10 -37
  41. package/dist/scanner/swift.d.ts +0 -3
  42. package/dist/scanner/swift.js +8 -35
  43. package/dist/scanner/utils.d.ts +41 -0
  44. package/dist/scanner/utils.js +97 -0
  45. package/dist/template/docs.json +5 -2
  46. package/dist/template/next.config.mjs +31 -0
  47. package/dist/template/package.json +5 -3
  48. package/dist/template/src/app/layout.tsx +13 -13
  49. package/dist/template/src/app/llms-full.md/route.ts +29 -0
  50. package/dist/template/src/app/llms.txt/route.ts +29 -0
  51. package/dist/template/src/app/md/[...slug]/route.ts +174 -0
  52. package/dist/template/src/app/reference/route.ts +22 -18
  53. package/dist/template/src/app/sitemap.ts +1 -1
  54. package/dist/template/src/components/ai-chat-impl.tsx +206 -0
  55. package/dist/template/src/components/ai-chat.tsx +20 -193
  56. package/dist/template/src/components/mdx/index.tsx +27 -4
  57. package/dist/template/src/lib/fonts.ts +135 -0
  58. package/dist/template/src/middleware.ts +101 -0
  59. package/dist/template/src/styles/globals.css +28 -20
  60. package/dist/utils/files.d.ts +0 -8
  61. package/dist/utils/files.js +0 -33
  62. package/package.json +1 -1
  63. package/dist/autofix/autofix.test.d.ts +0 -1
  64. package/dist/autofix/autofix.test.js +0 -487
  65. package/dist/commands/generate.d.ts +0 -9
  66. package/dist/commands/generate.js +0 -739
  67. package/dist/generator/generator.test.d.ts +0 -1
  68. package/dist/generator/generator.test.js +0 -259
  69. package/dist/generator/writer.test.d.ts +0 -1
  70. package/dist/generator/writer.test.js +0 -411
  71. package/dist/llm/llm.manual-test.d.ts +0 -1
  72. package/dist/llm/llm.manual-test.js +0 -112
  73. package/dist/llm/llm.mock-test.d.ts +0 -4
  74. package/dist/llm/llm.mock-test.js +0 -79
  75. package/dist/plugins/index.d.ts +0 -47
  76. package/dist/plugins/index.js +0 -181
  77. package/dist/scanner/content-type.test.d.ts +0 -1
  78. package/dist/scanner/content-type.test.js +0 -231
  79. package/dist/scanner/integration.test.d.ts +0 -4
  80. package/dist/scanner/integration.test.js +0 -180
  81. package/dist/scanner/scanner.test.d.ts +0 -1
  82. package/dist/scanner/scanner.test.js +0 -210
  83. package/dist/scanner/typescript.manual-test.d.ts +0 -1
  84. package/dist/scanner/typescript.manual-test.js +0 -112
  85. package/dist/template/src/app/docs/auth/page.mdx +0 -589
  86. package/dist/template/src/app/docs/autofix/page.mdx +0 -624
  87. package/dist/template/src/app/docs/cli/page.mdx +0 -217
  88. package/dist/template/src/app/docs/config/page.mdx +0 -428
  89. package/dist/template/src/app/docs/configuration/page.mdx +0 -86
  90. package/dist/template/src/app/docs/deployment/page.mdx +0 -112
  91. package/dist/template/src/app/docs/generator/generator.md +0 -504
  92. package/dist/template/src/app/docs/generator/organizer.md +0 -779
  93. package/dist/template/src/app/docs/generator/page.mdx +0 -613
  94. package/dist/template/src/app/docs/github/page.mdx +0 -502
  95. package/dist/template/src/app/docs/llm/anthropic-client.md +0 -549
  96. package/dist/template/src/app/docs/llm/index.md +0 -471
  97. package/dist/template/src/app/docs/llm/page.mdx +0 -428
  98. package/dist/template/src/app/docs/plugins/page.mdx +0 -1793
  99. package/dist/template/src/app/docs/pro/page.mdx +0 -121
  100. package/dist/template/src/app/docs/quickstart/page.mdx +0 -93
  101. package/dist/template/src/app/docs/scanner/content-type.md +0 -599
  102. package/dist/template/src/app/docs/scanner/index.md +0 -212
  103. package/dist/template/src/app/docs/scanner/page.mdx +0 -307
  104. package/dist/template/src/app/docs/scanner/python.md +0 -469
  105. package/dist/template/src/app/docs/scanner/python_parser.md +0 -1056
  106. package/dist/template/src/app/docs/scanner/rust.md +0 -325
  107. package/dist/template/src/app/docs/scanner/typescript.md +0 -201
  108. package/dist/template/src/app/icon.tsx +0 -29
  109. package/dist/utils/validation.d.ts +0 -1
  110. package/dist/utils/validation.js +0 -12
@@ -1 +0,0 @@
1
- export {};
@@ -1,259 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { formatAsMarkdown, generateForElement, generateForElements } from './generator.js';
3
- // Mock API elements for testing
4
- function createMockElement(overrides = {}) {
5
- return {
6
- kind: 'function',
7
- name: 'testFunction',
8
- signature: 'function testFunction(arg: string): void',
9
- parameters: [{ name: 'arg', type: 'string' }],
10
- returnType: 'void',
11
- docstring: 'Test docstring',
12
- filePath: '/test/file.ts',
13
- lineNumber: 1,
14
- isExported: true,
15
- isPublic: true,
16
- ...overrides
17
- };
18
- }
19
- // Mock generated doc for testing
20
- function createMockDoc(overrides = {}) {
21
- return {
22
- element: createMockElement(overrides.element),
23
- markdown: 'Test markdown documentation',
24
- codeExample: 'const example = "test"',
25
- codeLanguage: 'typescript',
26
- ...overrides
27
- };
28
- }
29
- // Create a mock LLM client
30
- function createMockLLMClient(response) {
31
- const defaultResponse = {
32
- content: `---MARKDOWN---
33
- Use this to perform a test operation.
34
-
35
- | Parameter | Type | Required | Description |
36
- |-----------|------|----------|-------------|
37
- | arg | string | Yes | The test argument |
38
-
39
- Returns void.
40
- ---CODE---
41
- const result = testFunction('hello')
42
- console.log(result)
43
- ---END---`,
44
- model: 'test-model',
45
- usage: { inputTokens: 100, outputTokens: 200, totalTokens: 300 },
46
- finishReason: 'stop',
47
- ...response
48
- };
49
- return {
50
- provider: 'openai',
51
- complete: vi.fn().mockResolvedValue(defaultResponse),
52
- isConfigured: vi.fn().mockReturnValue(true)
53
- };
54
- }
55
- describe('Generator', () => {
56
- describe('formatAsMarkdown', () => {
57
- it('should format a single function doc as markdown', () => {
58
- const docs = [createMockDoc({ element: { kind: 'function', name: 'greet' } })];
59
- const result = formatAsMarkdown(docs, 'API Reference');
60
- expect(result).toContain('# API Reference');
61
- expect(result).toContain('## Functions');
62
- expect(result).toContain('### `greet`');
63
- expect(result).toContain('Test markdown documentation');
64
- });
65
- it('should format classes and methods with proper hierarchy', () => {
66
- const classDoc = createMockDoc({
67
- element: { kind: 'class', name: 'Calculator' },
68
- markdown: 'A calculator class'
69
- });
70
- const methodDoc = createMockDoc({
71
- element: { kind: 'method', name: 'add', parentClass: 'Calculator' },
72
- markdown: 'Adds two numbers'
73
- });
74
- const result = formatAsMarkdown([classDoc, methodDoc], 'Calculator API');
75
- expect(result).toContain('## Classes');
76
- expect(result).toContain('### `Calculator`');
77
- expect(result).toContain('### Methods');
78
- expect(result).toContain('#### `add`');
79
- });
80
- it('should group functions and classes separately', () => {
81
- const funcDoc = createMockDoc({
82
- element: { kind: 'function', name: 'helper' }
83
- });
84
- const classDoc = createMockDoc({
85
- element: { kind: 'class', name: 'Service' }
86
- });
87
- const result = formatAsMarkdown([funcDoc, classDoc], 'Mixed API');
88
- expect(result).toContain('## Functions');
89
- expect(result).toContain('## Classes');
90
- expect(result.indexOf('## Functions')).toBeLessThan(result.indexOf('## Classes'));
91
- });
92
- it('should include code examples in markdown', () => {
93
- const doc = createMockDoc({
94
- codeExample: 'const x = doSomething()',
95
- codeLanguage: 'typescript'
96
- });
97
- const result = formatAsMarkdown([doc], 'Test');
98
- expect(result).toContain('**Example:**');
99
- expect(result).toContain('```typescript');
100
- expect(result).toContain('const x = doSomething()');
101
- });
102
- it('should handle multi-language examples with CodeGroup', () => {
103
- const doc = createMockDoc({
104
- typescriptExample: 'const x = 1',
105
- pythonExample: 'x = 1',
106
- codeExample: 'const x = 1'
107
- });
108
- const result = formatAsMarkdown([doc], 'Multi-lang Test');
109
- expect(result).toContain('**Examples:**');
110
- expect(result).toContain('<CodeGroup>');
111
- expect(result).toContain('```typescript example.ts');
112
- expect(result).toContain('```python example.py');
113
- expect(result).toContain('</CodeGroup>');
114
- });
115
- it('should detect Python language from file path', () => {
116
- const doc = createMockDoc({
117
- element: { kind: 'function', name: 'greet', filePath: '/test/file.py' },
118
- codeLanguage: 'python',
119
- codeExample: 'print("hello")'
120
- });
121
- const result = formatAsMarkdown([doc], 'Python Test');
122
- expect(result).toContain('```python');
123
- expect(result).toContain('print("hello")');
124
- });
125
- it('should handle empty docs array', () => {
126
- const result = formatAsMarkdown([], 'Empty');
127
- expect(result).toBe('# Empty\n\n');
128
- });
129
- it('should handle methods without matching parent class', () => {
130
- const orphanMethod = createMockDoc({
131
- element: { kind: 'method', name: 'orphan', parentClass: 'NonExistent' }
132
- });
133
- // Methods without matching class doc should not appear under a class section
134
- const result = formatAsMarkdown([orphanMethod], 'Test');
135
- // The method will not be rendered because there's no class section
136
- // and methods are only rendered under their parent class
137
- expect(result).not.toContain('### `orphan`');
138
- });
139
- });
140
- describe('generateForElement', () => {
141
- let mockClient;
142
- beforeEach(() => {
143
- mockClient = createMockLLMClient();
144
- });
145
- it('should generate documentation for an element', async () => {
146
- const element = createMockElement();
147
- const options = {};
148
- const result = await generateForElement(element, mockClient, options);
149
- expect(result.element).toBe(element);
150
- expect(result.markdown).toBeTruthy();
151
- expect(result.codeLanguage).toBe('typescript');
152
- expect(mockClient.complete).toHaveBeenCalled();
153
- });
154
- it('should detect language from file extension', async () => {
155
- const pythonElement = createMockElement({ filePath: '/test/file.py' });
156
- const result = await generateForElement(pythonElement, mockClient, {});
157
- expect(result.codeLanguage).toBe('python');
158
- });
159
- it('should detect Go language from file extension', async () => {
160
- const goElement = createMockElement({ filePath: '/test/file.go' });
161
- const result = await generateForElement(goElement, mockClient, {});
162
- expect(result.codeLanguage).toBe('go');
163
- });
164
- it('should detect Rust language from file extension', async () => {
165
- const rustElement = createMockElement({ filePath: '/test/file.rs' });
166
- const result = await generateForElement(rustElement, mockClient, {});
167
- expect(result.codeLanguage).toBe('rust');
168
- });
169
- it('should call progress callback with correct status', async () => {
170
- const element = createMockElement();
171
- const onProgress = vi.fn();
172
- await generateForElement(element, mockClient, {}, onProgress);
173
- expect(onProgress).toHaveBeenCalledWith(expect.objectContaining({ status: 'generating', element: 'testFunction' }));
174
- expect(onProgress).toHaveBeenCalledWith(expect.objectContaining({ status: 'done', element: 'testFunction' }));
175
- });
176
- it('should handle LLM errors gracefully', async () => {
177
- const failingClient = createMockLLMClient();
178
- vi.mocked(failingClient.complete).mockRejectedValue(new Error('API Error'));
179
- const element = createMockElement();
180
- const onProgress = vi.fn();
181
- const result = await generateForElement(element, failingClient, {}, onProgress);
182
- expect(result.error).toContain('Failed to generate');
183
- expect(result.markdown).toBe('');
184
- expect(onProgress).toHaveBeenCalledWith(expect.objectContaining({ status: 'failed' }));
185
- });
186
- it('should use multi-language option when specified', async () => {
187
- const multiLangClient = createMockLLMClient({
188
- content: `---MARKDOWN---
189
- Multi-lang docs
190
- ---TYPESCRIPT---
191
- const x = 1
192
- ---PYTHON---
193
- x = 1
194
- ---END---`
195
- });
196
- const element = createMockElement();
197
- const result = await generateForElement(element, multiLangClient, { multiLanguage: true });
198
- expect(result.typescriptExample).toBeTruthy();
199
- expect(result.pythonExample).toBeTruthy();
200
- });
201
- });
202
- describe('generateForElements', () => {
203
- let mockClient;
204
- beforeEach(() => {
205
- mockClient = createMockLLMClient();
206
- });
207
- it('should generate docs for multiple elements', async () => {
208
- const elements = [
209
- createMockElement({ name: 'func1' }),
210
- createMockElement({ name: 'func2' }),
211
- createMockElement({ name: 'func3' })
212
- ];
213
- const results = await generateForElements(elements, mockClient, {});
214
- expect(results).toHaveLength(3);
215
- expect(mockClient.complete).toHaveBeenCalledTimes(3);
216
- });
217
- it('should call progress callback with correct counts', async () => {
218
- const elements = [
219
- createMockElement({ name: 'func1' }),
220
- createMockElement({ name: 'func2' })
221
- ];
222
- const onProgress = vi.fn();
223
- await generateForElements(elements, mockClient, { onProgress });
224
- expect(onProgress).toHaveBeenCalledWith(expect.objectContaining({ current: 1, total: 2 }));
225
- expect(onProgress).toHaveBeenCalledWith(expect.objectContaining({ current: 2, total: 2 }));
226
- });
227
- it('should handle empty elements array', async () => {
228
- const results = await generateForElements([], mockClient, {});
229
- expect(results).toHaveLength(0);
230
- expect(mockClient.complete).not.toHaveBeenCalled();
231
- });
232
- it('should continue processing after errors', async () => {
233
- const failOnSecond = createMockLLMClient();
234
- let callCount = 0;
235
- vi.mocked(failOnSecond.complete).mockImplementation(async () => {
236
- callCount++;
237
- if (callCount === 2) {
238
- throw new Error('API Error');
239
- }
240
- return {
241
- content: '---MARKDOWN---\nDocs\n---CODE---\ncode\n---END---',
242
- model: 'test',
243
- usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
244
- finishReason: 'stop'
245
- };
246
- });
247
- const elements = [
248
- createMockElement({ name: 'func1' }),
249
- createMockElement({ name: 'func2' }),
250
- createMockElement({ name: 'func3' })
251
- ];
252
- const results = await generateForElements(elements, failOnSecond, {});
253
- expect(results).toHaveLength(3);
254
- expect(results[1]?.error).toBeTruthy();
255
- expect(results[0]?.error).toBeUndefined();
256
- expect(results[2]?.error).toBeUndefined();
257
- });
258
- });
259
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,411 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import { mkdir, rm, readFile, readdir } from 'fs/promises';
3
- import { join } from 'path';
4
- import { tmpdir } from 'os';
5
- import { randomUUID } from 'crypto';
6
- import { groupDocsByFile, writeDocsToDirectory, writeLlmsTxt, writeDocsByTopic } from './writer.js';
7
- // Create test fixtures
8
- function createMockElement(overrides = {}) {
9
- return {
10
- kind: 'function',
11
- name: 'testFunction',
12
- signature: 'function testFunction(arg: string): void',
13
- parameters: [{ name: 'arg', type: 'string' }],
14
- returnType: 'void',
15
- docstring: 'Test docstring',
16
- filePath: '/test/src/file.ts',
17
- lineNumber: 1,
18
- isExported: true,
19
- isPublic: true,
20
- ...overrides
21
- };
22
- }
23
- function createMockDoc(overrides = {}) {
24
- return {
25
- element: createMockElement(overrides.element),
26
- markdown: 'Use this to perform a test operation. It helps you do things.',
27
- codeExample: 'const result = testFunction("hello")\nconsole.log(result)',
28
- codeLanguage: 'typescript',
29
- ...overrides
30
- };
31
- }
32
- describe('Writer', () => {
33
- describe('groupDocsByFile', () => {
34
- it('should group docs by their file path', () => {
35
- const docs = [
36
- createMockDoc({ element: { filePath: '/src/a.ts', name: 'func1' } }),
37
- createMockDoc({ element: { filePath: '/src/a.ts', name: 'func2' } }),
38
- createMockDoc({ element: { filePath: '/src/b.ts', name: 'func3' } })
39
- ];
40
- const result = groupDocsByFile(docs);
41
- expect(result).toHaveLength(2);
42
- expect(result.find(r => r.filePath === '/src/a.ts')?.docs).toHaveLength(2);
43
- expect(result.find(r => r.filePath === '/src/b.ts')?.docs).toHaveLength(1);
44
- });
45
- it('should handle empty docs array', () => {
46
- const result = groupDocsByFile([]);
47
- expect(result).toHaveLength(0);
48
- });
49
- it('should preserve doc properties when grouping', () => {
50
- const doc = createMockDoc({
51
- element: { filePath: '/src/test.ts', name: 'myFunc' },
52
- markdown: 'My documentation',
53
- codeExample: 'myFunc()'
54
- });
55
- const result = groupDocsByFile([doc]);
56
- expect(result[0]?.docs[0]?.markdown).toBe('My documentation');
57
- expect(result[0]?.docs[0]?.codeExample).toBe('myFunc()');
58
- });
59
- it('should handle single file with multiple functions', () => {
60
- const docs = [
61
- createMockDoc({ element: { filePath: '/src/utils.ts', name: 'func1' } }),
62
- createMockDoc({ element: { filePath: '/src/utils.ts', name: 'func2' } }),
63
- createMockDoc({ element: { filePath: '/src/utils.ts', name: 'func3' } }),
64
- createMockDoc({ element: { filePath: '/src/utils.ts', name: 'func4' } })
65
- ];
66
- const result = groupDocsByFile(docs);
67
- expect(result).toHaveLength(1);
68
- expect(result[0]?.docs).toHaveLength(4);
69
- });
70
- });
71
- describe('writeDocsToDirectory', () => {
72
- let tempDir;
73
- let sourceDir;
74
- beforeEach(async () => {
75
- tempDir = join(tmpdir(), `autodocs-test-${randomUUID()}`);
76
- sourceDir = join(tempDir, 'source');
77
- await mkdir(sourceDir, { recursive: true });
78
- });
79
- afterEach(async () => {
80
- try {
81
- await rm(tempDir, { recursive: true, force: true });
82
- }
83
- catch {
84
- // Ignore cleanup errors
85
- }
86
- });
87
- it('should write docs to output directory', async () => {
88
- const outputDir = join(tempDir, 'output');
89
- const results = [
90
- {
91
- filePath: join(sourceDir, 'utils.ts'),
92
- docs: [createMockDoc({ element: { name: 'helper', filePath: join(sourceDir, 'utils.ts') } })]
93
- }
94
- ];
95
- const { filesWritten, totalDocs } = await writeDocsToDirectory(results, outputDir, sourceDir);
96
- expect(filesWritten).toBe(1);
97
- expect(totalDocs).toBe(1);
98
- // Check file was created
99
- const files = await readdir(outputDir);
100
- expect(files).toContain('utils.md');
101
- expect(files).toContain('README.md');
102
- });
103
- it('should create nested directory structure', async () => {
104
- const outputDir = join(tempDir, 'output');
105
- await mkdir(join(sourceDir, 'nested', 'deep'), { recursive: true });
106
- const results = [
107
- {
108
- filePath: join(sourceDir, 'nested', 'deep', 'module.ts'),
109
- docs: [createMockDoc({
110
- element: { kind: 'function', name: 'deepFunc', filePath: join(sourceDir, 'nested', 'deep', 'module.ts') }
111
- })]
112
- }
113
- ];
114
- await writeDocsToDirectory(results, outputDir, sourceDir);
115
- const content = await readFile(join(outputDir, 'nested', 'deep', 'module.md'), 'utf-8');
116
- expect(content).toContain('# Module');
117
- expect(content).toContain('deepFunc');
118
- });
119
- it('should skip empty results', async () => {
120
- const outputDir = join(tempDir, 'output');
121
- const results = [
122
- { filePath: join(sourceDir, 'empty.ts'), docs: [] },
123
- {
124
- filePath: join(sourceDir, 'full.ts'),
125
- docs: [createMockDoc({ element: { name: 'func', filePath: join(sourceDir, 'full.ts') } })]
126
- }
127
- ];
128
- const { filesWritten } = await writeDocsToDirectory(results, outputDir, sourceDir);
129
- expect(filesWritten).toBe(1);
130
- const files = await readdir(outputDir);
131
- expect(files).toContain('full.md');
132
- expect(files).not.toContain('empty.md');
133
- });
134
- it('should generate README index file', async () => {
135
- const outputDir = join(tempDir, 'output');
136
- const results = [
137
- {
138
- filePath: join(sourceDir, 'api.ts'),
139
- docs: [createMockDoc({ element: { name: 'apiFunc', filePath: join(sourceDir, 'api.ts') } })]
140
- },
141
- {
142
- filePath: join(sourceDir, 'utils.ts'),
143
- docs: [
144
- createMockDoc({ element: { name: 'util1', filePath: join(sourceDir, 'utils.ts') } }),
145
- createMockDoc({ element: { name: 'util2', filePath: join(sourceDir, 'utils.ts') } })
146
- ]
147
- }
148
- ];
149
- await writeDocsToDirectory(results, outputDir, sourceDir);
150
- const readme = await readFile(join(outputDir, 'README.md'), 'utf-8');
151
- expect(readme).toContain('# API Documentation');
152
- expect(readme).toContain('api.ts');
153
- expect(readme).toContain('utils.ts');
154
- expect(readme).toContain('(1 elements)');
155
- expect(readme).toContain('(2 elements)');
156
- });
157
- it('should format title from filename', async () => {
158
- const outputDir = join(tempDir, 'output');
159
- const results = [
160
- {
161
- filePath: join(sourceDir, 'user_service.py'),
162
- docs: [createMockDoc({
163
- element: { name: 'getUser', filePath: join(sourceDir, 'user_service.py') }
164
- })]
165
- }
166
- ];
167
- await writeDocsToDirectory(results, outputDir, sourceDir);
168
- const content = await readFile(join(outputDir, 'user_service.md'), 'utf-8');
169
- expect(content).toContain('# User service');
170
- });
171
- it('should skip files outside source directory', async () => {
172
- const outputDir = join(tempDir, 'output');
173
- const results = [
174
- {
175
- filePath: '/outside/path/evil.ts', // Outside source directory
176
- docs: [createMockDoc({ element: { name: 'evil', filePath: '/outside/path/evil.ts' } })]
177
- },
178
- {
179
- filePath: join(sourceDir, 'good.ts'),
180
- docs: [createMockDoc({ element: { name: 'good', filePath: join(sourceDir, 'good.ts') } })]
181
- }
182
- ];
183
- const { filesWritten } = await writeDocsToDirectory(results, outputDir, sourceDir);
184
- expect(filesWritten).toBe(1);
185
- });
186
- });
187
- describe('writeLlmsTxt', () => {
188
- let tempDir;
189
- beforeEach(async () => {
190
- tempDir = join(tmpdir(), `autodocs-llms-${randomUUID()}`);
191
- await mkdir(tempDir, { recursive: true });
192
- });
193
- afterEach(async () => {
194
- try {
195
- await rm(tempDir, { recursive: true, force: true });
196
- }
197
- catch {
198
- // Ignore cleanup errors
199
- }
200
- });
201
- it('should write llms.txt file', async () => {
202
- const docs = [
203
- createMockDoc({ element: { name: 'func1', filePath: '/src/api.ts' } }),
204
- createMockDoc({ element: { name: 'func2', filePath: '/src/api.ts' } })
205
- ];
206
- await writeLlmsTxt(docs, tempDir);
207
- const content = await readFile(join(tempDir, 'llms.txt'), 'utf-8');
208
- expect(content).toContain('# API');
209
- expect(content).toContain('func1');
210
- expect(content).toContain('func2');
211
- });
212
- it('should use custom project name and description', async () => {
213
- const docs = [createMockDoc()];
214
- await writeLlmsTxt(docs, tempDir, {
215
- projectName: 'My Project',
216
- description: 'A custom description'
217
- });
218
- const content = await readFile(join(tempDir, 'llms.txt'), 'utf-8');
219
- expect(content).toContain('# My Project');
220
- expect(content).toContain('> A custom description');
221
- });
222
- it('should include overview section', async () => {
223
- const docs = [
224
- createMockDoc({ element: { name: 'func1', filePath: '/src/module1.ts' } }),
225
- createMockDoc({ element: { name: 'func2', filePath: '/src/module2.ts' } })
226
- ];
227
- await writeLlmsTxt(docs, tempDir);
228
- const content = await readFile(join(tempDir, 'llms.txt'), 'utf-8');
229
- expect(content).toContain('## Overview');
230
- expect(content).toContain('2 documented API elements');
231
- expect(content).toContain('2 modules');
232
- });
233
- it('should include quick reference section', async () => {
234
- const docs = [
235
- createMockDoc({
236
- element: { name: 'greet', kind: 'function', filePath: '/src/utils.ts' },
237
- markdown: 'Generate a greeting message. Use this for welcoming users.'
238
- })
239
- ];
240
- await writeLlmsTxt(docs, tempDir);
241
- const content = await readFile(join(tempDir, 'llms.txt'), 'utf-8');
242
- expect(content).toContain('## Quick Reference');
243
- expect(content).toContain('### utils');
244
- expect(content).toContain('`greet`: function');
245
- });
246
- it('should include API details section', async () => {
247
- const docs = [
248
- createMockDoc({
249
- element: {
250
- name: 'calculate',
251
- kind: 'function',
252
- signature: 'function calculate(a: number, b: number): number',
253
- filePath: '/src/math.ts'
254
- },
255
- codeExample: 'const sum = calculate(1, 2)'
256
- })
257
- ];
258
- await writeLlmsTxt(docs, tempDir);
259
- const content = await readFile(join(tempDir, 'llms.txt'), 'utf-8');
260
- expect(content).toContain('## API Details');
261
- expect(content).toContain('### calculate');
262
- expect(content).toContain('**Type:** function');
263
- expect(content).toContain('**File:**');
264
- expect(content).toContain('**Example:**');
265
- });
266
- it('should also write llms-full.md', async () => {
267
- const docs = [createMockDoc()];
268
- await writeLlmsTxt(docs, tempDir);
269
- const content = await readFile(join(tempDir, 'llms-full.md'), 'utf-8');
270
- expect(content).toContain('# API');
271
- expect(content).toContain('## testFunction');
272
- });
273
- it('should handle docs without markdown', async () => {
274
- const docs = [
275
- createMockDoc({
276
- element: { name: 'noDoc', filePath: '/src/test.ts' },
277
- markdown: ''
278
- })
279
- ];
280
- await writeLlmsTxt(docs, tempDir);
281
- const content = await readFile(join(tempDir, 'llms.txt'), 'utf-8');
282
- expect(content).toContain('noDoc');
283
- });
284
- it('should truncate long content', async () => {
285
- const longMarkdown = 'A'.repeat(1000);
286
- const docs = [
287
- createMockDoc({
288
- element: { name: 'longDoc', filePath: '/src/test.ts' },
289
- markdown: longMarkdown
290
- })
291
- ];
292
- await writeLlmsTxt(docs, tempDir);
293
- const content = await readFile(join(tempDir, 'llms.txt'), 'utf-8');
294
- // Content should be truncated to 500 chars
295
- expect(content.length).toBeLessThan(longMarkdown.length + 500);
296
- });
297
- });
298
- describe('writeDocsByTopic', () => {
299
- let tempDir;
300
- beforeEach(async () => {
301
- tempDir = join(tmpdir(), `autodocs-topic-${randomUUID()}`);
302
- await mkdir(tempDir, { recursive: true });
303
- });
304
- afterEach(async () => {
305
- try {
306
- await rm(tempDir, { recursive: true, force: true });
307
- }
308
- catch {
309
- // Ignore cleanup errors
310
- }
311
- });
312
- it('should organize docs by topic', async () => {
313
- const docs = [
314
- createMockDoc({
315
- element: { name: 'createTool', kind: 'function', filePath: '/src/tools/helper.ts' }
316
- }),
317
- createMockDoc({
318
- element: { name: 'saveMemory', kind: 'function', filePath: '/src/memory/store.ts' }
319
- })
320
- ];
321
- const { filesWritten, topics } = await writeDocsByTopic(docs, tempDir);
322
- expect(filesWritten).toBeGreaterThan(0);
323
- expect(topics.length).toBeGreaterThan(0);
324
- });
325
- it('should write index.mdx file', async () => {
326
- const docs = [
327
- createMockDoc({
328
- element: { name: 'coreFunc', kind: 'function', filePath: '/src/core/main.ts' }
329
- })
330
- ];
331
- await writeDocsByTopic(docs, tempDir);
332
- const content = await readFile(join(tempDir, 'index.mdx'), 'utf-8');
333
- expect(content).toContain('title: "API Reference"');
334
- expect(content).toContain('<CardGroup');
335
- });
336
- it('should write navigation config', async () => {
337
- const docs = [
338
- createMockDoc({
339
- element: { name: 'toolFunc', kind: 'function', filePath: '/src/tools.ts' }
340
- })
341
- ];
342
- await writeDocsByTopic(docs, tempDir);
343
- const navContent = await readFile(join(tempDir, '_navigation.json'), 'utf-8');
344
- const nav = JSON.parse(navContent);
345
- expect(nav).toHaveProperty('navigation');
346
- expect(nav).toHaveProperty('groups');
347
- });
348
- it('should write sidebar config', async () => {
349
- const docs = [
350
- createMockDoc({
351
- element: { name: 'utilFunc', kind: 'function', filePath: '/src/utils.ts' }
352
- })
353
- ];
354
- await writeDocsByTopic(docs, tempDir);
355
- const sidebarContent = await readFile(join(tempDir, '_sidebars.json'), 'utf-8');
356
- const sidebar = JSON.parse(sidebarContent);
357
- expect(sidebar).toHaveProperty('apiSidebar');
358
- });
359
- it('should include cross-references in topic files', async () => {
360
- const docs = [
361
- createMockDoc({
362
- element: {
363
- name: 'parentClass',
364
- kind: 'class',
365
- filePath: '/src/core.ts',
366
- sourceContext: 'class parentClass { childMethod() {} }'
367
- }
368
- }),
369
- createMockDoc({
370
- element: {
371
- name: 'childMethod',
372
- kind: 'method',
373
- parentClass: 'parentClass',
374
- filePath: '/src/core.ts'
375
- }
376
- })
377
- ];
378
- const { topics } = await writeDocsByTopic(docs, tempDir);
379
- expect(topics.length).toBeGreaterThan(0);
380
- });
381
- it('should return topic statistics', async () => {
382
- const docs = [
383
- createMockDoc({ element: { name: 'func1', filePath: '/src/a.ts' } }),
384
- createMockDoc({ element: { name: 'func2', filePath: '/src/b.ts' } }),
385
- createMockDoc({ element: { name: 'func3', filePath: '/src/c.ts' } })
386
- ];
387
- const { totalDocs, topics } = await writeDocsByTopic(docs, tempDir);
388
- expect(totalDocs).toBe(3);
389
- expect(topics.length).toBeGreaterThan(0);
390
- });
391
- it('should handle multi-language examples in topic output', async () => {
392
- const docs = [
393
- createMockDoc({
394
- element: { name: 'multiLangFunc', filePath: '/src/core.ts' },
395
- typescriptExample: 'const x = 1',
396
- pythonExample: 'x = 1'
397
- })
398
- ];
399
- await writeDocsByTopic(docs, tempDir);
400
- // Find the topic file that was written
401
- const files = await readdir(tempDir);
402
- const topicFile = files.find(f => f.endsWith('.md'));
403
- if (topicFile) {
404
- const content = await readFile(join(tempDir, topicFile), 'utf-8');
405
- expect(content).toContain('<CodeGroup>');
406
- expect(content).toContain('```typescript');
407
- expect(content).toContain('```python');
408
- }
409
- });
410
- });
411
- });
@@ -1 +0,0 @@
1
- export {};