Skip to content

Commit ec334a2

Browse files
ZauberNerdfkling
authored andcommitted
Try to resolve MemberExpression to initialization value
This change enables `resolveToValue` to handle `MemberExpression`s. When it encounters a MemberExpression it tries to find the definition of the root object of that expression and then tries to resolve the property inside that ObjectExpression. If it cannot resolve the root object to an ObjectExpression (ie. because it was declared externally), it will stop and return the path to the original MemberExpression - or if the original expression did resolve to an Object but the properties inside that object were Identifier which could not resolve to an ObjectExpression it returns that MemberExpression instead.
1 parent e481e87 commit ec334a2

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

src/utils/__tests__/resolveToValue-test.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,62 @@ describe('resolveToValue', () => {
143143

144144
});
145145

146+
describe('MemberExpression', () => {
147+
148+
it('resolves a MemberExpression to it\'s init value', () => {
149+
var path = parse([
150+
'var foo = { bar: 1 };',
151+
'foo.bar;',
152+
].join('\n'));
153+
154+
expect(resolveToValue(path).node).toEqualASTNode(builders.literal(1));
155+
});
156+
157+
it('resolves a MemberExpression in the scope chain', () => {
158+
var path = parse([
159+
'var foo = 1;',
160+
'var bar = { baz: foo };',
161+
'bar.baz;',
162+
].join('\n'));
163+
164+
expect(resolveToValue(path).node).toEqualASTNode(builders.literal(1));
165+
});
166+
167+
it('resolves a nested MemberExpression in the scope chain', () => {
168+
var path = parse([
169+
'var foo = { bar: 1 };',
170+
'var bar = { baz: foo.bar };',
171+
'bar.baz;',
172+
].join('\n'));
173+
174+
expect(resolveToValue(path).node).toEqualASTNode(builders.literal(1));
175+
});
176+
177+
it('returns the last resolvable MemberExpression', () => {
178+
var path = parse([
179+
'import foo from "bar";',
180+
'var bar = { baz: foo.bar };',
181+
'bar.baz;',
182+
].join('\n'));
183+
184+
expect(resolveToValue(path).node).toEqualASTNode(
185+
builders.memberExpression(
186+
builders.identifier('foo'),
187+
builders.identifier('bar')
188+
)
189+
);
190+
});
191+
192+
it('returns the path itself if it can not resolve it any further', () => {
193+
var path = parse([
194+
'var foo = {};',
195+
'foo.bar = 1;',
196+
'foo.bar;',
197+
].join('\n'));
198+
199+
expect(resolveToValue(path).node).toEqualASTNode(path.node);
200+
});
201+
202+
});
203+
146204
});

src/utils/resolveToValue.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
*/
1212

1313
import recast from 'recast';
14+
import getMemberExpressionRoot from './getMemberExpressionRoot';
15+
import getPropertyValuePath from './getPropertyValuePath';
16+
import {Array as toArray} from './expressionTo';
1417
import {traverseShallow} from './traverse';
1518

1619
var {
@@ -105,6 +108,7 @@ function findLastAssignedValue(scope, name) {
105108
/**
106109
* If the path is an identifier, it is resolved in the scope chain.
107110
* If it is an assignment expression, it resolves to the right hand side.
111+
* If it is a member expression it is resolved to it's initialization value.
108112
*
109113
* Else the path itself is returned.
110114
*/
@@ -114,6 +118,18 @@ export default function resolveToValue(path: NodePath): NodePath {
114118
if (node.init) {
115119
return resolveToValue(path.get('init'));
116120
}
121+
} else if (types.MemberExpression.check(node)) {
122+
const resolved = resolveToValue(getMemberExpressionRoot(path));
123+
if (types.ObjectExpression.check(resolved.node)) {
124+
const memberParts = toArray(path).slice(1);
125+
const init = memberParts.reduce((propertyPath, propertyName) => {
126+
propertyPath = resolveToValue(propertyPath);
127+
return types.ObjectExpression.check(propertyPath.node) ?
128+
getPropertyValuePath(propertyPath, propertyName) :
129+
null;
130+
}, resolved);
131+
return init ? resolveToValue(init) : path;
132+
}
117133
} else if (
118134
types.ImportDefaultSpecifier.check(node) ||
119135
types.ImportNamespaceSpecifier.check(node) ||

0 commit comments

Comments
 (0)