UNPKG

3.39 kBJavaScriptView Raw
1"use strict";
2
3const { Syntax } = require("estraverse-fb");
4
5/**
6 * Visitor for estraverse.
7 *
8 * @param {Object} node .
9 * @param {Object} parent .
10 * @param {Array<string>} uses .
11 * @this {estravarse.Controller} this
12 */
13function leave(node, parent, uses) {
14 switch (node.type) {
15 case Syntax.MemberExpression:
16 case Syntax.JSXMemberExpression:
17 if (hasObjectIdentifier_(node) && !hasScope_(node, this.parents())) {
18 const parents = this.parents()
19 .concat(node)
20 .reverse();
21 const path = this.path()
22 .concat("object")
23 .reverse();
24 const use = registerIdentifier_(node.object, parents, path);
25 if (use) {
26 uses.push(use);
27 }
28 }
29 break;
30 default:
31 break;
32 }
33}
34
35/**
36 * @param {Object} node .
37 * @return {boolean} True if the node is not computed (accessed by dot operator)
38 * and the "object" property is an identifier node.
39 * @private
40 */
41function hasObjectIdentifier_(node) {
42 return !node.computed && isIdentifierType_(node.object.type);
43}
44
45/**
46 * @param {string} type .
47 * @return {boolean} True if the type is Syntax.Identifier or
48 * Syntax.JSXIdentifier.
49 */
50function isIdentifierType_(type) {
51 return type === Syntax.Identifier || type === Syntax.JSXIdentifier;
52}
53
54/**
55 * @param {Object} node .
56 * @param {Array<Object>} parents .
57 * @return {boolean} True if the node has a local or a lexical scope.
58 * @private
59 */
60function hasScope_(node, parents) {
61 const nodeName = node.object.name;
62 parents = parents.slice();
63 while ((node = parents.pop())) {
64 switch (node.type) {
65 case Syntax.FunctionExpression:
66 case Syntax.FunctionDeclaration:
67 if (node.params && node.params.some(param => param.name === nodeName)) {
68 return true;
69 }
70 break;
71 case Syntax.BlockStatement:
72 if (
73 node.body &&
74 node.body.some(
75 bodyPart =>
76 bodyPart.type === Syntax.VariableDeclaration &&
77 bodyPart.declarations.some(
78 declaration =>
79 declaration.type === Syntax.VariableDeclarator && declaration.id.name === nodeName
80 )
81 )
82 ) {
83 return true;
84 }
85 break;
86 default:
87 break;
88 }
89 }
90 return false;
91}
92
93/**
94 * @param {Object} node .
95 * @param {Array<Object>} parents .
96 * @param {Array<string>} path .
97 * @return {Object} .
98 * @private
99 */
100function registerIdentifier_(node, parents, path) {
101 const namespace = [node.name];
102 for (let i = 0; i < parents.length; i++) {
103 const current = parents[i];
104 const parentKey = path[i];
105 switch (current.type) {
106 case Syntax.MemberExpression:
107 case Syntax.JSXMemberExpression:
108 if (!current.computed && isIdentifierType_(current.property.type)) {
109 namespace.push(current.property.name);
110 } else {
111 return createMemberObject_(namespace, current, parentKey);
112 }
113 break;
114 default:
115 return createMemberObject_(namespace, current, parentKey);
116 }
117 }
118 return null;
119}
120
121/**
122 * @param {Array<string>} namespace .
123 * @param {Object} node .
124 * @param {string} parentKey .
125 * @return {!Object} .
126 * @private
127 */
128function createMemberObject_(namespace, node, parentKey) {
129 return {
130 name: namespace,
131 node,
132 key: parentKey,
133 };
134}
135
136module.exports = {
137 leave,
138};