-
Notifications
You must be signed in to change notification settings - Fork 429
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for functors in JSX tags #1927
Conversation
Thank you for your pull request and welcome to our community. We require contributors to sign our Contributor License Agreement, and we don't seem to have you on file. In order for us to review and merge your code, please sign up at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need the corporate CLA signed. If you have received this in error or have any questions, please contact us at [email protected]. Thanks! |
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Facebook open source project. Thanks! |
Could you check what happens with the use case <Foo.Bar/> That's where the |
Yeah, that seems to still work. To clarify, it should only affect the case with trailing dots, since I'm basically allowing for an arbitrary Longident (rather than only the |
@@ -532,7 +532,7 @@ rule token = parse | |||
| "[|" { LBRACKETBAR } | |||
| "[<" { LBRACKETLESS } | |||
| "[>" { LBRACKETGREATER } | |||
| "<" uppercase_or_lowercase (identchar | '.') * { | |||
| "<" (uppercase identchar* '.')* lowercase identchar* { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To double check: the main change here is uppercase_or_lowercase
to lowercase
, plus some .
cleanup correct?
The cleanup can be applied to the two cases below too, using I guess
(uppercase identchar* '.')* uppercase_or_lowercase identchar*
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. The change to lowercase
as the main pattern is to make it only parse lowercase tags (e.g. <div>
) as LESSIDENT
. The parser implements the uppercase (opening) tags as a different pattern without LESSIDENT
. The .
cleanup was specifically added to allow tags like <X.x>
, but doesn't support <X.Y>
. I could add this to the other cases, but they work simply by resetting the lexer to the beginning of the pattern, consuming only up to the <
token. I assume that the current patterns will run slightly faster, and should work correctly at the parsing level. That said, it may make more sense to switch to (uppercase identchar* '.')* lowercase identchar*
and (uppercase identchar* '.')* uppercase identchar*
for the other patterns to make them look more similar and make the patterns more accurate.
let createElement = X.createElement; | ||
}; | ||
|
||
let body = <div> <F(X) /> </div>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are several arguments supported?
<F(X,Y) />
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, as well as full paths , e.g.(<G.F(X, Y)/>)
. Applying multiple separate functors, e.g. <G(X).F(Y, Z)/>
doesn't work (the AST transformation required for that is a lot more complex).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tried to add a test for <F(X,Y) />
but I'm getting a syntax error on the file generated in actual_output
. Could you try adding a multi argument example to the tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I think the syntax error is from the idempotent tests. When I test it with refmt
it doesn't seem to print right. Just fixed it
Not sure if you got a notification from the last pushes, but I've fixed the pretty-printing for functors with multiple arguments. Let me know if there's anything else that might be an issue with this. |
I wasn't sure what the plan was about tests. The PR currently adds empty files for |
Weird, I must've accidentally cleared those files before pushing. Should be fixed now. |
Thanks!
|
I feel stupid cause I keep running |
the tag, then it would be consistent with array spread: | ||
[...list] evaluates to the thing as list. | ||
*) | ||
let rec extract_apps args = function |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The compiler gives a non-exhaustive pattern matching warning here.
if hasLabelledChildrenLiteral && hasSingleNonLabelledUnitAndIsAtTheEnd l then | ||
if List.length (Longident.flatten loc.txt) > 1 then | ||
if Longident.last loc.txt = "createElement" then | ||
let ftor::args = extract_apps [] app in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
non-exhaustive pattern matching here too
perhaps the code can be refactored a bit
Great! |
Yep, seems like it's all done. Thank you! |
Do you think it might surprise some people that each time you use the JSX functor application, it will instantiate a new module each time? So if the functor application has a side effect it would occur twice, or if it has a reference cell, it will not be shared. Do you have thoughts on that? |
I think anyone who's used OCaml/Reason a lot would assume it based on syntax, because each instance of |
Fixes #1924
This PR enables the use of functors in JSX tags. It may also alter some edge cases in the syntax (though these would not compile properly in the past) -- previously,
<M./>
would parse correctly but fail to bind to a module; in certain cases, this will still parse but it should result in an error earlier in the pipeline (indicating a syntax error). This PR allows for the following new syntax:The syntax is designed to make it clear that the functor application only occurs once, but again it is a relatively easy change to add support for (or even require) a matching end tag.
Internally, the transformation is done by creating a
let module
expression to temporarily bind the result of the applied functor and then calling thecreateElement
function on that result. Some more info, as well as an example use-case, is show in #1924.