diff options
author | cjihrig <cjihrig@gmail.com> | 2018-10-26 12:35:42 -0400 |
---|---|---|
committer | Daniel Bevenius <daniel.bevenius@gmail.com> | 2018-10-29 07:49:16 +0100 |
commit | 2f1c356d7abe4949b5ee68e0724ed7e493fc03e1 (patch) | |
tree | b830b2ba85798b69df5b2750b97e32ce97211bfc /tools/node_modules/eslint/node_modules/acorn-jsx/index.js | |
parent | 9697c0820f015ccf898a3662305a0caa3cd9c208 (diff) | |
download | android-node-v8-2f1c356d7abe4949b5ee68e0724ed7e493fc03e1.tar.gz android-node-v8-2f1c356d7abe4949b5ee68e0724ed7e493fc03e1.tar.bz2 android-node-v8-2f1c356d7abe4949b5ee68e0724ed7e493fc03e1.zip |
tools: update ESLint to 5.8.0
Update ESLint to 5.8.0.
PR-URL: https://github.com/nodejs/node/pull/23904
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
Diffstat (limited to 'tools/node_modules/eslint/node_modules/acorn-jsx/index.js')
-rw-r--r-- | tools/node_modules/eslint/node_modules/acorn-jsx/index.js | 432 |
1 files changed, 430 insertions, 2 deletions
diff --git a/tools/node_modules/eslint/node_modules/acorn-jsx/index.js b/tools/node_modules/eslint/node_modules/acorn-jsx/index.js index 58c8677740..292315f65b 100644 --- a/tools/node_modules/eslint/node_modules/acorn-jsx/index.js +++ b/tools/node_modules/eslint/node_modules/acorn-jsx/index.js @@ -1,3 +1,431 @@ -'use strict'; +const XHTMLEntities = require('./xhtml'); -module.exports = require('./inject')(require('acorn')); +const hexNumber = /^[\da-fA-F]+$/; +const decimalNumber = /^\d+$/; + +const {tokTypes: tt, TokContext, tokContexts, TokenType, isNewLine, isIdentifierStart, isIdentifierChar} = require("acorn"); + +const tc_oTag = new TokContext('<tag', false); +const tc_cTag = new TokContext('</tag', false); +const tc_expr = new TokContext('<tag>...</tag>', true, true); + +const tok = { + jsxName: new TokenType('jsxName'), + jsxText: new TokenType('jsxText', {beforeExpr: true}), + jsxTagStart: new TokenType('jsxTagStart'), + jsxTagEnd: new TokenType('jsxTagEnd') +} + +tok.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; +}; +tok.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; + } +}; + +// 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 = {}) { + return function(Parser) { + return plugin({ + allowNamespaces: options.allowNamespaces !== false, + allowNamespacedObjects: !!options.allowNamespacedObjects + }, Parser); + } +} +module.exports.tokTypes = tok + +function plugin(options, Parser) { + return class extends Parser { + // 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; + + 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(value) { + let node = this.parseLiteral(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(this.value); + 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); + } + } + }; +}; |