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)
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)
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
The AST node to find the scope for
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'
}
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
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);
The original source code as a single string
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.
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'
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)
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);