1 | import propName from './propName';
|
---|
2 |
|
---|
3 | const DEFAULT_OPTIONS = {
|
---|
4 | ignoreCase: true,
|
---|
5 | };
|
---|
6 |
|
---|
7 | /**
|
---|
8 | * Returns the JSXAttribute itself or undefined, indicating the prop
|
---|
9 | * is not present on the JSXOpeningElement.
|
---|
10 | *
|
---|
11 | */
|
---|
12 | export default function getProp(props = [], prop = '', options = DEFAULT_OPTIONS) {
|
---|
13 | function getName(name) { return options.ignoreCase ? name.toUpperCase() : name; }
|
---|
14 | const propToFind = getName(prop);
|
---|
15 | function isPropToFind(property) {
|
---|
16 | return property.type === 'Property'
|
---|
17 | && property.key.type === 'Identifier'
|
---|
18 | && propToFind === getName(property.key.name);
|
---|
19 | }
|
---|
20 |
|
---|
21 | const foundAttribute = props.find((attribute) => {
|
---|
22 | // If the props contain a spread prop, try to find the property in the object expression.
|
---|
23 | if (attribute.type === 'JSXSpreadAttribute') {
|
---|
24 | return attribute.argument.type === 'ObjectExpression'
|
---|
25 | && propToFind !== getName('key') // https://github.com/reactjs/rfcs/pull/107
|
---|
26 | && attribute.argument.properties.some(isPropToFind);
|
---|
27 | }
|
---|
28 |
|
---|
29 | return propToFind === getName(propName(attribute));
|
---|
30 | });
|
---|
31 |
|
---|
32 | if (foundAttribute && foundAttribute.type === 'JSXSpreadAttribute') {
|
---|
33 | return propertyToJSXAttribute(foundAttribute.argument.properties.find(isPropToFind));
|
---|
34 | }
|
---|
35 |
|
---|
36 | return foundAttribute;
|
---|
37 | }
|
---|
38 |
|
---|
39 | function propertyToJSXAttribute(node) {
|
---|
40 | const { key, value } = node;
|
---|
41 | return {
|
---|
42 | type: 'JSXAttribute',
|
---|
43 | name: { type: 'JSXIdentifier', name: key.name, ...getBaseProps(key) },
|
---|
44 | value: value.type === 'Literal'
|
---|
45 | ? adjustRangeOfNode(value)
|
---|
46 | : { type: 'JSXExpressionContainer', expression: adjustExpressionRange(value), ...getBaseProps(value) },
|
---|
47 | ...getBaseProps(node),
|
---|
48 | };
|
---|
49 | }
|
---|
50 |
|
---|
51 | function adjustRangeOfNode(node) {
|
---|
52 | const [start, end] = node.range || [node.start, node.end];
|
---|
53 |
|
---|
54 | return {
|
---|
55 | ...node,
|
---|
56 | end: undefined,
|
---|
57 | range: [start, end],
|
---|
58 | start: undefined,
|
---|
59 | };
|
---|
60 | }
|
---|
61 |
|
---|
62 | function adjustExpressionRange({ expressions, quasis, ...expression }) {
|
---|
63 | return {
|
---|
64 | ...adjustRangeOfNode(expression),
|
---|
65 | ...(expressions ? { expressions: expressions.map(adjustRangeOfNode) } : {}),
|
---|
66 | ...(quasis ? { quasis: quasis.map(adjustRangeOfNode) } : {}),
|
---|
67 | };
|
---|
68 | }
|
---|
69 |
|
---|
70 | function getBaseProps({ loc, ...node }) {
|
---|
71 | const { range } = adjustRangeOfNode(node);
|
---|
72 |
|
---|
73 | return {
|
---|
74 | loc: getBaseLocation(loc),
|
---|
75 | range,
|
---|
76 | };
|
---|
77 | }
|
---|
78 |
|
---|
79 | function getBaseLocation({
|
---|
80 | start,
|
---|
81 | end,
|
---|
82 | source,
|
---|
83 | filename,
|
---|
84 | }) {
|
---|
85 | return {
|
---|
86 | start,
|
---|
87 | end,
|
---|
88 | ...(source !== undefined ? { source } : {}),
|
---|
89 | ...(filename !== undefined ? { filename } : {}),
|
---|
90 | };
|
---|
91 | }
|
---|