Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .changeset/arrow-function-extraction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
"@lytics/dev-agent-core": minor
"@lytics/dev-agent": minor
---

feat(scanner): Extract arrow functions, function expressions, and exported constants

### New Features

**Arrow Function Extraction**
- Extract arrow functions assigned to `const`/`let` variables
- Extract function expressions assigned to variables
- Detect React hooks automatically (`use*` naming pattern)
- Detect async arrow functions

**Exported Constant Extraction**
- Extract exported `const` with object literal initializers (config objects)
- Extract exported `const` with array literal initializers (static lists)
- Extract exported `const` with call expression initializers (factories like `createContext()`)

### API Changes

**New DocumentType value:**
- Added `'variable'` to `DocumentType` union

**New metadata fields:**
- `isArrowFunction?: boolean` - true for arrow functions (vs function expressions)
- `isHook?: boolean` - true if name matches `/^use[A-Z]/` (React convention)
- `isAsync?: boolean` - true for async functions
- `isConstant?: boolean` - true for exported constants
- `constantKind?: 'object' | 'array' | 'value'` - kind of constant initializer

### Examples

Now extracts:
```typescript
export const useAuth = () => { ... } // Hook (isHook: true)
export const fetchData = async (url) => { ... } // Async (isAsync: true)
const validateEmail = (email: string) => ... // Utility function
export const API_CONFIG = { baseUrl: '...' } // Object constant
export const LANGUAGES = ['ts', 'js'] // Array constant
export const AppContext = createContext({}) // Factory constant
```

### Migration

No breaking changes. The new `'variable'` DocumentType is additive. Existing queries for `'function'`, `'class'`, etc. continue to work unchanged.

13 changes: 10 additions & 3 deletions packages/core/src/scanner/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Represents a single extracted code element or documentation section:
interface Document {
id: string; // Unique identifier: "file:name:line"
text: string; // Text to embed (for vector search)
type: DocumentType; // 'function' | 'class' | 'interface' | 'type' | 'method' | 'documentation'
type: DocumentType; // 'function' | 'class' | 'interface' | 'type' | 'method' | 'documentation' | 'variable'
language: string; // 'typescript' | 'javascript' | 'markdown'

metadata: {
Expand All @@ -67,6 +67,13 @@ interface Document {
signature?: string; // Full signature
exported: boolean; // Is it a public API?
docstring?: string; // Documentation comment

// Variable/function metadata (for type: 'variable')
isArrowFunction?: boolean; // True for arrow functions
isHook?: boolean; // True for React hooks (use* pattern)
isAsync?: boolean; // True for async functions
isConstant?: boolean; // True for exported constants
constantKind?: 'object' | 'array' | 'value'; // Kind of constant
};
}
```
Expand Down Expand Up @@ -364,8 +371,8 @@ const utilDocs = result.documents.filter(

| Language | Scanner | Extracts | Status |
|----------|---------|----------|--------|
| TypeScript | `TypeScriptScanner` | Functions, classes, methods, interfaces, types, JSDoc | ✅ Implemented |
| JavaScript | `TypeScriptScanner` | Functions, classes, methods, JSDoc | ✅ Implemented (via .ts scanner) |
| TypeScript | `TypeScriptScanner` | Functions, classes, methods, interfaces, types, arrow functions, exported constants, JSDoc | ✅ Implemented |
| JavaScript | `TypeScriptScanner` | Functions, classes, methods, arrow functions, exported constants, JSDoc | ✅ Implemented (via .ts scanner) |
| Markdown | `MarkdownScanner` | Documentation sections, code blocks | ✅ Implemented |
| Go | - | Functions, structs, interfaces | 🔄 Planned (tree-sitter) |
| Python | - | Functions, classes, docstrings | 🔄 Planned (tree-sitter) |
Expand Down
109 changes: 109 additions & 0 deletions packages/core/src/scanner/__tests__/fixtures/arrow-functions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/**
* Test fixtures for arrow function and function expression extraction.
* This file contains various patterns that should be extracted by the scanner.
*/

// Simple arrow function
const simpleArrow = () => {
return 'hello';
};

// Arrow function with parameters
const typedArrow = (name: string, age: number): string => {
return `${name} is ${age} years old`;
};

// Exported arrow function
export const exportedArrow = (value: number) => value * 2;

// Non-exported (private) helper
const privateHelper = (x: number) => x + 1;

// React-style hook (name starts with 'use')
export const useCustomHook = (initialValue: string) => {
const value = initialValue;
const setValue = (newValue: string) => newValue;
return { value, setValue };
};

// Async arrow function
export const fetchData = async (url: string) => {
const response = await fetch(url);
return response.json();
};

// Function expression (legacy style)
// biome-ignore lint/complexity/useArrowFunction: Testing function expression extraction
const legacyFunction = function (a: number, b: number) {
return a + b;
};

// Arrow function that calls other functions (for callee extraction)
const composedFunction = (input: string) => {
const trimmed = input.trim();
const upper = trimmed.toUpperCase();
return privateHelper(upper.length);
};

/**
* A well-documented arrow function.
* This should have its JSDoc extracted.
*/
const documentedArrow = (param: string) => {
return param.toLowerCase();
};

// ============================================
// EXPORTED CONSTANTS - Should be extracted
// ============================================

/**
* API configuration object.
* This should be extracted as an exported constant.
*/
export const API_CONFIG = {
baseUrl: '/api',
timeout: 5000,
retries: 3,
};

// Exported array constant
export const SUPPORTED_LANGUAGES = ['typescript', 'javascript', 'python', 'go'];

// Exported call expression (factory pattern)
// biome-ignore lint/suspicious/noEmptyBlockStatements: Test fixture
export const AppContext = (() => ({ value: null }))();

// Typed exported constant
export const THEME_CONFIG: { dark: boolean; primary: string } = {
dark: false,
primary: '#007bff',
};

// ============================================
// NON-EXPORTED - Should NOT be extracted
// ============================================
// biome-ignore lint/correctness/noUnusedVariables: Test fixtures for non-extraction

// Plain constant (primitive) - never extracted
const plainConstant = 42;

// Non-exported object - not extracted (only exported objects are extracted)
const configObject = {
apiUrl: '/api',
timeout: 5000,
};

// Non-exported array - not extracted
const colorList = ['red', 'green', 'blue'];

// Suppress unused warnings - these are test fixtures
void plainConstant;
void configObject;
void colorList;

// Exported primitive - NOT extracted (primitives have low semantic value)
export const API_ENDPOINT = 'https://api.example.com';

// Re-exported for testing
export { simpleArrow, typedArrow, composedFunction, documentedArrow, legacyFunction };
Loading