Create a new file
Add it to index.html
and via a script
tag, with a filepath relative to the index.html
Add the karma config, with a filepath relative to the karma config

Doesn’t seem like a big deal, but sticking it in an existing file is frictionless, and that’s probably how we ended up with so many files with ~1000 lines of code.

Having several Angular services or controllers that are each a few hundred lines long all in a controllers.js
/ services.js
/ directives.js
file makes maintenence work very painful. You have to keep scrolling up and down to reference other things in the same service/controller/directive, but scroll too far and you’ll end up in a separate service/controller/directive that has nothing to do with what you’re working on.

Things that weren’t Angular modules (mostly functions) also couldn’t be modularlized unless they were stuck into either the global variable or into an Angular service

Development Efficiency

Unable to leverage libraries from npm.

Many packages are philosophically against making themselves available on bower, e.g. invariant, which has 4 million monthly downloads

Performance
The legacy build system generated one 5MB bundle, 1.5MB of that was a library used on two pages that most users would be unlikely to hit. Being able to split that chunk out and defer loading it until it was needed would save quite a bit of unnecessary transfer and parsing.
Developer Happiness

“Arrow functions / destrucuring / spread operators make us happy” is rarely sufficient justification for expending large amounts of time.

It is a great side effect though :wink:

Steps
<script> tag to ‘require’s

One of the first things to do is to change all of the script tag imports <script src="*"></script>
into require
s. We had 169 of these script tags so we definitely didn’t want to do this manually.

regexer
came in handy. Using a pattern of <script src="(.+)"><\/script>
and a replace string of require('$1');
, our tags were easily moved from our html file into our entry file for webpack.

Fix what breaks

Many of the vendor files we’re using are expecting its dependencies to be in the global scope, and don’t export anything since they’re also expecting themselves to be in the global scope. We also had a vendor lib that ommited variable declarations and uses implied globals, which now cause a ReferenceError: x is not defined
error.

Luckily, Webpack already has solutions for this in the form of expose-loader
, imports-loader
, and exports-loader
.

imports?this=>window&outerRadius=>undefined
The module is expecting itself to be in the global scope, so we have to import this=>window
, setting the value of this
to window
for this module.
It’s also assigning to a variable outerRadius
without declaring it, so we need to declare the variable first and assign it as undefined
.
exports?donutChooserD3
The module does not export anything, this yanks donutChooserD3
out from the scope of the module and sets it as the exported value
expose?donutChooserD3!
This sets the variable donutChooserD3
onto the global scope since there are other modules that are expecting it to be there.

More info on shimming modules with webpack

There was one especially egregious library that had 104 objects/functions that needed to be exported into the global. To get that list of the 104 names we needed to add to the “imports” string, we pasted the contents of that library into the console for an about:blank page and ran this:

For maintenence reasons, we didn’t want to check this generated string in, and since require strings couldn’t be dynamic, in our code we
require in require(process.env.GENOME_VIEWER_REQUIRE_STRING)
), and then
use Webpack’s DefinePlugin to replace process.env.GENOME_VIEWER_REQUIRE_STRING
with the value exported from shims/genome-viewer.js
.

Code splitting
Splitting out a library into a separate deferred bundle is as simple as
wrapping require.ensure([], require => { /**/ })
at the very top of directive link functions where the library is used. Since we’re using an arrow function, context still remains the same and the code being wrapped shouldn’t need to be touched, the directive just takes a bit longer to initialize
Next steps
Migrate from lodash@3 to lodash@4.

It’s unfortunate that there doesn’t seem to be a completely painless way to upgrade from v3 to v4.

lodash-migrate
, lodash-codemods
, and eslint-plugin-lodash
all help, but still require some level of human effort that’s relatively higher than dropping in a library.

Still, the benefits of having access to some of the new utility functions without having to separately install each package outweigh the cost of running and potentially cleaning up after a codemod.

Migrate away from Bower.

We’re currently using both Bower and npm, with new dependencies using npm.

We should be able to migrate most of the packages still using bower without issue, but there are a few libraries whose versions may be so old that they aren’t available on npm, or they may have been abandoned. For those we may either internalize the dependencies (somewhat icky path of least resistence), upgrade to the versions that are available on npm (will need to test for regressions), or publish to npm ourselves (namespaced, of course).

In the process of our build tool upgrade, we’ve moved from Ruby Sass to node-sass and removed the dependency on Ruby.

Ruby was something that tended to snag developers on Windows machines during rampup, and we had hoped that node-sass would alleviate that problem. It turns out node-sass can be troublesome to setup on Windows as well, with binaries sometimes not downloading correctly, and the recommended version of Visual Studio (2013) no longer being available
.

A pure node solution would be ideal. However, although there is a pure JS implementation of Sass in sass.js
(libsass run through emscripten), it does warn that node-sass is “considerably faster”. The tradeoff in compilation speed would not be worth it to switch to sass.js for everyone, but perhaps we could look into using sassjs-loader
for windows environments only, or make it an opt-in.