summaryrefslogtreecommitdiff
path: root/tools/node_modules/eslint/node_modules/acorn-jsx/inject.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/node_modules/eslint/node_modules/acorn-jsx/inject.js')
-rw-r--r--tools/node_modules/eslint/node_modules/acorn-jsx/inject.js433
1 files changed, 433 insertions, 0 deletions
diff --git a/tools/node_modules/eslint/node_modules/acorn-jsx/inject.js b/tools/node_modules/eslint/node_modules/acorn-jsx/inject.js
new file mode 100644
index 0000000000..2bc4e9fd39
--- /dev/null
+++ b/tools/node_modules/eslint/node_modules/acorn-jsx/inject.js
@@ -0,0 +1,433 @@
+'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;
+};