Skip to main content
These helpers locate, add, remove, and rename imports in JavaScript and TypeScript programs, copy a file’s quote and indent, and resolve a use back to the binding that defines it.
These utilities work with JavaScript, TypeScript, JSX, and TSX ASTs. They handle both ESM (import) and CJS (require()) formats automatically.

getImport

Locate an import of a given module and return its alias/identifier node. Parameters
SgNode<T, 'program'>
required
The root program node to search within.
GetImportOptions
required
What to look for:
  • { type: "default", from: string } — Find the default import from a module.
  • { type: "named", name: string, from: string } — Find a specific named import from a module.
Returns GetImportResult<T> | null — An object containing: Returns null if the import is not found. Supported Import Shapes Example

getAllImports

Locate imports of a given module and return their alias/identifier nodes. Parameters
SgNode<T, 'program'>
required
The root program node to search within.
GetImportOptions
required
What to look for:
  • { type: "default", from: string } — Find all default imports from a module.
  • { type: "named", name: string, from: string } — Find all instances of a specific named import from a module.
Returns ResolvedImport<T>[] — An array containing: Returns an empty array if no imports are found. Example

listImports

List the import statements and require() / import() declarations in a file. getAllImports asks for one exported name. listImports answers “what does this file import from these packages?”, which is the question a barrel split asks (lodash, date-fns, rxjs, @mui/material). Parameters
SgNode<T, 'program'>
required
The root program node.
ListImportsOptions
{ from?: string | readonly string[] }. When set, only statements whose module specifier is exactly one of those strings are returned. "mod" does not match "mod/extra". Quotes on the specifier do not matter.
Returns An array of statements in source order. Each record has: specifiers[].isTypeOnly is true for both import type { Foo } and import { type Foo }. The statement flag isTypeOnly is only the first form. The same statements removeImport can edit are listed. A multi-declarator const a = require("mod"), b = 1 is omitted, so a later deletion cannot drop b. const x = import("mod") without await is omitted. import("mod").then(...) is included.

addImport

Add an import to the program. Smart behavior:
  • Skips if the import already exists
  • Merges named specifiers into an existing import statement from the same source
  • Creates a new import statement otherwise
  • Inserts after the last existing import (or at file start if no imports exist)
  • A new statement copies the quote and semicolon of the first module specifier already in the file. When the file has none, it copies the first string literal and the first semicolon-style statement, and keeps ' and ; only when those are absent. A merge does not rewrite the existing clause.
  • A named specifier with isTypeOnly: true is written as inline type (import { type Foo }), not import type. A value specifier is not merged into an existing import type statement. When a value import from the same module already exists, the specifier merges into that statement even if an import type line comes first. Otherwise it becomes a second statement. Type-only specifiers merged into import type do not repeat the type keyword.
  • A type-only binding does not count as the value. import { type Foo } drops the type modifier, and import type { Foo, Bar } becomes import { Foo, type Bar }, when the local name matches. A different local name (import type { Foo as F } plus a value Foo) stays a separate statement.
Parameters
SgNode<T, 'program'>
required
The root program node.
AddImportOptions
required
One of:
  • { type: "default", name: string, from: string, moduleType?: "esm" | "cjs" } — Add a default import.
  • { type: "namespace", name: string, from: string } — Add a namespace import (import * as name).
  • { type: "named", specifiers: Array<{ name: string, alias?: string, isTypeOnly?: boolean }>, from: string, moduleType?: "esm" | "cjs" } — Add named imports. isTypeOnly emits an inline type modifier.
Returns Edit | null — An edit to apply via rootNode.commitEdits(), or null if the import already exists. Example

removeImport

Remove an import from the program. Smart behavior:
  • Default/namespace: Removes the entire import statement.
  • Named (multiple specifiers): Removes only the specified specifiers, keeping the rest.
  • Named (last specifier): Removes the entire import statement.
  • Handles both ESM and CJS formats.
Parameters
SgNode<T, 'program'>
required
The root program node.
RemoveImportOptions
required
One of:
  • { type: "default", from: string, removeSideEffectForms?: boolean } — Remove the default import from a module. Set removeSideEffectForms to also remove a side-effect-only import "module" or require("module") when no binding import exists.
  • { type: "namespace", from: string } — Remove the namespace import from a module.
  • { type: "named", specifiers: string[], from: string } — Remove specific named imports.
