| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 | /** * @fileoverview Utility functions for JSX */'use strict';const elementType = require('jsx-ast-utils/elementType');const astUtil = require('./ast');const isCreateElement = require('./isCreateElement');const variableUtil = require('./variable');// See https://github.com/babel/babel/blob/ce420ba51c68591e057696ef43e028f41c6e04cd/packages/babel-types/src/validators/react/isCompatTag.js// for why we only test for the first characterconst COMPAT_TAG_REGEX = /^[a-z]/;/** * Checks if a node represents a DOM element according to React. * @param {object} node - JSXOpeningElement to check. * @returns {boolean} Whether or not the node corresponds to a DOM element. */function isDOMComponent(node) {  const name = elementType(node);  return COMPAT_TAG_REGEX.test(name);}/** * Test whether a JSXElement is a fragment * @param {JSXElement} node * @param {string} reactPragma * @param {string} fragmentPragma * @returns {boolean} */function isFragment(node, reactPragma, fragmentPragma) {  const name = node.openingElement.name;  // <Fragment>  if (name.type === 'JSXIdentifier' && name.name === fragmentPragma) {    return true;  }  // <React.Fragment>  if (    name.type === 'JSXMemberExpression'    && name.object.type === 'JSXIdentifier'    && name.object.name === reactPragma    && name.property.type === 'JSXIdentifier'    && name.property.name === fragmentPragma  ) {    return true;  }  return false;}/** * Checks if a node represents a JSX element or fragment. * @param {object} node - node to check. * @returns {boolean} Whether or not the node if a JSX element or fragment. */function isJSX(node) {  return node && ['JSXElement', 'JSXFragment'].indexOf(node.type) >= 0;}/** * Check if node is like `key={...}` as in `<Foo key={...} />` * @param {ASTNode} node * @returns {boolean} */function isJSXAttributeKey(node) {  return node.type === 'JSXAttribute'    && node.name    && node.name.type === 'JSXIdentifier'    && node.name.name === 'key';}/** * Check if value has only whitespaces * @param {string} value * @returns {boolean} */function isWhiteSpaces(value) {  return typeof value === 'string' ? /^\s*$/.test(value) : false;}/** * Check if the node is returning JSX or null * * @param {ASTNode} ASTnode The AST node being checked * @param {Context} context The context of `ASTNode`. * @param {Boolean} [strict] If true, in a ternary condition the node must return JSX in both cases * @param {Boolean} [ignoreNull] If true, null return values will be ignored * @returns {Boolean} True if the node is returning JSX or null, false if not */function isReturningJSX(ASTnode, context, strict, ignoreNull) {  const isJSXValue = (node) => {    if (!node) {      return false;    }    switch (node.type) {      case 'ConditionalExpression':        if (strict) {          return isJSXValue(node.consequent) && isJSXValue(node.alternate);        }        return isJSXValue(node.consequent) || isJSXValue(node.alternate);      case 'LogicalExpression':        if (strict) {          return isJSXValue(node.left) && isJSXValue(node.right);        }        return isJSXValue(node.left) || isJSXValue(node.right);      case 'SequenceExpression':        return isJSXValue(node.expressions[node.expressions.length - 1]);      case 'JSXElement':      case 'JSXFragment':        return true;      case 'CallExpression':        return isCreateElement(node, context);      case 'Literal':        if (!ignoreNull && node.value === null) {          return true;        }        return false;      case 'Identifier': {        const variable = variableUtil.findVariableByName(context, node.name);        return isJSX(variable);      }      default:        return false;    }  };  let found = false;  astUtil.traverseReturns(ASTnode, context, (node, breakTraverse) => {    if (isJSXValue(node)) {      found = true;      breakTraverse();    }  });  return found;}/** * Check if the node is returning only null values * * @param {ASTNode} ASTnode The AST node being checked * @param {Context} context The context of `ASTNode`. * @returns {Boolean} True if the node is returning only null values */function isReturningOnlyNull(ASTnode, context) {  let found = false;  let foundSomethingElse = false;  astUtil.traverseReturns(ASTnode, context, (node) => {    // Traverse return statement    astUtil.traverse(node, {      enter(childNode) {        const setFound = () => {          found = true;          this.skip();        };        const setFoundSomethingElse = () => {          foundSomethingElse = true;          this.skip();        };        switch (childNode.type) {          case 'ReturnStatement':            break;          case 'ConditionalExpression':            if (childNode.consequent.value === null && childNode.alternate.value === null) {              setFound();            }            break;          case 'Literal':            if (childNode.value === null) {              setFound();            }            break;          default:            setFoundSomethingElse();        }      },    });  });  return found && !foundSomethingElse;}module.exports = {  isDOMComponent,  isFragment,  isJSX,  isJSXAttributeKey,  isWhiteSpaces,  isReturningJSX,  isReturningOnlyNull,};
 |