From 0b1d94ff108ec8dc5bc53baa9b292bd2a9d0692a Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 19 Sep 2025 13:59:52 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 7 + .gitattributes | 1 + .gitignore | 2 + .npmignore | 2 + .travis.yml | 13 + LICENSE | 19 + README.md | 40 + index.js | 488 +++++ package.json | 27 + test/driver.js | 106 + test/run.js | 77 + test/tests-jsx.js | 4636 ++++++++++++++++++++++++++++++++++++++++++++ test/tests-misc.js | 50 + xhtml.js | 255 +++ 14 files changed, 5723 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 .travis.yml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 index.js create mode 100644 package.json create mode 100644 test/driver.js create mode 100644 test/run.js create mode 100644 test/tests-jsx.js create mode 100644 test/tests-misc.js create mode 100644 xhtml.js diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c14d5c6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..fcadb2c --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d502512 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/node_modules +/package-lock.json diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..2c630f1 --- /dev/null +++ b/.npmignore @@ -0,0 +1,2 @@ +test +.* diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..cb164d9 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +language: node_js +node_js: + - '4' + - '6' + - '8' + - '10' + - '12' +env: + - ACORN_VERSION=6 + - ACORN_VERSION=7 + +install: + - npm install -D acorn@$ACORN_VERSION diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..695d4b9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2012-2017 by Ingvar Stepanyan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..317c3ac --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +# Acorn-JSX + +[![Build Status](https://travis-ci.org/acornjs/acorn-jsx.svg?branch=master)](https://travis-ci.org/acornjs/acorn-jsx) +[![NPM version](https://img.shields.io/npm/v/acorn-jsx.svg)](https://www.npmjs.org/package/acorn-jsx) + +This is plugin for [Acorn](http://marijnhaverbeke.nl/acorn/) - a tiny, fast JavaScript parser, written completely in JavaScript. + +It was created as an experimental alternative, faster [React.js JSX](http://facebook.github.io/react/docs/jsx-in-depth.html) parser. Later, it replaced the [official parser](https://github.com/facebookarchive/esprima) and these days is used by many prominent development tools. + +## Transpiler + +Please note that this tool only parses source code to JSX AST, which is useful for various language tools and services. If you want to transpile your code to regular ES5-compliant JavaScript with source map, check out [Babel](https://babeljs.io/) and [Buble](https://buble.surge.sh/) transpilers which use `acorn-jsx` under the hood. + +## Usage + +Requiring this module provides you with an Acorn plugin that you can use like this: + +```javascript +var acorn = require("acorn"); +var jsx = require("acorn-jsx"); +acorn.Parser.extend(jsx()).parse("my(, 'code');"); +``` + +Note that official spec doesn't support mix of XML namespaces and object-style access in tag names (#27) like in ``, so it was deprecated in `acorn-jsx@3.0`. If you still want to opt-in to support of such constructions, you can pass the following option: + +```javascript +acorn.Parser.extend(jsx({ allowNamespacedObjects: true })) +``` + +Also, since most apps use pure React transformer, a new option was introduced that allows to prohibit namespaces completely: + +```javascript +acorn.Parser.extend(jsx({ allowNamespaces: false })) +``` + +Note that by default `allowNamespaces` is enabled for spec compliancy. + +## License + +This plugin is issued under the [MIT license](./LICENSE). diff --git a/index.js b/index.js new file mode 100644 index 0000000..0ff263c --- /dev/null +++ b/index.js @@ -0,0 +1,488 @@ +'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('...', 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'), + 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 ''); + } + } + 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); + } + } + }; +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..37fceab --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "acorn-jsx", + "description": "Modern, fast React.js JSX parser", + "homepage": "https://github.com/acornjs/acorn-jsx", + "version": "5.2.0", + "maintainers": [ + { + "name": "Ingvar Stepanyan", + "email": "me@rreverser.com", + "web": "http://rreverser.com/" + } + ], + "repository": { + "type": "git", + "url": "https://github.com/acornjs/acorn-jsx" + }, + "license": "MIT", + "scripts": { + "test": "node test/run.js" + }, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0" + }, + "devDependencies": { + "acorn": "^7.0.0" + } +} diff --git a/test/driver.js b/test/driver.js new file mode 100644 index 0000000..8d3df48 --- /dev/null +++ b/test/driver.js @@ -0,0 +1,106 @@ +var tests = []; + +exports.test = function(code, ast, options, pluginOptions) { + tests.push({code, ast, options, pluginOptions}); +}; +exports.testFail = function(code, message, options, pluginOptions) { + tests.push({code, error: message, options, pluginOptions}); +}; +exports.testAssert = function(code, assert, options) { + tests.push({code, assert, options}); +}; + +exports.runTests = function(config, callback) { + var parse = config.parse; + + for (var i = 0; i < tests.length; ++i) { + var test = tests[i]; + if (config.filter && !config.filter(test)) continue; + try { + var testOpts = test.options || {locations: true}; + var expected = {}; + if (expected.onComment = testOpts.onComment) { + testOpts.onComment = [] + } + if (expected.onToken = testOpts.onToken) { + testOpts.onToken = []; + } + var ast = parse(test.code, testOpts, test.pluginOptions); + if (test.error) { + if (config.loose) { + callback("ok", test.code); + } else { + callback("fail", test.code, "Expected error message: " + test.error + "\nBut parsing succeeded."); + } + } + else if (test.assert) { + var error = test.assert(ast); + if (error) callback("fail", test.code, + "\n Assertion failed:\n " + error); + else callback("ok", test.code); + } else { + var mis = misMatch(test.ast, ast); + for (var name in expected) { + if (mis) break; + if (expected[name]) { + mis = misMatch(expected[name], testOpts[name]); + testOpts[name] = expected[name]; + } + } + if (mis) callback("fail", test.code, mis); + else callback("ok", test.code); + } + } catch(e) { + if (!(e instanceof SyntaxError)) { + throw e; + } + if (test.error) { + if (e.message == test.error) callback("ok", test.code); + else callback("fail", test.code, + "Expected error message: " + test.error + "\nGot error message: " + e.message); + } else { + callback("error", test.code, e.stack || e.toString()); + } + } + } +}; + +function ppJSON(v) { return v instanceof RegExp ? v.toString() : JSON.stringify(v, null, 2); } +function addPath(str, pt) { + if (str.charAt(str.length-1) == ")") + return str.slice(0, str.length-1) + "/" + pt + ")"; + return str + " (" + pt + ")"; +} + +var misMatch = exports.misMatch = function(exp, act) { + if (!exp || !act || (typeof exp != "object") || (typeof act != "object")) { + if (exp !== act) return ppJSON(exp) + " !== " + ppJSON(act); + } else if (exp instanceof RegExp || act instanceof RegExp) { + var left = ppJSON(exp), right = ppJSON(act); + if (left !== right) return left + " !== " + right; + } else if (exp.splice) { + if (!act.slice) return ppJSON(exp) + " != " + ppJSON(act); + if (act.length != exp.length) return "array length mismatch " + exp.length + " != " + act.length; + for (var i = 0; i < act.length; ++i) { + var mis = misMatch(exp[i], act[i]); + if (mis) return addPath(mis, i); + } + } else { + for (var prop in exp) { + var mis = misMatch(exp[prop], act[prop]); + if (mis) return addPath(mis, prop); + } + } +}; + +function mangle(ast) { + if (typeof ast != "object" || !ast) return; + if (ast.slice) { + for (var i = 0; i < ast.length; ++i) mangle(ast[i]); + } else { + var loc = ast.start && ast.end && {start: ast.start, end: ast.end}; + if (loc) { delete ast.start; delete ast.end; } + for (var name in ast) if (ast.hasOwnProperty(name)) mangle(ast[name]); + if (loc) ast.loc = loc; + } +} diff --git a/test/run.js b/test/run.js new file mode 100644 index 0000000..b9d98bf --- /dev/null +++ b/test/run.js @@ -0,0 +1,77 @@ +var driver = require("./driver.js"); +require("./tests-jsx.js"); +require("./tests-misc.js"); + +function group(name) { + if (typeof console === "object" && console.group) { + console.group(name); + } +} + +function groupEnd() { + if (typeof console === "object" && console.groupEnd) { + console.groupEnd(name); + } +} + +function log(title, message) { + if (typeof console === "object") console.log(title, message); +} + +const acorn = require("acorn"), jsx = require("..") +const Parser = acorn.Parser.extend(jsx()) + +var stats, modes = { + Normal: { + config: { + parse: (input, options, pluginOptions) => { + if (!pluginOptions) return Parser.parse(input, options) + else return acorn.Parser.extend(jsx(pluginOptions)).parse(input, options) + } + } + } +}; + +function report(state, code, message) { + if (state != "ok") {++stats.failed; log(code, message);} + ++stats.testsRun; +} + +group("Errors"); + +for (var name in modes) { + group(name); + var mode = modes[name]; + stats = mode.stats = {testsRun: 0, failed: 0}; + var t0 = +new Date; + driver.runTests(mode.config, report); + mode.stats.duration = +new Date - t0; + groupEnd(); +} + +groupEnd(); + +function outputStats(name, stats) { + log(name + ":", stats.testsRun + " tests run in " + stats.duration + "ms; " + + (stats.failed ? stats.failed + " failures." : "all passed.")); +} + +var total = {testsRun: 0, failed: 0, duration: 0}; + +group("Stats"); + +for (var name in modes) { + var stats = modes[name].stats; + outputStats(name + " parser", stats); + for (var key in stats) total[key] += stats[key]; +} + +outputStats("Total", total); + +groupEnd(); + +if (total.failed && typeof process === "object") { + process.stdout.write("", function() { + process.exit(1); + }); +} diff --git a/test/tests-jsx.js b/test/tests-jsx.js new file mode 100644 index 0000000..c44c605 --- /dev/null +++ b/test/tests-jsx.js @@ -0,0 +1,4636 @@ +/* +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +var fbTestFixture = { + // Taken and adapted from esprima-fb/fbtest.js. + 'JSX': { + '': { + type: "ExpressionStatement", + expression: { + type: "JSXElement", + openingElement: { + type: "JSXOpeningElement", + name: { + type: "JSXIdentifier", + name: "a", + range: [1, 2], + loc: { + start: { line: 1, column: 1 }, + end: { line: 1, column: 2 } + } + }, + selfClosing: true, + attributes: [], + range: [0, 5], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 5 } + } + }, + closingElement: null, + children: [], + range: [0, 5], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 5 } + } + }, + range: [0, 5], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 5 } + } + }, + + '': { + type: 'ExpressionStatement', + expression: { + type: 'JSXElement', + openingElement: { + type: 'JSXOpeningElement', + name: { + type: 'JSXNamespacedName', + namespace: { + type: 'JSXIdentifier', + name: 'n', + range: [1, 2], + loc: { + start: { line: 1, column: 1 }, + end: { line: 1, column: 2 } + } + }, + name: { + type: 'JSXIdentifier', + name: 'a', + range: [3, 4], + loc: { + start: { line: 1, column: 3 }, + end: { line: 1, column: 4 } + } + }, + range: [1, 4], + loc: { + start: { line: 1, column: 1 }, + end: { line: 1, column: 4 } + } + }, + selfClosing: true, + attributes: [{ + type: 'JSXAttribute', + name: { + type: 'JSXNamespacedName', + namespace: { + type: 'JSXIdentifier', + name: 'n', + range: [5, 6], + loc: { + start: { line: 1, column: 5 }, + end: { line: 1, column: 6 } + } + }, + name: { + type: 'JSXIdentifier', + name: 'v', + range: [7, 8], + loc: { + start: { line: 1, column: 7 }, + end: { line: 1, column: 8 } + } + }, + range: [5, 8], + loc: { + start: { line: 1, column: 5 }, + end: { line: 1, column: 8 } + } + }, + value: null, + range: [5, 8], + loc: { + start: { line: 1, column: 5 }, + end: { line: 1, column: 8 } + } + }], + range: [0, 11], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 11 } + } + }, + closingElement: null, + children: [], + range: [0, 11], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 11 } + } + }, + range: [0, 11], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 11 } + } + }, + + ' {value} ': { + type: 'ExpressionStatement', + expression: { + type: 'JSXElement', + openingElement: { + type: 'JSXOpeningElement', + name: { + type: 'JSXIdentifier', + name: 'a', + range: [1, 2], + loc: { + start: { line: 1, column: 1 }, + end: { line: 1, column: 2 } + } + }, + selfClosing: false, + attributes: [{ + type: 'JSXAttribute', + name: { + type: 'JSXNamespacedName', + namespace: { + type: 'JSXIdentifier', + name: 'n', + range: [3, 4], + loc: { + start: { line: 1, column: 3 }, + end: { line: 1, column: 4 } + } + }, + name: { + type: 'JSXIdentifier', + name: 'foo', + range: [5, 8], + loc: { + start: { line: 1, column: 5 }, + end: { line: 1, column: 8 } + } + }, + range: [3, 8], + loc: { + start: { line: 1, column: 3 }, + end: { line: 1, column: 8 } + } + }, + value: { + type: 'Literal', + value: 'bar', + raw: '"bar"', + range: [9, 14], + loc: { + start: { line: 1, column: 9 }, + end: { line: 1, column: 14 } + } + }, + range: [3, 14], + loc: { + start: { line: 1, column: 3 }, + end: { line: 1, column: 14 } + } + }], + range: [0, 15], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 15 } + } + }, + closingElement: { + type: 'JSXClosingElement', + name: { + type: 'JSXIdentifier', + name: 'a', + range: [38, 39], + loc: { + start: { line: 1, column: 38 }, + end: { line: 1, column: 39 } + } + }, + range: [36, 40], + loc: { + start: { line: 1, column: 36 }, + end: { line: 1, column: 40 } + } + }, + children: [{ + type: 'JSXText', + value: ' ', + raw: ' ', + range: [15, 16], + loc: { + start: { line: 1, column: 15 }, + end: { line: 1, column: 16 } + } + }, { + type: 'JSXExpressionContainer', + expression: { + type: 'Identifier', + name: 'value', + range: [17, 22], + loc: { + start: { line: 1, column: 17 }, + end: { line: 1, column: 22 } + } + }, + range: [16, 23], + loc: { + start: { line: 1, column: 16 }, + end: { line: 1, column: 23 } + } + }, { + type: 'JSXText', + value: ' ', + raw: ' ', + range: [23, 24], + loc: { + start: { line: 1, column: 23 }, + end: { line: 1, column: 24 } + } + }, { + type: 'JSXElement', + openingElement: { + type: 'JSXOpeningElement', + name: { + type: 'JSXIdentifier', + name: 'b', + range: [25, 26], + loc: { + start: { line: 1, column: 25 }, + end: { line: 1, column: 26 } + } + }, + selfClosing: false, + attributes: [], + range: [24, 27], + loc: { + start: { line: 1, column: 24 }, + end: { line: 1, column: 27 } + } + }, + closingElement: { + type: 'JSXClosingElement', + name: { + type: 'JSXIdentifier', + name: 'b', + range: [34, 35], + loc: { + start: { line: 1, column: 34 }, + end: { line: 1, column: 35 } + } + }, + range: [32, 36], + loc: { + start: { line: 1, column: 32 }, + end: { line: 1, column: 36 } + } + }, + children: [{ + type: 'JSXElement', + openingElement: { + type: 'JSXOpeningElement', + name: { + type: 'JSXIdentifier', + name: 'c', + range: [28, 29], + loc: { + start: { line: 1, column: 28 }, + end: { line: 1, column: 29 } + } + }, + selfClosing: true, + attributes: [], + range: [27, 32], + loc: { + start: { line: 1, column: 27 }, + end: { line: 1, column: 32 } + } + }, + closingElement: null, + children: [], + range: [27, 32], + loc: { + start: { line: 1, column: 27 }, + end: { line: 1, column: 32 } + } + }], + range: [24, 36], + loc: { + start: { line: 1, column: 24 }, + end: { line: 1, column: 36 } + } + }], + range: [0, 40], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 40 } + } + }, + range: [0, 40], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 40 } + } + }, + + '': { + type: "ExpressionStatement", + expression: { + type: "JSXElement", + openingElement: { + type: "JSXOpeningElement", + name: { + type: "JSXIdentifier", + name: "a", + range: [1, 2] + }, + selfClosing: true, + attributes: [ + { + type: "JSXAttribute", + name: { + type: "JSXIdentifier", + name: "b", + range: [3, 4] + }, + value: { + type: "JSXExpressionContainer", + expression: { + type: "Literal", + value: " ", + raw: "\" \"", + range: [6, 9] + }, + range: [5, 10] + }, + range: [3, 10] + }, + { + type: "JSXAttribute", + name: { + type: "JSXIdentifier", + name: "c", + range: [11, 12] + }, + value: { + type: "Literal", + value: " ", + raw: "\" \"", + range: [13, 16] + }, + range: [11, 16] + }, + { + type: "JSXAttribute", + name: { + type: "JSXIdentifier", + name: "d", + range: [17, 18] + }, + value: { + type: "Literal", + value: "&", + raw: "\"&\"", + range: [19, 26] + }, + range: [17, 26] + }, + { + type: "JSXAttribute", + name: { + type: "JSXIdentifier", + name: "e", + range: [27, 28] + }, + value: { + type: "Literal", + value: "&r;", + raw: "\"&r;\"", + range: [29, 37] + }, + range: [27, 37] + } + ], + range: [0, 40] + }, + closingElement: null, + children: [], + range: [0, 40] + }, + range: [0, 40] + }, + + '': { + type: "ExpressionStatement", + expression: { + type: "JSXElement", + openingElement: { + type: "JSXOpeningElement", + name: { + type: "JSXIdentifier", + name: "a", + range: [ + 1, + 2 + ], + loc: { + start: { + line: 1, + column: 1 + }, + end: { + line: 1, + column: 2 + } + } + }, + selfClosing: true, + attributes: [], + range: [ + 0, + 5 + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 2, + column: 2 + } + } + }, + closingElement: null, + children: [], + range: [ + 0, + 5 + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 2, + column: 2 + } + } + }, + range: [ + 0, + 5 + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 2, + column: 2 + } + } + }, + + '<日本語>': { + type: "ExpressionStatement", + expression: { + type: "JSXElement", + openingElement: { + type: "JSXOpeningElement", + name: { + type: "JSXIdentifier", + name: "日本語", + range: [ + 1, + 4 + ], + loc: { + start: { + line: 1, + column: 1 + }, + end: { + line: 1, + column: 4 + } + } + }, + selfClosing: false, + attributes: [], + range: [ + 0, + 5 + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 5 + } + } + }, + closingElement: { + type: "JSXClosingElement", + name: { + type: "JSXIdentifier", + name: "日本語", + range: [ + 7, + 10 + ], + loc: { + start: { + line: 1, + column: 7 + }, + end: { + line: 1, + column: 10 + } + } + }, + range: [ + 5, + 11 + ], + loc: { + start: { + line: 1, + column: 5 + }, + end: { + line: 1, + column: 11 + } + } + }, + children: [], + range: [ + 0, + 11 + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 11 + } + } + }, + range: [ + 0, + 11 + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 11 + } + } + }, + + '\nbar\nbaz\n': { + type: "ExpressionStatement", + expression: { + type: "JSXElement", + openingElement: { + type: "JSXOpeningElement", + name: { + type: "JSXIdentifier", + name: "AbC-def", + range: [ + 1, + 8 + ], + loc: { + start: { + line: 1, + column: 1 + }, + end: { + line: 1, + column: 8 + } + } + }, + selfClosing: false, + attributes: [ + { + type: "JSXAttribute", + name: { + type: "JSXIdentifier", + name: "test", + range: [ + 11, + 15 + ], + loc: { + start: { + line: 2, + column: 2 + }, + end: { + line: 2, + column: 6 + } + } + }, + value: { + type: "Literal", + value: "&&", + raw: "\"&&\"", + range: [ + 16, + 31 + ], + loc: { + start: { + line: 2, + column: 7 + }, + end: { + line: 2, + column: 22 + } + } + }, + range: [ + 11, + 31 + ], + loc: { + start: { + line: 2, + column: 2 + }, + end: { + line: 2, + column: 22 + } + } + } + ], + range: [ + 0, + 32 + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 2, + column: 23 + } + } + }, + closingElement: { + type: "JSXClosingElement", + name: { + type: "JSXIdentifier", + name: "AbC-def", + range: [ + 43, + 50 + ], + loc: { + start: { + line: 5, + column: 2 + }, + end: { + line: 5, + column: 9 + } + } + }, + range: [ + 41, + 51 + ], + loc: { + start: { + line: 5, + column: 0 + }, + end: { + line: 5, + column: 10 + } + } + }, + children: [ + { + type: "JSXText", + value: "\nbar\nbaz\n", + raw: "\nbar\nbaz\n", + range: [ + 32, + 41 + ], + loc: { + start: { + line: 2, + column: 23 + }, + end: { + line: 5, + column: 0 + } + } + } + ], + range: [ + 0, + 51 + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 5, + column: 10 + } + } + }, + range: [ + 0, + 51 + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 5, + column: 10 + } + } + }, + + ' : } />': { + type: "ExpressionStatement", + expression: { + type: "JSXElement", + openingElement: { + type: "JSXOpeningElement", + name: { + type: "JSXIdentifier", + name: "a", + range: [ + 1, + 2 + ], + loc: { + start: { + line: 1, + column: 1 + }, + end: { + line: 1, + column: 2 + } + } + }, + selfClosing: true, + attributes: [ + { + type: "JSXAttribute", + name: { + type: "JSXIdentifier", + name: "b", + range: [ + 3, + 4 + ], + loc: { + start: { + line: 1, + column: 3 + }, + end: { + line: 1, + column: 4 + } + } + }, + value: { + type: "JSXExpressionContainer", + expression: { + type: "ConditionalExpression", + test: { + type: "Identifier", + name: "x", + range: [ + 6, + 7 + ], + loc: { + start: { + line: 1, + column: 6 + }, + end: { + line: 1, + column: 7 + } + } + }, + consequent: { + type: "JSXElement", + openingElement: { + type: "JSXOpeningElement", + name: { + type: "JSXIdentifier", + name: "c", + range: [ + 11, + 12 + ], + loc: { + start: { + line: 1, + column: 11 + }, + end: { + line: 1, + column: 12 + } + } + }, + selfClosing: true, + attributes: [], + range: [ + 10, + 15 + ], + loc: { + start: { + line: 1, + column: 10 + }, + end: { + line: 1, + column: 15 + } + } + }, + closingElement: null, + children: [], + range: [ + 10, + 15 + ], + loc: { + start: { + line: 1, + column: 10 + }, + end: { + line: 1, + column: 15 + } + } + }, + alternate: { + type: "JSXElement", + openingElement: { + type: "JSXOpeningElement", + name: { + type: "JSXIdentifier", + name: "d", + range: [ + 19, + 20 + ], + loc: { + start: { + line: 1, + column: 19 + }, + end: { + line: 1, + column: 20 + } + } + }, + selfClosing: true, + attributes: [], + range: [ + 18, + 23 + ], + loc: { + start: { + line: 1, + column: 18 + }, + end: { + line: 1, + column: 23 + } + } + }, + closingElement: null, + children: [], + range: [ + 18, + 23 + ], + loc: { + start: { + line: 1, + column: 18 + }, + end: { + line: 1, + column: 23 + } + } + }, + range: [ + 6, + 23 + ], + loc: { + start: { + line: 1, + column: 6 + }, + end: { + line: 1, + column: 23 + } + } + }, + range: [ + 5, + 24 + ], + loc: { + start: { + line: 1, + column: 5 + }, + end: { + line: 1, + column: 24 + } + } + }, + range: [ + 3, + 24 + ], + loc: { + start: { + line: 1, + column: 3 + }, + end: { + line: 1, + column: 24 + } + } + } + ], + range: [ + 0, + 27 + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 27 + } + } + }, + closingElement: null, + children: [], + range: [ + 0, + 27 + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 27 + } + } + }, + range: [ + 0, + 27 + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 27 + } + } + }, + + '{}': { + type: 'ExpressionStatement', + expression: { + type: 'JSXElement', + openingElement: { + type: 'JSXOpeningElement', + name: { + type: 'JSXIdentifier', + name: 'a', + range: [1, 2], + loc: { + start: { line: 1, column: 1 }, + end: { line: 1, column: 2 } + } + }, + selfClosing: false, + attributes: [], + range: [0, 3], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 3 } + } + }, + closingElement: { + type: 'JSXClosingElement', + name: { + type: 'JSXIdentifier', + name: 'a', + range: [7, 8], + loc: { + start: { line: 1, column: 7 }, + end: { line: 1, column: 8 } + } + }, + range: [5, 9], + loc: { + start: { line: 1, column: 5 }, + end: { line: 1, column: 9 } + } + }, + children: [{ + type: 'JSXExpressionContainer', + expression: { + type: 'JSXEmptyExpression', + range: [4, 4], + loc: { + start: { line: 1, column: 4 }, + end: { line: 1, column: 4 } + } + }, + range: [3, 5], + loc: { + start: { line: 1, column: 3 }, + end: { line: 1, column: 5 } + } + }], + range: [0, 9], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 9 } + } + }, + range: [0, 9], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 9 } + } + }, + + '{/* this is a comment */}': { + type: 'ExpressionStatement', + expression: { + type: 'JSXElement', + openingElement: { + type: 'JSXOpeningElement', + name: { + type: 'JSXIdentifier', + name: 'a', + range: [1, 2], + loc: { + start: { line: 1, column: 1 }, + end: { line: 1, column: 2 } + } + }, + selfClosing: false, + attributes: [], + range: [0, 3], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 3 } + } + }, + closingElement: { + type: 'JSXClosingElement', + name: { + type: 'JSXIdentifier', + name: 'a', + range: [30, 31], + loc: { + start: { line: 1, column: 30 }, + end: { line: 1, column: 31 } + } + }, + range: [28, 32], + loc: { + start: { line: 1, column: 28 }, + end: { line: 1, column: 32 } + } + }, + children: [{ + type: 'JSXExpressionContainer', + expression: { + type: 'JSXEmptyExpression', + range: [4, 27], + loc: { + start: { line: 1, column: 4 }, + end: { line: 1, column: 27 } + } + }, + range: [3, 28], + loc: { + start: { line: 1, column: 3 }, + end: { line: 1, column: 28 } + } + }], + range: [0, 32], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 32 } + } + }, + range: [0, 32], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 32 } + } + }, + + '
@test content
': { + type: 'ExpressionStatement', + expression: { + type: 'JSXElement', + openingElement: { + type: 'JSXOpeningElement', + name: { + type: 'JSXIdentifier', + name: 'div', + range: [1, 4], + loc: { + start: { line: 1, column: 1 }, + end: { line: 1, column: 4 } + } + }, + selfClosing: false, + attributes: [], + range: [0, 5], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 5 } + } + }, + closingElement: { + type: 'JSXClosingElement', + name: { + type: 'JSXIdentifier', + name: 'div', + range: [20, 23], + loc: { + start: { line: 1, column: 20 }, + end: { line: 1, column: 23 } + } + }, + range: [18, 24], + loc: { + start: { line: 1, column: 18 }, + end: { line: 1, column: 24 } + } + }, + children: [{ + type: 'JSXText', + value: '@test content', + raw: '@test content', + range: [5, 18], + loc: { + start: { line: 1, column: 5 }, + end: { line: 1, column: 18 } + } + }], + range: [0, 24], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 24 } + } + }, + range: [0, 24], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 24 } + } + }, + + '

