1 | # scope-analyzer
|
2 |
|
3 | simple scope analysis for javascript ASTs. tracks scopes and collects references to variables.
|
4 |
|
5 | Caveats and/or todos:
|
6 |
|
7 | - May be missing edge cases.
|
8 | - Things like `label:`s are not considered at all, but ideally in the future they will!
|
9 |
|
10 | [![stability][stability-image]][stability-url]
|
11 | [![npm][npm-image]][npm-url]
|
12 | [![travis][travis-image]][travis-url]
|
13 | [![standard][standard-image]][standard-url]
|
14 |
|
15 | [stability-image]: https://img.shields.io/badge/stability-experimental-orange.svg?style=flat-square
|
16 | [stability-url]: https://nodejs.org/api/documentation.html#documentation_stability_index
|
17 | [npm-image]: https://img.shields.io/npm/v/scope-analyzer.svg?style=flat-square
|
18 | [npm-url]: https://www.npmjs.com/package/scope-analyzer
|
19 | [travis-image]: https://img.shields.io/travis/goto-bus-stop/scope-analyzer.svg?style=flat-square
|
20 | [travis-url]: https://travis-ci.org/goto-bus-stop/scope-analyzer
|
21 | [standard-image]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square
|
22 | [standard-url]: http://npm.im/standard
|
23 |
|
24 | ## Install
|
25 |
|
26 | ```
|
27 | npm install scope-analyzer
|
28 | ```
|
29 |
|
30 | ## Usage
|
31 |
|
32 | Note: AST nodes passed to `scope-analyzer` functions are expected to reference the parent node on a `node.parent` property.
|
33 | Nodes from [falafel](https://github.com/substack/node-falafel) or [transform-ast](https://github.com/goto-bus-stop/transform-ast) have a `.parent` property, but others may not. You can use [estree-assign-parent](https://github.com/goto-bus-stop/estree-assign-parent) to quickly assign a parent property to all nodes in an AST.
|
34 |
|
35 | ```js
|
36 | var scan = require('scope-analyzer')
|
37 |
|
38 | var ast = parse('...')
|
39 | // Initialize node module variables
|
40 | scan.createScope(ast, ['module', 'exports', '__dirname', '__filename'])
|
41 | scan.analyze(ast)
|
42 |
|
43 | var binding = scan.getBinding(ast, 'exports')
|
44 | binding.getReferences().forEach(function (reference) {
|
45 | // Assume for the sake of the example that all references to `exports` are assignments like
|
46 | // `exports.xyz = abc`
|
47 | console.log('found export:', reference.parent.property.name)
|
48 | })
|
49 | ```
|
50 |
|
51 | ## API
|
52 |
|
53 | ### `crawl(ast)`
|
54 |
|
55 | Walk the ast and analyze all scopes. This will immediately allow you to use the `get*` methods on any node in the tree.
|
56 |
|
57 | ### `visitScope(node)`
|
58 |
|
59 | Visit a node to check if it initialises any scopes.
|
60 | For example, a function declaration will initialise a new scope to hold bindings for its parameters.
|
61 | Use this if you are already walking the AST manually, and if you don't need the scope information during this walk.
|
62 |
|
63 | ### `visitBinding(node)`
|
64 |
|
65 | Visit a node to check if it is a reference to an existing binding.
|
66 | If it is, the reference is added to the parent scope.
|
67 | Use this if you are already walking the AST manually.
|
68 |
|
69 | ### `createScope(node, bindings)`
|
70 |
|
71 | Initialise a new scope at the given node. `bindings` is an array of variable names.
|
72 | This can be useful to make the scope analyzer aware of preexisting global variables.
|
73 | In that case, call `createScope` on the root node with the names of globals:
|
74 |
|
75 | ```js
|
76 | var ast = parse('xyz')
|
77 | scopeAnalyzer.createScope(ast, ['HTMLElement', 'Notification', ...])
|
78 | ```
|
79 |
|
80 | ### `scope(node)`
|
81 |
|
82 | Get the [Scope](#scope) initialised by the given node.
|
83 |
|
84 | ### `getBinding(node)`
|
85 |
|
86 | Get the [Binding](#binding) referenced by the `Identifier` `node`.
|
87 |
|
88 | ### Scope
|
89 |
|
90 | #### `scope.has(name)`
|
91 |
|
92 | Check if this scope defines `name`.
|
93 |
|
94 | #### `scope.getBinding(name)`
|
95 |
|
96 | Get the [Binding](#binding) named `name` that is declared by this scope.
|
97 |
|
98 | #### `scope.getReferences(name)`
|
99 |
|
100 | Get a list of all nodes referencing the `name` binding that is declared by this scope.
|
101 |
|
102 | #### `scope.getUndeclaredNames()`
|
103 |
|
104 | Get a list of all names that were used in this scope, but not defined anywhere in the AST.
|
105 |
|
106 | #### `scope.forEach(cb(binding, name))`
|
107 |
|
108 | Loop over all bindings declared by this scope.
|
109 |
|
110 | #### `scope.forEachAvailable(cb(binding, name))`
|
111 |
|
112 | Loop over all bindings available to this scope, declared in this scope or any parent scope.
|
113 |
|
114 | ### Binding
|
115 |
|
116 | #### `binding.definition`
|
117 |
|
118 | The node that defined this binding. If this binding was not declared in the AST, `binding.definition` will be undefined.
|
119 |
|
120 | #### `binding.getReferences()`
|
121 |
|
122 | Return an array of nodes that reference this binding.
|
123 |
|
124 | #### `binding.isReferenced()`
|
125 |
|
126 | Check if the binding is referenced, i.e., if there are any identifier Nodes (other than `binding.definition`) referencing this binding.
|
127 |
|
128 | #### `binding.remove(node)`
|
129 |
|
130 | Remove a reference to this binding. Use this when you are replacing the node referencing the binding with something else.
|
131 |
|
132 | ## License
|
133 |
|
134 | [Apache-2.0](LICENSE.md)
|