How to cope with “broken modules” in webpack

Johannes Ewald
webpack
Published in
4 min readSep 16, 2016

When you’re writing a web application, you will always come to the point where you want to include some third-party code.

Then, you go on npm, search for the module, install it and everything works out-of-the-box.

This… will hopefully be the case when everyone is using the same module system and we have solved the puzzle how to distribute re-usable web components that bring their own HTML, CSS and images. Until then, we will need to cope with the current mess.

webpack 1 already supported two of the most common module formats out-of-the-box: CommonJS and AMD. webpack 2 will also understand the official ES2015 module syntax which enables webpack to remove unused code more efficiently. But there’s still a lot of code that has been written with no module system at all or in a pretty unusual style.

In this post, I’m going to give you some tips how I usually try to include third-party scripts. It was originally posted on Stack Overflow, but since I still get positive feedback, I think it might also be valuable for you.

Tip 1: Prefer unminified source over dist

Most modules link the pre-bundled dist version in the main field of their package.json. While this is useful for developers without a module bundler, for webpack users it is better to alias the source version. This avoids the usual module system overhead of pre-bundled files. However, in most cases the dist version works just fine as well.

Prefers jQuery’s source files over dist.

Tip 2: Use the ProvidePlugin to inject implicit globals

Most legacy modules rely on the presence of specific globals, like jQuery plugins do on $ or jQuery. In this scenario, you can configure webpack to prepend var $ = require(“jquery”) everytime it encounters the global $ identifier:

Everytime webpack encounters the given identifiers, the referenced module will be imported under that namespace into the local module.

The ProvidePlugin is a good way to “teach” legacy dependencies, that they should import everything they need instead of relying on the presence of a global variable.

Tip 3: Use the imports-loader to configure `this`

Some legacy modules rely on this being the window object. This becomes a problem when the module is executed with webpack where this equals module.exports (in the style of CommonJS). In this case, you can override this with the imports-loader.

Since all loaders are not included in webpack core, you need to run npm i imports-loader — save-dev first and then:

`this` now equals `window` inside the misbehaving module.

The imports-loader puts code before the actual module. It can be used to bootstrap it if there are any problems. For instance, it can also be used to manually inject variables of all kinds. But most of the time the ProvidePlugin is more useful when it comes to implicit globals.

Tip 4: Fix a module style with the imports-loader and exports-loader

Some dependencies use a module style in an unusual way that may conflict with webpack. In this case it may help to fool the third-party code that there is no module system at all. Most modules will then fall back to a global variable which you can export using the exports-loader. We are working on this to make it easier in future webpack versions.

Prepends var define = false and appends module.exports = someGlobalVariable.

Tip 5: Use the script-loader to import scripts globally

If you don’t care about global variables and just want legacy scripts to work, you can also use the script-loader. It executes the module in a global context, just as if you had included them via <script> tag.

In this case, require() will not return something meaningful. Just access the global variable afterwards:

After calling require(), the global variable will be available. But as you can see: code editors don’t like global variables. And I don’t like them, too. So, please don’t make me sad.

Tip 6: Use `noParse` to include large dists

When there is no source version of the module and you want to include the dist, you can flag this file as noParse. In this case, webpack will just include the file without parsing it, which can be used to improve the build time.

But be careful: This also means that any feature requiring the AST, like the ProvidePlugin, will not work inside this file. webpack just takes the source code without looking at it.

--

--