<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">/**
 * @fileoverview Prevent void elements (e.g. &lt;img /&gt;, &lt;br /&gt;) from receiving
 *   children
 * @author Joe Lencioni
 */

'use strict';

const has = require('object.hasown/polyfill')();

const docsUrl = require('../util/docsUrl');
const isCreateElement = require('../util/isCreateElement');
const report = require('../util/report');

// ------------------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------------------

// Using an object here to avoid array scan. We should switch to Set once
// support is good enough.
const VOID_DOM_ELEMENTS = {
  area: true,
  base: true,
  br: true,
  col: true,
  embed: true,
  hr: true,
  img: true,
  input: true,
  keygen: true,
  link: true,
  menuitem: true,
  meta: true,
  param: true,
  source: true,
  track: true,
  wbr: true,
};

function isVoidDOMElement(elementName) {
  return has(VOID_DOM_ELEMENTS, elementName);
}

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

const noChildrenInVoidEl = 'Void DOM element &lt;{{element}} /&gt; cannot receive children.';

module.exports = {
  meta: {
    docs: {
      description: 'Disallow void DOM elements (e.g. `&lt;img /&gt;`, `&lt;br /&gt;`) from receiving children',
      category: 'Best Practices',
      recommended: false,
      url: docsUrl('void-dom-elements-no-children'),
    },

    messages: {
      noChildrenInVoidEl,
    },

    schema: [],
  },

  create: (context) =&gt; ({
    JSXElement(node) {
      const elementName = node.openingElement.name.name;

      if (!isVoidDOMElement(elementName)) {
        // e.g. &lt;div /&gt;
        return;
      }

      if (node.children.length &gt; 0) {
        // e.g. &lt;br&gt;Foo&lt;/br&gt;
        report(context, noChildrenInVoidEl, 'noChildrenInVoidEl', {
          node,
          data: {
            element: elementName,
          },
        });
      }

      const attributes = node.openingElement.attributes;

      const hasChildrenAttributeOrDanger = attributes.some((attribute) =&gt; {
        if (!attribute.name) {
          return false;
        }

        return attribute.name.name === 'children' || attribute.name.name === 'dangerouslySetInnerHTML';
      });

      if (hasChildrenAttributeOrDanger) {
        // e.g. &lt;br children="Foo" /&gt;
        report(context, noChildrenInVoidEl, 'noChildrenInVoidEl', {
          node,
          data: {
            element: elementName,
          },
        });
      }
    },

    CallExpression(node) {
      if (node.callee.type !== 'MemberExpression' &amp;&amp; node.callee.type !== 'Identifier') {
        return;
      }

      if (!isCreateElement(node, context)) {
        return;
      }

      const args = node.arguments;

      if (args.length &lt; 1) {
        // React.createElement() should not crash linter
        return;
      }

      const elementName = args[0].value;

      if (!isVoidDOMElement(elementName)) {
        // e.g. React.createElement('div');
        return;
      }

      if (args.length &lt; 2 || args[1].type !== 'ObjectExpression') {
        return;
      }

      const firstChild = args[2];
      if (firstChild) {
        // e.g. React.createElement('br', undefined, 'Foo')
        report(context, noChildrenInVoidEl, 'noChildrenInVoidEl', {
          node,
          data: {
            element: elementName,
          },
        });
      }

      const props = args[1].properties;

      const hasChildrenPropOrDanger = props.some((prop) =&gt; {
        if (!prop.key) {
          return false;
        }

        return prop.key.name === 'children' || prop.key.name === 'dangerouslySetInnerHTML';
      });

      if (hasChildrenPropOrDanger) {
        // e.g. React.createElement('br', { children: 'Foo' })
        report(context, noChildrenInVoidEl, 'noChildrenInVoidEl', {
          node,
          data: {
            element: elementName,
          },
        });
      }
    },
  }),
};
</pre></body></html>