Before v11, I used reason
+ refmt
to print generated code to debug PPXs. Since Reason support is dropped, is there a way to debug generated code somehow?
AFACIT there’s no way to print generated code from rescript sources, so I came up with the following workflow.
Context
I have a few ppx’s to update to v11. I didn’t touch them for years, so I’m rusty on both - ppx themselves and OCaml in general.
Workflow
ReScript ships with bsc
binary that can do a lot of stuff. Don’t read its pubic --help
, go directly to its source code and inspect all the flags.
One of the things that bsc
can do is to print a readable representation of AST with the -dparsetree
flag. So when you run:
node_modules/rescript/bsc -dparsetree src/playground.res
It would give you an AST representation of what’s inside src/playground.res
.
This is how I inspect how new things, such as uncurried functions, are currently represented in ReScript’s AST.
To inspect an AST generated by ppx, I pass ppx binary via -ppx
flag:
node_modules/rescript/bsc \
-ppx _build/default/ppx/bin/bin.exe \
-dparsetree \
src/playground.res
With this, I can write the resulting code by hand and print its AST. Then print what’s generated by ppx. Put them side by side and see what’s missing/wrong in the ppx’ed version → then fix it in ppx source.
Another issue is that I don’t remember what the resulting code looks like So, in the project workspace, I added an older version of the ReScript compiler that supports Reason. It ships with refmt
which can print generated by ppx code from the Reason source.
cat path/to/ReasonModule.re \
| <PATH_TO_RESCRIPT_V9>/bsrefmt --parse re --print binary \
| _build/default/ppx/bin/bin.exe /dev/stdin /dev/stdout \
| <PATH_TO_RESCRIPT_V9>/bsrefmt --parse binary --interface false
I can take this Reason code (thankfully, I have lots of test cases written in Reason), convert it to ReScript, and then print its AST as explained above.
It’s also possible to use this approach for automated testing (to validate that compilation works, errors snapshots, etc.). The test case succeeds if bsc
exits with 0
. But to make it work, bsc
needs dependencies available. You can pass them with -I
flag.
node_modules/rescript/bsc \
-I ./node_modules/@rescript/react/lib/ocaml \
-I ./node_modules/my-lib/lib/ocaml \
-ppx _build/default/ppx/bin/bin.exe \
-w "+A" \ # enable all warnings
-warn-error "+A" \ # warnings would be treated like errors
-bs-cmi-only \. # no need to produce js, just make sure it compiles
src/playground.res
bsb
had -install
flag that builds these interfaces, but it doesn’t exist in rescript
. The easiest way to build these interfaces is to compile some project in the workspace that has these dependencies.
Can’t say I’m having fun here haha, but it should get the job done.
This is super useful! Thanks for all the pointers.
Yeah great write up @alex.fedoseev ! Ideally the rescript
bin should also be able to print the PPXed code. No idea if that’d be hard to implement though.