Fix #5046: Adjacent JSX (#5049)

* Fix #5046: Adjacent JSX

* check CSX only when wrapped in parentheses

* Fix indentation

* Add test for unlikely, but valid, JSX syntax
This commit is contained in:
zdenko
2018-05-20 18:39:09 +02:00
committed by Geoffrey Booth
parent 7cf739e2b6
commit 0e7677ad62
4 changed files with 60 additions and 31 deletions

View File

@@ -671,23 +671,29 @@
// A Block node does not return its entire body, rather it
// ensures that the final expression is returned.
makeReturn(res) {
var csxCheckIndex, expr, j, len, ref1;
var expr, expressions, last, lastExp, len, penult, ref1;
len = this.expressions.length;
ref1 = this.expressions, [lastExp] = slice1.call(ref1, -1);
lastExp = (lastExp != null ? lastExp.unwrap() : void 0) || false;
// We also need to check that were not returning a CSX tag if theres an
// adjacent one at the same level; JSX doesnt allow that.
if (lastExp && lastExp instanceof Parens && lastExp.body.expressions.length > 1) {
({
body: {expressions}
} = lastExp);
[penult, last] = slice1.call(expressions, -2);
penult = penult.unwrap();
last = last.unwrap();
if (penult instanceof Call && penult.csx && last instanceof Call && last.csx) {
expressions[expressions.length - 1].error('Adjacent JSX elements must be wrapped in an enclosing tag');
}
}
while (len--) {
expr = this.expressions[len];
this.expressions[len] = expr.makeReturn(res);
if (expr instanceof Return && !expr.expression) {
this.expressions.splice(len, 1);
}
// We also need to check that were not returning a CSX tag if theres an
// adjacent one at the same level; JSX doesnt allow that.
if (expr.unwrapAll().csx) {
for (csxCheckIndex = j = ref1 = len; (ref1 <= 0 ? j <= 0 : j >= 0); csxCheckIndex = ref1 <= 0 ? ++j : --j) {
if (this.expressions[csxCheckIndex].unwrapAll().csx) {
expr.error('Adjacent JSX elements must be wrapped in an enclosing tag');
}
}
}
break;
}
return this;

View File

@@ -478,16 +478,21 @@ exports.Block = class Block extends Base
# ensures that the final expression is returned.
makeReturn: (res) ->
len = @expressions.length
[..., lastExp] = @expressions
lastExp = lastExp?.unwrap() or no
# We also need to check that were not returning a CSX tag if theres an
# adjacent one at the same level; JSX doesnt allow that.
if lastExp and lastExp instanceof Parens and lastExp.body.expressions.length > 1
{body:{expressions}} = lastExp
[..., penult, last] = expressions
penult = penult.unwrap()
last = last.unwrap()
if penult instanceof Call and penult.csx and last instanceof Call and last.csx
expressions[expressions.length - 1].error 'Adjacent JSX elements must be wrapped in an enclosing tag'
while len--
expr = @expressions[len]
@expressions[len] = expr.makeReturn res
@expressions.splice(len, 1) if expr instanceof Return and not expr.expression
# We also need to check that were not returning a CSX tag if theres an
# adjacent one at the same level; JSX doesnt allow that.
if expr.unwrapAll().csx
for csxCheckIndex in [len..0]
if @expressions[csxCheckIndex].unwrapAll().csx
expr.error 'Adjacent JSX elements must be wrapped in an enclosing tag'
break
this

View File

@@ -771,11 +771,11 @@ test 'JSX fragments: fragment with text nodes', ->
test 'JSX fragments: fragment with component nodes', ->
eqJS '''
Component = (props) =>
<Fragment>
<OtherComponent />
<OtherComponent />
</Fragment>
Component = (props) =>
<Fragment>
<OtherComponent />
<OtherComponent />
</Fragment>
''', '''
var Component;
@@ -821,3 +821,20 @@ test '#5055: JSX expression indentation bug', ->
{typeof a !== "undefined" && a !== null ? a : <span />}
</div>;
'''
# JSX is like XML, in that there needs to be a root element; but
# technically, adjacent top-level elements where only the last one
# is returned (as opposed to a fragment or root element) is permissible
# syntax. Its almost certainly an error, but its valid, so need to leave it
# to linters to catch. https://github.com/jashkenas/coffeescript/pull/5049
test '“Adjacent” tags on separate lines should still compile', ->
eqJS '''
->
<a />
<b />
''', '''
(function() {
<a />;
return <b />;
});
'''

View File

@@ -1663,15 +1663,6 @@ test 'CSX error: invalid attributes', ->
'''
test '#5034: CSX error: Adjacent JSX elements must be wrapped in an enclosing tag', ->
assertErrorFormat '''
render = ->
<Row>a</Row>
<Row>b</Row>
''', '''
[stdin]:3:4: error: Adjacent JSX elements must be wrapped in an enclosing tag
<Row>b</Row>
^^^^^^^^^^^
'''
assertErrorFormat '''
render = -> (
<Row>a</Row>
@@ -1682,7 +1673,17 @@ test '#5034: CSX error: Adjacent JSX elements must be wrapped in an enclosing ta
<Row>b</Row>
^^^^^^^^^^^
'''
assertErrorFormat '''
render = -> (
a = "foo"
<Row>a</Row>
<Row>b</Row>
)
''', '''
[stdin]:4:4: error: Adjacent JSX elements must be wrapped in an enclosing tag
<Row>b</Row>
^^^^^^^^^^^
'''
test 'Bound method called as callback before binding throws runtime error', ->
class Base
constructor: ->