Returns Edit | null — An edit to apply via rootNode.commitEdits(), or null if the import was not found. Example

updateImport

Replace named specifiers of an existing import or require in place. The whole clause is one edit, so the rewrite does not overlap other specifier edits.
  • import { foo } from "mod" with { name: "foo", to: "bar" } becomes import { bar } from "mod".
  • An existing local alias is kept: import { foo as f } becomes import { bar as f }. Pass alias to override that local name.
  • Works for ESM named imports, destructured require(), and destructured dynamic import().
  • If to is already imported in the same clause, the old specifier is dropped instead of duplicated.
Parameters
SgNode<T, 'program'>
required
The root program node.
UpdateImportOptions
required
Named imports only:
  • { type: "named", from: string, specifiers: Array<{ name: string, to: string, alias?: string }> } — Rename name to to in the import from from.
Returns Edit | null — An edit to apply via rootNode.commitEdits(), or null if none of the named specifiers is found or specifiers is empty. Example

stringToExactRegexString

Escape a string and return a regex that matches only that exact text. The result is wrapped in ^...$, with regex metacharacters escaped.

getFileStyle

Quote, semicolon, and indent of a JavaScript or TypeScript file.
addImport uses the quote and semicolon when it creates a statement. Call getFileStyle yourself when you insert other code. The first module specifier wins. Later imports, and strings elsewhere in the file, are ignored while a specifier exists. This does not read Prettier or EditorConfig.

getLineIndent

Leading spaces or tabs on the line a node starts on. Returns "" when any other character sits before the node on that line, so foo(bar) and an indented foo(bar) do not treat the preceding code as indent. The node’s position is a UTF-8 byte offset, so non-ASCII text earlier in the file does not shift the line. A carriage return is not part of the indent.

indentText

Prefix every non-empty line. Blank lines stay blank, so an inserted block does not grow trailing whitespace.

importBindingOf

The import that defines this use, or null when the name is local. This is the opposite of getImport: the call site is known and the package is not.
setup() is rewritten only when importedName is "setup" and source is the package you mean. A parameter named setup, or the same name imported from another package, returns a different binding or null. The lookup stops at the import in the file that contains the use. import { server } from "./browser" reports source "./browser", not the package ./browser imported from. ns.graphql after import * as ns is not resolved; call this on ns and read the property text yourself. Returns null when semantic analysis is unavailable. definition() can throw in that case; this function does not.

resolveDefinition

The node that defines this use, following one import into another project file.
  • A local declaration returns that node.
  • An import the workspace can resolve returns that file’s definition. In the semantic API this kind is "external", which means another project file, not a package on npm.
  • An unresolved package import returns the import specifier.
  • const a = setup(); const b = a resolves b to b, not to setup(). A second hop is out of scope.
Returns null when semantic analysis is unavailable.

resolvesToFactory

True when the binding is a variable initialized by a direct call, or new, of importedName from one of sources.
new QueryClient(), new PrismaClient(), and express() are the same shape. One hop includes a binding imported from another project file (export const server = setupServer() in browser.ts, server.listen() in the file that imports it). These stay false:
  • const { listen } = setupServer()
  • lib.setupServer()
  • const a = setupServer(); const b = a
  • a shadowed local with the same spelling, such as const server = app.listen() inside a function

renameReferences

Edits that rename references of one binding in currentFile. Pass root.filename(). Shadowed names stay. references() only returns this binding, so a parameter spelled the same as the import is not renamed. Comments, string literals, and property names such as record.cleanUrl are not references and stay. A shorthand property is an identifier, so { cleanUrl } becomes { getCleanUrlString } and the key changes. The import clause is not renamed; use updateImport for that. Workspace references() also returns other files. Those edits are omitted. Writing them onto the file currently being committed targets the wrong tree. Cross-file renames stay with the caller, via root.write on the other file. Returns [] when semantic analysis is unavailable.

Common Patterns

Verify Import Before Transforming

Always verify that a symbol is imported from the expected package before transforming its usage:

Replace One Import With Another

Rename a named specifier in the same module with updateImport:
Switch packages with removeImport and addImport:

Match Existing Module Style

getImport automatically detects the module type. Use moduleType when adding imports to match the existing style: