elastic / elastic/semantic-code-search-indexer

feat: Add Export Alias Tracking for TypeScript/JavaScript

Open
#69 0 comments 1 reaction 0 assignees View on GitHub
Dominant language
TypeScript
Stars
19
Forks
10
PR merge metrics
No merged PRs in 30d

Description

# Enhancement: Add Export Alias Tracking for TypeScript/JavaScript

## Overview
Enhance the export detection to track both the original name and alias name for re-exported symbols. Currently, when code uses `export { originalName as aliasName }`, we only capture the alias. Tracking both enables better dependency analysis and code navigation.

## Current Behavior

For re-exports with aliasing:
```typescript
export { MyClass as MyClassAlias } from './module';
export { foo as bar };
```

The current implementation only captures the **alias name** (`MyClassAlias`, `bar`), but not the original name (`MyClass`, `foo`).

## Desired Behavior

Capture both the original name and the alias to enable full tracking:

```typescript
{
"exports": [
{
"name": "MyClassAlias", // What's exported
"type": "named",
"original": "MyClass", // Where it comes from (NEW)
"target": "src/module" // Module path (for re-exports)
}
]
}
```

## Implementation Plan

### 1. Update Type Definition ✅
**File**: `src/utils/elasticsearch.ts`

Add `original` field to export structure:
```typescript
export interface ExportInfo {
name: string; // Exported symbol name (alias if aliased)
type: 'named' | 'default' | 'namespace'; // Export type
target?: string; // For re-exports: source module path
original?: string; // For aliased exports: original symbol name
}

export interface CodeChunk {
// ... existing fields ...
exports?: ExportInfo[];
}
```

### 2. Update Elasticsearch Mapping ✅
**File**: `src/utils/elasticsearch.ts` (in `createIndex` function)

Add `original` field to the nested exports mapping:
```typescript
exports: {
type: 'nested',
properties: {
name: { type: 'keyword' },
type: { type: 'keyword' },
target: { type: 'keyword' },
original: { type: 'keyword' }, // NEW
},
}
```

### 3. Update TypeScript Export Queries ✅
**File**: `src/languages/typescript.ts`

Update the export specifier queries to capture both names:
```typescript
// Current query only captures alias:
'(export_statement (export_clause (export_specifier name: (identifier) alias: (identifier) @export.name)))'

// Update to capture both:
'(export_statement (export_clause (export_specifier name: (identifier) @export.original alias: (identifier) @export.name)))'

// For non-aliased exports, keep existing:
'(export_statement (export_clause (export_specifier name: (identifier) @export.name)))'
```

### 4. Update JavaScript Export Queries ✅
**File**: `src/languages/javascript.ts`

Apply the same updates as TypeScript since JavaScript uses identical export syntax.

### 5. Update Parser Logic ✅
**File**: `src/utils/parser.ts` (in `parseWithTreeSitter` method)

Update the export processing to capture and store the original name:
```typescript
for (const capture of match.captures) {
if (capture.name === 'export.name') {
exportName = capture.node.text;
} else if (capture.name === 'export.original') {
exportOriginal = capture.node.text; // NEW
}
// ... existing logic
}

// When adding to exportsByLine:
exportsByLine[line].push({
name: exportName,
type: exportType,
...(exportTarget && { target: exportTarget }),
...(exportOriginal && { original: exportOriginal }), // NEW
});
```

### 6. Add Tests ✅
**File**: `tests/parser.test.ts`

Add test cases for aliased exports:
```typescript
it('should extract export aliases correctly', () => {
// Test file with:
// export { MyClass as MyClassAlias } from './module';
// export { foo as bar };

const allExports = chunks.flatMap(chunk => chunk.exports || []);

expect(allExports).toEqual(
expect.arrayContaining([
expect.objectContaining({
name: 'MyClassAlias',
original: 'MyClass',
type: 'named',
target: 'tests/fixtures/module'
}),
expect.objectContaining({
name: 'bar',
original: 'foo',
type: 'named'
}),
])
);
});
```

**Update test fixtures**:
- `tests/fixtures/typescript.ts` - Add aliased export examples
- `tests/fixtures/javascript.js` - Add aliased export examples

### 7. Update Documentation ✅
**File**: `docs/EXPORTS_IMPLEMENTATION.md`

Add section on export aliasing:
```markdown
### Export Aliasing

For aliased exports, both the alias (exported name) and original (source name) are captured:

**Example**:
```typescript
export { MyClass as MyClassAlias } from './module';
```

**Output**:
```json
{
"exports": [
{
"name": "MyClassAlias",
"original": "MyClass",
"type": "named",
"target": "src/module"
}
]
}
```
```

## Use Cases Enabled

1. **Dependency Tracking**: Understand the complete export chain even when symbols are renamed
2. **Refactoring Support**: Find all places where a symbol is re-exported under different names
3. **Code Navigation**: Navigate from an aliased export back to its original definition
4. **API Documentation**: Generate complete API docs showing both public names and their sources

## Breaking Changes

None. The `original` field is optional and backward compatible with existing export data.

## Related

- Builds on PR #68 (Add exports field to CodeChunk type)
- Complements existing `target` field for re-export source tracking

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.