Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80538 views
1
var astw = require('astw');
2
3
module.exports = function (src) {
4
var locals = {};
5
var implicit = {};
6
var exported = {};
7
8
if (typeof src === 'string') {
9
src = String(src).replace(/^#![^\n]*\n/, '');
10
}
11
if (src && typeof src === 'object'
12
&& typeof src.copy === 'function' && typeof src.toString === 'function') {
13
src = src.toString('utf8');
14
}
15
var walk = astw(src);
16
17
walk(function (node) {
18
if (node.type === 'VariableDeclaration') {
19
// take off the leading `var `
20
var id = getScope(node);
21
for (var i = 0; i < node.declarations.length; i++) {
22
var d = node.declarations[i];
23
locals[id][d.id.name] = d;
24
}
25
}
26
else if (node.type === 'CatchClause') {
27
var id = getScope(node);
28
locals[id][node.param.name] = node.param
29
}
30
else if (isFunction(node)) {
31
var id = getScope(node.parent);
32
if (node.id) locals[id][node.id.name] = node;
33
var nid = node.params.length && getScope(node);
34
if (nid && !locals[nid]) locals[nid] = {};
35
for (var i = 0; i < node.params.length; i++) {
36
var p = node.params[i];
37
locals[nid][p.name] = p;
38
}
39
}
40
});
41
42
walk(function (node) {
43
if (node.type === 'Identifier'
44
&& lookup(node) === undefined) {
45
if (node.parent.type === 'Property'
46
&& node.parent.key === node) return;
47
if (node.parent.type === 'MemberExpression'
48
&& node.parent.property === node) return;
49
if (isFunction(node.parent)) return;
50
if (node.parent.type === 'LabeledStatement') return;
51
if (node.parent.type === 'ContinueStatement') return;
52
if (node.parent.type === 'BreakStatement') return;
53
54
if (node.parent.type === 'AssignmentExpression') {
55
var isLeft0 = node.parent.left.type === 'MemberExpression'
56
&& node.parent.left.object === node.name
57
;
58
var isLeft1 = node.parent.left.type === 'Identifier'
59
&& node.parent.left.name === node.name
60
;
61
if (isLeft0 || isLeft1) {
62
exported[node.name] = keyOf(node).length;
63
}
64
}
65
if (!exported[node.name]
66
|| exported[node.name] < keyOf(node).length) {
67
implicit[node.name] = keyOf(node).length;
68
}
69
}
70
});
71
72
var localScopes = {};
73
var lks = objectKeys(locals);
74
for (var i = 0; i < lks.length; i++) {
75
var key = lks[i];
76
localScopes[key] = objectKeys(locals[key]);
77
}
78
79
return {
80
locals: localScopes,
81
globals: {
82
implicit: objectKeys(implicit),
83
exported: objectKeys(exported)
84
}
85
};
86
87
function lookup (node) {
88
for (var p = node; p; p = p.parent) {
89
if (isFunction(p) || p.type === 'Program') {
90
var id = getScope(p);
91
if (locals[id][node.name]) {
92
return id;
93
}
94
}
95
}
96
return undefined;
97
}
98
99
function getScope (node) {
100
for (
101
var p = node;
102
!isFunction(p) && p.type !== 'Program';
103
p = p.parent
104
);
105
var id = idOf(p);
106
if (!locals[id]) locals[id] = {};
107
return id;
108
}
109
110
};
111
112
function isFunction (x) {
113
return x.type === 'FunctionDeclaration'
114
|| x.type === 'FunctionExpression'
115
;
116
}
117
118
function idOf (node) {
119
var id = [];
120
for (var n = node; n.type !== 'Program'; n = n.parent) {
121
var key = keyOf(n).join('.');
122
id.unshift(key);
123
}
124
return id.join('.');
125
}
126
127
function keyOf (node) {
128
if (node.lexicalScopeKey) return node.lexicalScopeKey;
129
var p = node.parent;
130
var ks = objectKeys(p);
131
var kv = { keys : [], values : [], top : [] };
132
133
for (var i = 0; i < ks.length; i++) {
134
var key = ks[i];
135
kv.keys.push(key);
136
kv.values.push(p[key]);
137
kv.top.push(undefined);
138
139
if (isArray(p[key])) {
140
var keys = objectKeys(p[key]);
141
kv.keys.push.apply(kv.keys, keys);
142
kv.values.push.apply(kv.values, p[key]);
143
144
var nkeys = [];
145
for (var j = 0; j < keys.length; j++) nkeys.push(key);
146
kv.top.push.apply(kv.top, nkeys);
147
}
148
}
149
var ix = indexOf(kv.values, node);
150
var res = [];
151
if (kv.top[ix]) res.push(kv.top[ix]);
152
if (kv.keys[ix]) res.push(kv.keys[ix]);
153
if (node.parent.type === 'CallExpression') {
154
res.unshift.apply(res, keyOf(node.parent.parent));
155
}
156
return node.lexicalScopeKey = res;
157
}
158
159
var isArray = Array.isArray || function (xs) {
160
return Object.prototype.toString.call(xs) === '[object Array]';
161
};
162
163
var objectKeys = Object.keys || function (obj) {
164
var keys = [];
165
for (var key in obj) keys.push(key);
166
return keys;
167
};
168
169
function indexOf (xs, x) {
170
if (xs.indexOf) return xs.indexOf(x);
171
for (var i = 0; i < xs.length; i++) {
172
if (x === xs[i]) return i;
173
}
174
return -1;
175
}
176
177