Skip to content

Commit 8a15069

Browse files
committed
feat: code extraction from astro files
1 parent ef301e3 commit 8a15069

6 files changed

Lines changed: 297 additions & 60 deletions

File tree

eslint.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default [
1313
'docs/architecture/**',
1414
'tools/**',
1515
'site/**',
16-
// `src/extractors/builtin/basic-astro`,
16+
`src/extractors/builtin/basic-astro`,
1717
],
1818
},
1919

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/**
2+
* @module basic-astro
3+
* Extract TypeScript/JavaScript code from Astro frontmatter
4+
* Reuses basic-node extractors by parsing frontmatter as TypeScript
5+
*/
6+
7+
import { Project, ts } from 'ts-morph';
8+
import { createLogger } from '../../../core/logger.js';
9+
import { extractClasses } from '../basic-node/class-extractor.js';
10+
import {
11+
extractFunctions,
12+
extractArrowFunctions,
13+
} from '../basic-node/function-extractor.js';
14+
import { extractTypeAliases, extractInterfaces } from '../basic-node/type-extractor.js';
15+
import type {
16+
ExtractedClass,
17+
ExtractedFunction,
18+
ExtractedType,
19+
ExtractedInterface,
20+
} from '../basic-node/types.js';
21+
22+
const log = createLogger({ context: 'AstroCodeExtractor' });
23+
24+
export interface CodeExtractionResult {
25+
classes: ExtractedClass[];
26+
functions: ExtractedFunction[];
27+
types: ExtractedType[];
28+
interfaces: ExtractedInterface[];
29+
}
30+
31+
/**
32+
* Extract TypeScript/JavaScript code from Astro frontmatter
33+
* Parses the frontmatter as TypeScript and uses basic-node extractors
34+
*
35+
* @param frontmatter - The frontmatter content (code between --- markers)
36+
* @param filePath - Original Astro file path (for error reporting)
37+
* @returns Extracted code items (classes, functions, types, interfaces)
38+
*/
39+
export function extractCodeFromFrontmatter(
40+
frontmatter: string,
41+
filePath: string,
42+
): CodeExtractionResult {
43+
// Return empty result if no frontmatter
44+
if (!frontmatter || frontmatter.trim().length === 0) {
45+
return {
46+
classes: [],
47+
functions: [],
48+
types: [],
49+
interfaces: [],
50+
};
51+
}
52+
53+
try {
54+
// Create a ts-morph project to parse the frontmatter
55+
const project = new Project({
56+
compilerOptions: {
57+
allowJs: true,
58+
jsx: ts.JsxEmit.React,
59+
target: ts.ScriptTarget.ESNext,
60+
module: ts.ModuleKind.ESNext,
61+
},
62+
skipAddingFilesFromTsConfig: true,
63+
useInMemoryFileSystem: true,
64+
});
65+
66+
// Create a virtual .ts file with the frontmatter content
67+
// Use original file path with .ts extension for better error messages
68+
const virtualPath = filePath.replace(/\.astro$/, '.frontmatter.ts');
69+
const sourceFile = project.createSourceFile(virtualPath, frontmatter);
70+
71+
// Extract code using basic-node extractors
72+
const classes = extractClasses(sourceFile);
73+
const regularFunctions = extractFunctions(sourceFile);
74+
const arrowFunctions = extractArrowFunctions(sourceFile);
75+
const functions = [...regularFunctions, ...arrowFunctions];
76+
const types = extractTypeAliases(sourceFile);
77+
const interfaces = extractInterfaces(sourceFile);
78+
79+
log.debug(
80+
`Extracted from ${filePath} frontmatter: ${classes.length} classes, ${functions.length} functions, ${types.length} types, ${interfaces.length} interfaces`,
81+
);
82+
83+
return {
84+
classes,
85+
functions,
86+
types,
87+
interfaces,
88+
};
89+
} catch (error) {
90+
const errorMsg = error instanceof Error ? error.message : String(error);
91+
log.warn(`Failed to extract code from ${filePath} frontmatter: ${errorMsg}`);
92+
93+
// Return empty result on error
94+
return {
95+
classes: [],
96+
functions: [],
97+
types: [],
98+
interfaces: [],
99+
};
100+
}
101+
}

src/extractors/builtin/basic-astro/file-parser.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
extractFileActors,
1313
extractFileRelationships,
1414
} from './component-detector.js';
15+
import { extractCodeFromFrontmatter } from './code-extractor.js';
1516
import type { FileExtraction, ExtractedComponent } from './types.js';
1617

