"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

exports.default = function (ast, strictCode, env, realm) {
  (0, _invariant2.default)(realm.react.enabled, "JSXElements can only be evaluated with the reactEnabled option");
  let openingElement = ast.openingElement;
  let type = evaluateJSXIdentifier(openingElement.name, strictCode, env, realm);
  let children = evaluateJSXChildren(ast.children, strictCode, env, realm);
  let config = evaluateJSXAttributes(openingElement.attributes, strictCode, env, realm);
  (0, _invariant2.default)(type instanceof _index.Value);
  return (0, _elements.createReactElement)(realm, type, config, children);
};

var _index = require("../values/index.js");

var _jsx = require("../react/jsx.js");

var _babelTypes = require("babel-types");

var t = _interopRequireWildcard(_babelTypes);

var _index2 = require("../methods/index.js");

var _singletons = require("../singletons.js");

var _invariant = require("../invariant.js");

var _invariant2 = _interopRequireDefault(_invariant);

var _elements = require("../react/elements.js");

var _utils = require("../react/utils.js");

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

// taken from Babel
function cleanJSXElementLiteralChild(child) {
  let lines = child.split(/\r\n|\n|\r/);

  let lastNonEmptyLine = 0;

  for (let i = 0; i < lines.length; i++) {
    if (lines[i].match(/[^ \t]/)) {
      lastNonEmptyLine = i;
    }
  }

  let str = "";

  for (let i = 0; i < lines.length; i++) {
    let line = lines[i];

    let isFirstLine = i === 0;
    let isLastLine = i === lines.length - 1;
    let isLastNonEmptyLine = i === lastNonEmptyLine;

    // replace rendered whitespace tabs with spaces
    let trimmedLine = line.replace(/\t/g, " ");

    // trim whitespace touching a newline
    if (!isFirstLine) {
      trimmedLine = trimmedLine.replace(/^[ ]+/, "");
    }

    // trim whitespace touching an endline
    if (!isLastLine) {
      trimmedLine = trimmedLine.replace(/[ ]+$/, "");
    }

    if (trimmedLine) {
      if (!isLastNonEmptyLine) {
        trimmedLine += " ";
      }

      str += trimmedLine;
    }
  }

  if (str) {
    return str;
  }
  return null;
} /**
   * Copyright (c) 2017-present, Facebook, Inc.
   * All rights reserved.
   *
   * This source code is licensed under the BSD-style license found in the
   * LICENSE file in the root directory of this source tree. An additional grant
   * of patent rights can be found in the PATENTS file in the same directory.
   */

function evaluateJSXMemberExpression(ast, strictCode, env, realm) {
  switch (ast.type) {
    case "JSXIdentifier":
      return _singletons.Environment.GetValue(realm, _singletons.Environment.ResolveBinding(realm, ast.name, strictCode, env));
    case "JSXMemberExpression":
      return _singletons.Environment.GetValue(realm, env.evaluate((0, _jsx.convertJSXExpressionToIdentifier)(ast), strictCode));
    default:
      (0, _invariant2.default)(false, "Unknown JSX Identifier");
  }
}

function evaluateJSXIdentifier(ast, strictCode, env, realm) {
  if (isTagName(ast)) {
    // special cased lower-case and custom elements
    return new _index.StringValue(realm, ast.name);
  }
  return evaluateJSXMemberExpression(ast, strictCode, env, realm);
}

function evaluateJSXValue(value, strictCode, env, realm) {
  if (value != null) {
    switch (value.type) {
      case "JSXText":
        return new _index.StringValue(realm, value.value);
      case "StringLiteral":
        return new _index.StringValue(realm, value.value);
      case "JSXExpressionContainer":
        return _singletons.Environment.GetValue(realm, env.evaluate(value.expression, strictCode));
      case "JSXElement":
        return _singletons.Environment.GetValue(realm, env.evaluate(value, strictCode));
      default:
        (0, _invariant2.default)(false, `Unknown JSX value type: ${value.type}`);
    }
  }
  (0, _invariant2.default)(false, `Null or undefined value passed when trying to evaluate JSX node value`);
}

function isTagName(ast) {
  return ast.type === "JSXIdentifier" && /^[a-z]|\-/.test(ast.name);
}

function evaluateJSXChildren(children, strictCode, env, realm) {
  if (children.length === 0) {
    return realm.intrinsics.undefined;
  }
  if (children.length === 1) {
    let singleChild = evaluateJSXValue(children[0], strictCode, env, realm);

    if (singleChild instanceof _index.StringValue) {
      let text = cleanJSXElementLiteralChild(singleChild.value);
      if (text !== null) {
        singleChild.value = text;
      }
    }
    return singleChild;
  }
  let array = _singletons.Create.ArrayCreate(realm, 0);
  let dynamicChildrenLength = children.length;
  let dynamicIterator = 0;
  let lastChildValue = realm.intrinsics.undefined;
  for (let i = 0; i < children.length; i++) {
    let value = evaluateJSXValue(children[i], strictCode, env, realm);
    if (value instanceof _index.StringValue) {
      let text = cleanJSXElementLiteralChild(value.value);
      if (text === null) {
        dynamicChildrenLength--;
        // this is a space full of whitespace, so let's proceed
        continue;
      } else {
        value.value = text;
      }
    }
    lastChildValue = value;
    _singletons.Create.CreateDataPropertyOrThrow(realm, array, "" + dynamicIterator, value);
    dynamicIterator++;
  }
  if (dynamicChildrenLength === 1) {
    return lastChildValue;
  }

  _singletons.Properties.Set(realm, array, "length", new _index.NumberValue(realm, dynamicChildrenLength), false);
  array.makeFinal();
  return array;
}

