npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ecma-variable-scope

v2.1.0

Published

AST utility to collect scope info for variables

Downloads

19

Readme

ecma-variable-scope Build status Coverage Status

AST utility to collect scope info for variables

Scope detection is hard, especially when with exists. This utility extracts all relevant info for making decisions. This project was built as part of esformatter-phonetic, a esformatter plugin that makes obfuscated variable names more comprehensible.

Features:

  • Detect with usage
  • Does not mark labels
  • Support for let and const
  • Support for destructured variables (e.g. var {hello, world} = obj;)
  • Support for arrow expressions (e.g. (hello) => hello.world)

Getting Started

Install the module with: npm install ecma-variable-scope

// Gather an AST to analyze
var esprima = require('esprima');
var ecmaVariableScope = require('ecma-variable-scope');
var ast = esprima.parse([
  'function logger(str) {',
    'console.log(str);',
  '}'
].join('\n'));

// Determine the scope of a variable
ecmaVariableScope(ast);
ast.body[0].id;
/*
// `logger` variable
  scope:
   { type: 'lexical',
     node: Program,
     parent: undefined,
     identifiers: { logger: [Circular] },
     children: [ [Object] ] },
  scopeInfo:
   { insideWith: false,
     topLevel: true,
     type: 'lexical',
     usedInAWith: false }
*/

ast.body[0].body.body[0].expression.callee.object;
/*
// `console` variable
  scope: undefined,
  scopeInfo:
   { insideWith: false,
     topLevel: true,
     type: 'undeclared',
     usedInAWith: false }
*/

Documentation

ecma-variable-scope exports ecmaVariableScope as its module.exports.

ecmaVariableScope(ast)

Walk an abstract syntax tree and add scope and scopeInfo properties to appropriate nodes.

  • ast Object - Abstract syntax tree to walk over/mutate

Returns:

We return the same ast variable but with the addition of scope and scopeInfo nodes on Identifiers that are variables.

// `scope` and `scopeInfo` will be defined for all `hello`, `world`, and `moon` references
function hello(world) {
  var moon;
  hello(world, moon);
}

// `log` is an `Identifier` but will not have `scope` and `scopeInfo`
console.log('hello');

scope

Object containing information about the outermost scope a variable can be accessed from:

  • type String - Variant of scope that a variable is in
    • This can be lexical or block. When we traverse scope.parent, we can run into with but this is not directly found from node.scope.
    • For example in function a() { var b; }, we have type: 'lexical' for b's scope.
    • We make these values available via exports.SCOPE_TYPES.LEXICAL ('lexical'), exports.SCOPE_TYPES.WITH ('with'), andexports.SCOPE_TYPES.BLOCK ('block').
  • node Object<Node> - AST node that corresponds to the top of scope
    • For example in function a() { var b; }, we have b.scope.node === a
  • parent Object<Scope>|undefined - Next scope containing the current scope. This can be any other type (e.g. lexical, block, with).
  • identifiers Object - Map from identifier name to Identifier reference of variables declared within this scope
    • For example in function a() { var b; }, we have a.scope.identifiers === {b: b's Identifier}
    • This will not contain identifiers within child scopes
  • children Scope[] - Array of child scopes contained by this scope
    • For example in function a() { }, Program's scope will contain a.scope

It is possible for an Identifier to have scopeInfo but not scope. For example, console is defined as a global outside of a script context. We cannot determine if it is defined or not and make the decision to leave it as undefined.

scopeInfo

Object containing information about the variable itself:

  • insideWith Boolean - Indicator of whether a variable has a with between its declaration and its containing scope
    • true if there was a with,false if there was not one
    • For example in function a() { with (window) { document(); } }, we have a.insideWith === false and document.insideWith === true.
    • We provide exports.SCOPE_INFO_INSIDE_WITH.YES (true) and exports.SCOPE_INFO_INSIDE_WITH.NO (false).
  • toplevel Boolean - Indicator of whether a variable was declared in the Program scope or not
    • true if it was, false if it was not
    • For example in function a() { var b; }, we have a.scopeInfo.topLevel === true and b.scopeInfo.topLevel === false.
    • We provide exports.SCOPE_INFO_TOP_LEVEL.YES (true) and exports.SCOPE_INFO_TOP_LEVEL.NO (false).
  • type String - Reference to the type of scope given to a variable
    • This can vary from the containing scope (e.g. a let is scoped to a function; block to lexical)
    • This can be lexical (e.g. function), block (e.g. for loop), or undeclared (not declared in any containing scope)
    • For example in function a() { let b; }, we have a.scopeInfo.type === 'lexical' and b.scopeInfo.type === 'block'.
    • We provide exports.SCOPE_INFO_TYPES.LEXICAL ('lexical'), exports.SCOPE_INFO_TYPES.BLOCK ('block'), and exports.SCOPE_INFO_TYPES.UNDECLARED ('undeclared').
  • usedInAWith Boolean - Indicator of whether a variable has ever been referenced inside a with in its entire scope
    • true if it has been, false if it has not
    • For example in var hello; var obj = {}; with (obj) { hello; } }, we have hello1.usedInAWith === true and obj1.usedInAWith === false.
    • We provide exports.SCOPE_INFO_USED_IN_A_WITH.YES (true) and exports.SCOPE_INFO_USED_IN_A_WITH.NO (false).

If you would like to determine if a variable can be renamed without causing other problems, use usedInAWith (false), topLevel (false), and type (lexical/block).

Unstable

There are a few extra properties that are thrown in for preparation of scope and scopeInfo. They could be replaced with a better algorithm but are there if you need them. If you are using them, please let us know via an issue.

  • _nearestScope - Present on every node and points to the closest scope of any type up its parents. This is useful for jumping through block scopes until reaching a lexical one.
  • _scopeType - Stored on initial declarations of identifiers. This is the same as scopeInfo.type but needs to be preserved before scopeInfo is generated.

Contributing

In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint via grunt and test via npm test.

Donating

Support this project and others by twolfson via gratipay.

Support via Gratipay

Unlicense

As of Nov 04 2014, Todd Wolfson has released this repository and its contents to the public domain.

It has been released under the UNLICENSE.