jsx icon indicating copy to clipboard operation
jsx copied to clipboard

Add early error to disallow invalid adjacent JSX elements

Open nicolo-ribaudo opened this issue 5 years ago • 3 comments

Currently this is perfectly valid JSX code:

<El>text</El> <g> / </g>
0

How is it possible? Adjacent JSX elemets must be wrapped inside another element, or inside a fragment. Well, those are not two JSX elements.

It's a jsx element (<El>text</El>), which is compared by < with the variable g, which is then compared by > with the regex / </g, and which is then compared by > with 0. If we add some parentheses for readability, it becomes

(((<El>text</El>) < g) > / </g) > 0

Currently JSX parsers don't handle this (valid) code well, because they all assume that the user is trying to use to JSX elements:

  • TypeScript parses it as a a sequence expression containing two JSX elements, it automatically inserts a semicolon and parses 0 as a separate statement: https://astexplorer.net/#/gist/66ddd5a43c135ee13b7872e65de033c4/79d737c1cc925d253d937e30a439586a30610c3b
  • Babel throws an error about adjacent JSX elements being disallowed: https://astexplorer.net/#/gist/66ddd5a43c135ee13b7872e65de033c4/d49f3355f5e0055e320da7da6d7a32521b1b4259
  • Acorn throws an error about adjacent JSX elements being disallowed: https://astexplorer.net/#/gist/66ddd5a43c135ee13b7872e65de033c4/374709ddc0b8dc6d9335676a26e163a484a5a302
  • ESLint throws the same error as Babel and Acorn: https://astexplorer.net/#/gist/66ddd5a43c135ee13b7872e65de033c4/27dbd1c36a8c42845031d3d8d0f77a75b73a34d9
  • jsx-transform, which uses esprima-fb, throws the same error. On the other hand, esprima doesn't throw.
  • :warning: Flow correctly parses it: https://astexplorer.net/#/gist/66ddd5a43c135ee13b7872e65de033c4/47ffc8a8b45cbbc6e8152423b9c7d5c351934288 Most flow users use Babel to remove their types, but otherwise this would be a breaking change.

I propose to disallow that code since it is highly confusing, similarly to how -1 ** 2 is disallowed by the ECMAScript specification. To do so, we would need to define a new early error which applies to the RelationalExpression production:

It is an early Syntax Error if RelationalExpression matches the JSXElement < ShiftExpression or JSXFragment < ShiftExpression productions

By doing so, the spec would align with the ecosystem and allow parsers to throw errors helpful for the users, like Babel's Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...</>?

nicolo-ribaudo avatar Sep 02 '19 14:09 nicolo-ribaudo

@nicolo-ribaudo Oh wow this is interesting. Thanks for reporting this and providing a very clear analysis.

Since we've moved the spec to ecmarkup (#135), I think we are finally in a good position to formalize errors like this. Would you mind making a PR?

Regarding the potential breaking change on the Flow parser: @pieterv, is Flow team comfortable with this change?

Huxpro avatar Feb 25 '22 05:02 Huxpro

Looks like this has been fixed in Flow since this issue has been created as the astexplorer link is showing the elements after the first JSX element parsed as binary expressions. Seems like a reasonable change anyway, we can fix any issues that come up.

See: https://astexplorer.net/#/gist/66ddd5a43c135ee13b7872e65de033c4/47ffc8a8b45cbbc6e8152423b9c7d5c351934288

pieterv avatar Mar 01 '22 00:03 pieterv

@nicolo-ribaudo

I propose to disallow that code since it is highly confusing, similarly to how -1 ** 2 is disallowed by the ECMAScript specification.

How is this specified? I couldn't find any SS: early errors clause dedicated for that in the full spec or the original https://tc39.es/proposal-exponentiation-operator/ addition. I was imaging that we can specify JSXElement < similarly in #141

Huxpro avatar Mar 07 '22 03:03 Huxpro