7x invalid-js-identifier
': { + type: 'ExpressionStatement', + expression: { + type: 'JSXElement', + openingElement: { + type: 'JSXOpeningElement', + name: { + type: 'JSXIdentifier', + name: 'div', + range: [ + 1, + 4 + ], + loc: { + start: { + line: 1, + column: 1 + }, + end: { + line: 1, + column: 4 + } + } + }, + selfClosing: false, + attributes: [], + range: [ + 0, + 5 + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 5 + } + } + }, + closingElement: { + type: 'JSXClosingElement', + name: { + type: 'JSXIdentifier', + name: 'div', + range: [ + 37, + 40 + ], + loc: { + start: { + line: 1, + column: 37 + }, + end: { + line: 1, + column: 40 + } + } + }, + range: [ + 35, + 41 + ], + loc: { + start: { + line: 1, + column: 35 + }, + end: { + line: 1, + column: 41 + } + } + }, + children: [{ + type: 'JSXElement', + openingElement: { + type: 'JSXOpeningElement', + name: { + type: 'JSXIdentifier', + name: 'br', + range: [ + 6, + 8 + ], + loc: { + start: { + line: 1, + column: 6 + }, + end: { + line: 1, + column: 8 + } + } + }, + selfClosing: true, + attributes: [], + range: [ + 5, + 11 + ], + loc: { + start: { + line: 1, + column: 5 + }, + end: { + line: 1, + column: 11 + } + } + }, + closingElement: null, + children: [], + range: [ + 5, + 11 + ], + loc: { + start: { + line: 1, + column: 5 + }, + end: { + line: 1, + column: 11 + } + } + }, { + type: 'JSXText', + value: '7x invalid-js-identifier', + raw: '7x invalid-js-identifier', + range: [ + 11, + 35 + ], + loc: { + start: { + line: 1, + column: 11 + }, + end: { + line: 1, + column: 35 + } + } + }], + range: [ + 0, + 41 + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 41 + } + } + }, + range: [ + 0, + 41 + ], + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 41 + } + } + }, + + ' right=monkeys /{">"} gorillas />': { + "type": "ExpressionStatement", + "start": 0, + "end": 61, + "expression": { + "type": "JSXElement", + "start": 0, + "end": 61, + "openingElement": { + "type": "JSXOpeningElement", + "start": 0, + "end": 61, + "attributes": [ + { + "type": "JSXAttribute", + "start": 11, + "end": 21, + "name": { + "type": "JSXIdentifier", + "start": 11, + "end": 15, + "name": "left" + }, + "value": { + "type": "JSXElement", + "start": 16, + "end": 21, + "openingElement": { + "type": "JSXOpeningElement", + "start": 16, + "end": 21, + "attributes": [], + "name": { + "type": "JSXIdentifier", + "start": 17, + "end": 18, + "name": "a" + }, + "selfClosing": true + }, + "closingElement": null, + "children": [] + } + }, + { + "type": "JSXAttribute", + "start": 22, + "end": 58, + "name": { + "type": "JSXIdentifier", + "start": 22, + "end": 27, + "name": "right" + }, + "value": { + "type": "JSXElement", + "start": 28, + "end": 58, + "openingElement": { + "type": "JSXOpeningElement", + "start": 28, + "end": 31, + "attributes": [], + "name": { + "type": "JSXIdentifier", + "start": 29, + "end": 30, + "name": "b" + }, + "selfClosing": false + }, + "closingElement": { + "type": "JSXClosingElement", + "start": 54, + "end": 58, + "name": { + "type": "JSXIdentifier", + "start": 56, + "end": 57, + "name": "b" + } + }, + "children": [ + { + "type": "JSXText", + "start": 31, + "end": 40, + "value": "monkeys /", + "raw": "monkeys /" + }, + { + "type": "JSXExpressionContainer", + "start": 40, + "end": 45, + "expression": { + "type": "Literal", + "start": 41, + "end": 44, + "value": ">", + "raw": "\">\"" + } + }, + { + "type": "JSXText", + "start": 45, + "end": 54, + "value": " gorillas", + "raw": " gorillas" + } + ] + } + } + ], + "name": { + "type": "JSXIdentifier", + "start": 1, + "end": 10, + "name": "LeftRight" + }, + "selfClosing": true + }, + "closingElement": null, + "children": [] + } + }, + + ' right=monkeys /> gorillas />': { + "type": "ExpressionStatement", + "start": 0, + "end": 60, + "expression": { + "type": "JSXElement", + "start": 0, + "end": 60, + "openingElement": { + "type": "JSXOpeningElement", + "start": 0, + "end": 60, + "attributes": [ + { + "type": "JSXAttribute", + "start": 11, + "end": 21, + "name": { + "type": "JSXIdentifier", + "start": 11, + "end": 15, + "name": "left" + }, + "value": { + "type": "JSXElement", + "start": 16, + "end": 21, + "openingElement": { + "type": "JSXOpeningElement", + "start": 16, + "end": 21, + "attributes": [], + "name": { + "type": "JSXIdentifier", + "start": 17, + "end": 18, + "name": "a" + }, + "selfClosing": true + }, + "closingElement": null, + "children": [] + } + }, + { + "type": "JSXAttribute", + "start": 22, + "end": 57, + "name": { + "type": "JSXIdentifier", + "start": 22, + "end": 27, + "name": "right" + }, + "value": { + "type": "JSXElement", + "start": 28, + "end": 57, + "openingElement": { + "type": "JSXOpeningElement", + "start": 28, + "end": 31, + "attributes": [], + "name": { + "type": "JSXIdentifier", + "start": 29, + "end": 30, + "name": "b" + }, + "selfClosing": false + }, + "closingElement": { + "type": "JSXClosingElement", + "start": 53, + "end": 57, + "name": { + "type": "JSXIdentifier", + "start": 55, + "end": 56, + "name": "b" + } + }, + "children": [ + { + "type": "JSXText", + "start": 31, + "end": 53, + "value": "monkeys /> gorillas", + "raw": "monkeys /> gorillas" + } + ] + } + } + ], + "name": { + "type": "JSXIdentifier", + "start": 1, + "end": 10, + "name": "LeftRight" + }, + "selfClosing": true + }, + "closingElement": null, + "children": [] + } + }, + + '': { + type: 'ExpressionStatement', + expression: { + type: 'JSXElement', + openingElement: { + type: 'JSXOpeningElement', + name: { + type: 'JSXMemberExpression', + object: { + type: 'JSXIdentifier', + name: 'a', + range: [1, 2], + loc: { + start: { line: 1, column: 1 }, + end: { line: 1, column: 2 } + } + }, + property: { + type: 'JSXIdentifier', + name: 'b', + range: [3, 4], + loc: { + start: { line: 1, column: 3 }, + end: { line: 1, column: 4 } + } + }, + range: [1, 4], + loc: { + start: { line: 1, column: 1 }, + end: { line: 1, column: 4 } + } + }, + selfClosing: false, + attributes: [], + range: [0, 5], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 5 } + } + }, + closingElement: { + type: 'JSXClosingElement', + name: { + type: 'JSXMemberExpression', + object: { + type: 'JSXIdentifier', + name: 'a', + range: [7, 8], + loc: { + start: { line: 1, column: 7 }, + end: { line: 1, column: 8 } + } + }, + property: { + type: 'JSXIdentifier', + name: 'b', + range: [9, 10], + loc: { + start: { line: 1, column: 9 }, + end: { line: 1, column: 10 } + } + }, + range: [7, 10], + loc: { + start: { line: 1, column: 7 }, + end: { line: 1, column: 10 } + } + }, + range: [5, 11], + loc: { + start: { line: 1, column: 5 }, + end: { line: 1, column: 11 } + } + }, + children: [], + range: [0, 11], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 11 } + } + }, + range: [0, 11], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 11 } + } + }, + + '': { + type: 'ExpressionStatement', + expression: { + type: 'JSXElement', + openingElement: { + type: 'JSXOpeningElement', + name: { + type: 'JSXMemberExpression', + object: { + type: 'JSXMemberExpression', + object: { + type: 'JSXIdentifier', + name: 'a', + range: [1, 2], + loc: { + start: { line: 1, column: 1 }, + end: { line: 1, column: 2 } + } + }, + property: { + type: 'JSXIdentifier', + name: 'b', + range: [3, 4], + loc: { + start: { line: 1, column: 3 }, + end: { line: 1, column: 4 } + } + }, + range: [1, 4], + loc: { + start: { line: 1, column: 1 }, + end: { line: 1, column: 4 } + } + }, + property: { + type: 'JSXIdentifier', + name: 'c', + range: [5, 6], + loc: { + start: { line: 1, column: 5 }, + end: { line: 1, column: 6 } + } + }, + range: [1, 6], + loc: { + start: { line: 1, column: 1 }, + end: { line: 1, column: 6 } + } + }, + selfClosing: false, + attributes: [], + range: [0, 7], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 7 } + } + }, + closingElement: { + type: 'JSXClosingElement', + name: { + type: 'JSXMemberExpression', + object: { + type: 'JSXMemberExpression', + object: { + type: 'JSXIdentifier', + name: 'a', + range: [9, 10], + loc: { + start: { line: 1, column: 9 }, + end: { line: 1, column: 10 } + } + }, + property: { + type: 'JSXIdentifier', + name: 'b', + range: [11, 12], + loc: { + start: { line: 1, column: 11 }, + end: { line: 1, column: 12 } + } + }, + range: [9, 12], + loc: { + start: { line: 1, column: 9 }, + end: { line: 1, column: 12 } + } + }, + property: { + type: 'JSXIdentifier', + name: 'c', + range: [13, 14], + loc: { + start: { line: 1, column: 13 }, + end: { line: 1, column: 14 } + } + }, + range: [9, 14], + loc: { + start: { line: 1, column: 9 }, + end: { line: 1, column: 14 } + } + }, + range: [7, 15], + loc: { + start: { line: 1, column: 7 }, + end: { line: 1, column: 15 } + } + }, + children: [], + range: [0, 15], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 15 } + } + }, + range: [0, 15], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 15 } + } + }, + + // In order to more useful parse errors, we disallow following an + // JSXElement by a less-than symbol. In the rare case that the binary + // operator was intended, the tag can be wrapped in parentheses: + '(
) < x;': { + type: 'ExpressionStatement', + expression: { + type: 'BinaryExpression', + operator: '<', + left: { + type: 'JSXElement', + openingElement: { + type: 'JSXOpeningElement', + name: { + type: 'JSXIdentifier', + name: 'div', + range: [2, 5], + loc: { + start: { line: 1, column: 2 }, + end: { line: 1, column: 5 } + } + }, + selfClosing: true, + attributes: [], + range: [1, 8], + loc: { + start: { line: 1, column: 1 }, + end: { line: 1, column: 8 } + } + }, + closingElement: null, + children: [], + range: [1, 8], + loc: { + start: { line: 1, column: 1 }, + end: { line: 1, column: 8 } + } + }, + right: { + type: 'Identifier', + name: 'x', + range: [12, 13], + loc: { + start: { line: 1, column: 12 }, + end: { line: 1, column: 13 } + } + }, + range: [0, 13], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 13 } + } + }, + range: [0, 14], + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: 14 } + } + }, + + '
': { + "type": "ExpressionStatement", + "expression": { + "type": "JSXElement", + "openingElement": { + "type": "JSXOpeningElement", + "name": { + "type": "JSXIdentifier", + "name": "div", + "range": [ + 1, + 4 + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 4 + } + } + }, + "selfClosing": true, + "attributes": [ + { + "type": "JSXSpreadAttribute", + "argument": { + "type": "Identifier", + "name": "props", + "range": [ + 9, + 14 + ], + "loc": { + "start": { + "line": 1, + "column": 9 + }, + "end": { + "line": 1, + "column": 14 + } + } + }, + "range": [ + 5, + 15 + ], + "loc": { + "start": { + "line": 1, + "column": 5 + }, + "end": { + "line": 1, + "column": 15 + } + } + } + ], + "range": [ + 0, + 18 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 18 + } + } + }, + closingElement: null, + "children": [], + "range": [ + 0, + 18 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 18 + } + } + }, + "range": [ + 0, + 18 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 18 + } + } + }, + + '
': { + "type": "ExpressionStatement", + "expression": { + "type": "JSXElement", + "openingElement": { + "type": "JSXOpeningElement", + "name": { + "type": "JSXIdentifier", + "name": "div", + "range": [ + 1, + 4 + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 4 + } + } + }, + "selfClosing": true, + "attributes": [ + { + "type": "JSXSpreadAttribute", + "argument": { + "type": "Identifier", + "name": "props", + "range": [ + 9, + 14 + ], + "loc": { + "start": { + "line": 1, + "column": 9 + }, + "end": { + "line": 1, + "column": 14 + } + } + }, + "range": [ + 5, + 15 + ], + "loc": { + "start": { + "line": 1, + "column": 5 + }, + "end": { + "line": 1, + "column": 15 + } + } + }, + { + "type": "JSXAttribute", + "name": { + "type": "JSXIdentifier", + "name": "post", + "range": [ + 16, + 20 + ], + "loc": { + "start": { + "line": 1, + "column": 16 + }, + "end": { + "line": 1, + "column": 20 + } + } + }, + "value": { + "type": "Literal", + "value": "attribute", + "raw": "\"attribute\"", + "range": [ + 21, + 32 + ], + "loc": { + "start": { + "line": 1, + "column": 21 + }, + "end": { + "line": 1, + "column": 32 + } + } + }, + "range": [ + 16, + 32 + ], + "loc": { + "start": { + "line": 1, + "column": 16 + }, + "end": { + "line": 1, + "column": 32 + } + } + } + ], + "range": [ + 0, + 35 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 35 + } + } + }, + closingElement: null, + "children": [], + "range": [ + 0, + 35 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 35 + } + } + }, + "range": [ + 0, + 35 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 35 + } + } + }, + + '
': { + "type": "ExpressionStatement", + "expression": { + "type": "JSXElement", + "openingElement": { + "type": "JSXOpeningElement", + "name": { + "type": "JSXIdentifier", + "name": "div", + "range": [ + 1, + 4 + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 4 + } + } + }, + "selfClosing": false, + "attributes": [ + { + "type": "JSXAttribute", + "name": { + "type": "JSXIdentifier", + "name": "pre", + "range": [ + 5, + 8 + ], + "loc": { + "start": { + "line": 1, + "column": 5 + }, + "end": { + "line": 1, + "column": 8 + } + } + }, + "value": { + "type": "Literal", + "value": "leading", + "raw": "\"leading\"", + "range": [ + 9, + 18 + ], + "loc": { + "start": { + "line": 1, + "column": 9 + }, + "end": { + "line": 1, + "column": 18 + } + } + }, + "range": [ + 5, + 18 + ], + "loc": { + "start": { + "line": 1, + "column": 5 + }, + "end": { + "line": 1, + "column": 18 + } + } + }, + { + "type": "JSXAttribute", + "name": { + "type": "JSXIdentifier", + "name": "pre2", + "range": [ + 19, + 23 + ], + "loc": { + "start": { + "line": 1, + "column": 19 + }, + "end": { + "line": 1, + "column": 23 + } + } + }, + "value": { + "type": "Literal", + "value": "attribute", + "raw": "\"attribute\"", + "range": [ + 24, + 35 + ], + "loc": { + "start": { + "line": 1, + "column": 24 + }, + "end": { + "line": 1, + "column": 35 + } + } + }, + "range": [ + 19, + 35 + ], + "loc": { + "start": { + "line": 1, + "column": 19 + }, + "end": { + "line": 1, + "column": 35 + } + } + }, + { + "type": "JSXSpreadAttribute", + "argument": { + "type": "Identifier", + "name": "props", + "range": [ + 40, + 45 + ], + "loc": { + "start": { + "line": 1, + "column": 40 + }, + "end": { + "line": 1, + "column": 45 + } + } + }, + "range": [ + 36, + 46 + ], + "loc": { + "start": { + "line": 1, + "column": 36 + }, + "end": { + "line": 1, + "column": 46 + } + } + } + ], + "range": [ + 0, + 47 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 47 + } + } + }, + "closingElement": { + "type": "JSXClosingElement", + "name": { + "type": "JSXIdentifier", + "name": "div", + "range": [ + 49, + 52 + ], + "loc": { + "start": { + "line": 1, + "column": 49 + }, + "end": { + "line": 1, + "column": 52 + } + } + }, + "range": [ + 47, + 53 + ], + "loc": { + "start": { + "line": 1, + "column": 47 + }, + "end": { + "line": 1, + "column": 53 + } + } + }, + "children": [], + "range": [ + 0, + 53 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 53 + } + } + }, + "range": [ + 0, + 53 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 53 + } + } + }, + + '
{aa.b}
': { + "type": "ExpressionStatement", + "start": 0, + "end": 52, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 52 + } + }, + "range": [ + 0, + 52 + ], + "expression": { + "type": "JSXElement", + "start": 0, + "end": 52, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 52 + } + }, + "range": [ + 0, + 52 + ], + "openingElement": { + "type": "JSXOpeningElement", + "start": 0, + "end": 31, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 31 + } + }, + "range": [ + 0, + 31 + ], + "attributes": [ + { + "type": "JSXAttribute", + "start": 3, + "end": 16, + "loc": { + "start": { + "line": 1, + "column": 3 + }, + "end": { + "line": 1, + "column": 16 + } + }, + "range": [ + 3, + 16 + ], + "name": { + "type": "JSXIdentifier", + "start": 3, + "end": 5, + "loc": { + "start": { + "line": 1, + "column": 3 + }, + "end": { + "line": 1, + "column": 5 + } + }, + "range": [ + 3, + 5 + ], + "name": "aa" + }, + "value": { + "type": "JSXExpressionContainer", + "start": 6, + "end": 16, + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 16 + } + }, + "range": [ + 6, + 16 + ], + "expression": { + "type": "MemberExpression", + "start": 7, + "end": 15, + "loc": { + "start": { + "line": 1, + "column": 7 + }, + "end": { + "line": 1, + "column": 15 + } + }, + "range": [ + 7, + 15 + ], + "object": { + "type": "MemberExpression", + "start": 7, + "end": 12, + "loc": { + "start": { + "line": 1, + "column": 7 + }, + "end": { + "line": 1, + "column": 12 + } + }, + "range": [ + 7, + 12 + ], + "object": { + "type": "Identifier", + "start": 7, + "end": 9, + "loc": { + "start": { + "line": 1, + "column": 7 + }, + "end": { + "line": 1, + "column": 9 + } + }, + "range": [ + 7, + 9 + ], + "name": "aa" + }, + "property": { + "type": "Identifier", + "start": 10, + "end": 12, + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 1, + "column": 12 + } + }, + "range": [ + 10, + 12 + ], + "name": "bb" + }, + "computed": false + }, + "property": { + "type": "Identifier", + "start": 13, + "end": 15, + "loc": { + "start": { + "line": 1, + "column": 13 + }, + "end": { + "line": 1, + "column": 15 + } + }, + "range": [ + 13, + 15 + ], + "name": "cc" + }, + "computed": false + } + } + }, + { + "type": "JSXAttribute", + "start": 17, + "end": 30, + "loc": { + "start": { + "line": 1, + "column": 17 + }, + "end": { + "line": 1, + "column": 30 + } + }, + "range": [ + 17, + 30 + ], + "name": { + "type": "JSXIdentifier", + "start": 17, + "end": 19, + "loc": { + "start": { + "line": 1, + "column": 17 + }, + "end": { + "line": 1, + "column": 19 + } + }, + "range": [ + 17, + 19 + ], + "name": "bb" + }, + "value": { + "type": "JSXExpressionContainer", + "start": 20, + "end": 30, + "loc": { + "start": { + "line": 1, + "column": 20 + }, + "end": { + "line": 1, + "column": 30 + } + }, + "range": [ + 20, + 30 + ], + "expression": { + "type": "MemberExpression", + "start": 21, + "end": 29, + "loc": { + "start": { + "line": 1, + "column": 21 + }, + "end": { + "line": 1, + "column": 29 + } + }, + "range": [ + 21, + 29 + ], + "object": { + "type": "MemberExpression", + "start": 21, + "end": 26, + "loc": { + "start": { + "line": 1, + "column": 21 + }, + "end": { + "line": 1, + "column": 26 + } + }, + "range": [ + 21, + 26 + ], + "object": { + "type": "Identifier", + "start": 21, + "end": 23, + "loc": { + "start": { + "line": 1, + "column": 21 + }, + "end": { + "line": 1, + "column": 23 + } + }, + "range": [ + 21, + 23 + ], + "name": "bb" + }, + "property": { + "type": "Identifier", + "start": 24, + "end": 26, + "loc": { + "start": { + "line": 1, + "column": 24 + }, + "end": { + "line": 1, + "column": 26 + } + }, + "range": [ + 24, + 26 + ], + "name": "cc" + }, + "computed": false + }, + "property": { + "type": "Identifier", + "start": 27, + "end": 29, + "loc": { + "start": { + "line": 1, + "column": 27 + }, + "end": { + "line": 1, + "column": 29 + } + }, + "range": [ + 27, + 29 + ], + "name": "dd" + }, + "computed": false + } + } + } + ], + "name": { + "type": "JSXIdentifier", + "start": 1, + "end": 2, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 2 + } + }, + "range": [ + 1, + 2 + ], + "name": "A" + }, + "selfClosing": false + }, + "closingElement": { + "type": "JSXClosingElement", + "start": 48, + "end": 52, + "loc": { + "start": { + "line": 1, + "column": 48 + }, + "end": { + "line": 1, + "column": 52 + } + }, + "range": [ + 48, + 52 + ], + "name": { + "type": "JSXIdentifier", + "start": 50, + "end": 51, + "loc": { + "start": { + "line": 1, + "column": 50 + }, + "end": { + "line": 1, + "column": 51 + } + }, + "range": [ + 50, + 51 + ], + "name": "A" + } + }, + "children": [ + { + "type": "JSXElement", + "start": 31, + "end": 48, + "loc": { + "start": { + "line": 1, + "column": 31 + }, + "end": { + "line": 1, + "column": 48 + } + }, + "range": [ + 31, + 48 + ], + "openingElement": { + "type": "JSXOpeningElement", + "start": 31, + "end": 36, + "loc": { + "start": { + "line": 1, + "column": 31 + }, + "end": { + "line": 1, + "column": 36 + } + }, + "range": [ + 31, + 36 + ], + "attributes": [], + "name": { + "type": "JSXIdentifier", + "start": 32, + "end": 35, + "loc": { + "start": { + "line": 1, + "column": 32 + }, + "end": { + "line": 1, + "column": 35 + } + }, + "range": [ + 32, + 35 + ], + "name": "div" + }, + "selfClosing": false + }, + "closingElement": { + "type": "JSXClosingElement", + "start": 42, + "end": 48, + "loc": { + "start": { + "line": 1, + "column": 42 + }, + "end": { + "line": 1, + "column": 48 + } + }, + "range": [ + 42, + 48 + ], + "name": { + "type": "JSXIdentifier", + "start": 44, + "end": 47, + "loc": { + "start": { + "line": 1, + "column": 44 + }, + "end": { + "line": 1, + "column": 47 + } + }, + "range": [ + 44, + 47 + ], + "name": "div" + } + }, + "children": [ + { + "type": "JSXExpressionContainer", + "start": 36, + "end": 42, + "loc": { + "start": { + "line": 1, + "column": 36 + }, + "end": { + "line": 1, + "column": 42 + } + }, + "range": [ + 36, + 42 + ], + "expression": { + "type": "MemberExpression", + "start": 37, + "end": 41, + "loc": { + "start": { + "line": 1, + "column": 37 + }, + "end": { + "line": 1, + "column": 41 + } + }, + "range": [ + 37, + 41 + ], + "object": { + "type": "Identifier", + "start": 37, + "end": 39, + "loc": { + "start": { + "line": 1, + "column": 37 + }, + "end": { + "line": 1, + "column": 39 + } + }, + "range": [ + 37, + 39 + ], + "name": "aa" + }, + "property": { + "type": "Identifier", + "start": 40, + "end": 41, + "loc": { + "start": { + "line": 1, + "column": 40 + }, + "end": { + "line": 1, + "column": 41 + } + }, + "range": [ + 40, + 41 + ], + "name": "b" + }, + "computed": false + } + } + ] + } + ] + } + }, + + '<>
': { + type: 'ExpressionStatement', + start: 0, + end: 16, + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 16 + } + }, + range: [0, 16], + expression: { + type: 'JSXFragment', + start: 0, + end: 16, + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 16 + } + }, + range: [0, 16], + openingFragment: { + type: 'JSXOpeningFragment', + start: 0, + end: 2, + loc: { + start: { + line: 1, + column: 0 + }, + end: { + line: 1, + column: 2 + } + }, + range: [0, 2], + attributes: [], + selfClosing: false + }, + closingFragment: { + type: 'JSXClosingFragment', + start: 13, + end: 16, + loc: { + start: { + line: 1, + column: 13 + }, + end: { + line: 1, + column: 16 + } + }, + range: [13, 16] + }, + children: [{ + type: 'JSXElement', + start: 2, + end: 13, + loc: { + start: { + line: 1, + column: 2 + }, + end: { + line: 1, + column: 13 + } + }, + range: [2, 13], + openingElement: { + type: 'JSXOpeningElement', + start: 2, + end: 7, + loc: { + start: { + line: 1, + column: 2 + }, + end: { + line: 1, + column: 7 + } + }, + range: [2, 7], + attributes: [], + name: { + type: 'JSXIdentifier', + start: 3, + end: 6, + loc: { + start: { + line: 1, + column: 3 + }, + end: { + line: 1, + column: 6 + } + }, + range: [3, 6], + name: 'div' + }, + selfClosing: false + }, + closingElement: { + type: 'JSXClosingElement', + start: 7, + end: 13, + loc: { + start: { + line: 1, + column: 7 + }, + end: { + line: 1, + column: 13 + } + }, + range: [7, 13], + name: { + type: 'JSXIdentifier', + start: 9, + end: 12, + loc: { + start: { + line: 1, + column: 9 + }, + end: { + line: 1, + column: 12 + } + }, + range: [9, 12], + name: 'div' + } + }, + children: [] + }] + } + } + }, + 'Regression': { + '

