1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | 'use strict';
|
7 |
|
8 | const variableUtil = require('../util/variable');
|
9 | const jsxUtil = require('../util/jsx');
|
10 | const docsUrl = require('../util/docsUrl');
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | module.exports = {
|
16 | meta: {
|
17 | docs: {
|
18 | description: 'Report when a DOM element is using both children and dangerouslySetInnerHTML',
|
19 | category: '',
|
20 | recommended: true,
|
21 | url: docsUrl('no-danger-with-children')
|
22 | },
|
23 | schema: []
|
24 | },
|
25 | create(context) {
|
26 | function findSpreadVariable(name) {
|
27 | return variableUtil.variablesInScope(context).find((item) => item.name === name);
|
28 | }
|
29 | |
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 | function findObjectProp(node, propName, seenProps) {
|
37 | if (!node.properties) {
|
38 | return false;
|
39 | }
|
40 | return node.properties.find((prop) => {
|
41 | if (prop.type === 'Property') {
|
42 | return prop.key.name === propName;
|
43 | }
|
44 | if (prop.type === 'ExperimentalSpreadProperty' || prop.type === 'SpreadElement') {
|
45 | const variable = findSpreadVariable(prop.argument.name);
|
46 | if (variable && variable.defs.length && variable.defs[0].node.init) {
|
47 | if (seenProps.indexOf(prop.argument.name) > -1) {
|
48 | return false;
|
49 | }
|
50 | const newSeenProps = seenProps.concat(prop.argument.name || []);
|
51 | return findObjectProp(variable.defs[0].node.init, propName, newSeenProps);
|
52 | }
|
53 | }
|
54 | return false;
|
55 | });
|
56 | }
|
57 |
|
58 | |
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 | function findJsxProp(node, propName) {
|
65 | const attributes = node.openingElement.attributes;
|
66 | return attributes.find((attribute) => {
|
67 | if (attribute.type === 'JSXSpreadAttribute') {
|
68 | const variable = findSpreadVariable(attribute.argument.name);
|
69 | if (variable && variable.defs.length && variable.defs[0].node.init) {
|
70 | return findObjectProp(variable.defs[0].node.init, propName, []);
|
71 | }
|
72 | }
|
73 | return attribute.name && attribute.name.name === propName;
|
74 | });
|
75 | }
|
76 |
|
77 | |
78 |
|
79 |
|
80 |
|
81 |
|
82 | function isLineBreak(node) {
|
83 | const isLiteral = node.type === 'Literal' || node.type === 'JSXText';
|
84 | const isMultiline = node.loc.start.line !== node.loc.end.line;
|
85 | const isWhiteSpaces = jsxUtil.isWhiteSpaces(node.value);
|
86 |
|
87 | return isLiteral && isMultiline && isWhiteSpaces;
|
88 | }
|
89 |
|
90 | return {
|
91 | JSXElement(node) {
|
92 | let hasChildren = false;
|
93 |
|
94 | if (node.children.length && !isLineBreak(node.children[0])) {
|
95 | hasChildren = true;
|
96 | } else if (findJsxProp(node, 'children')) {
|
97 | hasChildren = true;
|
98 | }
|
99 |
|
100 | if (
|
101 | node.openingElement.attributes
|
102 | && hasChildren
|
103 | && findJsxProp(node, 'dangerouslySetInnerHTML')
|
104 | ) {
|
105 | context.report({
|
106 | node,
|
107 | message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'
|
108 | });
|
109 | }
|
110 | },
|
111 | CallExpression(node) {
|
112 | if (
|
113 | node.callee
|
114 | && node.callee.type === 'MemberExpression'
|
115 | && node.callee.property.name === 'createElement'
|
116 | && node.arguments.length > 1
|
117 | ) {
|
118 | let hasChildren = false;
|
119 |
|
120 | let props = node.arguments[1];
|
121 |
|
122 | if (props.type === 'Identifier') {
|
123 | const variable = variableUtil.variablesInScope(context).find((item) => item.name === props.name);
|
124 | if (variable && variable.defs.length && variable.defs[0].node.init) {
|
125 | props = variable.defs[0].node.init;
|
126 | }
|
127 | }
|
128 |
|
129 | const dangerously = findObjectProp(props, 'dangerouslySetInnerHTML', []);
|
130 |
|
131 | if (node.arguments.length === 2) {
|
132 | if (findObjectProp(props, 'children', [])) {
|
133 | hasChildren = true;
|
134 | }
|
135 | } else {
|
136 | hasChildren = true;
|
137 | }
|
138 |
|
139 | if (dangerously && hasChildren) {
|
140 | context.report({
|
141 | node,
|
142 | message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'
|
143 | });
|
144 | }
|
145 | }
|
146 | }
|
147 | };
|
148 | }
|
149 | };
|