> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/nodejs/userland-migrations/llms.txt
> Use this file to discover all available pages before exploring further.

# Binding Resolution

> Utilities for resolving and transforming import bindings

Utilities for resolving how global API paths should be accessed based on import patterns, and for safely removing or updating bindings.

## Overview

These utilities handle the complexity of:

* Determining how imported functions are accessed locally (namespace, destructured, aliased)
* Safely removing unused imports while preserving other bindings
* Updating or replacing import bindings
* Handling both ES modules and CommonJS

## Binding Path Resolution

### resolveBindingPath

Resolves how a global API path should be accessed based on the import pattern.

```typescript theme={null}
import { resolveBindingPath } from '@nodejs/codemod-utils/ast-grep/resolve-binding-path';

// Example 1: Destructured import
// Given: const { types } = require('node:util');
const path = resolveBindingPath(node, '$.types.isNativeError');
// Returns: 'types.isNativeError'

// Example 2: Namespace import
// Given: const util = require('node:util');
const path = resolveBindingPath(node, '$.types.isNativeError');
// Returns: 'util.types.isNativeError'

// Example 3: Aliased import
// Given: import { types as utilTypes } from 'node:util';
const path = resolveBindingPath(node, '$.types.isNativeError');
// Returns: 'utilTypes.isNativeError'
```

<ParamField path="node" type="SgNode<Js>" required>
  The AST node representing the import or require statement. Must be one of:

  * `lexical_declaration`
  * `variable_declarator`
  * `import_statement`
  * `import_clause`
</ParamField>

<ParamField path="path" type="string" required>
  The expected dotted path to resolve (e.g., "$.types.isNativeError"). The `$\` represents the module namespace.
</ParamField>

**Returns**: `string | undefined` - The local access path that should be used in code, or undefined if the binding is not found

<Note>
  The `$` in the path parameter represents the root module namespace and gets replaced with the actual local binding name.
</Note>

### Advanced Examples

<Tabs>
  <Tab title="Namespace Require">
    ```typescript theme={null}
    // Given: const util = require('node:util');
    const path = resolveBindingPath(node, '$.types.isNativeError');
    // Returns: 'util.types.isNativeError'

    const path2 = resolveBindingPath(node, '$.isDeepStrictEqual');
    // Returns: 'util.isDeepStrictEqual'
    ```
  </Tab>

  <Tab title="Destructured Import">
    ```typescript theme={null}
    // Given: import { types } from 'node:util';
    const path = resolveBindingPath(node, '$.types.isNativeError');
    // Returns: 'types.isNativeError'
    ```
  </Tab>

  <Tab title="Aliased Destructure">
    ```typescript theme={null}
    // Given: const { types: utilTypes } = require('node:util');
    const path = resolveBindingPath(node, '$.types.isNativeError');
    // Returns: 'utilTypes.isNativeError'
    ```
  </Tab>

  <Tab title="Member Expression Require">
    ```typescript theme={null}
    // Given: const isNativeError = require('node:util').types.isNativeError;
    const path = resolveBindingPath(node, '$.types.isNativeError');
    // Returns: undefined (specific member access, not a binding pattern)
    ```
  </Tab>
</Tabs>

## Binding Removal

### removeBinding

Removes a specific binding from imports/requires, or removes the entire statement if it's the only binding.

```typescript theme={null}
import { removeBinding } from '@nodejs/codemod-utils/ast-grep/remove-binding';

// Example 1: Remove one binding from multiple
// Given: const { types, isNativeError } = require('node:util');
const result = removeBinding(node, 'isNativeError');
// Result: { edit: Edit } transforms to: const { types } = require('node:util');

// Example 2: Remove the only binding
// Given: const { isNativeError } = require('node:util');
const result = removeBinding(node, 'isNativeError');
// Result: { lineToRemove: Range } to remove entire statement

// Example 3: Remove namespace binding
// Given: const util = require('node:util');
const result = removeBinding(node, 'util');
// Result: { lineToRemove: Range } to remove entire statement
```

<ParamField path="node" type="SgNode<Js>" required>
  The AST node representing the import or require statement
</ParamField>

<ParamField path="binding" type="string" required>
  The name of the binding to remove (e.g., "isNativeError")
</ParamField>

<ParamField path="options" type="object">
  Optional configuration object

  <ParamField path="options.usageCheck" type="object">
    If provided, the binding will only be removed if it is unused

    <ParamField path="options.usageCheck.ignoredRanges" type="Range[]">
      Array of ranges to ignore when checking for usage
    </ParamField>
  </ParamField>

  <ParamField path="options.root" type="SgNode<Js>">
    Custom root node for usage checking. Defaults to the file root.
  </ParamField>
</ParamField>

**Returns**: `{ edit?: Edit, lineToRemove?: Range } | undefined`

* If multiple bindings exist: returns `{ edit }` to modify the destructuring pattern
* If single binding: returns `{ lineToRemove }` with the line range to remove
* If binding not found: returns `undefined`

### Usage Check Example

```typescript theme={null}
const result = removeBinding(node, 'isNativeError', {
  usageCheck: {
    ignoredRanges: [node.range()] // Ignore references in the import itself
  }
});

