Skip to main content
Core utilities for working with AST nodes, including indentation detection, scope resolution, and code manipulation.

Indentation Utilities

detectIndentUnit

Detects the indentation unit used in source code (\t or spaces).
import { detectIndentUnit } from '@nodejs/codemod-utils/ast-grep/indent';

const indentUnit = detectIndentUnit(sourceCode);
// Returns: '\t' for tabs or '  ' for spaces (number of spaces determined by GCD)
source
string
required
The source code to analyze
Returns: string - The indentation unit ('\t' for tabs or a string of spaces)
The function analyzes all indented lines and uses the greatest common divisor (GCD) to determine the space-based indentation unit.

getLineIndent

Returns the indentation prefix for the line containing the given character index.
import { getLineIndent } from '@nodejs/codemod-utils/ast-grep/indent';

const indent = getLineIndent(sourceCode, node.range().start.index);
// Returns: '    ' (the leading whitespace for that line)
source
string
required
The source code
index
number
required
The character index within the source code
Returns: string - The indentation string (spaces or tabs) at the start of the line

Scope Resolution

getScope

Finds the enclosing scope for a node (e.g., statement_block or program).
import { getScope } from '@nodejs/codemod-utils/ast-grep/get-scope';

const scope = getScope(node);
// Returns: The nearest statement_block or program node

const customScope = getScope(node, 'function_declaration');
// Returns: The nearest function_declaration node
node
SgNode<Js>
required
The AST node to find the scope for
customParent
string
Optional custom parent node type to stop at
Returns: SgNode<Js> | null - The scope node or null if not found

Shebang Utilities

getShebang

Returns the Node.js shebang line node when present (e.g., #!/usr/bin/env node).
import { getShebang } from '@nodejs/codemod-utils/ast-grep/shebang';

const shebang = getShebang(ast);
if (shebang) {
  console.log(shebang.text()); // '#!/usr/bin/env node'
}
rootNode
SgRoot
required
The root AST node to search
Returns: SgNode | null - The shebang node if found, otherwise null
Only returns shebangs that appear at the start of the file (line 0) and contain “node” or “node.exe”.

replaceNodeJsArgs (shebang)

Replaces Node.js arguments in shebang lines.
import { replaceNodeJsArgs } from '@nodejs/codemod-utils/ast-grep/shebang';

// Given: #!/usr/bin/env node --inspect
const edits = replaceNodeJsArgs(ast, { '--inspect': '' });
// Results in: #!/usr/bin/env node

const edits2 = replaceNodeJsArgs(ast, { '--inspect': '--inspect-brk' });
// Results in: #!/usr/bin/env node --inspect-brk
rootNode
SgRoot
required
The root AST node to search
argsToValues
Record<string, string>
required
A mapping of argument names to their replacement values. Use empty string to remove an argument.
Returns: Edit[] - Array of edit operations to apply

Code Manipulation

removeLines

Safely removes multiple line ranges from source code, handling overlaps and duplicates.
import { removeLines } from '@nodejs/codemod-utils/ast-grep/remove-lines';

const ranges = [
  { start: { line: 5, column: 0 }, end: { line: 5, column: 50 } },
  { start: { line: 12, column: 0 }, end: { line: 14, column: 0 } }
];

const cleanedCode = removeLines(sourceCode, ranges);
sourceCode
string
required
The original source code as a single string
ranges
Range[]
required
An array of ranges to remove. Each range has start and end with line and column properties.
Returns: string - The modified source code with the specified ranges removed
The function automatically handles duplicate and overlapping ranges by normalizing them before applying removals.

Import/Require Identifier Extraction

getDefaultImportIdentifier

Returns the identifier node for a default import (e.g., import fs from 'fs').
import { getDefaultImportIdentifier } from '@nodejs/codemod-utils/ast-grep/import-statement';

// Given: import fs from 'fs';
const id = getDefaultImportIdentifier(importNode);
console.log(id?.text()); // 'fs'
importNode
SgNode<Js>
required
The import_statement or import_clause node
Returns: SgNode<Js> | null - The identifier node for the default import, or null if none

getRequireNamespaceIdentifier

Returns the identifier node for namespace-style require (e.g., const util = require('util')).
import { getRequireNamespaceIdentifier } from '@nodejs/codemod-utils/ast-grep/require-call';

// Given: const util = require('util');
const id = getRequireNamespaceIdentifier(varDeclaratorNode);
console.log(id?.text()); // 'util'

// Given: const { types } = require('util');
const id2 = getRequireNamespaceIdentifier(varDeclaratorNode);
console.log(id2); // null (destructured, not namespace)
requireNode
SgNode<Js>
required
The variable_declarator node containing the require call
Returns: SgNode<Js> | null - The identifier node for the namespace binding, or null for destructured requires

Usage Examples

Preserving Indentation When Adding Code

const indent = getLineIndent(sourceCode, node.range().start.index);
const indentUnit = detectIndentUnit(sourceCode);
const newCode = `${indent}${indentUnit}// New line\n${indent}${indentUnit}console.log('added');`;

Finding and Modifying Shebang Lines

const shebang = getShebang(ast);
if (shebang && shebang.text().includes('--experimental-modules')) {
  const edits = replaceNodeJsArgs(ast, { '--experimental-modules': '' });
  context.applyEdits(edits);
}

Cleaning Up Multiple Import Statements

const rangesToRemove = unusedImports.map(node => node.range());
const cleanedSource = removeLines(sourceCode, rangesToRemove);
context.writeFile('file.js', cleanedSource);

Build docs developers (and LLMs) love