Using Webpack with legacy Angular SPAs: Exposing global variables to child modules.

This is short and sweet.

Problem: I have a legacy Angular app that I would like to modularize and build with Webpack. It is written in Angular and I expect a global variable called myappto exist everywhere in the app.

Who it benefits: People attempting to migrate legacy SPAs into a more modularized Webpack style build environment, or simply test out Webpack with more traditional SPA with no modules.

The App

Imagine an Angular application declared as follows

// client/app/app.js
var myApp = angular.module('app', ['ui.router']);

Then in controller and directive files we see code like this:

// client/app/landing.js
myApp.controller('LandingCtrl', ...
// client/app/idiot/drumpf-directive.js
myApp.directive('DrumpfDirective', ...

When you first use Webpack you will encounter a problem because Globals disappear with Modularization and every file / component / module is wrapped in an IIFE and globals are not automatically injected. Globals are no longer automatically exposed to your SPA assets.

The Crap Solution

Expose the myApp variable on module.exports and then add require statements throughout the app. Just awful and labor intensive with large legacy apps when all you want to do is try things out.

The Alternative Solution — Use Shimming
Step 1 : Expose the global variable using the expose-loader

In the webpack entrypoint file you will have an entry like:


Change it to:


(Install exports-loader)

This says use the expose-loader to expose exports from ./app/app.js globally as name entry.

However when we get to the Angular Controller this variable is not available because we have only exposed it but not yet injected it into the controller module.

Step 2 — Injecting the globals

To inject the globals we use the imports-loader

We’d like to inject these globals into the two Angular controller modules.


Again this says make a variable name myApp available in each of those controllers and make it equal to entry.myApp.

Why entry.myApp, well…

app.js exposes myApp as myApp.

var myApp = angular.module('app', ['ui.router']);
module.exports = {
myApp: myApp

expose-loader sets the module app.js to entry = { myApp: myApp }


Therefore when we inject it into the dependent modules we use:


Further Enhancements

In larger SPAs you would automate the imports-loader


By adding some rules to you webpack.config.js. This is out of scope here but you would simply add a rule which does this injection on all js files within a named directory.


This problem stumped me for a while so passing the details on here at my personal github thinkjones.

UPDATE Feb 21st 2017: Follow up Article. Using Webpack with legacy Angular SPAs: Automating imports using require.context.