|                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |  | 'use strict';
const XHTMLEntities = require('./xhtml');
const hexNumber = /^[\da-fA-F]+$/;const decimalNumber = /^\d+$/;
// The map to `acorn-jsx` tokens from `acorn` namespace objects.
const acornJsxMap = new WeakMap();
// Get the original tokens for the given `acorn` namespace object.
function getJsxTokens(acorn) {  acorn = acorn.Parser.acorn || acorn;  let acornJsx = acornJsxMap.get(acorn);  if (!acornJsx) {    const tt = acorn.tokTypes;    const TokContext = acorn.TokContext;    const TokenType = acorn.TokenType;    const tc_oTag = new TokContext('<tag', false);    const tc_cTag = new TokContext('</tag', false);    const tc_expr = new TokContext('<tag>...</tag>', true, true);    const tokContexts = {      tc_oTag: tc_oTag,      tc_cTag: tc_cTag,      tc_expr: tc_expr    };    const tokTypes = {      jsxName: new TokenType('jsxName'),      jsxText: new TokenType('jsxText', {beforeExpr: true}),      jsxTagStart: new TokenType('jsxTagStart', {startsExpr: true}),      jsxTagEnd: new TokenType('jsxTagEnd')    };
    tokTypes.jsxTagStart.updateContext = function() {      this.context.push(tc_expr); // treat as beginning of JSX expression
      this.context.push(tc_oTag); // start opening tag context
      this.exprAllowed = false;    };    tokTypes.jsxTagEnd.updateContext = function(prevType) {      let out = this.context.pop();      if (out === tc_oTag && prevType === tt.slash || out === tc_cTag) {        this.context.pop();        this.exprAllowed = this.curContext() === tc_expr;      } else {        this.exprAllowed = true;      }    };
    acornJsx = { tokContexts: tokContexts, tokTypes: tokTypes };    acornJsxMap.set(acorn, acornJsx);  }
  return acornJsx;}
// Transforms JSX element name to string.
function getQualifiedJSXName(object) {  if (!object)    return object;
  if (object.type === 'JSXIdentifier')    return object.name;
  if (object.type === 'JSXNamespacedName')    return object.namespace.name + ':' + object.name.name;
  if (object.type === 'JSXMemberExpression')    return getQualifiedJSXName(object.object) + '.' +    getQualifiedJSXName(object.property);}
