Auto-load Angular Modules, Directives, Services, Controllers with Webpack!

When working on an Angular project, sometimes you just want to automatically load all the directives or all the services in a bunch of directories. Webpack allows you to create custom context to do that. First, let's see how you can load a bunch of custom angular modules that are in a modules whose files look something like this:

modules/cart/cart.js

const cartModule = angular.module('am.cart', []);  
cartModule.service();  
//...
//..
export default cartModule.name;  

Then in the entry file of your Webpack, you can create a custom context, load all the files, and append to the list of modules:

entry.js

//..
//..
const myModuleNames = [];  
const loadModules = require.context('./modules', true, /^((?![\\/]test[\\/]).)*\.js$/);  
loadModules.keys().forEach(function (key) {  
  myModuleNames.push(loadModules(key));
});

const myModule = angular.module('app', [  
  'some-module1',
  'some-module2',
  'some-module3'
  ].concat(myModuleNames));
//..
//..

The key here is the second line:

var loadModules = require.context('./modules', true, /^((?![\\/]test[\\/]).)*\.js$/);  

This creates a custom context and list all the files in the ./modules directory, and all the subdirectores that ends with the .js extension, except those that are in the test folder. For example, it will match all the following paths:

modules/someFolder/myModule.js  
modules/anotherFolder/subFolder/betterModule.js  

But it will not match the following:

modules/someFolder/test/root-ctrl.js  
modules/anotherFolder/test/another-ctrl.js  

Loading directives, services, controllers, ...

Now let's say that you have a single Angular module, and you just want to add your directives, controllers, etc on that module. Without a custom context you might end up with something like this:

directives/index.js

export default ngModule => {

  require('./dir1')(ngModule);
  require('./dir2')(ngModule);
  require('./dir3')(ngModule);

};

Assuming that you have all the directives in a directives folder and they follow a pattern like this:

export default ngModule => {  
  ngModule.directive('dirx', function () {});
};

Then you can simply load them in your entry file using a custom context:

/* create app's module */
const ngModule = angular.module('myModule', []);

/* helper to get the list of files */
function requireAll(requireContext) {  
  return requireContext.keys().map(requireContext);
}

/* create a context list */
const contextList = [  
  require.context('./directives', true, /^((?![\\/]test[\\/]).)*\.js$/)
];

/* make a list containing the result of requiring each file
defined by the context list */  
const allDeps = contextList.reduce((deps, context) => (  
  deps.concat(requireAll(context))
), []);

/* call each function and pass in the angular module
do add to the module */  
allDeps.forEach(dep => { dep(ngModule); });  

The custom context here is the same as the context object that we used to load modules, except it is looking at the directives folder. Now, if you want to add other components, all you have to do is to add to the contextList array:

const contextList = [  
  require.context('./directives', true, /^((?![\\/]test[\\/]).)*\.js$/),
  require.context('./services', true, /^((?![\\/]test[\\/]).)*\.js$/),
  require.context('./controllers', true, /^((?![\\/]test[\\/]).)*\.js$/)
];

And that's it! you won't have to ever worry about what happens if you add or rename a file, it will just automatically work.