Detect Global Variables in JavaScript ASTs
acorn-globals is a utility for identifying global variable references within JavaScript code by leveraging the Acorn AST parser. It traverses the Abstract Syntax Tree (AST) generated by Acorn to distinguish between locally declared variables and those that implicitly refer to the global scope. The package is currently at version 7.0.1 and appears to maintain an active release cadence, primarily driven by updates to its underlying Acorn dependency and bug fixes related to scope resolution (e.g., switch statement bodies, catch handlers). Its key differentiator is its focus solely on global variable detection based on AST analysis, providing precise lexical scope information without performing any runtime evaluation. It is particularly useful for static analysis tools, linters, and bundlers that need to understand variable leakage or undeclared globals.
Common errors
-
TypeError: detect is not a function
cause Attempting to destructure the default export or using `require` incorrectly in an ESM module.fixUse `import detect from 'acorn-globals';` for ESM, or `const detect = require('acorn-globals');` for CommonJS. -
SyntaxError: Unexpected token (XX:YY)
cause The input JavaScript code uses syntax features not supported by the default `ecmaVersion` of the underlying Acorn parser (e.g., modern syntax in an older `ecmaVersion`).fixUpgrade `acorn-globals` to v7+ (which defaults to `ecmaVersion: 'latest'`) or explicitly pass `ecmaVersion: 'latest'` or a higher year (e.g., `ecmaVersion: 2020`) in the `acorn` options: `detect(src, { acorn: { ecmaVersion: 'latest' } })`. -
Error: Cannot find module 'acorn'
cause The `acorn` package is a peer dependency but is not installed or the installed version is incompatible with `acorn-globals`.fixInstall a compatible version of `acorn`. For `acorn-globals` v7+, use `npm install acorn@^8`.
Warnings
- breaking `acorn-globals` v7.0.0 introduces a breaking change by upgrading its underlying `acorn` parser dependency to v8. This requires users to ensure their `acorn` dependency is also updated to v8 or higher.
- breaking Starting with v7.0.0, the default `ecmaVersion` passed to the `acorn` parser is set to `'latest'`, ensuring support for the most recent ECMAScript features. Code that relies on older `ecmaVersion` defaults or expects specific parsing behavior might be affected.
- breaking Version 6.0.0 of `acorn-globals` updated `acorn` and `acorn-walk` to v7. This was a significant bump from previous `acorn` v6 versions and could introduce parsing behavior changes or require users to update their `acorn` dependency.
- gotcha Prior to v7.0.1, variables declared within a `switch` statement body (not directly in `case` blocks but within the statement's scope) were incorrectly identified as global. For example, `switch (3) { case 3: let a; } a; // this 'a' was global`.
- gotcha Older versions (pre-4.3.2) might incorrectly handle class declarations, misidentifying them as function/module scoped instead of block-scoped, leading to incorrect global detection.
Install
-
npm install acorn-globals -
yarn add acorn-globals -
pnpm add acorn-globals
Imports
- detect
import { detect } from 'acorn-globals';import detect from 'acorn-globals';
- detect
const detect = require('acorn-globals');
Quickstart
import fs from 'node:fs';
import path from 'node:path';
import detect from 'acorn-globals';
// Create a dummy input file for demonstration
const inputJsPath = path.join(process.cwd(), 'input.js');
const srcContent = `
var x = 5;
var y = 3, z = 2;
w.foo();
w = 2;
RAWR=444;
RAWR.foo();
BLARG=3;
foo(function () {
var BAR = 3;
process.nextTick(function (ZZZZZZZZZZZZ) {
console.log('beep boop');
var xyz = 4;
x += 10;
x.zzzzzz;
ZZZ=6;
});
function doom () {
}
ZZZ.foo();
});
console.log(xyz);
`;
fs.writeFileSync(inputJsPath, srcContent, 'utf8');
// Read the source code
const src = fs.readFileSync(inputJsPath, 'utf8');
// Detect global variables
const scope = detect(src);
console.log('Detected globals:');
scope.forEach(globalVar => {
console.log(`- ${globalVar.name} (found at positions: ${globalVar.nodes.map(node => node.start).join(', ')})`);
});
// Clean up the dummy file
fs.unlinkSync(inputJsPath);