module.exports = function(options) {  options = options || {};  return function(Parser) {    return plugin({      allowNamespaces: options.allowNamespaces !== false,      allowNamespacedObjects: !!options.allowNamespacedObjects    }, Parser);  };};
// This is `tokTypes` of the peer dep.
// This can be different instances from the actual `tokTypes` this plugin uses.
Object.defineProperty(module.exports, "tokTypes", {  get: function get_tokTypes() {    return getJsxTokens(require("acorn")).tokTypes;  },  configurable: true,  enumerable: true});
function plugin(options, Parser) {  const acorn = Parser.acorn || require("acorn");  const acornJsx = getJsxTokens(acorn);  const tt = acorn.tokTypes;  const tok = acornJsx.tokTypes;  const tokContexts = acorn.tokContexts;  const tc_oTag = acornJsx.tokContexts.tc_oTag;  const tc_cTag = acornJsx.tokContexts.tc_cTag;  const tc_expr = acornJsx.tokContexts.tc_expr;  const isNewLine = acorn.isNewLine;  const isIdentifierStart = acorn.isIdentifierStart;  const isIdentifierChar = acorn.isIdentifierChar;
  return class extends Parser {    // Expose actual `tokTypes` and `tokContexts` to other plugins.
    static get acornJsx() {      return acornJsx;    }
    // Reads inline JSX contents token.
    jsx_readToken() {      let out = '', chunkStart = this.pos;      for (;;) {        if (this.pos >= this.input.length)          this.raise(this.start, 'Unterminated JSX contents');        let ch = this.input.charCodeAt(this.pos);
        switch (ch) {        case 60: // '<'
        case 123: // '{'
          if (this.pos === this.start) {            if (ch === 60 && this.exprAllowed) {              ++this.pos;              return this.finishToken(tok.jsxTagStart);            }            return this.getTokenFromCode(ch);          }          out += this.input.slice(chunkStart, this.pos);          return this.finishToken(tok.jsxText, out);
        case 38: // '&'
          out += this.input.slice(chunkStart, this.pos);          out += this.jsx_readEntity();          chunkStart = this.pos;          break;
        case 62: // '>'
        case 125: // '}'
          this.raise(            this.pos,            "Unexpected token `" + this.input[this.pos] + "`. Did you mean `" +              (ch === 62 ? ">" : "}") + "` or " + "`{\"" + this.input[this.pos] + "\"}" + "`?"          );
        default:          if (isNewLine(ch)) {            out += this.input.slice(chunkStart, this.pos);            out += this.jsx_readNewLine(true);            chunkStart = this.pos;          } else {            ++this.pos;          }        }      }    }
    jsx_readNewLine(normalizeCRLF) {      let ch = this.input.charCodeAt(this.pos);      let out;      ++this.pos;      if (ch === 13 && this.input.charCodeAt(this.pos) === 10) {        ++this.pos;        out = normalizeCRLF ? '\n' : '\r\n';      } else {        out = String.fromCharCode(ch);      }      if (this.options.locations) {        ++this.curLine;        this.lineStart = this.pos;      }
      return out;    }
    jsx_readString(quote) {      let out = '', chunkStart = ++this.pos;      for (;;) {        if (this.pos >= this.input.length)          this.raise(this.start, 'Unterminated string constant');        let ch = this.input.charCodeAt(this.pos);        if (ch === quote) break;        if (ch === 38) { // '&'
          out += this.input.slice(chunkStart, this.pos);          out += this.jsx_readEntity();          chunkStart = this.pos;        } else if (isNewLine(ch)) {          out += this.input.slice(chunkStart, this.pos);          out += this.jsx_readNewLine(false);          chunkStart = this.pos;        } else {          ++this.pos;        }      }      out += this.input.slice(chunkStart, this.pos++);      return this.finishToken(tt.string, out);    }
    jsx_readEntity() {      let str = '', count = 0, entity;      let ch = this.input[this.pos];      if (ch !== '&')        this.raise(this.pos, 'Entity must start with an ampersand');      let startPos = ++this.pos;      while (this.pos < this.input.length && count++ < 10) {        ch = this.input[this.pos++];        if (ch === ';') {          if (str[0] === '#') {            if (str[1] === 'x') {              str = str.substr(2);              if (hexNumber.test(str))                entity = String.fromCharCode(parseInt(str, 16));            } else {              str = str.substr(1);              if (decimalNumber.test(str))                entity = String.fromCharCode(parseInt(str, 10));            }          } else {            entity = XHTMLEntities[str];          }          break;        }        str += ch;      }      if (!entity) {        this.pos = startPos;        return '&';      }      return entity;    }
    // Read a JSX identifier (valid tag or attribute name).
    //
    // Optimized version since JSX identifiers can't contain
    // escape characters and so can be read as single slice.
    // Also assumes that first character was already checked
    // by isIdentifierStart in readToken.
    jsx_readWord() {      let ch, start = this.pos;      do {        ch = this.input.charCodeAt(++this.pos);      } while (isIdentifierChar(ch) || ch === 45); // '-'
      return this.finishToken(tok.jsxName, this.input.slice(start, this.pos));    }
    // Parse next token as JSX identifier
    jsx_parseIdentifier() {      let node = this.startNode();      if (this.type === tok.jsxName)        node.name = this.value;      else if (this.type.keyword)        node.name = this.type.keyword;      else        this.unexpected();      this.next();      return this.finishNode(node, 'JSXIdentifier');    }
    // Parse namespaced identifier.
    jsx_parseNamespacedName() {      let startPos = this.start, startLoc = this.startLoc;      let name = this.jsx_parseIdentifier();      if (!options.allowNamespaces || !this.eat(tt.colon)) return name;      var node = this.startNodeAt(startPos, startLoc);      node.namespace = name;      node.name = this.jsx_parseIdentifier();      return this.finishNode(node, 'JSXNamespacedName');    }
    // Parses element name in any form - namespaced, member
    // or single identifier.
    jsx_parseElementName() {      if (this.type === tok.jsxTagEnd) return '';      let startPos = this.start, startLoc = this.startLoc;      let node = this.jsx_parseNamespacedName();      if (this.type === tt.dot && node.type === 'JSXNamespacedName' && !options.allowNamespacedObjects) {        this.unexpected();      }      while (this.eat(tt.dot)) {        let newNode = this.startNodeAt(startPos, startLoc);        newNode.object = node;        newNode.property = this.jsx_parseIdentifier();        node = this.finishNode(newNode, 'JSXMemberExpression');      }      return node;    }
    // Parses any type of JSX attribute value.
    jsx_parseAttributeValue() {      switch (this.type) {      case tt.braceL:        let node = this.jsx_parseExpressionContainer();        if (node.expression.type === 'JSXEmptyExpression')          this.raise(node.start, 'JSX attributes must only be assigned a non-empty expression');        return node;
      case tok.jsxTagStart:      case tt.string:        return this.parseExprAtom();
      default:        this.raise(this.start, 'JSX value should be either an expression or a quoted JSX text');      }    }
    // JSXEmptyExpression is unique type since it doesn't actually parse anything,
    // and so it should start at the end of last read token (left brace) and finish
    // at the beginning of the next one (right brace).
    jsx_parseEmptyExpression() {      let node = this.startNodeAt(this.lastTokEnd, this.lastTokEndLoc);      return this.finishNodeAt(node, 'JSXEmptyExpression', this.start, this.startLoc);    }
    // Parses JSX expression enclosed into curly brackets.
    jsx_parseExpressionContainer() {      let node = this.startNode();      this.next();      node.expression = this.type === tt.braceR        ? this.jsx_parseEmptyExpression()        : this.parseExpression();      this.expect(tt.braceR);      return this.finishNode(node, 'JSXExpressionContainer');    }
    // Parses following JSX attribute name-value pair.
    jsx_parseAttribute() {      let node = this.startNode();      if (this.eat(tt.braceL)) {        this.expect(tt.ellipsis);        node.argument = this.parseMaybeAssign();        this.expect(tt.braceR);        return this.finishNode(node, 'JSXSpreadAttribute');      }      node.name = this.jsx_parseNamespacedName();      node.value = this.eat(tt.eq) ? this.jsx_parseAttributeValue() : null;      return this.finishNode(node, 'JSXAttribute');    }
    // Parses JSX opening tag starting after '<'.
    jsx_parseOpeningElementAt(startPos, startLoc) {      let node = this.startNodeAt(startPos, startLoc);      node.attributes = [];      let nodeName = this.jsx_parseElementName();      if (nodeName) node.name = nodeName;      while (this.type !== tt.slash && this.type !== tok.jsxTagEnd)        node.attributes.push(this.jsx_parseAttribute());      node.selfClosing = this.eat(tt.slash);      this.expect(tok.jsxTagEnd);      return this.finishNode(node, nodeName ? 'JSXOpeningElement' : 'JSXOpeningFragment');    }
    // Parses JSX closing tag starting after '</'.
    jsx_parseClosingElementAt(startPos, startLoc) {      let node = this.startNodeAt(startPos, startLoc);      let nodeName = this.jsx_parseElementName();      if (nodeName) node.name = nodeName;      this.expect(tok.jsxTagEnd);      return this.finishNode(node, nodeName ? 'JSXClosingElement' : 'JSXClosingFragment');    }
    // Parses entire JSX element, including it's opening tag
    // (starting after '<'), attributes, contents and closing tag.
    jsx_parseElementAt(startPos, startLoc) {      let node = this.startNodeAt(startPos, startLoc);      let children = [];      let openingElement = this.jsx_parseOpeningElementAt(startPos, startLoc);      let closingElement = null;
      if (!openingElement.selfClosing) {        contents: for (;;) {          switch (this.type) {          case tok.jsxTagStart:            startPos = this.start; startLoc = this.startLoc;            this.next();            if (this.eat(tt.slash)) {              closingElement = this.jsx_parseClosingElementAt(startPos, startLoc);              break contents;            }            children.push(this.jsx_parseElementAt(startPos, startLoc));            break;
          case tok.jsxText:            children.push(this.parseExprAtom());            break;
          case tt.braceL:            children.push(this.jsx_parseExpressionContainer());            break;
          default:            this.unexpected();          }        }        if (getQualifiedJSXName(closingElement.name) !== getQualifiedJSXName(openingElement.name)) {          this.raise(            closingElement.start,            'Expected corresponding JSX closing tag for <' + getQualifiedJSXName(openingElement.name) + '>');        }      }      let fragmentOrElement = openingElement.name ? 'Element' : 'Fragment';
      node['opening' + fragmentOrElement] = openingElement;      node['closing' + fragmentOrElement] = closingElement;      node.children = children;      if (this.type === tt.relational && this.value === "<") {        this.raise(this.start, "Adjacent JSX elements must be wrapped in an enclosing tag");      }      return this.finishNode(node, 'JSX' + fragmentOrElement);    }
    // Parse JSX text
    jsx_parseText() {      let node = this.parseLiteral(this.value);      node.type = "JSXText";      return node;    }
    // Parses entire JSX element from current position.
    jsx_parseElement() {      let startPos = this.start, startLoc = this.startLoc;      this.next();      return this.jsx_parseElementAt(startPos, startLoc);    }
    parseExprAtom(refShortHandDefaultPos) {      if (this.type === tok.jsxText)        return this.jsx_parseText();      else if (this.type === tok.jsxTagStart)        return this.jsx_parseElement();      else        return super.parseExprAtom(refShortHandDefaultPos);    }
    readToken(code) {      let context = this.curContext();
      if (context === tc_expr) return this.jsx_readToken();
      if (context === tc_oTag || context === tc_cTag) {        if (isIdentifierStart(code)) return this.jsx_readWord();
        if (code == 62) {          ++this.pos;          return this.finishToken(tok.jsxTagEnd);        }
        if ((code === 34 || code === 39) && context == tc_oTag)          return this.jsx_readString(code);      }
      if (code === 60 && this.exprAllowed && this.input.charCodeAt(this.pos + 1) !== 33) {        ++this.pos;        return this.finishToken(tok.jsxTagStart);      }      return super.readToken(code);    }
    updateContext(prevType) {      if (this.type == tt.braceL) {        var curContext = this.curContext();        if (curContext == tc_oTag) this.context.push(tokContexts.b_expr);        else if (curContext == tc_expr) this.context.push(tokContexts.b_tmpl);        else super.updateContext(prevType);        this.exprAllowed = true;      } else if (this.type === tt.slash && prevType === tok.jsxTagStart) {        this.context.length -= 2; // do not consider JSX expr -> JSX open tag -> ... anymore
        this.context.push(tc_cTag); // reconsider as closing tag context
        this.exprAllowed = false;      } else {        return super.updateContext(prevType);      }    }  };}
 |