foo bar baz

;': { + type: "ExpressionStatement", + start: 0, + end: 40, + expression: { + type: "JSXElement", + start: 0, + end: 38, + openingElement: { + type: "JSXOpeningElement", + start: 0, + end: 3, + attributes: [], + name: { + type: "JSXIdentifier", + start: 1, + end: 2, + name: "p" + }, + selfClosing: false + }, + closingElement: { + type: "JSXClosingElement", + start: 34, + end: 38, + name: { + type: "JSXIdentifier", + start: 36, + end: 37, + name: "p" + } + }, + children: [ + { + type: "JSXText", + start: 3, + end: 7, + value: "foo ", + raw: "foo " + }, + { + type: "JSXElement", + start: 7, + end: 30, + openingElement: { + type: "JSXOpeningElement", + start: 7, + end: 22, + attributes: [{ + type: "JSXAttribute", + start: 10, + end: 21, + name: { + type: "JSXIdentifier", + start: 10, + end: 14, + name: "href" + }, + value: { + type: "Literal", + start: 15, + end: 21, + value: "test", + raw: "\"test\"" + } + }], + name: { + type: "JSXIdentifier", + start: 8, + end: 9, + name: "a" + }, + selfClosing: false + }, + closingElement: { + type: "JSXClosingElement", + start: 26, + end: 30, + name: { + type: "JSXIdentifier", + start: 28, + end: 29, + name: "a" + } + }, + children: [{ + type: "JSXText", + start: 22, + end: 26, + value: " bar", + raw: " bar" + }] + }, + { + type: "JSXText", + start: 30, + end: 34, + value: " baz", + raw: " baz" + } + ] + } + }, + + '
{
}
': { + type: 'ExpressionStatement', + start: 0, + end: 30, + expression: { + type: 'JSXElement', + start: 0, + end: 30, + openingElement: { + type: 'JSXOpeningElement', + start: 0, + end: 5, + attributes: [], + name: { + type: 'JSXIdentifier', + start: 1, + end: 4, + name: 'div' + }, + selfClosing: false + }, + closingElement: { + type: 'JSXClosingElement', + start: 24, + end: 30, + name: { + type: 'JSXIdentifier', + start: 26, + end: 29, + name: 'div' + } + }, + children: [{ + type: 'JSXExpressionContainer', + start: 5, + end: 24, + expression: { + type: 'JSXElement', + start: 6, + end: 23, + openingElement: { + type: 'JSXOpeningElement', + start: 6, + end: 23, + attributes: [ + { + type: 'JSXSpreadAttribute', + start: 11, + end: 20, + argument: { + type: 'Identifier', + start: 15, + end: 19, + name: 'test' + } + } + ], + name: { + type: 'JSXIdentifier', + start: 7, + end: 10, + name: 'div' + }, + selfClosing: true + }, + closingElement: null, + children: [] + } + }] + } + }, + + '
{ {a} }
': { + type: "ExpressionStatement", + start: 0, + end: 18, + expression: { + type: "JSXElement", + start: 0, + end: 18, + openingElement: { + type: "JSXOpeningElement", + start: 0, + end: 5, + attributes: [], + name: { + type: "JSXIdentifier", + start: 1, + end: 4, + name: "div" + }, + selfClosing: false + }, + closingElement: { + type: "JSXClosingElement", + start: 12, + end: 18, + name: { + type: "JSXIdentifier", + start: 14, + end: 17, + name: "div" + } + }, + children: [{ + type: "JSXExpressionContainer", + start: 5, + end: 12, + expression: { + type: "ObjectExpression", + start: 7, + end: 10, + properties: [{ + type: "Property", + start: 8, + end: 9, + method: false, + shorthand: true, + computed: false, + key: { + type: "Identifier", + start: 8, + end: 9, + name: "a" + }, + kind: "init", + value: { + type: "Identifier", + start: 8, + end: 9, + name: "a" + } + }] + } + }] + } + }, + + '
/text
': { + type: "ExpressionStatement", + start: 0, + end: 16, + expression: { + type: "JSXElement", + start: 0, + end: 16, + openingElement: { + type: "JSXOpeningElement", + start: 0, + end: 5, + attributes: [], + name: { + type: "JSXIdentifier", + start: 1, + end: 4, + name: "div" + }, + selfClosing: false + }, + closingElement: { + type: "JSXClosingElement", + start: 10, + end: 16, + name: { + type: "JSXIdentifier", + start: 12, + end: 15, + name: "div" + } + }, + children: [{ + type: "JSXText", + start: 5, + end: 10, + value: "/text", + raw: "/text" + }] + } + }, + + '
{a}{b}
': { + type: "ExpressionStatement", + start: 0, + end: 17, + expression: { + type: "JSXElement", + start: 0, + end: 17, + openingElement: { + type: "JSXOpeningElement", + start: 0, + end: 5, + attributes: [], + name: { + type: "JSXIdentifier", + start: 1, + end: 4, + name: "div" + }, + selfClosing: false + }, + closingElement: { + type: "JSXClosingElement", + start: 11, + end: 17, + name: { + type: "JSXIdentifier", + start: 13, + end: 16, + name: "div" + } + }, + children: [{ + type: 'JSXExpressionContainer', + expression: { + type: 'Identifier', + name: 'a', + range: [6, 7], + loc: { + start: { + line: 1, + column: 6 + }, + end: { + line: 1, + column: 7 + } + } + }, + range: [5, 8], + loc: { + start: { + line: 1, + column: 5 + }, + end: { + line: 1, + column: 8 + } + } + }, { + type: 'JSXExpressionContainer', + expression: { + type: 'Identifier', + name: 'b', + range: [9, 10], + loc: { + start: { + line: 1, + column: 9 + }, + end: { + line: 1, + column: 10 + } + } + }, + range: [8, 11], + loc: { + start: { + line: 1, + column: 8 + }, + end: { + line: 1, + column: 11 + } + } + } + ] + } + }, + + '
': { + type: "ExpressionStatement", + range: [0, 32], + expression: { + type: "JSXElement", + range: [0, 32], + openingElement: { + type: "JSXOpeningElement", + range: [0, 32], + attributes: [ + { + type: "JSXAttribute", + range: [5, 18], + name: { + type: "JSXIdentifier", + range: [5, 8], + name: "pre" + }, + value: { + type: "Literal", + range: [9, 18], + value: "leading" + } + }, + { + type: "JSXSpreadAttribute", + range: [19, 29], + argument: { + type: "Identifier", + range: [23, 28], + name: "props" + } + } + ], + name: { + type: "JSXIdentifier", + range: [1, 4], + name: "div" + }, + selfClosing: true + }, + closingElement: null, + children: [] + } + }, + '': { + type: "ExpressionStatement", + expression: { + type: "JSXElement", + range: [0, 64], + openingElement: { + type: "JSXOpeningElement", + range: [0, 64], + attributes: [ + { + type: "JSXAttribute", + range: [6, 62], + name: { + type: "JSXIdentifier", + range: [6, 7], + name: "d" + }, + value: { + type: "Literal", + loc: { + start: { line: 1, column: 8 }, + end: { line: 3, column: 15 } + }, + range: [8, 62], + value: "M230 80\n\t\tA 45 45, 0, 1, 0, 275 125 \r\n L 275 80 Z", + raw: "\"M230 80\n\t\tA 45 45, 0, 1, 0, 275 125 \r\n L 275 80 Z\"" + } + } + ], + name: { + type: "JSXIdentifier", + range: [1, 5], + name: "path" + }, + selfClosing: true + }, + closingElement: null, + children: [] + } + } + } +}; + +if (typeof exports !== "undefined") { + var test = require("./driver.js").test; + var testFail = require("./driver.js").testFail; + var jsxTokens = require("..").tokTypes; + var acornTokens = require("acorn").tokTypes; +} + +testFail("var x =
one
two
;", "Adjacent JSX elements must be wrapped in an enclosing tag (1:22)"); + +testFail("", "Unexpected token (1:4)"); + +test("", { + type: "Program", + range: [0, 9], + body: [{ + type: "ExpressionStatement", + range: [0, 9], + expression: { + type: "JSXElement", + range: [0, 9], + openingElement: { + type: "JSXOpeningElement", + range: [0, 9], + attributes: [], + name: { + type: "JSXMemberExpression", + range: [1, 6], + object: { + type: "JSXNamespacedName", + range: [1, 4], + namespace: { + type: "JSXIdentifier", + range: [1, 2], + name: "a" + }, + name: { + type: "JSXIdentifier", + range: [3, 4], + name: "b" + } + }, + property: { + type: "JSXIdentifier", + range: [5, 6], + name: "c" + } + }, + selfClosing: true + }, + closingElement: null, + children: [] + } + }] +}, { + ranges: true +}, { + allowNamespacedObjects: true +}); + +testFail('', 'Unexpected token (1:3)', {}, { + allowNamespaces: false +}); + +testFail('
', 'Unexpected token (1:7)', {}, { + allowNamespaces: false +}); + +test('{/* foo */}', {}, { + onToken: [ + { + type: jsxTokens.jsxTagStart, + value: undefined, + start: 0, + end: 1 + }, + { + type: jsxTokens.jsxName, + value: 'a', + start: 1, + end: 2 + }, + { + type: jsxTokens.jsxTagEnd, + value: undefined, + start: 2, + end: 3 + }, + { + type: acornTokens.braceL, + value: undefined, + start: 3, + end: 4 + }, + { + type: acornTokens.braceR, + value: undefined, + start: 13, + end: 14 + }, + { + type: jsxTokens.jsxTagStart, + value: undefined, + start: 14, + end: 15 + }, + { + type: acornTokens.slash, + value: '/', + start: 15, + end: 16 + }, + { + type: jsxTokens.jsxName, + value: 'a', + start: 16, + end: 17 + }, + { + type: jsxTokens.jsxTagEnd, + value: undefined, + start: 17, + end: 18 + }, + { + type: acornTokens.eof, + value: undefined, + start: 18, + end: 18 + } + ] +}); + +test('