Skip to content

Add --stop-at-aliases option to find-usages for scoped symbol tracking #68

@devill

Description

@devill

Problem

When performing focused refactoring operations like renaming, users sometimes need to find usages only of the original symbol name and not follow imported aliases where the symbol is used under a different name. Currently, find-usages always follows all references including aliased imports.

Use Case: Focused Renaming

Consider this scenario:

File A (original definition):

// utils/helpers.ts
export function processData(input: string): string {
  return input.trim().toLowerCase();
}

File B (imports with same name - SHOULD be included):

// services/data-service.ts  
import { processData } from '../utils/helpers';

class DataService {
  clean(input: string) {
    return processData(input); // Same name - should be tracked
  }
}

File C (imports with alias - SHOULD be excluded with new option):

// services/user-service.ts  
import { processData as sanitizeInput } from '../utils/helpers';

class UserService {
  validateUser(name: string) {
    const cleanName = sanitizeInput(name); // Aliased - exclude with new option
    return cleanName.length > 0;
  }
}

Current Behavior:

refakts find-usages '[utils/helpers.ts 2:17-2:28]'
# Returns ALL:
# - [utils/helpers.ts 2:17-2:28] processData (original)
# - [services/data-service.ts 6:12-6:23] processData (same name import)  
# - [services/user-service.ts 6:21-6:33] sanitizeInput (aliased import)

Desired Behavior with New Option:

refakts find-usages '[utils/helpers.ts 2:17-2:28]' --stop-at-aliases
# Returns:
# - [utils/helpers.ts 2:17-2:28] processData (original)
# - [services/data-service.ts 6:12-6:23] processData (same name - included)
# - Does NOT include sanitizeInput usage (aliased import - excluded)

Proposed Solution

Add a new option to find-usages that stops tracking when symbols are imported with different names:

Option Name

  • --stop-at-aliases (recommended)
  • --exclude-aliases
  • --same-name-only
  • --no-renamed-imports

Behavior Specification

Include these usages:

  • ✅ Original symbol definition
  • ✅ Direct usages in same file
  • ✅ Imports with same name: import { processData } from '...'
  • ✅ Namespace imports: import * as utils from '...' then utils.processData

Exclude these usages (with --stop-at-aliases):

  • ❌ Aliased imports: import { processData as sanitize } from '...'
  • ❌ Default imports with different names: import sanitize from '...' (if original was named export)
  • ❌ Re-exports with different names: export { processData as sanitize }

Implementation Approach

Modify the CrossFileReferenceFinder to track symbol name consistency:

// Enhanced CrossFileReferenceFinder
class CrossFileReferenceFinder {
  constructor(
    private project: Project,
    private options: {
      stopAtAliases?: boolean; // New option
    } = {}
  ) {}
  
  findAllReferences(node: Node): UsageLocation[] {
    const originalName = this.getSymbolName(node);
    const references = this.findDirectReferences(node);
    
    if (\!this.options.stopAtAliases) {
      // Current behavior - follow all imports including aliases
      references.push(...this.findAllImportedReferences(node));
    } else {
      // New behavior - only follow imports that preserve the name
      references.push(...this.findSameNameImportedReferences(node, originalName));
    }
    
    return references;
  }
  
  private findSameNameImportedReferences(node: Node, originalName: string): UsageLocation[] {
    // Only include imports where symbol is used with same name
    // Skip: import { foo as bar }, export { foo as bar }
    // Include: import { foo }, import * as utils -> utils.foo
  }
}

Command Interface

# Find all usages including aliases (current default behavior)
refakts find-usages '[file.ts 5:10-5:20]'

# Find usages only with same name, exclude aliases
refakts find-usages '[file.ts 5:10-5:20]' --stop-at-aliases

# Alternative shorter syntax
refakts find-usages '[file.ts 5:10-5:20]' --same-name-only

Benefits

Focused Refactoring

  • Name-based tracking: Only see usages under the original symbol name
  • Exclude intentional aliases: Don't get overwhelmed by deliberately renamed imports
  • Safer renaming: Focus on direct name usages without worrying about alias conflicts

Workflow Integration

  • Rename preparation: See exactly what shares the same name that would be affected
  • Impact analysis: Understand direct vs. aliased dependencies
  • Incremental refactoring: Rename original symbol without affecting intentional aliases

Implementation Details

1. Command Option

// find-usages-options.json
{
  "stopAtAliases": {
    "alias": "same-name-only",
    "description": "Don't follow usages through aliased imports (import { foo as bar })",
    "type": "boolean",
    "default": false
  }
}

2. Service Integration

// Enhanced FindUsagesCommand
private async performFindUsages(location: LocationRange, options: FindUsagesOptions): Promise<UsageLocation[]> {
  const targetNode = location.getNode();
  const astService = ASTService.createForFile(location.file);
  
  return new CrossFileReferenceFinder(astService.getProject(), {
    stopAtAliases: options.stopAtAliases
  }).findAllReferences(targetNode);
}

3. Output Distinction

When --stop-at-aliases is used, the output could indicate the scope limitation:

refakts find-usages '[file.ts 5:10-5:20]' --stop-at-aliases
# Output:
# Found 3 usages (same name only):
# [file.ts 5:10-5:20] processData
# [other.ts 12:5-12:16] processData  
# [utils.ts 18:8-18:19] processData
# (Excluded 2 aliased usages)

Acceptance Criteria

  • Add --stop-at-aliases option to find-usages command
  • Modify CrossFileReferenceFinder to support alias filtering
  • Include same-name imports: import { foo }
  • Include namespace access: import * as utilsutils.foo
  • Exclude aliased imports: import { foo as bar }
  • Exclude renamed re-exports: export { foo as bar }
  • Update command help text to document the new option
  • Add fixture tests for both behaviors
  • Ensure backward compatibility (default behavior unchanged)
  • Update documentation with examples

Related Issues

This enhancement would work well with:

Priority

Medium - Valuable for focused refactoring workflows, especially useful for rename operations, but doesn't block existing functionality.

🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com

Metadata

Metadata

Assignees

No one assigned

    Labels

    RefakTS CommandIssues that introduce new commands to the productenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions