| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433 | 
							- 'use strict';
 
- var XHTMLEntities = require('./xhtml');
 
- var hexNumber = /^[\da-fA-F]+$/;
 
- var decimalNumber = /^\d+$/;
 
- module.exports = function(acorn) {
 
-   var tt = acorn.tokTypes;
 
-   var tc = acorn.tokContexts;
 
-   tc.j_oTag = new acorn.TokContext('<tag', false);
 
-   tc.j_cTag = new acorn.TokContext('</tag', false);
 
-   tc.j_expr = new acorn.TokContext('<tag>...</tag>', true, true);
 
-   tt.jsxName = new acorn.TokenType('jsxName');
 
-   tt.jsxText = new acorn.TokenType('jsxText', {beforeExpr: true});
 
-   tt.jsxTagStart = new acorn.TokenType('jsxTagStart');
 
-   tt.jsxTagEnd = new acorn.TokenType('jsxTagEnd');
 
-   tt.jsxTagStart.updateContext = function() {
 
-     this.context.push(tc.j_expr); // treat as beginning of JSX expression
 
-     this.context.push(tc.j_oTag); // start opening tag context
 
-     this.exprAllowed = false;
 
-   };
 
-   tt.jsxTagEnd.updateContext = function(prevType) {
 
-     var out = this.context.pop();
 
-     if (out === tc.j_oTag && prevType === tt.slash || out === tc.j_cTag) {
 
-       this.context.pop();
 
-       this.exprAllowed = this.curContext() === tc.j_expr;
 
-     } else {
 
-       this.exprAllowed = true;
 
-     }
 
-   };
 
-   var pp = acorn.Parser.prototype;
 
-   // Reads inline JSX contents token.
 
-   pp.jsx_readToken = function() {
 
-     var out = '', chunkStart = this.pos;
 
-     for (;;) {
 
-       if (this.pos >= this.input.length)
 
-         this.raise(this.start, 'Unterminated JSX contents');
 
-       var 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(tt.jsxTagStart);
 
-           }
 
-           return this.getTokenFromCode(ch);
 
-         }
 
-         out += this.input.slice(chunkStart, this.pos);
 
-         return this.finishToken(tt.jsxText, out);
 
-       case 38: // '&'
 
-         out += this.input.slice(chunkStart, this.pos);
 
-         out += this.jsx_readEntity();
 
-         chunkStart = this.pos;
 
-         break;
 
-       default:
 
-         if (acorn.isNewLine(ch)) {
 
-           out += this.input.slice(chunkStart, this.pos);
 
-           out += this.jsx_readNewLine(true);
 
-           chunkStart = this.pos;
 
-         } else {
 
-           ++this.pos;
 
-         }
 
-       }
 
-     }
 
-   };
 
-   pp.jsx_readNewLine = function(normalizeCRLF) {
 
-     var ch = this.input.charCodeAt(this.pos);
 
-     var 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;
 
-   };
 
-   pp.jsx_readString = function(quote) {
 
-     var out = '', chunkStart = ++this.pos;
 
-     for (;;) {
 
-       if (this.pos >= this.input.length)
 
-         this.raise(this.start, 'Unterminated string constant');
 
-       var 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 (acorn.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);
 
-   };
 
-   pp.jsx_readEntity = function() {
 
-     var str = '', count = 0, entity;
 
-     var ch = this.input[this.pos];
 
-     if (ch !== '&')
 
-       this.raise(this.pos, 'Entity must start with an ampersand');
 
-     var 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.
 
-   pp.jsx_readWord = function() {
 
-     var ch, start = this.pos;
 
-     do {
 
-       ch = this.input.charCodeAt(++this.pos);
 
-     } while (acorn.isIdentifierChar(ch) || ch === 45); // '-'
 
-     return this.finishToken(tt.jsxName, this.input.slice(start, this.pos));
 
-   };
 
-   // Transforms JSX element name to string.
 
-   function getQualifiedJSXName(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);
 
-   }
 
-   // Parse next token as JSX identifier
 
-   pp.jsx_parseIdentifier = function() {
 
-     var node = this.startNode();
 
-     if (this.type === tt.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.
 
-   pp.jsx_parseNamespacedName = function() {
 
-     var startPos = this.start, startLoc = this.startLoc;
 
-     var name = this.jsx_parseIdentifier();
 
-     if (!this.options.plugins.jsx.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.
 
-   pp.jsx_parseElementName = function() {
 
-     var startPos = this.start, startLoc = this.startLoc;
 
-     var node = this.jsx_parseNamespacedName();
 
-     if (this.type === tt.dot && node.type === 'JSXNamespacedName' && !this.options.plugins.jsx.allowNamespacedObjects) {
 
-       this.unexpected();
 
-     }
 
-     while (this.eat(tt.dot)) {
 
-       var 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.
 
-   pp.jsx_parseAttributeValue = function() {
 
-     switch (this.type) {
 
-     case tt.braceL:
 
-       var 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 tt.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).
 
-   pp.jsx_parseEmptyExpression = function() {
 
-     var node = this.startNodeAt(this.lastTokEnd, this.lastTokEndLoc);
 
-     return this.finishNodeAt(node, 'JSXEmptyExpression', this.start, this.startLoc);
 
-   };
 
-   // Parses JSX expression enclosed into curly brackets.
 
-   pp.jsx_parseExpressionContainer = function() {
 
-     var 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.
 
-   pp.jsx_parseAttribute = function() {
 
-     var 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 '<'.
 
-   pp.jsx_parseOpeningElementAt = function(startPos, startLoc) {
 
-     var node = this.startNodeAt(startPos, startLoc);
 
-     node.attributes = [];
 
-     node.name = this.jsx_parseElementName();
 
-     while (this.type !== tt.slash && this.type !== tt.jsxTagEnd)
 
-       node.attributes.push(this.jsx_parseAttribute());
 
-     node.selfClosing = this.eat(tt.slash);
 
-     this.expect(tt.jsxTagEnd);
 
-     return this.finishNode(node, 'JSXOpeningElement');
 
-   };
 
-   // Parses JSX closing tag starting after '</'.
 
-   pp.jsx_parseClosingElementAt = function(startPos, startLoc) {
 
-     var node = this.startNodeAt(startPos, startLoc);
 
-     node.name = this.jsx_parseElementName();
 
-     this.expect(tt.jsxTagEnd);
 
-     return this.finishNode(node, 'JSXClosingElement');
 
-   };
 
-   // Parses entire JSX element, including it's opening tag
 
-   // (starting after '<'), attributes, contents and closing tag.
 
-   pp.jsx_parseElementAt = function(startPos, startLoc) {
 
-     var node = this.startNodeAt(startPos, startLoc);
 
-     var children = [];
 
-     var openingElement = this.jsx_parseOpeningElementAt(startPos, startLoc);
 
-     var closingElement = null;
 
-     if (!openingElement.selfClosing) {
 
-       contents: for (;;) {
 
-         switch (this.type) {
 
-         case tt.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 tt.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) + '>');
 
-       }
 
-     }
 
-     node.openingElement = openingElement;
 
-     node.closingElement = 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, 'JSXElement');
 
-   };
 
-   // Parses entire JSX element from current position.
 
-   pp.jsx_parseElement = function() {
 
-     var startPos = this.start, startLoc = this.startLoc;
 
-     this.next();
 
-     return this.jsx_parseElementAt(startPos, startLoc);
 
-   };
 
-   acorn.plugins.jsx = function(instance, opts) {
 
-     if (!opts) {
 
-       return;
 
-     }
 
-     if (typeof opts !== 'object') {
 
-       opts = {};
 
-     }
 
-     instance.options.plugins.jsx = {
 
-       allowNamespaces: opts.allowNamespaces !== false,
 
-       allowNamespacedObjects: !!opts.allowNamespacedObjects
 
-     };
 
-     instance.extend('parseExprAtom', function(inner) {
 
-       return function(refShortHandDefaultPos) {
 
-         if (this.type === tt.jsxText)
 
-           return this.parseLiteral(this.value);
 
-         else if (this.type === tt.jsxTagStart)
 
-           return this.jsx_parseElement();
 
-         else
 
-           return inner.call(this, refShortHandDefaultPos);
 
-       };
 
-     });
 
-     instance.extend('readToken', function(inner) {
 
-       return function(code) {
 
-         var context = this.curContext();
 
-         if (context === tc.j_expr) return this.jsx_readToken();
 
-         if (context === tc.j_oTag || context === tc.j_cTag) {
 
-           if (acorn.isIdentifierStart(code)) return this.jsx_readWord();
 
-           if (code == 62) {
 
-             ++this.pos;
 
-             return this.finishToken(tt.jsxTagEnd);
 
-           }
 
-           if ((code === 34 || code === 39) && context == tc.j_oTag)
 
-             return this.jsx_readString(code);
 
-         }
 
-         if (code === 60 && this.exprAllowed) {
 
-           ++this.pos;
 
-           return this.finishToken(tt.jsxTagStart);
 
-         }
 
-         return inner.call(this, code);
 
-       };
 
-     });
 
-     instance.extend('updateContext', function(inner) {
 
-       return function(prevType) {
 
-         if (this.type == tt.braceL) {
 
-           var curContext = this.curContext();
 
-           if (curContext == tc.j_oTag) this.context.push(tc.b_expr);
 
-           else if (curContext == tc.j_expr) this.context.push(tc.b_tmpl);
 
-           else inner.call(this, prevType);
 
-           this.exprAllowed = true;
 
-         } else if (this.type === tt.slash && prevType === tt.jsxTagStart) {
 
-           this.context.length -= 2; // do not consider JSX expr -> JSX open tag -> ... anymore
 
-           this.context.push(tc.j_cTag); // reconsider as closing tag context
 
-           this.exprAllowed = false;
 
-         } else {
 
-           return inner.call(this, prevType);
 
-         }
 
-       };
 
-     });
 
-   };
 
-   return acorn;
 
- };
 
 
  |