Skip to main content
Semantic analysis enables your codemods to understand symbol relationships in code—finding where variables are defined, tracking all references to a function, or discovering cross-file dependencies. This goes beyond pattern matching to provide IDE-like intelligence for your transformations.

Supported Languages

Enterprise customers can get semantic analysis for Go, Rust, Java, C, C++, C Sharp, Ruby, PHP, Swift, Kotlin, Haskell, and Elixir. Contact us to learn more.

Analysis Modes

Semantic analysis operates in two modes, each with different performance and accuracy trade-offs:

File Scope (Default)

Single-file analysis that processes symbols within the current file only. This mode is fast and requires no additional configuration. Best for:
  • Quick analysis of local variables
  • Single-file transformations
  • Dry runs and exploratory analysis
Limitations:
  • Cannot resolve cross-file imports
  • Cannot find references in other files

Workspace Scope

Workspace-wide analysis that resolves cross-file imports and finds references across your entire project. This mode requires specifying a workspace root. Best for:
  • Renaming symbols across files
  • Finding all usages of exported functions
  • Dependency analysis and migration codemods
Requirements:
  • Workspace root path must be specified
  • Files must be processed (indexed) before cross-file queries work

API Reference

node.definition(options?)

Get the definition location for the symbol at this node’s position.
DefinitionResult | null
Returns an object containing the definition node, its root, and the kind of definition, or null if not found.
Definition kinds:
  • 'local' — Definition is in the same file (local variable, function, class, etc.)
  • 'import' — Definition traced to an import statement, but module couldn’t be resolved (e.g., external package)
  • 'external' — Definition resolved to a different file in the workspace
Returns null when:
  • No semantic provider is configured
  • No symbol is found at this position
When a symbol comes from an unresolved import (e.g., import x from "some-external-module"), definition() now returns the import statement with kind: 'import' instead of returning null. This allows you to at least trace the symbol back to where it was imported.

node.references()

Find all references to the symbol at this node’s position.
Array<FileReferences>
Returns an array of file references, grouped by file.
Returns empty array when:
  • No semantic provider is configured
  • No symbol is found at this position
In file scope mode, references() only searches the current file. In workspace scope mode, it searches all indexed files in the workspace.

root.write(content)

Write content to a file obtained via definition() or references(). This method allows cross-file editing within a single codemod execution.
void
Writes the provided content to the file and updates the semantic provider’s cache.
Throws an error when:
  • Called on the current file being processed (use return instead)
  • The file has no path
  • The write operation fails
You cannot call write() on the current file. For the current file, return the modified content from transform() instead.

Using Semantic Analysis

The recommended way to use semantic analysis is through workflow files. Create a workflow.yaml that references your codemod script:
string | object
Configure semantic analysis mode. Can be:
  • "file" — Single-file analysis (default)
  • "workspace" — Workspace-wide analysis using the target path
  • { mode: "workspace", root: "./path" } — Workspace-wide with custom root
Run the workflow:
PATH
Path to the workflow YAML file.
PATH
Path to the target directory containing files to transform.
When semantic analysis returns an SgRoot for another file, you can use root.relativeFilename() to get that file’s path relative to the active target directory.

Via CLI Commands

For quick testing or simple use cases, you can also use the JSSG CLI directly:
PATH
Enable workspace-wide semantic analysis using the provided path as the workspace root.

Examples

Renaming a Utility Function Across Files (TypeScript)

This example renames formatDate to formatDateTime across a multi-file TypeScript project. Source codebase:
Codemod and workflow:
Run the workflow:
Result:

Finding Usages of a Python Class

Analyze how a class is used across a Python project without making changes. Source codebase:
Codemod and workflow:
Run the workflow:
Output:

Tracing External Module Imports

Find where external dependencies are imported when node_modules isn’t available. Source codebase:
Codemod and workflow:
Run the workflow:
Output:
When a symbol comes from an unresolved import (e.g., import x from "some-external-module"), definition() returns the import statement with kind: 'import'. This allows you to trace symbols back to their import source even when the module can’t be resolved or the semantic mode is set to file scope.

Cross-File Editing with Definition Lookup

Rename a constant and update all files that import it. Source codebase:
Codemod and workflow:
Run the workflow:
Result:
You cannot call write() on the current file being processed. For the current file, return the modified content from transform() instead. This ensures the engine properly tracks and applies changes.
When you call write() on an SgRoot obtained from definition() or references(), the semantic provider’s cache is automatically updated. This ensures subsequent semantic queries reflect the changes.

Best Practices

File scope analysis is faster and doesn’t require workspace configuration. Use it when your codemod only needs to understand symbols within a single file.
workflow.yaml
Semantic analysis may return null or empty results for various reasons. Always check return values:
When processing references across files, verify you’re editing the correct file:

Troubleshooting

Possible causes:
  • No semantic analysis configured in workflow (add semantic_analysis: workspace)
  • The language isn’t supported (only JavaScript/TypeScript and Python)
  • The symbol couldn’t be resolved (external library, syntax error)
Debug steps:
Possible causes:
  • Using file scope mode instead of workspace scope
  • The target file hasn’t been indexed yet
  • Import resolution failed
Solution: Ensure you’re using workspace scope mode in your workflow:
workflow.yaml
Then run:
Tips:
  • Start with file scope for initial development
  • Use workspace scope only when cross-file analysis is needed
  • Consider running workflows on subsets of your codebase by targeting specific directories

Next Steps

API Reference

Complete API documentation for SgNode and SgRoot methods.

Advanced Patterns

Learn advanced transformation techniques and best practices.

Testing

Test your codemods with fixtures and the test runner.

Security

Understand JSSG’s security model and capabilities.