123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- 'use strict';
- const variableUtil = require('../util/variable');
- const jsxUtil = require('../util/jsx');
- const docsUrl = require('../util/docsUrl');
- const report = require('../util/report');
- const messages = {
- dangerWithChildren: 'Only set one of `children` or `props.dangerouslySetInnerHTML`',
- };
- module.exports = {
- meta: {
- docs: {
- description: 'Disallow when a DOM element is using both children and dangerouslySetInnerHTML',
- category: 'Possible Errors',
- recommended: true,
- url: docsUrl('no-danger-with-children'),
- },
- messages,
- schema: [],
- },
- create(context) {
- function findSpreadVariable(name) {
- return variableUtil.variablesInScope(context).find((item) => item.name === name);
- }
-
- function findObjectProp(node, propName, seenProps) {
- if (!node.properties) {
- return false;
- }
- return node.properties.find((prop) => {
- if (prop.type === 'Property') {
- return prop.key.name === propName;
- }
- if (prop.type === 'ExperimentalSpreadProperty' || prop.type === 'SpreadElement') {
- const variable = findSpreadVariable(prop.argument.name);
- if (variable && variable.defs.length && variable.defs[0].node.init) {
- if (seenProps.indexOf(prop.argument.name) > -1) {
- return false;
- }
- const newSeenProps = seenProps.concat(prop.argument.name || []);
- return findObjectProp(variable.defs[0].node.init, propName, newSeenProps);
- }
- }
- return false;
- });
- }
-
- function findJsxProp(node, propName) {
- const attributes = node.openingElement.attributes;
- return attributes.find((attribute) => {
- if (attribute.type === 'JSXSpreadAttribute') {
- const variable = findSpreadVariable(attribute.argument.name);
- if (variable && variable.defs.length && variable.defs[0].node.init) {
- return findObjectProp(variable.defs[0].node.init, propName, []);
- }
- }
- return attribute.name && attribute.name.name === propName;
- });
- }
-
- function isLineBreak(node) {
- const isLiteral = node.type === 'Literal' || node.type === 'JSXText';
- const isMultiline = node.loc.start.line !== node.loc.end.line;
- const isWhiteSpaces = jsxUtil.isWhiteSpaces(node.value);
- return isLiteral && isMultiline && isWhiteSpaces;
- }
- return {
- JSXElement(node) {
- let hasChildren = false;
- if (node.children.length && !isLineBreak(node.children[0])) {
- hasChildren = true;
- } else if (findJsxProp(node, 'children')) {
- hasChildren = true;
- }
- if (
- node.openingElement.attributes
- && hasChildren
- && findJsxProp(node, 'dangerouslySetInnerHTML')
- ) {
- report(context, messages.dangerWithChildren, 'dangerWithChildren', {
- node,
- });
- }
- },
- CallExpression(node) {
- if (
- node.callee
- && node.callee.type === 'MemberExpression'
- && node.callee.property.name === 'createElement'
- && node.arguments.length > 1
- ) {
- let hasChildren = false;
- let props = node.arguments[1];
- if (props.type === 'Identifier') {
- const variable = variableUtil.variablesInScope(context).find((item) => item.name === props.name);
- if (variable && variable.defs.length && variable.defs[0].node.init) {
- props = variable.defs[0].node.init;
- }
- }
- const dangerously = findObjectProp(props, 'dangerouslySetInnerHTML', []);
- if (node.arguments.length === 2) {
- if (findObjectProp(props, 'children', [])) {
- hasChildren = true;
- }
- } else {
- hasChildren = true;
- }
- if (dangerously && hasChildren) {
- report(context, messages.dangerWithChildren, 'dangerWithChildren', {
- node,
- });
- }
- }
- },
- };
- },
- };
|