function isObjectEmpty(object) {
  let propertyCount = 0;
  for (let [, binding] of object.properties) {
    if (binding && binding.descriptor && binding.descriptor.enumerable) {
      propertyCount++;
    }
  }
  return propertyCount === 0;
}

function evaluateJSXAttributes(astAttributes, strictCode, env, realm) {
  let config = _singletons.Create.ObjectCreate(realm, realm.intrinsics.ObjectPrototype);
  // start by having key and ref deleted, if they actually exist, they will be added later
  (0, _utils.deleteRefAndKeyFromProps)(realm, config);
  let abstractPropsArgs = [];
  let containsAbstractSpreadAttribute = false;
  let mayContainRefOrKey = false;
  let attributesAssigned = 0;
  let spreadValue;

  const setConfigProperty = (name, value) => {
    (0, _invariant2.default)(config instanceof _index.ObjectValue);
    if (name === "key" || name === "ref") {
      mayContainRefOrKey = true;
    }
    _singletons.Properties.Set(realm, config, name, value, true);
    attributesAssigned++;
  };

  for (let astAttribute of astAttributes) {
    switch (astAttribute.type) {
      case "JSXAttribute":
        let { name, value } = astAttribute;

        (0, _invariant2.default)(name.type === "JSXIdentifier", `JSX attribute name type not supported: ${astAttribute.type}`);
        setConfigProperty(name.name, evaluateJSXValue(value, strictCode, env, realm));
        break;
      case "JSXSpreadAttribute":
        spreadValue = _singletons.Environment.GetValue(realm, env.evaluate(astAttribute.argument, strictCode));

        if (spreadValue instanceof _index.ObjectValue && !spreadValue.isPartialObject()) {
          for (let [spreadPropKey, binding] of spreadValue.properties) {
            if (binding && binding.descriptor && binding.descriptor.enumerable) {
              setConfigProperty(spreadPropKey, (0, _index2.Get)(realm, spreadValue, spreadPropKey));
            }
          }
        } else {
          containsAbstractSpreadAttribute = true;
          (0, _invariant2.default)(spreadValue instanceof _index.AbstractValue || spreadValue instanceof _index.ObjectValue);

          if (!(0, _utils.objectHasNoPartialKeyAndRef)(realm, spreadValue)) {
            mayContainRefOrKey = true;
          }
          if (!isObjectEmpty(config)) {
            abstractPropsArgs.push(config);
          }
          abstractPropsArgs.push(spreadValue);
          config = _singletons.Create.ObjectCreate(realm, realm.intrinsics.ObjectPrototype);
          (0, _utils.deleteRefAndKeyFromProps)(realm, config);
        }
        break;
      default:
        (0, _invariant2.default)(false, `Unknown JSX attribute type: ${astAttribute.type}`);
    }
  }

  if (containsAbstractSpreadAttribute) {
    // if we haven't assigned any attributes and we are dealing with a single
    // spread attribute, we can just make the spread object the props
    if (attributesAssigned === 0 && (spreadValue instanceof _index.ObjectValue && spreadValue.isPartialObject() || spreadValue instanceof _index.AbstractValue)) {
      // the spread is partial, so we can re-use that value
      config = spreadValue;
      if (config instanceof _index.ObjectValue || config instanceof _index.AbstractObjectValue) {
        // as we're applying a spread, the config needs to be simple/partial
        config.makePartial();
        config.makeSimple();
      }
    } else {
      // we create an abstract Object.assign() to deal with the fact that we don't what
      // the props are because they contain abstract spread attributes that we can't
      // evaluate ahead of time
      // push the current config
      if (config.properties.size > 0) {
        abstractPropsArgs.push(config);
      }
      // create a new config object that will be the target of the Object.assign
      config = _singletons.Create.ObjectCreate(realm, realm.intrinsics.ObjectPrototype);
      // as this is "config that is abstract, we need to make it partial and simple
      config.makePartial();
      config.makeSimple();
      // get the global Object.assign
      let globalObj = (0, _index2.Get)(realm, realm.$GlobalObject, "Object");
      (0, _invariant2.default)(globalObj instanceof _index.ObjectValue);
      let objAssign = (0, _index2.Get)(realm, globalObj, "assign");
      (0, _invariant2.default)(realm.generator);

      (0, _invariant2.default)(realm.generator);
      _index.AbstractValue.createTemporalFromBuildFunction(realm, _index.FunctionValue, [objAssign, config, ...abstractPropsArgs], ([methodNode, ..._args]) => {
        return t.callExpression(methodNode, _args);
      });
      if (!mayContainRefOrKey) {
        (0, _utils.deleteRefAndKeyFromProps)(realm, config);
      }
    }
  }
  (0, _invariant2.default)(config instanceof _index.ObjectValue || config instanceof _index.AbstractValue);
  return config;
}
//# sourceMappingURL=JSXElement.js.map