@pithyjs/codex 0.1.0-beta.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/LICENSE +21 -0
- package/README.md +4401 -0
- package/dist/annotations.d.ts +106 -0
- package/dist/annotations.js +306 -0
- package/dist/apply.d.ts +2 -0
- package/dist/apply.js +80 -0
- package/dist/changed-scope.d.ts +23 -0
- package/dist/changed-scope.js +31 -0
- package/dist/check.d.ts +2 -0
- package/dist/check.js +117 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +67 -0
- package/dist/env.d.ts +5 -0
- package/dist/env.js +35 -0
- package/dist/extract-cli.d.ts +23 -0
- package/dist/extract-cli.js +192 -0
- package/dist/extraction/breaking-changes.d.ts +66 -0
- package/dist/extraction/breaking-changes.js +352 -0
- package/dist/extraction/example-extractor.d.ts +55 -0
- package/dist/extraction/example-extractor.js +272 -0
- package/dist/extraction/index.d.ts +27 -0
- package/dist/extraction/index.js +31 -0
- package/dist/extraction/jsdoc-parser.d.ts +43 -0
- package/dist/extraction/jsdoc-parser.js +274 -0
- package/dist/extraction/pipeline.d.ts +54 -0
- package/dist/extraction/pipeline.js +526 -0
- package/dist/extraction/readme-sync.d.ts +108 -0
- package/dist/extraction/readme-sync.js +592 -0
- package/dist/extraction/snapshot-store.d.ts +40 -0
- package/dist/extraction/snapshot-store.js +153 -0
- package/dist/extraction/source-linker.d.ts +80 -0
- package/dist/extraction/source-linker.js +316 -0
- package/dist/extraction/test-example-extractor.d.ts +69 -0
- package/dist/extraction/test-example-extractor.js +400 -0
- package/dist/extraction/test-pattern-extractor.d.ts +68 -0
- package/dist/extraction/test-pattern-extractor.js +261 -0
- package/dist/extraction/testing-pyramid.d.ts +44 -0
- package/dist/extraction/testing-pyramid.js +163 -0
- package/dist/extraction/type-extractor.d.ts +34 -0
- package/dist/extraction/type-extractor.js +494 -0
- package/dist/extraction/types.d.ts +401 -0
- package/dist/extraction/types.js +34 -0
- package/dist/indexer.d.ts +1 -0
- package/dist/indexer.js +107 -0
- package/dist/llm.d.ts +8 -0
- package/dist/llm.js +75 -0
- package/dist/readme-sync-cli.d.ts +20 -0
- package/dist/readme-sync-cli.js +167 -0
- package/dist/review.d.ts +2 -0
- package/dist/review.js +93 -0
- package/dist/scan.d.ts +29 -0
- package/dist/scan.js +221 -0
- package/dist/schema.d.ts +169 -0
- package/dist/schema.js +70 -0
- package/dist/snapshot-cli.d.ts +45 -0
- package/dist/snapshot-cli.js +217 -0
- package/dist/sync-cli.d.ts +22 -0
- package/dist/sync-cli.js +154 -0
- package/dist/sync-pipeline.d.ts +58 -0
- package/dist/sync-pipeline.js +104 -0
- package/dist/sync.d.ts +2 -0
- package/dist/sync.js +318 -0
- package/dist/validate-cli.d.ts +20 -0
- package/dist/validate-cli.js +144 -0
- package/dist/validate.d.ts +76 -0
- package/dist/validate.js +183 -0
- package/dist/watch-cli.d.ts +21 -0
- package/dist/watch-cli.js +220 -0
- package/package.json +62 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @codex
|
|
3
|
+
* {
|
|
4
|
+
* "id": "pithy.codex.extraction",
|
|
5
|
+
* "title": "Code Extraction Pipeline",
|
|
6
|
+
* "category": "feature"
|
|
7
|
+
* }
|
|
8
|
+
*
|
|
9
|
+
* Multi-source extraction pipeline for documentation generation.
|
|
10
|
+
* Extracts JSDoc, examples, test patterns, and links to source code.
|
|
11
|
+
*/
|
|
12
|
+
export type { SourceLocation, ParamDoc, ReturnDoc, ExtractedExample, ParsedJSDoc, ExtractedTestCase, TestFileAnalysis, EnrichedApiEntry, TestReference, EnrichedComponent, ReadmeSection, ExtractionConfig, ExtractionResult, ExtractionStats, ExtractedTypeDefinition, TypeMember, TestExample, TestStatus, ExtendedExtractionResult, TestLevel, TestingRequirements, RiskLevel, } from './types.js';
|
|
13
|
+
export { DEFAULT_EXTRACTION_CONFIG } from './types.js';
|
|
14
|
+
export { parseJSDocComment, extractJSDocBlocks, linkJSDocToDeclarations, extractSignature, } from './jsdoc-parser.js';
|
|
15
|
+
export { extractExamples, validateExample, wrapDoctestInHarness, extractExamplesFromSource, generateRunnableExampleFile, } from './example-extractor.js';
|
|
16
|
+
export { extractTestCases, extractTestImports, identifyCoveredApis, analyzeTestFile, testCasesToReferences, matchTestToEntry, generateCoverageSummary, } from './test-pattern-extractor.js';
|
|
17
|
+
export type { DeclarationLocation } from './source-linker.js';
|
|
18
|
+
export { findDeclarationLocation, extractAllDeclarations, parseReadmeSections, linkReadmeToEntries, generateSourceLink, toRelativePath, enrichSourceLocation, } from './source-linker.js';
|
|
19
|
+
export { runExtractionPipeline, extractSingleFile, generateExtractionReport, exportToCodexFormat, } from './pipeline.js';
|
|
20
|
+
export { extractTypesFromFile, getTypeSignature, generateMethodTable, } from './type-extractor.js';
|
|
21
|
+
export { extractTestExamples, parseVitestOutput, matchExamplesToStatuses, generateExampleReport, validateTestExamples, } from './test-example-extractor.js';
|
|
22
|
+
export type { ReadmeSyncResult, ReadmeSyncConfig } from './types.js';
|
|
23
|
+
export { syncReadmeContent, syncAllReadmes } from './readme-sync.js';
|
|
24
|
+
export type { SnapshotEntry, ApiSnapshot, ChangeKind, ApiChange, SnapshotDiff, ChangeClassification, } from './types.js';
|
|
25
|
+
export { createApiSnapshot, snapshotFromTypeDefinitions, diffSnapshots, classifyChange, detectBreakingChanges, generateMigrationGuide, generateChangelog, } from './breaking-changes.js';
|
|
26
|
+
export { saveSnapshot, loadSnapshot, listSnapshots, getSnapshotPath, } from './snapshot-store.js';
|
|
27
|
+
export { getTestingDefaults, applyRiskModifiers, resolveTestingRequirements, computeTestingStatus, } from './testing-pyramid.js';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @codex
|
|
3
|
+
* {
|
|
4
|
+
* "id": "pithy.codex.extraction",
|
|
5
|
+
* "title": "Code Extraction Pipeline",
|
|
6
|
+
* "category": "feature"
|
|
7
|
+
* }
|
|
8
|
+
*
|
|
9
|
+
* Multi-source extraction pipeline for documentation generation.
|
|
10
|
+
* Extracts JSDoc, examples, test patterns, and links to source code.
|
|
11
|
+
*/
|
|
12
|
+
export { DEFAULT_EXTRACTION_CONFIG } from './types.js';
|
|
13
|
+
// JSDoc Parser
|
|
14
|
+
export { parseJSDocComment, extractJSDocBlocks, linkJSDocToDeclarations, extractSignature, } from './jsdoc-parser.js';
|
|
15
|
+
// Example Extractor
|
|
16
|
+
export { extractExamples, validateExample, wrapDoctestInHarness, extractExamplesFromSource, generateRunnableExampleFile, } from './example-extractor.js';
|
|
17
|
+
// Test Pattern Extractor
|
|
18
|
+
export { extractTestCases, extractTestImports, identifyCoveredApis, analyzeTestFile, testCasesToReferences, matchTestToEntry, generateCoverageSummary, } from './test-pattern-extractor.js';
|
|
19
|
+
export { findDeclarationLocation, extractAllDeclarations, parseReadmeSections, linkReadmeToEntries, generateSourceLink, toRelativePath, enrichSourceLocation, } from './source-linker.js';
|
|
20
|
+
// Pipeline
|
|
21
|
+
export { runExtractionPipeline, extractSingleFile, generateExtractionReport, exportToCodexFormat, } from './pipeline.js';
|
|
22
|
+
// Type Extractor (TypeScript Compiler API)
|
|
23
|
+
export { extractTypesFromFile, getTypeSignature, generateMethodTable, } from './type-extractor.js';
|
|
24
|
+
// Test Example Extractor (@codex:example markers)
|
|
25
|
+
export { extractTestExamples, parseVitestOutput, matchExamplesToStatuses, generateExampleReport, validateTestExamples, } from './test-example-extractor.js';
|
|
26
|
+
export { syncReadmeContent, syncAllReadmes } from './readme-sync.js';
|
|
27
|
+
export { createApiSnapshot, snapshotFromTypeDefinitions, diffSnapshots, classifyChange, detectBreakingChanges, generateMigrationGuide, generateChangelog, } from './breaking-changes.js';
|
|
28
|
+
// Snapshot Store (Version Persistence)
|
|
29
|
+
export { saveSnapshot, loadSnapshot, listSnapshots, getSnapshotPath, } from './snapshot-store.js';
|
|
30
|
+
// Testing Pyramid
|
|
31
|
+
export { getTestingDefaults, applyRiskModifiers, resolveTestingRequirements, computeTestingStatus, } from './testing-pyramid.js';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @codex
|
|
3
|
+
* {
|
|
4
|
+
* "id": "pithy.codex.extraction.jsdoc-parser",
|
|
5
|
+
* "title": "JSDoc/TSDoc Parser",
|
|
6
|
+
* "category": "compiler"
|
|
7
|
+
* }
|
|
8
|
+
*/
|
|
9
|
+
import type { ParsedJSDoc, SourceLocation } from './types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Parses a single JSDoc comment block
|
|
12
|
+
* @param comment - The JSDoc comment content (without delimiters)
|
|
13
|
+
* @param location - Source location of the comment
|
|
14
|
+
* @returns Parsed JSDoc structure
|
|
15
|
+
* @codexApi {"parent":"pithy.codex.extraction.jsdoc-parser","name":"parseJSDocComment","stability":"stable","signature":"(comment: string, location: SourceLocation) => ParsedJSDoc"}
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseJSDocComment(comment: string, location: SourceLocation): ParsedJSDoc;
|
|
18
|
+
/**
|
|
19
|
+
* Extracts all JSDoc blocks from a source file
|
|
20
|
+
* @param content - File content
|
|
21
|
+
* @param filePath - Path to the file for location tracking
|
|
22
|
+
* @returns Array of parsed JSDoc blocks with locations
|
|
23
|
+
* @codexApi {"parent":"pithy.codex.extraction.jsdoc-parser","name":"extractJSDocBlocks","stability":"stable","signature":"(content: string, filePath: string) => { jsdoc: ParsedJSDoc; followingCode: string }[]"}
|
|
24
|
+
*/
|
|
25
|
+
export declare function extractJSDocBlocks(content: string, filePath: string): {
|
|
26
|
+
jsdoc: ParsedJSDoc;
|
|
27
|
+
followingCode: string;
|
|
28
|
+
}[];
|
|
29
|
+
/**
|
|
30
|
+
* Links JSDoc blocks to their associated function/class declarations
|
|
31
|
+
* @param content - File content
|
|
32
|
+
* @param filePath - Path to the file
|
|
33
|
+
* @returns Map of declaration names to their JSDoc
|
|
34
|
+
* @codexApi {"parent":"pithy.codex.extraction.jsdoc-parser","name":"linkJSDocToDeclarations","stability":"stable","signature":"(content: string, filePath: string) => Map<string, ParsedJSDoc>"}
|
|
35
|
+
*/
|
|
36
|
+
export declare function linkJSDocToDeclarations(content: string, filePath: string): Map<string, ParsedJSDoc>;
|
|
37
|
+
/**
|
|
38
|
+
* Extracts function signature from source code
|
|
39
|
+
* @param code - Code following the JSDoc block
|
|
40
|
+
* @returns Extracted signature or undefined
|
|
41
|
+
* @codexApi {"parent":"pithy.codex.extraction.jsdoc-parser","name":"extractSignature","stability":"stable","signature":"(code: string) => string | undefined"}
|
|
42
|
+
*/
|
|
43
|
+
export declare function extractSignature(code: string): string | undefined;
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @codex
|
|
3
|
+
* {
|
|
4
|
+
* "id": "pithy.codex.extraction.jsdoc-parser",
|
|
5
|
+
* "title": "JSDoc/TSDoc Parser",
|
|
6
|
+
* "category": "compiler"
|
|
7
|
+
* }
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Regex patterns for JSDoc parsing
|
|
11
|
+
* Note: Tag regex uses atomic grouping simulation to prevent ReDoS
|
|
12
|
+
*/
|
|
13
|
+
// Require /** at line start (optional whitespace) to avoid matching
|
|
14
|
+
// /** inside line comments like: // delimiters (*, */, /**)
|
|
15
|
+
const JSDOC_BLOCK_REGEX = /(?:^|(?<=\n))\s*\/\*\*\s*([\s\S]*?)\s*\*\//g;
|
|
16
|
+
const EXAMPLE_CODE_BLOCK_REGEX = /```(\w*)\s*\n([\s\S]*?)\n\s*```/g;
|
|
17
|
+
/**
|
|
18
|
+
* Parses JSDoc tags using a safer line-by-line approach to avoid ReDoS
|
|
19
|
+
* @param tagContent - Content containing JSDoc tags
|
|
20
|
+
* @returns Array of parsed tag objects
|
|
21
|
+
*/
|
|
22
|
+
function parseJSDocTags(tagContent) {
|
|
23
|
+
const tags = [];
|
|
24
|
+
const lines = tagContent.split('\n');
|
|
25
|
+
let currentTag = null;
|
|
26
|
+
for (const line of lines) {
|
|
27
|
+
const trimmed = line.replace(/^\s*\*?\s*/, '');
|
|
28
|
+
// Check if this line starts a new tag
|
|
29
|
+
const tagMatch = trimmed.match(/^@(\w+)(?:\s+\{([^}]*)\})?\s*(\[?\w+(?:\.\w+)*\]?)?\s*(?:-\s*)?(.*)$/);
|
|
30
|
+
if (tagMatch) {
|
|
31
|
+
// Save previous tag if exists
|
|
32
|
+
if (currentTag) {
|
|
33
|
+
tags.push(currentTag);
|
|
34
|
+
}
|
|
35
|
+
currentTag = {
|
|
36
|
+
tagName: tagMatch[1],
|
|
37
|
+
type: tagMatch[2] || undefined,
|
|
38
|
+
name: tagMatch[3] || undefined,
|
|
39
|
+
description: tagMatch[4] || '',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
else if (currentTag && trimmed && !trimmed.startsWith('@')) {
|
|
43
|
+
// Continue previous tag's description
|
|
44
|
+
currentTag.description += '\n' + trimmed;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Don't forget the last tag
|
|
48
|
+
if (currentTag) {
|
|
49
|
+
tags.push(currentTag);
|
|
50
|
+
}
|
|
51
|
+
return tags;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Parses a single JSDoc comment block
|
|
55
|
+
* @param comment - The JSDoc comment content (without delimiters)
|
|
56
|
+
* @param location - Source location of the comment
|
|
57
|
+
* @returns Parsed JSDoc structure
|
|
58
|
+
* @codexApi {"parent":"pithy.codex.extraction.jsdoc-parser","name":"parseJSDocComment","stability":"stable","signature":"(comment: string, location: SourceLocation) => ParsedJSDoc"}
|
|
59
|
+
*/
|
|
60
|
+
export function parseJSDocComment(comment, location) {
|
|
61
|
+
// Clean up the comment: remove leading asterisks and normalize whitespace
|
|
62
|
+
const cleanedComment = comment
|
|
63
|
+
.split('\n')
|
|
64
|
+
.map(line => line.replace(/^\s*\*\s?/, ''))
|
|
65
|
+
.join('\n')
|
|
66
|
+
.trim();
|
|
67
|
+
const result = {
|
|
68
|
+
description: '',
|
|
69
|
+
params: [],
|
|
70
|
+
examples: [],
|
|
71
|
+
tags: {},
|
|
72
|
+
location,
|
|
73
|
+
};
|
|
74
|
+
// Extract the main description (text before the first tag at line start).
|
|
75
|
+
// Mid-sentence @mentions (e.g. "parses @codex annotations") are not tags.
|
|
76
|
+
const firstTagIndex = cleanedComment.search(/^@\w+/m);
|
|
77
|
+
if (firstTagIndex === -1) {
|
|
78
|
+
result.description = cleanedComment;
|
|
79
|
+
}
|
|
80
|
+
else if (firstTagIndex > 0) {
|
|
81
|
+
result.description = cleanedComment.slice(0, firstTagIndex).trim();
|
|
82
|
+
}
|
|
83
|
+
// Parse all tags using safer line-by-line approach
|
|
84
|
+
const tagContent = firstTagIndex >= 0 ? cleanedComment.slice(firstTagIndex) : '';
|
|
85
|
+
const parsedTags = parseJSDocTags(tagContent);
|
|
86
|
+
for (const { tagName, type, name, description } of parsedTags) {
|
|
87
|
+
const cleanDesc = (description || '').trim();
|
|
88
|
+
switch (tagName.toLowerCase()) {
|
|
89
|
+
case 'param':
|
|
90
|
+
case 'parameter':
|
|
91
|
+
case 'arg':
|
|
92
|
+
case 'argument':
|
|
93
|
+
result.params.push(parseParam(type, name, cleanDesc));
|
|
94
|
+
break;
|
|
95
|
+
case 'returns':
|
|
96
|
+
case 'return':
|
|
97
|
+
result.returns = parseReturn(type, cleanDesc);
|
|
98
|
+
break;
|
|
99
|
+
case 'example':
|
|
100
|
+
result.examples.push(...parseExamples(cleanDesc, location));
|
|
101
|
+
break;
|
|
102
|
+
case 'throws':
|
|
103
|
+
case 'exception':
|
|
104
|
+
result.throws = result.throws || [];
|
|
105
|
+
result.throws.push(cleanDesc);
|
|
106
|
+
break;
|
|
107
|
+
case 'see':
|
|
108
|
+
result.see = result.see || [];
|
|
109
|
+
result.see.push(cleanDesc);
|
|
110
|
+
break;
|
|
111
|
+
case 'since':
|
|
112
|
+
result.since = cleanDesc;
|
|
113
|
+
break;
|
|
114
|
+
case 'deprecated':
|
|
115
|
+
result.deprecated = cleanDesc || true;
|
|
116
|
+
break;
|
|
117
|
+
default:
|
|
118
|
+
// Store unknown tags for extensibility
|
|
119
|
+
if (!result.tags[tagName]) {
|
|
120
|
+
result.tags[tagName] = [];
|
|
121
|
+
}
|
|
122
|
+
result.tags[tagName].push(cleanDesc);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Parses a @param tag into structured data
|
|
129
|
+
*/
|
|
130
|
+
function parseParam(type, name, description) {
|
|
131
|
+
const isOptional = name?.startsWith('[') || false;
|
|
132
|
+
let cleanName = name?.replace(/^\[|\]$/g, '') || '';
|
|
133
|
+
let defaultValue;
|
|
134
|
+
// Handle default values: [param=default]
|
|
135
|
+
if (cleanName.includes('=')) {
|
|
136
|
+
const [paramName, defVal] = cleanName.split('=');
|
|
137
|
+
cleanName = paramName;
|
|
138
|
+
defaultValue = defVal;
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
name: cleanName,
|
|
142
|
+
type: type || undefined,
|
|
143
|
+
description,
|
|
144
|
+
optional: isOptional,
|
|
145
|
+
defaultValue,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Parses a @returns tag into structured data
|
|
150
|
+
*/
|
|
151
|
+
function parseReturn(type, description) {
|
|
152
|
+
return {
|
|
153
|
+
type: type || undefined,
|
|
154
|
+
description,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Parses @example content into ExtractedExample objects
|
|
159
|
+
*/
|
|
160
|
+
function parseExamples(content, baseLocation) {
|
|
161
|
+
const examples = [];
|
|
162
|
+
// Reset regex state
|
|
163
|
+
EXAMPLE_CODE_BLOCK_REGEX.lastIndex = 0;
|
|
164
|
+
let match;
|
|
165
|
+
while ((match = EXAMPLE_CODE_BLOCK_REGEX.exec(content)) !== null) {
|
|
166
|
+
const [, language, code] = match;
|
|
167
|
+
const isDoctest = language?.includes('doctest') || false;
|
|
168
|
+
const lang = language?.replace('doctest', '').trim() || 'typescript';
|
|
169
|
+
examples.push({
|
|
170
|
+
code: code.trim(),
|
|
171
|
+
language: lang || 'typescript',
|
|
172
|
+
runnable: isDoctest || lang === 'ts' || lang === 'typescript',
|
|
173
|
+
isDoctest,
|
|
174
|
+
location: baseLocation,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
// If no code blocks found, treat the whole content as an example
|
|
178
|
+
if (examples.length === 0 && content.trim()) {
|
|
179
|
+
// Strip JSDoc comment delimiter artifacts that leak in when
|
|
180
|
+
// two adjacent /** */ blocks are captured as one.
|
|
181
|
+
// Only remove lines that are exactly delimiters (*, */, /**), not blank lines.
|
|
182
|
+
const cleanedCode = content
|
|
183
|
+
.split('\n')
|
|
184
|
+
.filter(l => !/^\s*\*\/$/.test(l) &&
|
|
185
|
+
!/^\s*\/\*\*?\s*$/.test(l) &&
|
|
186
|
+
!/^\s*\*\s*$/.test(l))
|
|
187
|
+
.join('\n')
|
|
188
|
+
.trim();
|
|
189
|
+
if (cleanedCode) {
|
|
190
|
+
examples.push({
|
|
191
|
+
code: cleanedCode,
|
|
192
|
+
language: 'typescript',
|
|
193
|
+
runnable: false,
|
|
194
|
+
isDoctest: false,
|
|
195
|
+
location: baseLocation,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return examples;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Extracts all JSDoc blocks from a source file
|
|
203
|
+
* @param content - File content
|
|
204
|
+
* @param filePath - Path to the file for location tracking
|
|
205
|
+
* @returns Array of parsed JSDoc blocks with locations
|
|
206
|
+
* @codexApi {"parent":"pithy.codex.extraction.jsdoc-parser","name":"extractJSDocBlocks","stability":"stable","signature":"(content: string, filePath: string) => { jsdoc: ParsedJSDoc; followingCode: string }[]"}
|
|
207
|
+
*/
|
|
208
|
+
export function extractJSDocBlocks(content, filePath) {
|
|
209
|
+
const results = [];
|
|
210
|
+
// Reset regex state
|
|
211
|
+
JSDOC_BLOCK_REGEX.lastIndex = 0;
|
|
212
|
+
let match;
|
|
213
|
+
while ((match = JSDOC_BLOCK_REGEX.exec(content)) !== null) {
|
|
214
|
+
const [fullMatch, commentContent] = match;
|
|
215
|
+
const startIndex = match.index;
|
|
216
|
+
// Calculate line number
|
|
217
|
+
const textBefore = content.slice(0, startIndex);
|
|
218
|
+
const lineNumber = textBefore.split('\n').length;
|
|
219
|
+
// Find the code following this JSDoc block
|
|
220
|
+
const afterMatch = content.slice(startIndex + fullMatch.length);
|
|
221
|
+
const nextLineMatch = afterMatch.match(/^\s*\n?\s*(.+)/);
|
|
222
|
+
const followingCode = nextLineMatch ? nextLineMatch[1] : '';
|
|
223
|
+
const location = {
|
|
224
|
+
file: filePath,
|
|
225
|
+
line: lineNumber,
|
|
226
|
+
};
|
|
227
|
+
const jsdoc = parseJSDocComment(commentContent, location);
|
|
228
|
+
results.push({ jsdoc, followingCode });
|
|
229
|
+
}
|
|
230
|
+
return results;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Links JSDoc blocks to their associated function/class declarations
|
|
234
|
+
* @param content - File content
|
|
235
|
+
* @param filePath - Path to the file
|
|
236
|
+
* @returns Map of declaration names to their JSDoc
|
|
237
|
+
* @codexApi {"parent":"pithy.codex.extraction.jsdoc-parser","name":"linkJSDocToDeclarations","stability":"stable","signature":"(content: string, filePath: string) => Map<string, ParsedJSDoc>"}
|
|
238
|
+
*/
|
|
239
|
+
export function linkJSDocToDeclarations(content, filePath) {
|
|
240
|
+
const linked = new Map();
|
|
241
|
+
const blocks = extractJSDocBlocks(content, filePath);
|
|
242
|
+
for (const { jsdoc, followingCode } of blocks) {
|
|
243
|
+
// Try to extract declaration name from following code
|
|
244
|
+
const declarationMatch = followingCode.match(/(?:export\s+)?(?:async\s+)?(?:function|class|const|let|var|interface|type)\s+(\w+)|(\w+)\s*[=:]\s*(?:function|\(|async)/);
|
|
245
|
+
if (declarationMatch) {
|
|
246
|
+
const name = declarationMatch[1] || declarationMatch[2];
|
|
247
|
+
if (name) {
|
|
248
|
+
linked.set(name, jsdoc);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return linked;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Extracts function signature from source code
|
|
256
|
+
* @param code - Code following the JSDoc block
|
|
257
|
+
* @returns Extracted signature or undefined
|
|
258
|
+
* @codexApi {"parent":"pithy.codex.extraction.jsdoc-parser","name":"extractSignature","stability":"stable","signature":"(code: string) => string | undefined"}
|
|
259
|
+
*/
|
|
260
|
+
export function extractSignature(code) {
|
|
261
|
+
// Match function declarations
|
|
262
|
+
const funcMatch = code.match(/(?:export\s+)?(?:async\s+)?function\s+\w+\s*(<[^>]+>)?\s*\(([^)]*)\)\s*(?::\s*([^{]+))?/);
|
|
263
|
+
if (funcMatch) {
|
|
264
|
+
const [, generics, params, returnType] = funcMatch;
|
|
265
|
+
return `${generics || ''}(${params})${returnType ? ` => ${returnType.trim()}` : ''}`;
|
|
266
|
+
}
|
|
267
|
+
// Match arrow functions
|
|
268
|
+
const arrowMatch = code.match(/(?:const|let|var)\s+\w+\s*(?::\s*[^=]+)?\s*=\s*(?:async\s+)?(?:<[^>]+>)?\s*\(([^)]*)\)\s*(?::\s*([^=]+))?\s*=>/);
|
|
269
|
+
if (arrowMatch) {
|
|
270
|
+
const [, params, returnType] = arrowMatch;
|
|
271
|
+
return `(${params})${returnType ? ` => ${returnType.trim()}` : ''}`;
|
|
272
|
+
}
|
|
273
|
+
return undefined;
|
|
274
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @codex
|
|
3
|
+
* {
|
|
4
|
+
* "id": "pithy.codex.extraction.pipeline",
|
|
5
|
+
* "title": "Extraction Pipeline",
|
|
6
|
+
* "category": "feature"
|
|
7
|
+
* }
|
|
8
|
+
*/
|
|
9
|
+
import { type ExtractionConfig, type ExtractionResult, type EnrichedComponent, type ExtendedExtractionResult } from './types.js';
|
|
10
|
+
/**
|
|
11
|
+
* @codexApi {"parent":"pithy.codex.extraction.pipeline","name":"runExtractionPipeline","stability":"stable","signature":"(config?: Partial<ExtractionConfig>) => Promise<ExtendedExtractionResult>"}
|
|
12
|
+
*
|
|
13
|
+
* Runs the complete multi-source extraction pipeline
|
|
14
|
+
* @param config - Configuration options
|
|
15
|
+
* @returns Extraction result with all enriched data including types and test examples
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const result = await runExtractionPipeline({
|
|
20
|
+
* root: '/path/to/project',
|
|
21
|
+
* extractRunnableExamples: true,
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* console.log(`Found ${result.stats.componentsFound} components`);
|
|
25
|
+
* console.log(`Extracted ${result.typeDefinitions.length} types`);
|
|
26
|
+
* console.log(`Found ${result.testExamples.length} test examples`);
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function runExtractionPipeline(config?: Partial<ExtractionConfig>): Promise<ExtendedExtractionResult>;
|
|
30
|
+
/**
|
|
31
|
+
* @codexApi {"parent":"pithy.codex.extraction.pipeline","name":"extractSingleFile","stability":"stable","signature":"(filePath: string, content?: string) => Promise<EnrichedComponent | null>"}
|
|
32
|
+
*
|
|
33
|
+
* Extracts documentation from a single source file
|
|
34
|
+
* @param filePath - Path to the source file
|
|
35
|
+
* @param content - Optional file content (will read if not provided)
|
|
36
|
+
* @returns Enriched component or null if no codex annotation found
|
|
37
|
+
*/
|
|
38
|
+
export declare function extractSingleFile(filePath: string, content?: string): Promise<EnrichedComponent | null>;
|
|
39
|
+
/**
|
|
40
|
+
* @codexApi {"parent":"pithy.codex.extraction.pipeline","name":"generateExtractionReport","stability":"stable","signature":"(result: ExtractionResult) => string"}
|
|
41
|
+
*
|
|
42
|
+
* Generates a human-readable report from extraction results
|
|
43
|
+
* @param result - Extraction result
|
|
44
|
+
* @returns Formatted report string
|
|
45
|
+
*/
|
|
46
|
+
export declare function generateExtractionReport(result: ExtractionResult): string;
|
|
47
|
+
/**
|
|
48
|
+
* @codexApi {"parent":"pithy.codex.extraction.pipeline","name":"exportToCodexFormat","stability":"stable","signature":"(result: ExtractionResult) => object"}
|
|
49
|
+
*
|
|
50
|
+
* Exports extraction results to codex-compatible JSON format
|
|
51
|
+
* @param result - Extraction result
|
|
52
|
+
* @returns Object suitable for codex.index.json
|
|
53
|
+
*/
|
|
54
|
+
export declare function exportToCodexFormat(result: ExtractionResult): object;
|