read
Updated July 19th, 2015
Part 2

Complex Webpack Dependencies

If you're unfamiliar with Webpack, you might want to checkout Part 1 of this Article on setting up a project with Webpack. This demo will continue from the previous article's code-base.

In this article we'll look at loading different types of dependencies: scripts, styles, fonts, etc. using Webpack. We'll also compare and contrast loading modules from NPM & Bower.

Webpack & Angular & Lumx

LumX

LumX is a great Material Design based CSS Framework built for Angular.

I would argue LumX looks better in both style and code-style when compared to angular-material, though Angular Material has improved a lot. Again, that's largely a matter of opinion.

LumX will make a good example as it comes with a lot of different types of dependencies: scripts, styles, fonts. Let's see how Webpack can combine them into a single bundle.js file.

LumX Setup

In an earlier version of this post, we loaded LumX with Bower. However, LumX is now available via NPM using node-lumx.

npm  install --save node-lumx  

We should let Angular know we're going to be using Lumx.

/app/index.js

module.exports = angular.module('app', [  
  'lumx'
]);

LumX comes with a party of dependencies. Look in app/bower_components and you'll see them all.

node_modules  
├── angular
├── bourbon            // Sass mixins
├── jquery
├── node-lumx                
├── node-mdi                // Material Design Icons
├── moment             // time
└── velocity-animate            // jQuery animations

NPM vs. Bower

It's true, Webpack can handle both CommonJS & AMD (asynchronous) modules. But Webpack has a preference: CommonJS.

Let's compare NPM and Bower for a minute.

NPM has nested dependencies, meaning that you can have different packages all loading different versions of lodash at the same time. It's very specific.

Bower, on the other hand, flattens dependencies. As such, it is often used on the front-end because, well, obviously, it isn't ideal to have 3 versions of jQuery loaded every time you visit a webpage.

Anyway, that information probably wasn't very helpful, but it's nice to know. To the point:

NPM and Bower aren't the same, and Webpack prefers NPM (CommonJS). According to the Docs:

In many cases modules from npm are better than the same module from bower. Bower mostly contain only concatenated/bundled files which are:

More difficult to handle for webpack More difficult to optimize for webpack Sometimes only useable without a module system So prefer to use the CommonJs-style module and let webpack build it. Source.

Luckily most packages have NPM & Bower equivalents, and we can rely on the NPM version 'node-lumx'.

Node LumX comes with a series of dependencies.

                               /* NPM Package Name */
"dependencies": {               ====================
    "jquery": "~2.1.4",            // jquery
    "angular": "~1.3.11",        // angular
    "velocity": "~1.2.1",        // velocity-animate
    "moment": "~2.9.0",            // moment
    "bourbon": "~4.1.1",// bourbon
    "node-mdi": "1.0.0"            // material design icons
  }

You may have to npm install --save jquery as well, node-lumx seems to be missing it.

Require(NPM_Module)

It should still work. Now let's load some primary NPM dependencies in a file we'll call vendor.js.

/app/core/vendor.js

module.exports = function () {  
    /* must be in order */
  require('jquery');
  require('velocity-animate');
  require('angular');
  require('node-lumx');
};

LumX seeks a few dependencies as globals, so we'll have to change this a little.

global attaches a value to the global context, likely the browser window.

/app/core/vendor.js

module.exports = function () {  
  global.$ = global.jQuery = require('jquery');   
    // $ for Lumx, jQuery for velocity
  require('velocity-animate');
  require('angular');
  global.moment = require('moment'); 
    // LumX uses a global 'moment'
  require('node-lumx');
  };

Require(styles)

Lumx styles can be required using a path.

/app/core/vendor.js

require('../index.scss');

Style sheets can be loaded using require('.path/to/_lumx.scss'), as in the previous article but due to the cascading nature of stylesheets, it's likely better to keep them in a root index.scss file. Simply import the Lumx styles.

/app/index.scss

@import '../node_modules/node-lumx/dist/scss/lumx';

The leading _ can be dropped on Sass imports. It indicates that the file is a partial, and not the root Sass file.

Require(Fonts & Icons)

Due to an issue with node-mdi, we'll instead use an earlier version of the mdi package that LumX relies on. Thanks @ Jeff Lu for the fix.

npm install --save mdi@1.0.8-beta

We'll need two more loaders for fonts & icons. Install the file-loader & css-loader.

npm install -D file-loader css-loader

Add the loaders to your webpack.config file and tell it to grab any css or any file that looks like a font.

/webpack.config.js

{
     test: /\.css$/,
     loader: "style!css"
},{
     test: /\.(woff|woff2|ttf|eot|svg)(\?]?.*)?$/,
     loader : 'file-loader?name=res/[name].[ext]?[hash]'
}

Load up the Material Design Icons. The CSS file will be processed by the CSS-loader, and will in turn load the fonts using the file-loader.

/app/core/vendor.js

/* Styles */
 require('../index.scss');
 require('../../node_modules/mdi/css/materialdesignicons.min.css');

Create a test to see if icons and fonts are loading in index.html.

/app/index.html

<p class='fs-headline'>Icon Test: <i class="mdi mdi-twitter"></i> @Sh_McK</p>  

Conclusion

We now have our LumX dependencies running: scripts, styles, fonts & icons, oh my! We saw how Webpack can load different file formats, quite easily when they're all node modules. Thanks NPM!

Checkout Github for the full codebase.

In Part 3 we'll finally be able to take advantage of using Webpack with Angular for creating incredibly modular code.

Blog Logo

Shawn McKay

Published

Image

ShMcK

JavaScript Web Dev

Back Home