-
Notifications
You must be signed in to change notification settings - Fork 38
Move React ppx from compiler repo, add tests #124
Changes from 1 commit
523ff1e
d24c0df
a3f0b44
8a7a1bd
d3dcd85
dd2f680
e84640a
739a1df
23336db
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
(* | ||
This is the module that handles turning Reason JSX' agnostic function call into | ||
a ReasonReact-specific function call. Aka, this is a macro, using OCaml's ppx | ||
facilities; https://whitequark.org/blog/2014/04/16/a-guide-to-extension- | ||
points-in-ocaml/ | ||
You wouldn't use this file directly; it's used by ReScript's | ||
bsconfig.json. Specifically, there's a field called `react-jsx` inside the | ||
field `reason`, which enables this ppx through some internal call in bsb | ||
*) | ||
|
||
(* | ||
There are two different transforms that can be selected in this file (v2 and v3): | ||
v2: | ||
transform `[@JSX] div(~props1=a, ~props2=b, ~children=[foo, bar], ())` into | ||
`ReactDOMRe.createElement("div", ~props={"props1": 1, "props2": b}, [|foo, | ||
bar|])`. | ||
transform `[@JSX] div(~props1=a, ~props2=b, ~children=foo, ())` into | ||
`ReactDOMRe.createElementVariadic("div", ~props={"props1": 1, "props2": b}, foo)`. | ||
transform the upper-cased case | ||
`[@JSX] Foo.createElement(~key=a, ~ref=b, ~foo=bar, ~children=[], ())` into | ||
`ReasonReact.element(~key=a, ~ref=b, Foo.make(~foo=bar, [||]))` | ||
transform `[@JSX] [foo]` into | ||
`ReactDOMRe.createElement(ReasonReact.fragment, [|foo|])` | ||
v3: | ||
transform `[@JSX] div(~props1=a, ~props2=b, ~children=[foo, bar], ())` into | ||
`ReactDOMRe.createDOMElementVariadic("div", ReactDOMRe.domProps(~props1=1, ~props2=b), [|foo, bar|])`. | ||
transform the upper-cased case | ||
`[@JSX] Foo.createElement(~key=a, ~ref=b, ~foo=bar, ~children=[], ())` into | ||
`React.createElement(Foo.make, Foo.makeProps(~key=a, ~ref=b, ~foo=bar, ()))` | ||
transform the upper-cased case | ||
`[@JSX] Foo.createElement(~foo=bar, ~children=[foo, bar], ())` into | ||
`React.createElementVariadic(Foo.make, Foo.makeProps(~foo=bar, ~children=React.null, ()), [|foo, bar|])` | ||
transform `[@JSX] [foo]` into | ||
`ReactDOMRe.createElement(ReasonReact.fragment, [|foo|])` | ||
*) | ||
|
||
val rewrite_implementation : Parsetree.structure -> Parsetree.structure | ||
|
||
val rewrite_signature : Parsetree.signature -> Parsetree.signature |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -207,11 +207,12 @@ module CliArgProcessor = struct | |||||||||
let processInterface = | ||||||||||
isInterface || len > 0 && (String.get [@doesNotRaise]) filename (len - 1) = 'i' | ||||||||||
in | ||||||||||
let parsingEngine = | ||||||||||
let parsingEngine, react = | ||||||||||
match origin with | ||||||||||
| "reasonBinary" -> Parser Res_driver_reason_binary.parsingEngine | ||||||||||
| "ml" | "ocaml" -> Parser Res_driver_ml_parser.parsingEngine | ||||||||||
| _ -> Parser Res_driver.parsingEngine | ||||||||||
| "reasonBinary" -> Parser Res_driver_reason_binary.parsingEngine, false | ||||||||||
| "ml" | "ocaml" -> Parser Res_driver_ml_parser.parsingEngine, false | ||||||||||
| "reactJsx" -> Parser Res_driver.parsingEngine, true | ||||||||||
| _ -> Parser Res_driver.parsingEngine, false | ||||||||||
in | ||||||||||
let printEngine = | ||||||||||
match target with | ||||||||||
|
@@ -237,40 +238,50 @@ module CliArgProcessor = struct | |||||||||
~source:parseResult.source | ||||||||||
~filename:parseResult.filename | ||||||||||
parseResult.diagnostics; | ||||||||||
if recover || not parseResult.invalid then | ||||||||||
if react then | ||||||||||
exit 1 | ||||||||||
else if recover then | ||||||||||
printEngine.printInterface | ||||||||||
~width ~filename ~comments:parseResult.comments parseResult.parsetree | ||||||||||
else () | ||||||||||
end | ||||||||||
else | ||||||||||
let parsetree = | ||||||||||
if react then Reactjs_jsx_ppx.rewrite_signature parseResult.parsetree else parseResult.parsetree | ||||||||||
in | ||||||||||
printEngine.printInterface | ||||||||||
~width ~filename ~comments:parseResult.comments parseResult.parsetree | ||||||||||
~width ~filename ~comments:parseResult.comments parsetree | ||||||||||
else | ||||||||||
let parseResult = backend.parseImplementation ~forPrinter ~filename in | ||||||||||
if parseResult.invalid then begin | ||||||||||
backend.stringOfDiagnostics | ||||||||||
~source:parseResult.source | ||||||||||
~filename:parseResult.filename | ||||||||||
parseResult.diagnostics; | ||||||||||
if recover || not parseResult.invalid then | ||||||||||
if react then | ||||||||||
exit 1 | ||||||||||
else if recover then | ||||||||||
printEngine.printImplementation | ||||||||||
~width ~filename ~comments:parseResult.comments parseResult.parsetree | ||||||||||
else () | ||||||||||
end | ||||||||||
else | ||||||||||
let parsetree = | ||||||||||
if react then Reactjs_jsx_ppx.rewrite_implementation parseResult.parsetree else parseResult.parsetree | ||||||||||
in | ||||||||||
printEngine.printImplementation | ||||||||||
~width ~filename ~comments:parseResult.comments parseResult.parsetree | ||||||||||
~width ~filename ~comments:parseResult.comments parsetree | ||||||||||
with | ||||||||||
| Failure txt -> | ||||||||||
prerr_string txt; | ||||||||||
prerr_newline(); | ||||||||||
exit 1 | ||||||||||
| _ -> exit 1 | ||||||||||
[@@raises exit] | ||||||||||
[@@raises Invalid_argument, exit] | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What raises There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The existing react ppx uses that exception to note cases with unexpected situations. I can count at least 19 different places, e.g. Lines 351 to 354 in 23336db
|
||||||||||
end | ||||||||||
|
||||||||||
|
||||||||||
let [@raises exit] () = | ||||||||||
let [@raises Invalid_argument, exit] () = | ||||||||||
if not !Sys.interactive then begin | ||||||||||
ResClflags.parse (); | ||||||||||
match !ResClflags.files with | ||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
(* Interface to print source code from different languages to res. | ||
* Takes a filename called "input" and returns the corresponding formatted res syntax *) | ||
val print: [`ml | `res | `refmt of string (* path to refmt *)] -> input: string -> string | ||
val print: [`jsx | `ml | `res | `refmt of string (* path to refmt *)] -> input: string -> string |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// test React JSX file | ||
|
||
@react.component | ||
let make = (~msg) => { | ||
<div> {msg->React.string} </div> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we should make a separate folder for ppx tests: |
||
|
||
exports[`commentAtTop.react.res 1`] = ` | ||
"@bs.obj | ||
external makeProps: ( | ||
~msg: 'msg, | ||
~key: string=?, | ||
unit, | ||
) => { | ||
\\"msg\\": 'msg, | ||
// test React JSX file | ||
} = \\"\\" | ||
|
||
let make = | ||
(@warning(\\"-16\\") ~msg) => { | ||
ReactDOMRe.createDOMElementVariadic(\\"div\\", [{msg->React.string}]) | ||
} | ||
let make = { | ||
let \\\\\\"CommentAtTop.react\\" = (\\\\\\"Props\\": {\\"msg\\": 'msg}) => | ||
make(~msg=\\\\\\"Props\\"[\\"msg\\"]) | ||
\\\\\\"CommentAtTop.react\\" | ||
} | ||
" | ||
`; | ||
|
||
exports[`externalWithCustomName.react.res.fixme 1`] = `""`; | ||
|
||
exports[`innerModule.react.res 1`] = ` | ||
"module Bar = { | ||
@bs.obj | ||
external makeProps: ( | ||
~a: 'a, | ||
~b: 'b, | ||
~key: string=?, | ||
unit, | ||
) => {\\"a\\": 'a, \\"b\\": 'b} = \\"\\" | ||
let make = | ||
(@warning(\\"-16\\") ~a, @warning(\\"-16\\") ~b, _) => { | ||
Js.log(\\"This function should be named \`Test$Bar\`\\") | ||
ReactDOMRe.createDOMElementVariadic(\\"div\\", []) | ||
} | ||
let make = { | ||
let \\\\\\"InnerModule.react$Bar\\" = (\\\\\\"Props\\": {\\"a\\": 'a, \\"b\\": 'b}) => | ||
make(~b=\\\\\\"Props\\"[\\"b\\"], ~a=\\\\\\"Props\\"[\\"a\\"], ()) | ||
\\\\\\"InnerModule.react$Bar\\" | ||
} | ||
@bs.obj | ||
external componentProps: ( | ||
~a: 'a, | ||
~b: 'b, | ||
~key: string=?, | ||
unit, | ||
) => {\\"a\\": 'a, \\"b\\": 'b} = \\"\\" | ||
|
||
let component = | ||
(@warning(\\"-16\\") ~a, @warning(\\"-16\\") ~b, _) => { | ||
Js.log(\\"This function should be named \`Test$Bar$component\`\\") | ||
ReactDOMRe.createDOMElementVariadic(\\"div\\", []) | ||
} | ||
let component = { | ||
let \\\\\\"InnerModule.react$Bar$component\\" = (\\\\\\"Props\\": {\\"a\\": 'a, \\"b\\": 'b}) => | ||
component(~b=\\\\\\"Props\\"[\\"b\\"], ~a=\\\\\\"Props\\"[\\"a\\"], ()) | ||
\\\\\\"InnerModule.react$Bar$component\\" | ||
} | ||
} | ||
" | ||
`; | ||
|
||
exports[`topLevel.react.res 1`] = ` | ||
"@bs.obj | ||
external makeProps: ( | ||
~a: 'a, | ||
~b: 'b, | ||
~key: string=?, | ||
unit, | ||
) => {\\"a\\": 'a, \\"b\\": 'b} = \\"\\" | ||
let make = | ||
(@warning(\\"-16\\") ~a, @warning(\\"-16\\") ~b, _) => { | ||
Js.log(\\"This function should be named 'TopLevel.react'\\") | ||
ReactDOMRe.createDOMElementVariadic(\\"div\\", []) | ||
} | ||
let make = { | ||
let \\\\\\"TopLevel.react\\" = (\\\\\\"Props\\": {\\"a\\": 'a, \\"b\\": 'b}) => | ||
make(~b=\\\\\\"Props\\"[\\"b\\"], ~a=\\\\\\"Props\\"[\\"a\\"], ()) | ||
\\\\\\"TopLevel.react\\" | ||
} | ||
" | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// test React JSX file | ||
|
||
@react.component | ||
let make = (~msg) => { | ||
<div> {msg->React.string} </div> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
module Foo = { | ||
@react.component @bs.module("Foo") | ||
external component: (~a: int, ~b: string, _) => React.element = "component" | ||
} | ||
|
||
let t = <Foo.component a=1 b={"1"} /> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
module Bar = { | ||
@react.component | ||
let make = (~a, ~b, _) => { | ||
Js.log( | ||
"This function should be named `Test$Bar`", | ||
) | ||
<div /> | ||
} | ||
@react.component | ||
let component = (~a, ~b, _) => { | ||
Js.log( | ||
"This function should be named `Test$Bar$component`", | ||
) | ||
<div /> | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
runPrinter(__dirname, false, false) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
@react.component | ||
let make = (~a, ~b, _) => { | ||
Js.log("This function should be named 'TopLevel.react'") | ||
<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.
why does the ppx need a separate
build-ppx
rule? If it's a part "API_FILES", is this sufficient?