1 | ;
|
2 |
|
3 | const invariant = require('invariant');
|
4 | const t = require('babel-types');
|
5 | const generate = require('babel-generator').default;
|
6 |
|
7 | const 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 |
|
20 | function 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 |
|
70 | module.exports = getPropValueFromAttributes;
|