Feature request: add a babel plugin macro for automatic display name for react dev tools and jest snapshots
So I'd like to request adding a babel plugin macro to automatically set the display name of the component. Optionally you it can also add a data attribute to include the module name in the rendered dom for easy debugging.
The display name really improves the experience with react dev tools, but also enables shallow rendered snapshots for jest/enzyme etc. You can set the display name manually for each, but its very redundant and won't be changed on a refactor. This is exactly how styled components works, which is where the inspiration came from. The babel plugin macro babel plugin is already included in scaffolding tools like create-react-app by default, so you just need to change the import name start using it. You can also add configuration in if you'd like it to avoid the extra output for production builds.
I've already created the macro here: https://www.npmjs.com/package/classed.macro but it would be nice to not have an extra dependency. Keeping it in the same library also simplifies type exports.
It doesn't add to the build output and is only run if users change their imports from "classed-components" to "classed-components/macro". If you'd like I can create the PR to add the macro and associated documentation. Should be pretty simple as its already written and being used in some of my personal projects. Jest tests included.
Hi! Thanks for raising this issue.
I would be glad to accept your PR about that.
Could you do it in typescript to match the project?
Do you know if we can test the behavior of a babel macro?
Also we could directly add a babel plugin, in addition of the macro. If you want to do it, feel free.
Thanks, Mathieu.
Actually I would prefer, like styled components does, create a babel plugin, and use it in the macro. It should be the same amount of work, and you can directly take and adapt their code.
Hi @mathieutu . There is testing, I used the testing utility and instructions from the creator of babel-plugin-macros.
Currently there aren't typings but it shouldn't be too hard to add. However because it's a code mod it only runs during the build phase and therefore needs to target commonjs. So you'd need a separate call to tsc with its own tsconfig file with a new target output. Right now it doesn't need a build step as its already written in commonjs/javascript. I don't mind adding the typings, just want to double check that's the way you'd prefer it. One less build step vs having those excellent typings. Honestly having worked on the mod, the typings (while helpful) really only help a little. Knowing which to use is actually the bigger challenge. The docs on it aren't obvious so its more trial and error, looking at output from the snapshots. You can't really debug it and the end product is usually not that much code. Anyways just let me know.
I would however strongly advise against making a babel plugin for the following reasons:
- A babel-plugin-macro is easier to code, requires less configuration, and no additional dependencies. This is because most scaffolding tools (create-react-app, next.js etc) already have the
babel-plugin-macrosbabel plugin as a dependency and have already configured the default babel configuration to include it. It's basically one less step and one less dependency. Now all you have to do is rename your imports, and only configure the macro if you don't like the defaults or want environment specific configuration. - Calling a babel plugin this way would be redoing the same work. All babel plugins run on the complete AST of every file.
babel-plugin-macrosalready does this for you. You end up with only the references to any macro imports and references of everywhere that import is being used in the file. So having ababel-plugin-macrosmacro run another babel plugin is only efficient if that helps filter files for you, but you still need the full file AST. However if you only need the references thatbabel-plugin-macrosprovides you, you just end up redoing the the same work. It's both more complicated and redundant.
So why does styled components do it?
- First
babel-plugin-macrosis new, the old plugin was written first. They use the macro because its more convenient (zero config, zero dependencies, just change the import name), but then call the plugin that was already written. Not only does this consolidate code, for historical reasons they can't really get rid of the original plugin. They need to keep maintaining it to not break older projects. - Secondly their plugin does a TON of work. Display name, debug attribute, dead code elimination, minification, template string transpilation, SSR unique ids (because their class hashes aren't deterministic). Some of these do require traversing the entire file AST. The features I've added don't.
This would most definitely require more code to be written that would essentially accomplish the same task. Also you don't actually need a babel plugin to traverse the file's full AST. You have access to the same babel utilities inside the macro, so you could still add in similar features in the future with just the macro. For efficiency reasons you want to avoid traversing the full file AST until you actually need to though.
I can add typings if you'd like, but I disagree with making a normal babel plugin. You don't have backwards compatibility issues, and their plugin has way too much added in to pull from it without extra work. I can PR the macro and you can take it from there, but honestly I'm having trouble seeing any benefit to doing it that way. Hopefully what I've outlined above adds some clarity as to why.
Actually looks like module is already set to commonjs so probably don't need a separate tsc call/config. Types should be very easy to add, will definitely add them.
Hi, Firstly thank you so much for these so detailed comments.
My idea of a plugin was to avoid asking the devs to change manually their import (I'm personally not a fan of macro because of that), but you definitely made your point, and teach me by the way, so thanks!
I can't remember why I chose commonjs at the time, but as it's working like that, so let's keep it this way, and take the opportunity to add type on the macro.
Can I let you do the PR?
Thanks.
Just another thing: maybe could we add the data attribute only for development purpose by default and not in production? This can avoid any "code leaks".
I think that's a good idea. I'll change the data attribute configuration to be off by default or try to see if I can change it's default to only run in dev mode. After i change that and the typings I'll submit the PR
@rsimp Any updates on that?
Thx.