From a1125b26ce4143e8b02dcc3ee9af1bbef448a610 Mon Sep 17 00:00:00 2001 From: Clement Hoang Date: Sat, 28 Oct 2017 13:12:14 -0700 Subject: [PATCH] Add JSX fragment syntax support (#755) --- src/plugins/jsx/index.js | 63 ++- src/types.js | 3 + test/fixtures/jsx/basic/fragment-1/actual.js | 1 + .../jsx/basic/fragment-1/expected.json | 95 +++++ test/fixtures/jsx/basic/fragment-2/actual.js | 1 + .../jsx/basic/fragment-2/expected.json | 116 ++++++ test/fixtures/jsx/basic/fragment-3/actual.js | 6 + .../jsx/basic/fragment-3/expected.json | 358 ++++++++++++++++ test/fixtures/jsx/basic/fragment-4/actual.js | 7 + .../jsx/basic/fragment-4/expected.json | 290 +++++++++++++ test/fixtures/jsx/basic/fragment-5/actual.js | 7 + .../jsx/basic/fragment-5/expected.json | 385 ++++++++++++++++++ test/fixtures/jsx/basic/fragment-6/actual.js | 1 + .../jsx/basic/fragment-6/expected.json | 252 ++++++++++++ .../errors/attributes-in-fragment/actual.js | 1 + .../attributes-in-fragment/options.json | 3 + .../errors/nested-fragment-unclosed/actual.js | 1 + .../nested-fragment-unclosed/options.json | 3 + .../wrong-closing-tag-fragment/actual.js | 1 + .../wrong-closing-tag-fragment/options.json | 3 + .../wrong-opening-tag-fragment/actual.js | 1 + .../wrong-opening-tag-fragment/options.json | 3 + 22 files changed, 1596 insertions(+), 5 deletions(-) create mode 100644 test/fixtures/jsx/basic/fragment-1/actual.js create mode 100644 test/fixtures/jsx/basic/fragment-1/expected.json create mode 100644 test/fixtures/jsx/basic/fragment-2/actual.js create mode 100644 test/fixtures/jsx/basic/fragment-2/expected.json create mode 100644 test/fixtures/jsx/basic/fragment-3/actual.js create mode 100644 test/fixtures/jsx/basic/fragment-3/expected.json create mode 100644 test/fixtures/jsx/basic/fragment-4/actual.js create mode 100644 test/fixtures/jsx/basic/fragment-4/expected.json create mode 100644 test/fixtures/jsx/basic/fragment-5/actual.js create mode 100644 test/fixtures/jsx/basic/fragment-5/expected.json create mode 100644 test/fixtures/jsx/basic/fragment-6/actual.js create mode 100644 test/fixtures/jsx/basic/fragment-6/expected.json create mode 100644 test/fixtures/jsx/errors/attributes-in-fragment/actual.js create mode 100644 test/fixtures/jsx/errors/attributes-in-fragment/options.json create mode 100644 test/fixtures/jsx/errors/nested-fragment-unclosed/actual.js create mode 100644 test/fixtures/jsx/errors/nested-fragment-unclosed/options.json create mode 100644 test/fixtures/jsx/errors/wrong-closing-tag-fragment/actual.js create mode 100644 test/fixtures/jsx/errors/wrong-closing-tag-fragment/options.json create mode 100644 test/fixtures/jsx/errors/wrong-opening-tag-fragment/actual.js create mode 100644 test/fixtures/jsx/errors/wrong-opening-tag-fragment/options.json diff --git a/src/plugins/jsx/index.js b/src/plugins/jsx/index.js index 0cd591e5a5..905284d8ae 100644 --- a/src/plugins/jsx/index.js +++ b/src/plugins/jsx/index.js @@ -37,6 +37,13 @@ tt.jsxTagEnd.updateContext = function(prevType) { } }; +function isFragment(object: ?N.JSXElement): boolean { + return object + ? object.type === "JSXOpeningFragment" || + object.type === "JSXClosingFragment" + : false; +} + // Transforms JSX element name to string. function getQualifiedJSXName( @@ -341,6 +348,10 @@ export default (superClass: Class): Class => startLoc: Position, ): N.JSXOpeningElement { const node = this.startNodeAt(startPos, startLoc); + if (this.match(tt.jsxTagEnd)) { + this.expect(tt.jsxTagEnd); + return this.finishNode(node, "JSXOpeningFragment"); + } node.attributes = []; node.name = this.jsxParseElementName(); while (!this.match(tt.slash) && !this.match(tt.jsxTagEnd)) { @@ -358,6 +369,10 @@ export default (superClass: Class): Class => startLoc: Position, ): N.JSXClosingElement { const node = this.startNodeAt(startPos, startLoc); + if (this.match(tt.jsxTagEnd)) { + this.expect(tt.jsxTagEnd); + return this.finishNode(node, "JSXClosingFragment"); + } node.name = this.jsxParseElementName(); this.expect(tt.jsxTagEnd); return this.finishNode(node, "JSXClosingElement"); @@ -410,8 +425,18 @@ export default (superClass: Class): Class => if ( // $FlowIgnore - getQualifiedJSXName(closingElement.name) !== - getQualifiedJSXName(openingElement.name) + isFragment(openingElement) && + !isFragment(closingElement) + ) { + this.raise( + // $FlowIgnore + closingElement.start, + "Expected corresponding JSX closing tag for <>", + ); + } else if ( + // $FlowIgnore + !isFragment(openingElement) && + isFragment(closingElement) ) { this.raise( // $FlowIgnore @@ -420,11 +445,35 @@ export default (superClass: Class): Class => getQualifiedJSXName(openingElement.name) + ">", ); + } else if ( + // $FlowIgnore + !isFragment(openingElement) && + !isFragment(closingElement) + ) { + if ( + // $FlowIgnore + getQualifiedJSXName(closingElement.name) !== + getQualifiedJSXName(openingElement.name) + ) { + this.raise( + // $FlowIgnore + closingElement.start, + "Expected corresponding JSX closing tag for <" + + getQualifiedJSXName(openingElement.name) + + ">", + ); + } } } - node.openingElement = openingElement; - node.closingElement = closingElement; + // $FlowIgnore + if (isFragment(openingElement)) { + node.openingFragment = openingElement; + node.closingFragment = closingElement; + } else { + node.openingElement = openingElement; + node.closingElement = closingElement; + } node.children = children; if (this.match(tt.relational) && this.state.value === "<") { this.raise( @@ -432,7 +481,11 @@ export default (superClass: Class): Class => "Adjacent JSX elements must be wrapped in an enclosing tag", ); } - return this.finishNode(node, "JSXElement"); + + // $FlowIgnore + return isFragment(openingElement) + ? this.finishNode(node, "JSXFragment") + : this.finishNode(node, "JSXElement"); } // Parses entire JSX element from current position. diff --git a/src/types.js b/src/types.js index 93839e88f8..782469fe9a 100644 --- a/src/types.js +++ b/src/types.js @@ -799,6 +799,9 @@ export type JSXAttribute = Node; export type JSXOpeningElement = Node; export type JSXClosingElement = Node; export type JSXElement = Node; +export type JSXOpeningFragment = Node; +export type JSXClosingFragment = Node; +export type JSXFragment = Node; // Flow/TypeScript common (TODO: Not in spec) diff --git a/test/fixtures/jsx/basic/fragment-1/actual.js b/test/fixtures/jsx/basic/fragment-1/actual.js new file mode 100644 index 0000000000..4a80b22bee --- /dev/null +++ b/test/fixtures/jsx/basic/fragment-1/actual.js @@ -0,0 +1 @@ +<> diff --git a/test/fixtures/jsx/basic/fragment-1/expected.json b/test/fixtures/jsx/basic/fragment-1/expected.json new file mode 100644 index 0000000000..b374291c56 --- /dev/null +++ b/test/fixtures/jsx/basic/fragment-1/expected.json @@ -0,0 +1,95 @@ +{ + "type": "File", + "start": 0, + "end": 5, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 5 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 5, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 5 + } + }, + "sourceType": "script", + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 5, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 5 + } + }, + "expression": { + "type": "JSXFragment", + "start": 0, + "end": 5, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 5 + } + }, + "openingFragment": { + "type": "JSXOpeningFragment", + "start": 0, + "end": 2, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 2 + } + } + }, + "closingFragment": { + "type": "JSXClosingFragment", + "start": 2, + "end": 5, + "loc": { + "start": { + "line": 1, + "column": 2 + }, + "end": { + "line": 1, + "column": 5 + } + } + }, + "children": [] + } + } + ], + "directives": [] + } +} diff --git a/test/fixtures/jsx/basic/fragment-2/actual.js b/test/fixtures/jsx/basic/fragment-2/actual.js new file mode 100644 index 0000000000..28a10596d3 --- /dev/null +++ b/test/fixtures/jsx/basic/fragment-2/actual.js @@ -0,0 +1 @@ +<>Hi, I'm a string! diff --git a/test/fixtures/jsx/basic/fragment-2/expected.json b/test/fixtures/jsx/basic/fragment-2/expected.json new file mode 100644 index 0000000000..980a7860bc --- /dev/null +++ b/test/fixtures/jsx/basic/fragment-2/expected.json @@ -0,0 +1,116 @@ +{ + "type": "File", + "start": 0, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 22 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 22 + } + }, + "sourceType": "script", + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 22 + } + }, + "expression": { + "type": "JSXFragment", + "start": 0, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 22 + } + }, + "openingFragment": { + "type": "JSXOpeningFragment", + "start": 0, + "end": 2, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 2 + } + } + }, + "closingFragment": { + "type": "JSXClosingFragment", + "start": 19, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 19 + }, + "end": { + "line": 1, + "column": 22 + } + } + }, + "children": [ + { + "type": "JSXText", + "start": 2, + "end": 19, + "loc": { + "start": { + "line": 1, + "column": 2 + }, + "end": { + "line": 1, + "column": 19 + } + }, + "extra": { + "rawValue": "Hi, I'm a string!", + "raw": "Hi, I'm a string!" + }, + "value": "Hi, I'm a string!" + } + ] + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/test/fixtures/jsx/basic/fragment-3/actual.js b/test/fixtures/jsx/basic/fragment-3/actual.js new file mode 100644 index 0000000000..23f71a31a3 --- /dev/null +++ b/test/fixtures/jsx/basic/fragment-3/actual.js @@ -0,0 +1,6 @@ +< > + + hi + +
bye
+ diff --git a/test/fixtures/jsx/basic/fragment-3/expected.json b/test/fixtures/jsx/basic/fragment-3/expected.json new file mode 100644 index 0000000000..c09408300b --- /dev/null +++ b/test/fixtures/jsx/basic/fragment-3/expected.json @@ -0,0 +1,358 @@ +{ + "type": "File", + "start": 0, + "end": 50, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 6, + "column": 3 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 50, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 6, + "column": 3 + } + }, + "sourceType": "script", + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 50, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 6, + "column": 3 + } + }, + "expression": { + "type": "JSXFragment", + "start": 0, + "end": 50, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 6, + "column": 3 + } + }, + "openingFragment": { + "type": "JSXOpeningFragment", + "start": 0, + "end": 3, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 3 + } + } + }, + "closingFragment": { + "type": "JSXClosingFragment", + "start": 47, + "end": 50, + "loc": { + "start": { + "line": 6, + "column": 0 + }, + "end": { + "line": 6, + "column": 3 + } + } + }, + "children": [ + { + "type": "JSXText", + "start": 3, + "end": 6, + "loc": { + "start": { + "line": 1, + "column": 3 + }, + "end": { + "line": 2, + "column": 2 + } + }, + "extra": { + "rawValue": "\n ", + "raw": "\n " + }, + "value": "\n " + }, + { + "type": "JSXElement", + "start": 6, + "end": 29, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 4, + "column": 9 + } + }, + "openingElement": { + "type": "JSXOpeningElement", + "start": 6, + "end": 12, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 8 + } + }, + "attributes": [], + "name": { + "type": "JSXIdentifier", + "start": 7, + "end": 11, + "loc": { + "start": { + "line": 2, + "column": 3 + }, + "end": { + "line": 2, + "column": 7 + } + }, + "name": "span" + }, + "selfClosing": false + }, + "closingElement": { + "type": "JSXClosingElement", + "start": 22, + "end": 29, + "loc": { + "start": { + "line": 4, + "column": 2 + }, + "end": { + "line": 4, + "column": 9 + } + }, + "name": { + "type": "JSXIdentifier", + "start": 24, + "end": 28, + "loc": { + "start": { + "line": 4, + "column": 4 + }, + "end": { + "line": 4, + "column": 8 + } + }, + "name": "span" + } + }, + "children": [ + { + "type": "JSXText", + "start": 12, + "end": 22, + "loc": { + "start": { + "line": 2, + "column": 8 + }, + "end": { + "line": 4, + "column": 2 + } + }, + "extra": { + "rawValue": "\n hi\n ", + "raw": "\n hi\n " + }, + "value": "\n hi\n " + } + ] + }, + { + "type": "JSXText", + "start": 29, + "end": 32, + "loc": { + "start": { + "line": 4, + "column": 9 + }, + "end": { + "line": 5, + "column": 2 + } + }, + "extra": { + "rawValue": "\n ", + "raw": "\n " + }, + "value": "\n " + }, + { + "type": "JSXElement", + "start": 32, + "end": 46, + "loc": { + "start": { + "line": 5, + "column": 2 + }, + "end": { + "line": 5, + "column": 16 + } + }, + "openingElement": { + "type": "JSXOpeningElement", + "start": 32, + "end": 37, + "loc": { + "start": { + "line": 5, + "column": 2 + }, + "end": { + "line": 5, + "column": 7 + } + }, + "attributes": [], + "name": { + "type": "JSXIdentifier", + "start": 33, + "end": 36, + "loc": { + "start": { + "line": 5, + "column": 3 + }, + "end": { + "line": 5, + "column": 6 + } + }, + "name": "div" + }, + "selfClosing": false + }, + "closingElement": { + "type": "JSXClosingElement", + "start": 40, + "end": 46, + "loc": { + "start": { + "line": 5, + "column": 10 + }, + "end": { + "line": 5, + "column": 16 + } + }, + "name": { + "type": "JSXIdentifier", + "start": 42, + "end": 45, + "loc": { + "start": { + "line": 5, + "column": 12 + }, + "end": { + "line": 5, + "column": 15 + } + }, + "name": "div" + } + }, + "children": [ + { + "type": "JSXText", + "start": 37, + "end": 40, + "loc": { + "start": { + "line": 5, + "column": 7 + }, + "end": { + "line": 5, + "column": 10 + } + }, + "extra": { + "rawValue": "bye", + "raw": "bye" + }, + "value": "bye" + } + ] + }, + { + "type": "JSXText", + "start": 46, + "end": 47, + "loc": { + "start": { + "line": 5, + "column": 16 + }, + "end": { + "line": 6, + "column": 0 + } + }, + "extra": { + "rawValue": "\n", + "raw": "\n" + }, + "value": "\n" + } + ] + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/test/fixtures/jsx/basic/fragment-4/actual.js b/test/fixtures/jsx/basic/fragment-4/actual.js new file mode 100644 index 0000000000..b3f295db9d --- /dev/null +++ b/test/fixtures/jsx/basic/fragment-4/actual.js @@ -0,0 +1,7 @@ +<> + <> + <> + super deep + + + diff --git a/test/fixtures/jsx/basic/fragment-4/expected.json b/test/fixtures/jsx/basic/fragment-4/expected.json new file mode 100644 index 0000000000..421567aeb1 --- /dev/null +++ b/test/fixtures/jsx/basic/fragment-4/expected.json @@ -0,0 +1,290 @@ +{ + "type": "File", + "start": 0, + "end": 49, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 7, + "column": 3 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 49, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 7, + "column": 3 + } + }, + "sourceType": "script", + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 49, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 7, + "column": 3 + } + }, + "expression": { + "type": "JSXFragment", + "start": 0, + "end": 49, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 7, + "column": 3 + } + }, + "openingFragment": { + "type": "JSXOpeningFragment", + "start": 0, + "end": 2, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 2 + } + } + }, + "closingFragment": { + "type": "JSXClosingFragment", + "start": 46, + "end": 49, + "loc": { + "start": { + "line": 7, + "column": 0 + }, + "end": { + "line": 7, + "column": 3 + } + } + }, + "children": [ + { + "type": "JSXText", + "start": 2, + "end": 5, + "loc": { + "start": { + "line": 1, + "column": 2 + }, + "end": { + "line": 2, + "column": 2 + } + }, + "extra": { + "rawValue": "\n ", + "raw": "\n " + }, + "value": "\n " + }, + { + "type": "JSXFragment", + "start": 5, + "end": 45, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 6, + "column": 5 + } + }, + "openingFragment": { + "type": "JSXOpeningFragment", + "start": 5, + "end": 7, + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 4 + } + } + }, + "closingFragment": { + "type": "JSXClosingFragment", + "start": 42, + "end": 45, + "loc": { + "start": { + "line": 6, + "column": 2 + }, + "end": { + "line": 6, + "column": 5 + } + } + }, + "children": [ + { + "type": "JSXText", + "start": 7, + "end": 12, + "loc": { + "start": { + "line": 2, + "column": 4 + }, + "end": { + "line": 3, + "column": 4 + } + }, + "extra": { + "rawValue": "\n ", + "raw": "\n " + }, + "value": "\n " + }, + { + "type": "JSXFragment", + "start": 12, + "end": 39, + "loc": { + "start": { + "line": 3, + "column": 4 + }, + "end": { + "line": 5, + "column": 7 + } + }, + "openingFragment": { + "type": "JSXOpeningFragment", + "start": 12, + "end": 14, + "loc": { + "start": { + "line": 3, + "column": 4 + }, + "end": { + "line": 3, + "column": 6 + } + } + }, + "closingFragment": { + "type": "JSXClosingFragment", + "start": 36, + "end": 39, + "loc": { + "start": { + "line": 5, + "column": 4 + }, + "end": { + "line": 5, + "column": 7 + } + } + }, + "children": [ + { + "type": "JSXText", + "start": 14, + "end": 36, + "loc": { + "start": { + "line": 3, + "column": 6 + }, + "end": { + "line": 5, + "column": 4 + } + }, + "extra": { + "rawValue": "\n super deep\n ", + "raw": "\n super deep\n " + }, + "value": "\n super deep\n " + } + ] + }, + { + "type": "JSXText", + "start": 39, + "end": 42, + "loc": { + "start": { + "line": 5, + "column": 7 + }, + "end": { + "line": 6, + "column": 2 + } + }, + "extra": { + "rawValue": "\n ", + "raw": "\n " + }, + "value": "\n " + } + ] + }, + { + "type": "JSXText", + "start": 45, + "end": 46, + "loc": { + "start": { + "line": 6, + "column": 5 + }, + "end": { + "line": 7, + "column": 0 + } + }, + "extra": { + "rawValue": "\n", + "raw": "\n" + }, + "value": "\n" + } + ] + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/test/fixtures/jsx/basic/fragment-5/actual.js b/test/fixtures/jsx/basic/fragment-5/actual.js new file mode 100644 index 0000000000..99aa40498f --- /dev/null +++ b/test/fixtures/jsx/basic/fragment-5/actual.js @@ -0,0 +1,7 @@ +< +// comment1 +/* comment2 */ +> +
+
+ diff --git a/test/fixtures/jsx/basic/fragment-5/expected.json b/test/fixtures/jsx/basic/fragment-5/expected.json new file mode 100644 index 0000000000..a303606313 --- /dev/null +++ b/test/fixtures/jsx/basic/fragment-5/expected.json @@ -0,0 +1,385 @@ +{ + "type": "File", + "start": 0, + "end": 62, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 7, + "column": 3 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 62, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 7, + "column": 3 + } + }, + "sourceType": "script", + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 62, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 7, + "column": 3 + } + }, + "expression": { + "type": "JSXFragment", + "start": 0, + "end": 62, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 7, + "column": 3 + } + }, + "openingFragment": { + "type": "JSXOpeningFragment", + "start": 0, + "end": 30, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 4, + "column": 1 + } + }, + "leadingComments": null, + "innerComments": [ + { + "type": "CommentLine", + "value": " comment1", + "start": 2, + "end": 13, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 11 + } + } + }, + { + "type": "CommentBlock", + "value": " comment2 ", + "start": 14, + "end": 28, + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 14 + } + } + } + ] + }, + "closingFragment": { + "type": "JSXClosingFragment", + "start": 59, + "end": 62, + "loc": { + "start": { + "line": 7, + "column": 0 + }, + "end": { + "line": 7, + "column": 3 + } + } + }, + "children": [ + { + "type": "JSXText", + "start": 30, + "end": 33, + "loc": { + "start": { + "line": 4, + "column": 1 + }, + "end": { + "line": 5, + "column": 2 + } + }, + "extra": { + "rawValue": "\n ", + "raw": "\n " + }, + "value": "\n " + }, + { + "type": "JSXElement", + "start": 33, + "end": 44, + "loc": { + "start": { + "line": 5, + "column": 2 + }, + "end": { + "line": 5, + "column": 13 + } + }, + "openingElement": { + "type": "JSXOpeningElement", + "start": 33, + "end": 38, + "loc": { + "start": { + "line": 5, + "column": 2 + }, + "end": { + "line": 5, + "column": 7 + } + }, + "attributes": [], + "name": { + "type": "JSXIdentifier", + "start": 34, + "end": 37, + "loc": { + "start": { + "line": 5, + "column": 3 + }, + "end": { + "line": 5, + "column": 6 + } + }, + "name": "div" + }, + "selfClosing": false + }, + "closingElement": { + "type": "JSXClosingElement", + "start": 38, + "end": 44, + "loc": { + "start": { + "line": 5, + "column": 7 + }, + "end": { + "line": 5, + "column": 13 + } + }, + "name": { + "type": "JSXIdentifier", + "start": 40, + "end": 43, + "loc": { + "start": { + "line": 5, + "column": 9 + }, + "end": { + "line": 5, + "column": 12 + } + }, + "name": "div" + } + }, + "children": [] + }, + { + "type": "JSXText", + "start": 44, + "end": 47, + "loc": { + "start": { + "line": 5, + "column": 13 + }, + "end": { + "line": 6, + "column": 2 + } + }, + "extra": { + "rawValue": "\n ", + "raw": "\n " + }, + "value": "\n " + }, + { + "type": "JSXElement", + "start": 47, + "end": 58, + "loc": { + "start": { + "line": 6, + "column": 2 + }, + "end": { + "line": 6, + "column": 13 + } + }, + "openingElement": { + "type": "JSXOpeningElement", + "start": 47, + "end": 52, + "loc": { + "start": { + "line": 6, + "column": 2 + }, + "end": { + "line": 6, + "column": 7 + } + }, + "attributes": [], + "name": { + "type": "JSXIdentifier", + "start": 48, + "end": 51, + "loc": { + "start": { + "line": 6, + "column": 3 + }, + "end": { + "line": 6, + "column": 6 + } + }, + "name": "div" + }, + "selfClosing": false + }, + "closingElement": { + "type": "JSXClosingElement", + "start": 52, + "end": 58, + "loc": { + "start": { + "line": 6, + "column": 7 + }, + "end": { + "line": 6, + "column": 13 + } + }, + "name": { + "type": "JSXIdentifier", + "start": 54, + "end": 57, + "loc": { + "start": { + "line": 6, + "column": 9 + }, + "end": { + "line": 6, + "column": 12 + } + }, + "name": "div" + } + }, + "children": [] + }, + { + "type": "JSXText", + "start": 58, + "end": 59, + "loc": { + "start": { + "line": 6, + "column": 13 + }, + "end": { + "line": 7, + "column": 0 + } + }, + "extra": { + "rawValue": "\n", + "raw": "\n" + }, + "value": "\n" + } + ] + } + } + ], + "directives": [] + }, + "comments": [ + { + "type": "CommentLine", + "value": " comment1", + "start": 2, + "end": 13, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 11 + } + } + }, + { + "type": "CommentBlock", + "value": " comment2 ", + "start": 14, + "end": 28, + "loc": { + "start": { + "line": 3, + "column": 0 + }, + "end": { + "line": 3, + "column": 14 + } + } + } + ] +} \ No newline at end of file diff --git a/test/fixtures/jsx/basic/fragment-6/actual.js b/test/fixtures/jsx/basic/fragment-6/actual.js new file mode 100644 index 0000000000..b3ef87dfc6 --- /dev/null +++ b/test/fixtures/jsx/basic/fragment-6/actual.js @@ -0,0 +1 @@ +<>
JSXElement
JSXText{'JSXExpressionContainer'} diff --git a/test/fixtures/jsx/basic/fragment-6/expected.json b/test/fixtures/jsx/basic/fragment-6/expected.json new file mode 100644 index 0000000000..a4487e1c0e --- /dev/null +++ b/test/fixtures/jsx/basic/fragment-6/expected.json @@ -0,0 +1,252 @@ +{ + "type": "File", + "start": 0, + "end": 59, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 59 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 59, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 59 + } + }, + "sourceType": "script", + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 59, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 59 + } + }, + "expression": { + "type": "JSXFragment", + "start": 0, + "end": 59, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 59 + } + }, + "openingFragment": { + "type": "JSXOpeningFragment", + "start": 0, + "end": 2, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 2 + } + } + }, + "closingFragment": { + "type": "JSXClosingFragment", + "start": 56, + "end": 59, + "loc": { + "start": { + "line": 1, + "column": 56 + }, + "end": { + "line": 1, + "column": 59 + } + } + }, + "children": [ + { + "type": "JSXElement", + "start": 2, + "end": 23, + "loc": { + "start": { + "line": 1, + "column": 2 + }, + "end": { + "line": 1, + "column": 23 + } + }, + "openingElement": { + "type": "JSXOpeningElement", + "start": 2, + "end": 7, + "loc": { + "start": { + "line": 1, + "column": 2 + }, + "end": { + "line": 1, + "column": 7 + } + }, + "attributes": [], + "name": { + "type": "JSXIdentifier", + "start": 3, + "end": 6, + "loc": { + "start": { + "line": 1, + "column": 3 + }, + "end": { + "line": 1, + "column": 6 + } + }, + "name": "div" + }, + "selfClosing": false + }, + "closingElement": { + "type": "JSXClosingElement", + "start": 17, + "end": 23, + "loc": { + "start": { + "line": 1, + "column": 17 + }, + "end": { + "line": 1, + "column": 23 + } + }, + "name": { + "type": "JSXIdentifier", + "start": 19, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 19 + }, + "end": { + "line": 1, + "column": 22 + } + }, + "name": "div" + } + }, + "children": [ + { + "type": "JSXText", + "start": 7, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 7 + }, + "end": { + "line": 1, + "column": 17 + } + }, + "extra": { + "rawValue": "JSXElement", + "raw": "JSXElement" + }, + "value": "JSXElement" + } + ] + }, + { + "type": "JSXText", + "start": 23, + "end": 30, + "loc": { + "start": { + "line": 1, + "column": 23 + }, + "end": { + "line": 1, + "column": 30 + } + }, + "extra": { + "rawValue": "JSXText", + "raw": "JSXText" + }, + "value": "JSXText" + }, + { + "type": "JSXExpressionContainer", + "start": 30, + "end": 56, + "loc": { + "start": { + "line": 1, + "column": 30 + }, + "end": { + "line": 1, + "column": 56 + } + }, + "expression": { + "type": "StringLiteral", + "start": 31, + "end": 55, + "loc": { + "start": { + "line": 1, + "column": 31 + }, + "end": { + "line": 1, + "column": 55 + } + }, + "extra": { + "rawValue": "JSXExpressionContainer", + "raw": "'JSXExpressionContainer'" + }, + "value": "JSXExpressionContainer" + } + } + ] + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/test/fixtures/jsx/errors/attributes-in-fragment/actual.js b/test/fixtures/jsx/errors/attributes-in-fragment/actual.js new file mode 100644 index 0000000000..6897704057 --- /dev/null +++ b/test/fixtures/jsx/errors/attributes-in-fragment/actual.js @@ -0,0 +1 @@ +< key="nope"> diff --git a/test/fixtures/jsx/errors/attributes-in-fragment/options.json b/test/fixtures/jsx/errors/attributes-in-fragment/options.json new file mode 100644 index 0000000000..91b308200e --- /dev/null +++ b/test/fixtures/jsx/errors/attributes-in-fragment/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Unexpected token (1:5)" +} diff --git a/test/fixtures/jsx/errors/nested-fragment-unclosed/actual.js b/test/fixtures/jsx/errors/nested-fragment-unclosed/actual.js new file mode 100644 index 0000000000..5980e371a9 --- /dev/null +++ b/test/fixtures/jsx/errors/nested-fragment-unclosed/actual.js @@ -0,0 +1 @@ +<><> diff --git a/test/fixtures/jsx/errors/nested-fragment-unclosed/options.json b/test/fixtures/jsx/errors/nested-fragment-unclosed/options.json new file mode 100644 index 0000000000..c958665c03 --- /dev/null +++ b/test/fixtures/jsx/errors/nested-fragment-unclosed/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Unexpected token (1:7)" +} diff --git a/test/fixtures/jsx/errors/wrong-closing-tag-fragment/actual.js b/test/fixtures/jsx/errors/wrong-closing-tag-fragment/actual.js new file mode 100644 index 0000000000..75f76d10cf --- /dev/null +++ b/test/fixtures/jsx/errors/wrong-closing-tag-fragment/actual.js @@ -0,0 +1 @@ +<> diff --git a/test/fixtures/jsx/errors/wrong-closing-tag-fragment/options.json b/test/fixtures/jsx/errors/wrong-closing-tag-fragment/options.json new file mode 100644 index 0000000000..4ca478c5b4 --- /dev/null +++ b/test/fixtures/jsx/errors/wrong-closing-tag-fragment/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Expected corresponding JSX closing tag for <> (1:2)" +} diff --git a/test/fixtures/jsx/errors/wrong-opening-tag-fragment/actual.js b/test/fixtures/jsx/errors/wrong-opening-tag-fragment/actual.js new file mode 100644 index 0000000000..1a6dea11d3 --- /dev/null +++ b/test/fixtures/jsx/errors/wrong-opening-tag-fragment/actual.js @@ -0,0 +1 @@ + diff --git a/test/fixtures/jsx/errors/wrong-opening-tag-fragment/options.json b/test/fixtures/jsx/errors/wrong-opening-tag-fragment/options.json new file mode 100644 index 0000000000..25d9465b06 --- /dev/null +++ b/test/fixtures/jsx/errors/wrong-opening-tag-fragment/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Expected corresponding JSX closing tag for (1:11)" +}