UNPKG

2.3 kBJavaScriptView Raw
1'use strict';
2
3const invariant = require('invariant');
4const t = require('babel-types');
5const generate = require('babel-generator').default;
6
7const accessSafe = require('./accessSafe');
8
9// getPropValueFromAttributes gets a prop by name from a list of attributes and accounts for potential spread operators.
10
11// Here's an example. Given this component:
12// <Block coolProp="wow" {...spread1} neatProp="ok" {...spread2} />
13// getPropValueFromAttributes will return the following:
14// - for propName 'coolProp': accessSafe(spread1, 'coolProp') || accessSafe(spread2, 'coolProp') || 'wow'
15// - for propName 'neatProp': accessSafe(spread2, 'neatProp') || 'ok'
16// - for propName 'notPresent': null
17
18// The returned value should (obviously) be placed after spread operators.
19
20function getPropValueFromAttributes(propName, attrs) {
21 const propIndex = attrs.findIndex(
22 attr => attr.name && attr.name.name === propName
23 );
24
25 if (propIndex === -1) {
26 return null;
27 }
28
29 let propValue = attrs[propIndex].value;
30
31 if (t.isJSXExpressionContainer(propValue)) {
32 propValue = propValue.expression;
33 }
34
35 // filter out spread props that occur before propValue
36 const applicableSpreads = attrs
37 .filter(
38 // 1. idx is greater than propValue prop index
39 // 2. attr is a spread operator
40 (attr, idx) => {
41 if (t.isJSXSpreadAttribute(attr)) {
42 invariant(
43 // only allow member expressions and identifiers to be spread for now
44 t.isIdentifier(attr.argument) ||
45 t.isMemberExpression(attr.argument),
46 'Unhandled spread operator value of type `%s` (`%s`)',
47 attr.argument.type,
48 generate(attr).code
49 );
50 return idx > propIndex;
51 }
52 return false;
53 }
54 )
55 .map(attr => attr.argument);
56
57 // if spread operators occur after propValue, create a binary expression for each operator
58 // i.e. before1.propValue || before2.propValue || propValue
59 // TODO: figure out how to do this without all the extra parens
60 if (applicableSpreads.length > 0) {
61 propValue = applicableSpreads.reduce(
62 (acc, val) => t.logicalExpression('||', accessSafe(val, propName), acc),
63 propValue
64 );
65 }
66
67 return propValue;
68}
69
70module.exports = getPropValueFromAttributes;