Make maintainable workarounds with codegen 💥

October 9th, 2017 — 3 min read

by Alexandre Godreau
by Alexandre Godreau
No translations available.Add translation

This last week Andrew Blick filed an issue on glamorous indicating that the UMD build of glamorous doesn't work with React 16. The problem is that glamorous does a lazy-require for the prop-types module and apparently when building the UMD bundle, rollup can't handle the CommonJS require and make the UMD pass it in from the global object.

You can see this problem in the glamorous@4.9.5 UMD file. The fact that PropTypes isn't included at the top with global.Glamor and global.React is an indication of the issue. If you look further down, you'll also see: PropTypes = require('prop-types')and it's that require statement which is the problem. It's not transpiled due to the aforementioned issue.

This is the part where we get frustrated at our tools and their authors right?

a bird saying nope

Definitely not! What we do is we file an issue with a reproduction and suggest a solution! Then we implement the solution when the maintainer says they're good with the solution! Woo!

The big bird saying yep

In my case, I had no idea where to start and I don't have time to look into it any further (I did, just have my fourth kid less than two weeks ago afterall). So I filed the issue with the reproduction in part to verify that the issue was Rollup. Then I started thinking about a good workaround.

Here's where codegen comes in. babel-plugin-codegen is a babel plugin I wrote inspired by my other plugin babel-plugin-preval. Both of these ship with a babel-macro and they each have a companion package to make using that easier.

So I installed codegen.macro and changed from a simple PropTypes = require('prop-types') to this:

PropTypes = codegen`  
  if (process.env.BUILD_FORMAT === 'umd') {  
    module.exports = "(typeof window !== 'undefined' ? window : global).PropTypes"  
  } else {  
    module.exports = "require('prop-types')"  
  }  
`

What codegen does is it will take the string of code you provide and run it like a regular module, then it takes the string of code that you export and will replace itself with that string of code. Because this happens at build-time, it's a great way to do these kinds of optimizations.

Note, generally it's advisable to avoid putting too much code in a string because you lose a lot of benefits like syntax highlighting and lintability, so if it's a fair amount of code in there, you can pull it out into another file and do:

PropTypes = codegen`module.exports = require('./prop-types-workaround')`

Anyway, these changes we've made will leave all the other builds as it was before, but for the UMD build, it'll pull PropTypes from the global.

What I love about this kind of workaround is that because it's using a babel-macrothe magic involved is pretty optimized and localized. So it's much more straightforward than other workarounds would be and literally took less than 10 minutes.

I hope this is helpful to you! Good luck to you all! 👍

Kent C. Dodds
Written by Kent C. Dodds

Kent C. Dodds is a JavaScript software engineer and teacher. Kent's taught hundreds of thousands of people how to make the world a better place with quality software development tools and practices. He lives with his wife and four kids in Utah.

Learn more about Kent

If you found this article helpful.

You will love these ones as well.