{"id":11988,"library":"scope-analyzer","title":"Scope Analyzer","description":"scope-analyzer is a JavaScript library designed for performing basic scope analysis on JavaScript Abstract Syntax Trees (ASTs). It tracks variable scopes and collects references to variables within a given AST, enabling tasks like refactoring, renaming, and understanding variable usage patterns. The current stable version is 2.1.2. The package has seen consistent minor and patch releases, indicating active maintenance, though its stability badge still labels it as \"experimental.\" A key differentiator is its focus on simplicity and direct manipulation of AST nodes, providing methods to crawl the tree, create, delete, and inspect scopes, and retrieve bindings and references. It is particularly useful for tools that need to understand the lexical environment of JavaScript code. It expects AST nodes to have a `.parent` property, often requiring a pre-processing step with utilities like `estree-assign-parent`.","status":"active","version":"2.1.2","language":"javascript","source_language":"en","source_url":"https://github.com/goto-bus-stop/scope-analyzer","tags":["javascript","analysis","ast","nodes","refactor","rename","scope"],"install":[{"cmd":"npm install scope-analyzer","lang":"bash","label":"npm"},{"cmd":"yarn add scope-analyzer","lang":"bash","label":"yarn"},{"cmd":"pnpm add scope-analyzer","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"This library primarily exposes its API via a CommonJS module export. Direct ESM `import` statements are not officially supported or documented and may lead to issues without proper tooling.","wrong":"import scan from 'scope-analyzer'","symbol":"scan","correct":"const scan = require('scope-analyzer')"},{"note":"`crawl` is a method accessed on the default export object, typically assigned to a variable like `scan`.","wrong":"crawl(ast)","symbol":"crawl","correct":"scan.crawl(ast)"},{"note":"`createScope` is a method for initializing new scopes, often used for global or predefined variables, and is accessed via the default export.","wrong":"createScope(node, bindings)","symbol":"createScope","correct":"scan.createScope(node, bindings)"},{"note":"`getBinding` is a method for retrieving a specific binding by name from a given AST node's scope, accessed via the default export.","wrong":"getBinding(node, name)","symbol":"getBinding","correct":"scan.getBinding(node, name)"}],"quickstart":{"code":"const parse = require('acorn').parse;\nconst scan = require('scope-analyzer');\n\n// Example JavaScript source code\nconst code = `\n  function greet(name) {\n    const message = 'Hello, ' + name;\n    console.log(message);\n    exports.greeting = message; // Example of an 'exports' reference\n  }\n  greet('World');\n  var globalVar = 10;\n`;\n\n// Parse the code into an AST. 'acorn' is a common choice.\n// Ensure to specify ecmaVersion and sourceType for accurate parsing.\nconst ast = parse(code, { ecmaVersion: 2020, sourceType: 'script' });\n\n// IMPORTANT: scope-analyzer expects AST nodes to have a `node.parent` property.\n// Most parsers (like Acorn) do not add this by default.\n// Uncomment the following line and install 'estree-assign-parent' if your AST lacks parent pointers:\n// require('estree-assign-parent')(ast);\n\n// Initialize the root scope with common Node.js global variables\n// This makes 'exports' a known binding for analysis.\nscan.createScope(ast, ['module', 'exports', '__dirname', '__filename', 'console']);\n\n// Crawl the AST to analyze all scopes and collect variable references.\nscan.crawl(ast);\n\n// Example 1: Find all references to the 'exports' binding\nconst exportsBinding = scan.getBinding(ast, 'exports');\n\nif (exportsBinding) {\n  console.log('--- References to \"exports\" ---');\n  exportsBinding.getReferences().forEach(function (reference) {\n    // Assuming references like `exports.property = value`\n    if (reference.parent && reference.parent.type === 'MemberExpression' && reference.parent.property) {\n      console.log(`- Exported property: '${reference.parent.property.name}' at line ${reference.loc.start.line}`);\n    } else {\n      console.log(`- General reference at line ${reference.loc.start.line}`);\n    }\n  });\n} else {\n  console.log('No binding found for \"exports\".');\n}\n\n// Example 2: Find undeclared names in the root scope\nconst rootScope = scan.scope(ast);\nif (rootScope) {\n  const undeclared = rootScope.getUndeclaredNames();\n  if (undeclared.length > 0) {\n    console.log('\\n--- Undeclared names in root scope ---');\n    console.log(`- ${undeclared.join(', ')}`);\n  } else {\n    console.log('\\nNo undeclared names found in root scope.');\n  }\n}\n","lang":"javascript","description":"This quickstart demonstrates how to parse a JavaScript string into an AST using Acorn, prepare it for `scope-analyzer` (potentially by assigning parent nodes), then define global variables with `createScope` and analyze the AST with `crawl`. It illustrates how to retrieve a specific binding (e.g., `exports`) and iterate over its references, as well as how to find undeclared names within a given scope."},"warnings":[{"fix":"Before passing your AST to `scope-analyzer`, use a utility like `estree-assign-parent` to add parent references to all nodes. Example: `require('estree-assign-parent')(ast);`","message":"The library critically relies on AST nodes having a `.parent` property for correct traversal and scope resolution. Most standard parsers (e.g., Acorn, Esprima) do not automatically provide this, requiring manual preprocessing of the AST.","severity":"gotcha","affected_versions":"*"},{"fix":"If `recast` compatibility is not a concern and performance is critical, consider pinning your dependency to version `2.1.1`. Otherwise, be aware of the potential performance impact when using or upgrading to `>=2.1.2`.","message":"Version 2.1.2 introduced a change making `.parent` and `[kScope]` properties non-enumerable. While this resolves compatibility issues with `recast`, it can lead to a significant performance regression of 20-30% in analysis speed compared to v2.1.1.","severity":"breaking","affected_versions":">=2.1.2"},{"fix":"Exercise caution when relying on specific internal implementations or undocumented features. Regularly review release notes for updates and potential breaking changes, even in minor versions.","message":"The package's stability badge labels it as \"experimental.\" This implies that while actively maintained, its API or internal behavior might still undergo changes in future versions, potentially without strict adherence to semantic versioning for minor changes.","severity":"gotcha","affected_versions":"*"},{"fix":"When using `scan.getBinding()`, always check `binding.definition` if you need to distinguish between formally declared variables and implicitly used (undeclared) identifiers.","message":"As of version 2.0.0, `scan.getBinding()` will now return a `Binding` object even for undeclared identifiers. In such cases, the `binding.definition` property will be `undefined`.","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"In ES Module projects, use dynamic `import()` (e.g., `const scan = await import('scope-analyzer');`) or ensure your build tooling correctly transpiles CommonJS `require` statements if you are using static `import` syntax.","message":"This library is published as a CommonJS module and does not officially support or document direct ES Module (`import`) syntax. Attempting to import it directly in an ESM context might result in errors or unexpected behavior depending on your bundler or Node.js version.","severity":"gotcha","affected_versions":"*"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Preprocess your AST using a utility like `estree-assign-parent` to add the `.parent` property to all nodes. Example: `require('estree-assign-parent')(ast);`","cause":"The AST nodes passed to `scope-analyzer` do not have a `.parent` property, which is required for its internal traversal logic.","error":"TypeError: Cannot read property 'parent' of undefined"},{"fix":"If in an ESM context, consider using dynamic `import()` (`const scan = await import('scope-analyzer');`) or configure your bundler/Node.js environment to properly handle CommonJS modules.","cause":"You are attempting to use the CommonJS `require` function in an ES Module environment (e.g., a `.mjs` file or a project with `\"type\": \"module\"` in `package.json`).","error":"ReferenceError: require is not defined"},{"fix":"Ensure you are using the correct CommonJS import: `const scan = require('scope-analyzer');`. Avoid named imports (`import { crawl } from 'scope-analyzer';`) as the package exports a single object.","cause":"The `scope-analyzer` module was imported incorrectly, preventing the `scan` variable from holding the expected object with its API methods. This often happens with incorrect destructuring or ES Module imports.","error":"TypeError: scan.crawl is not a function"}],"ecosystem":"npm"}