if (result?.edit) {
  context.applyEdit(result.edit);
} else if (result?.lineToRemove) {
  // Remove the entire import line
  const cleaned = removeLines(sourceCode, [result.lineToRemove]);
  context.writeFile(filePath, cleaned);
}
```

## Binding Updates

### updateBinding

Updates a specific binding from imports/requires. Can replace, add, or remove bindings.

```typescript theme={null}
import { updateBinding } from '@nodejs/codemod-utils/ast-grep/update-binding';

// Example 1: Replace a binding
// Given: const { isNativeError } = require('node:util');
const result = updateBinding(node, { old: 'isNativeError', new: 'types' });
// Result: const { types } = require('node:util');

// Example 2: Add a new binding
// Given: const { isNativeError } = require('node:util');
const result = updateBinding(node, { old: undefined, new: 'types' });
// Result: const { isNativeError, types } = require('node:util');

// Example 3: Remove a binding (same as removeBinding)
// Given: const { isNativeError, types } = require('node:util');
const result = updateBinding(node, { old: 'isNativeError', new: undefined });
// Result: const { types } = require('node:util');

// Example 4: Many-to-many replacement
// Given: const { fips } = require('node:crypto');
const result = updateBinding(node, { 
  old: 'fips', 
  new: ['getFips', 'setFips'] 
});
// Result: const { getFips, setFips } = require('node:crypto');
```

<ParamField path="node" type="SgNode<Js>" required>
  The AST node representing the import or require statement
</ParamField>

<ParamField path="options" type="object">
  Configuration object for the update operation

  <ParamField path="options.old" type="string">
    The name of the binding to update or remove. Omit to add a new binding.
  </ParamField>

  <ParamField path="options.new" type="string | string[]">
    The new binding name(s) to replace the old one. Can be:

    * A single string for one-to-one replacement
    * An array of strings for one-to-many replacement
    * `undefined` to remove the binding
  </ParamField>

  <ParamField path="options.usageCheck" type="object">
    Optional usage checking configuration

    <ParamField path="options.usageCheck.ignoredRanges" type="Range[]">
      Ranges to ignore when checking if a binding is used
    </ParamField>
  </ParamField>

  <ParamField path="options.root" type="SgNode<Js>">
    Custom root node for usage checking
  </ParamField>
</ParamField>

**Returns**: `{ edit?: Edit, lineToRemove?: Range } | undefined`

### Advanced Update Patterns

<Tabs>
  <Tab title="One-to-Many">
    ```typescript theme={null}
    // Replacing deprecated single export with multiple new exports
    // Given: const { fips } = require('node:crypto');
    updateBinding(node, { 
      old: 'fips', 
      new: ['getFips', 'setFips'] 
    });
    // Result: const { getFips, setFips } = require('node:crypto');
    ```
  </Tab>

  <Tab title="Adding Multiple">
    ```typescript theme={null}
    // Adding multiple new bindings
    // Given: import { readFile } from 'fs';
    updateBinding(node, { 
      new: ['writeFile', 'appendFile'] 
    });
    // Result: import { readFile, writeFile, appendFile } from 'fs';
    ```
  </Tab>

  <Tab title="Rename Alias">
    ```typescript theme={null}
    // Given: import { types as utilTypes } from 'node:util';
    updateBinding(node, { old: 'types', new: 'nodeTypes' });
    // Result: import { nodeTypes } from 'node:util';
    ```
  </Tab>
</Tabs>

## Complete Migration Example

```typescript theme={null}
import { getModuleDependencies } from '@nodejs/codemod-utils/ast-grep/module-dependencies';
import { resolveBindingPath } from '@nodejs/codemod-utils/ast-grep/resolve-binding-path';
import { updateBinding } from '@nodejs/codemod-utils/ast-grep/update-binding';

const ast = context.getAST();
const utilImports = getModuleDependencies(ast, 'util');

for (const importNode of utilImports) {
  // Check if using deprecated isNativeError
  const oldPath = resolveBindingPath(importNode, '$.isNativeError');
  
  if (oldPath) {
    // Need to replace isNativeError with types.isNativeError
    const result = updateBinding(importNode, {
      old: 'isNativeError',
      new: 'types'
    });
    
    if (result?.edit) {
      context.applyEdit(result.edit);
      
      // Now update all usage sites
      const usages = ast.findAll({
        rule: { kind: 'identifier', pattern: 'isNativeError' }
      });
      
      for (const usage of usages) {
        context.applyEdit(usage.replace('types.isNativeError'));
      }
    }
  }
}
```
