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.
- package/dist/auth/index.js +3 -3
- package/dist/cli.js +1 -1
- package/dist/commands/cron.js +0 -4
- package/dist/commands/generate/index.d.ts +3 -0
- package/dist/commands/generate/index.js +393 -0
- package/dist/commands/generate/scan.d.ts +41 -0
- package/dist/commands/generate/scan.js +256 -0
- package/dist/commands/generate/verify.d.ts +14 -0
- package/dist/commands/generate/verify.js +122 -0
- package/dist/commands/generate/write.d.ts +25 -0
- package/dist/commands/generate/write.js +120 -0
- package/dist/commands/import.js +4 -1
- package/dist/commands/llms-txt.js +6 -4
- package/dist/config/loader.d.ts +0 -1
- package/dist/config/loader.js +1 -1
- package/dist/generator/agents-md.d.ts +25 -0
- package/dist/generator/agents-md.js +122 -0
- package/dist/generator/index.d.ts +2 -0
- package/dist/generator/index.js +2 -0
- package/dist/generator/mdx-serializer.d.ts +11 -0
- package/dist/generator/mdx-serializer.js +135 -0
- package/dist/generator/organizer.d.ts +1 -16
- package/dist/generator/organizer.js +0 -38
- package/dist/generator/writer.js +5 -4
- package/dist/llm/proxy-client.d.ts +32 -0
- package/dist/llm/proxy-client.js +103 -0
- package/dist/scanner/csharp.d.ts +0 -4
- package/dist/scanner/csharp.js +9 -49
- package/dist/scanner/go.d.ts +0 -3
- package/dist/scanner/go.js +8 -35
- package/dist/scanner/java.d.ts +0 -4
- package/dist/scanner/java.js +9 -49
- package/dist/scanner/kotlin.d.ts +0 -3
- package/dist/scanner/kotlin.js +6 -33
- package/dist/scanner/php.d.ts +0 -10
- package/dist/scanner/php.js +11 -55
- package/dist/scanner/ruby.d.ts +0 -3
- package/dist/scanner/ruby.js +8 -38
- package/dist/scanner/rust.d.ts +0 -3
- package/dist/scanner/rust.js +10 -37
- package/dist/scanner/swift.d.ts +0 -3
- package/dist/scanner/swift.js +8 -35
- package/dist/scanner/utils.d.ts +41 -0
- package/dist/scanner/utils.js +97 -0
- package/dist/template/docs.json +5 -2
- package/dist/template/next.config.mjs +31 -0
- package/dist/template/package.json +5 -3
- package/dist/template/src/app/layout.tsx +13 -13
- package/dist/template/src/app/llms-full.md/route.ts +29 -0
- package/dist/template/src/app/llms.txt/route.ts +29 -0
- package/dist/template/src/app/md/[...slug]/route.ts +174 -0
- package/dist/template/src/app/reference/route.ts +22 -18
- package/dist/template/src/app/sitemap.ts +1 -1
- package/dist/template/src/components/ai-chat-impl.tsx +206 -0
- package/dist/template/src/components/ai-chat.tsx +20 -193
- package/dist/template/src/components/mdx/index.tsx +27 -4
- package/dist/template/src/lib/fonts.ts +135 -0
- package/dist/template/src/middleware.ts +101 -0
- package/dist/template/src/styles/globals.css +28 -20
- package/dist/utils/files.d.ts +0 -8
- package/dist/utils/files.js +0 -33
- package/package.json +1 -1
- package/dist/autofix/autofix.test.d.ts +0 -1
- package/dist/autofix/autofix.test.js +0 -487
- package/dist/commands/generate.d.ts +0 -9
- package/dist/commands/generate.js +0 -739
- package/dist/generator/generator.test.d.ts +0 -1
- package/dist/generator/generator.test.js +0 -259
- package/dist/generator/writer.test.d.ts +0 -1
- package/dist/generator/writer.test.js +0 -411
- package/dist/llm/llm.manual-test.d.ts +0 -1
- package/dist/llm/llm.manual-test.js +0 -112
- package/dist/llm/llm.mock-test.d.ts +0 -4
- package/dist/llm/llm.mock-test.js +0 -79
- package/dist/plugins/index.d.ts +0 -47
- package/dist/plugins/index.js +0 -181
- package/dist/scanner/content-type.test.d.ts +0 -1
- package/dist/scanner/content-type.test.js +0 -231
- package/dist/scanner/integration.test.d.ts +0 -4
- package/dist/scanner/integration.test.js +0 -180
- package/dist/scanner/scanner.test.d.ts +0 -1
- package/dist/scanner/scanner.test.js +0 -210
- package/dist/scanner/typescript.manual-test.d.ts +0 -1
- package/dist/scanner/typescript.manual-test.js +0 -112
- package/dist/template/src/app/docs/auth/page.mdx +0 -589
- package/dist/template/src/app/docs/autofix/page.mdx +0 -624
- package/dist/template/src/app/docs/cli/page.mdx +0 -217
- package/dist/template/src/app/docs/config/page.mdx +0 -428
- package/dist/template/src/app/docs/configuration/page.mdx +0 -86
- package/dist/template/src/app/docs/deployment/page.mdx +0 -112
- package/dist/template/src/app/docs/generator/generator.md +0 -504
- package/dist/template/src/app/docs/generator/organizer.md +0 -779
- package/dist/template/src/app/docs/generator/page.mdx +0 -613
- package/dist/template/src/app/docs/github/page.mdx +0 -502
- package/dist/template/src/app/docs/llm/anthropic-client.md +0 -549
- package/dist/template/src/app/docs/llm/index.md +0 -471
- package/dist/template/src/app/docs/llm/page.mdx +0 -428
- package/dist/template/src/app/docs/plugins/page.mdx +0 -1793
- package/dist/template/src/app/docs/pro/page.mdx +0 -121
- package/dist/template/src/app/docs/quickstart/page.mdx +0 -93
- package/dist/template/src/app/docs/scanner/content-type.md +0 -599
- package/dist/template/src/app/docs/scanner/index.md +0 -212
- package/dist/template/src/app/docs/scanner/page.mdx +0 -307
- package/dist/template/src/app/docs/scanner/python.md +0 -469
- package/dist/template/src/app/docs/scanner/python_parser.md +0 -1056
- package/dist/template/src/app/docs/scanner/rust.md +0 -325
- package/dist/template/src/app/docs/scanner/typescript.md +0 -201
- package/dist/template/src/app/icon.tsx +0 -29
- package/dist/utils/validation.d.ts +0 -1
- 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 {};
|