1718
const log = createLogger({ context: 'AstroFileParser' });
@@ -55,15 +56,20 @@ export async function parseFiles(filePaths: string[]): Promise<FileExtraction[]>
5556
// Extract component usage from template
5657
const componentUsage = extractComponentUsage(content, imports, filePath);
5758

59+
// Extract TypeScript/JavaScript code from frontmatter
60+
const codeExtraction = extractCodeFromFrontmatter(frontmatter, filePath);
61+
5862
results.push({
5963
filePath,
6064
language: 'astro',
6165
component,
6266
actors,
6367
relationships,
6468
components: componentUsage,
65-
functions: [], // Will be populated from frontmatter TS/JS
66-
classes: [], // Will be populated from frontmatter TS/JS
69+
functions: codeExtraction.functions,
70+
classes: codeExtraction.classes,
71+
types: codeExtraction.types,
72+
interfaces: codeExtraction.interfaces,
6773
imports,
6874
parseError:
6975
parseResult.diagnostics && parseResult.diagnostics.length > 0
@@ -78,6 +84,8 @@ export async function parseFiles(filePaths: string[]): Promise<FileExtraction[]>
7884
filePath,
7985
language: 'astro',
8086
component: undefined,
87+
types: [],
88+
interfaces: [],
8189
actors: [],
8290
relationships: [],
8391
components: [],

src/extractors/builtin/basic-astro/types.ts

Lines changed: 28 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,32 @@
44
* Intermediate representations before mapping to ArchletteIR
55
*/
66

7+
// Import code-related types from basic-node (reused for frontmatter analysis)
8+
import type {
9+
ExtractedClass,
10+
ExtractedFunction,
11+
ExtractedType,
12+
ExtractedInterface,
13+
ExtractedMethod,
14+
ParameterInfo,
15+
DocInfo,
16+
DeprecationInfo,
17+
SourceLocation,
18+
} from '../basic-node/types.js';
19+
20+
// Re-export for external use
21+
export type {
22+
ExtractedClass,
23+
ExtractedFunction,
24+
ExtractedType,
25+
ExtractedInterface,
26+
ExtractedMethod,
27+
ParameterInfo,
28+
DocInfo,
29+
DeprecationInfo,
30+
SourceLocation,
31+
};
32+
733
export interface ExtractorInputs {
834
include?: string[];
935
exclude?: string[];
@@ -46,6 +72,8 @@ export interface FileExtraction {
4672
components: ExtractedComponent[];
4773
functions: ExtractedFunction[];
4874
classes: ExtractedClass[];
75+
types: ExtractedType[];
76+
interfaces: ExtractedInterface[];
4977
imports: ExtractedImport[];
5078
parseError?: string;
5179
packageInfo?: PackageInfo;
@@ -79,69 +107,13 @@ export interface ExtractedSlot {
79107
location: SourceLocation;
80108
}
81109

82-
export interface ExtractedClass {
83-
name: string;
84-
isExported: boolean;
85-
location: SourceLocation;
86-
documentation?: DocInfo;
87-
methods: ExtractedMethod[];
88-
}
89-
90-
export interface ExtractedMethod {
91-
name: string;
92-
visibility: 'public' | 'private' | 'protected';
93-
isStatic: boolean;
94-
isAsync: boolean;
95-
location: SourceLocation;
96-
documentation?: DocInfo;
97-
parameters: ParameterInfo[];
98-
returnType?: string;
99-
}
100-
101-
export interface ExtractedFunction {
102-
name: string;
103-
isExported: boolean;
104-
isAsync: boolean;
105-
location: SourceLocation;
106-
documentation?: DocInfo;
107-
parameters: ParameterInfo[];
108-
returnType?: string;
109-
}
110-
111110
export interface ExtractedImport {
112111
source: string;
113112
importedNames: string[];
114113
isDefault: boolean;
115114
isNamespace: boolean;
116115
}
117116

118-
export interface ParameterInfo {
119-
name: string;
120-
type?: string;
121-
description?: string;
122-
optional: boolean;
123-
defaultValue?: string;
124-
}
125-
126-
export interface DocInfo {
127-
summary?: string;
128-
details?: string;
129-
examples?: string[];
130-
remarks?: string[];
131-
seeAlso?: string[];
132-
}
133-
134-
export interface DeprecationInfo {
135-
reason?: string;
136-
alternative?: string;
137-
}
138-
139-
export interface SourceLocation {
140-
filePath: string;
141-
line: number;
142-
column: number;
143-
}
144-
145117
/**
146118
* Output structure from Astro compiler
147119
*/

0 commit comments

Comments
 (0)