{"id":308,"date":"2016-06-10T17:53:28","date_gmt":"2016-06-10T21:53:28","guid":{"rendered":"http:\/\/ryepup.unwashedmeme.com\/blog\/?p=308"},"modified":"2016-06-10T17:53:46","modified_gmt":"2016-06-10T21:53:46","slug":"javascript-frontend-build-tooling","status":"publish","type":"post","link":"http:\/\/ryepup.unwashedmeme.com\/blog\/2016\/06\/10\/javascript-frontend-build-tooling\/","title":{"rendered":"Javascript frontend build tooling"},"content":{"rendered":"<p>Javascript is an essential tool for building great user interfaces these days, and has some really mature tools for getting a great development environment. One problem I&#8217;ve had is there are many different ways to hooks these tools together.<\/p>\n<p>After a lot of trial and error, I found a set that seems to work pretty well for me across different javascript frameworks.<\/p>\n<h2>Goals<\/h2>\n<ul>\n<li>write my app with multiple files, but deploy as one js file<\/li>\n<li>have a machine handle mechanical boilerplate like IIFEs, strict mode, etc<\/li>\n<li>automated tests (with code coverage), that run whenever I change a file<\/li>\n<li>static analysis of my code to catch problems<\/li>\n<li>local webserver to use with my app, that automatically reloads the page whenever I change a file<\/li>\n<li>use ecmascript 6 (ES6) or other &#8220;compiles down to js&#8221; systems (e.g. typescript, react)<\/li>\n<li>use other open source javascript libraries<\/li>\n<li>use a small number of tools so it&#8217;s easier to understand<\/li>\n<li>cross-platform development (sometimes I&#8217;m on windows, sometimes on linux)<\/li>\n<\/ul>\n<p>This feels like a pretty high bar, but javascript has some crazy capabilities, and it&#8217;d be a shame not to use them.<\/p>\n<h2>Solution<\/h2>\n<h3><a href=\"https:\/\/www.npmjs.com\">npm<\/a> for tasks and dependencies<\/h3>\n<p>I use <a href=\"https:\/\/www.npmjs.com\">npm<\/a> as a task runner and for dependencies. It also seems like everything published to <a href=\"http:\/\/bower.io\">bower<\/a> is also published to <a href=\"https:\/\/www.npmjs.com\">npm<\/a>, so I&#8217;m happy to skip other package managers. Just don&#8217;t look too closely in the <code>node_modules<\/code> folder, it&#8217;s madness in there.<\/p>\n<p>The <a href=\"https:\/\/docs.npmjs.com\/misc\/scripts\">npm scripts<\/a> are effective for defining tasks without needing to install things globally nor edit my <code>$PATH<\/code>. Any node-based CLI tools installed to the local <code>node_modules<\/code> folder via <code>npm install<\/code> are automatically on the <code>$PATH<\/code> when using <a href=\"https:\/\/docs.npmjs.com\/misc\/scripts\">npm scripts<\/a>. I&#8217;d rather spend a little disk space than manage the unintended consequences of shared global state. It also helps reduce setup time for other developers. We can get away with just <code>git clone &amp;&amp; npm install &amp;&amp; npm start<\/code>.<\/p>\n<p>I generally make a few custom tasks:<\/p>\n<ul>\n<li><code>npm start<\/code> &#8211; start a local webserver and spin up a bunch of filesystem watchers to run tests and refresh my browser<\/li>\n<li><code>npm run build<\/code> &#8211; create a final bundle for distribution<\/li>\n<li><code>npm run ci<\/code> &#8211; run tests and static analysis for continous integration bots<\/li>\n<\/ul>\n<p>I also tend to make subtasks, and chain them together using the limited syntax that is shared between <code>sh<\/code> and <code>cmd.exe<\/code>.<\/p>\n<h3><a href=\"https:\/\/github.com\/thlorenz\/exorcist\">exorcist<\/a> + <a href=\"http:\/\/browserify.org\/\">browserify<\/a> + plugins for compilation<\/h3>\n<p>I use <a href=\"http:\/\/browserify.org\/\">browserify<\/a> for an awful lot of things. It&#8217;s my compiler, translator, minifier, and bundler. I tend to think of it all as compilation, but that&#8217;s a simplification.<\/p>\n<p>The most basic feature is using <a href=\"https:\/\/nodejs.org\/docs\/latest\/api\/modules.html\">nodejs modules<\/a> to write my code in many files and specify the dependencies between them using <code>require<\/code> statements at the top of each file. This nicely mirrors imports in every other programming environment (e.g. <code>using<\/code> in C#, <code>import<\/code> in python, <code>require<\/code>, <code>require_once<\/code>, <code>include<\/code> in php, etc).<\/p>\n<p><a href=\"https:\/\/github.com\/thlorenz\/exorcist\">exorcist<\/a> creates a separate source map file from the browserify output. This is really useful for testing, since most browsers will load up the source map automatically and show you you&#8217;re actual source code in the debugger instead of whatever unreadable garbage that actually gets executed.<\/p>\n<p>Most of the <a href=\"http:\/\/browserify.org\/\">browserify<\/a> functionality comes through plugins:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/cheton\/browserify-css\">browserify-css<\/a> &#8211; include css in my bundle like <code>require('.\/foo.css')<\/code><\/li>\n<li><a href=\"https:\/\/github.com\/JohnPostlethwait\/stringify\">stringify<\/a> &#8211; include html in my bundle<\/li>\n<li><a href=\"https:\/\/github.com\/babel\/babelify\">babelify<\/a> &#8211; convert ES6 or react into plain old ES5 that the world can actually execute, using the <a href=\"https:\/\/babeljs.io\/\">babel<\/a> compiler\n<ul>\n<li><a href=\"http:\/\/babeljs.io\/docs\/plugins\/preset-es2015\/\">babel-preset-2015<\/a> &#8211; lets me use ES6<\/li>\n<li><a href=\"http:\/\/babeljs.io\/docs\/plugins\/preset-react\/\">babel-preset-react<\/a> &#8211; lets me use <a href=\"https:\/\/facebook.github.io\/react\/\">react<\/a>&#8216;s weird syntax<\/li>\n<\/ul>\n<\/li>\n<li><a href=\"https:\/\/github.com\/devongovett\/browserify-istanbul\">browserify-istanbul<\/a> &#8211; calculate code coverage during tests, using <a href=\"https:\/\/github.com\/gotwarlost\/istanbul\">istanbul<\/a>.<\/li>\n<li><a href=\"https:\/\/github.com\/hughsk\/uglifyify\">uglifyify<\/a> &#8211; compact my code using <a href=\"http:\/\/lisperator.net\/uglifyjs\/\">uglifyJS<\/a> to reduce load times<\/li>\n<li><a href=\"https:\/\/github.com\/hughsk\/envify\">envify<\/a> &#8211; bake build-time environment variables into my code. Mostly for including version information (git tag, commit hash, build number, etc)<\/li>\n<li><a href=\"https:\/\/github.com\/omsmith\/browserify-ngannotate\">browserify-ngannotate<\/a> &#8211; automate some annoying boilerplate to make <a href=\"https:\/\/angularjs.org\/\">angularjs<\/a>&#8216;s dependency injection work with minified code<\/li>\n<\/ul>\n<h3><a href=\"http:\/\/eslint.org\/\">eslint<\/a> + <a href=\"https:\/\/karma-runner.github.io\">karma<\/a> + <a href=\"http:\/\/jasmine.github.io\/\">jasmine<\/a> + <a href=\"http:\/\/phantomjs.org\/\">phantomjs<\/a> + plugins for testing<\/h3>\n<p>This is probably the first bit of javascript build tooling I ever figured out, and I&#8217;ve been using it every since. <a href=\"https:\/\/karma-runner.github.io\">karma<\/a> has a &#8220;watch&#8221; option where it will keep re-running tests as code changes.<\/p>\n<ul>\n<li><a href=\"http:\/\/jasmine.github.io\/\">jasmine<\/a> &#8211; library for writing tests<\/li>\n<li><a href=\"http:\/\/phantomjs.org\/\">phantomjs<\/a> &#8211; headless browser to run tests on the command line<\/li>\n<li><a href=\"https:\/\/karma-runner.github.io\">karma<\/a> + plugins &#8211; test runner, executes tests and reports the results\n<ul>\n<li><a href=\"https:\/\/github.com\/nikku\/karma-browserify\">karma-browserify<\/a> &#8211; pass tests through <a href=\"http:\/\/browserify.org\/\">browserify<\/a> so I can use ES6, <code>require<\/code>, and calcuate code coverage<\/li>\n<li><a href=\"https:\/\/github.com\/karma-runner\/karma-coverage\">karma-coverage<\/a> &#8211; write code coverage reports<\/li>\n<li><a href=\"https:\/\/github.com\/karma-runner\/karma-jasmine\">karma-jasmine<\/a> &#8211; run jasmine tests<\/li>\n<li><a href=\"https:\/\/github.com\/karma-runner\/karma-phantomjs-launcher\">karma-phantomjs-launcher<\/a> &#8211; run tests in phantomjs<\/li>\n<li><a href=\"https:\/\/github.com\/karma-runner\/karma-junit-reporter\">karma-junit-reporter<\/a> &#8211; write tests results in junit-compatable xml for CI tools like Jenkins<\/li>\n<\/ul>\n<\/li>\n<li><a href=\"http:\/\/eslint.org\/\">eslint<\/a> &#8211; static analyzer to let me know when I&#8217;m writing obviously bad code<\/li>\n<\/ul>\n<h3><a href=\"https:\/\/github.com\/substack\/watchify\">watchify<\/a> + <a href=\"http:\/\/tapiov.net\/live-server\/\">live-server<\/a> + <a href=\"https:\/\/github.com\/mysticatea\/npm-run-all\">npm-run-all<\/a> for active development<\/h3>\n<p>The &#8220;reload-as-you-edit&#8221; features are part of what make javascript such a productive environment for user interfaces, especially when you have multiple monitors. I love having a browser \/ terminal on one monitor showing the UI and test output, and then my editor on another monitor. It&#8217;s really great to be able to simply save a file and glance over and see if my tests pass or the UI looks alright.<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/substack\/watchify\">watchify<\/a> &#8211; sister project to <a href=\"http:\/\/browserify.org\/\">browserify<\/a>, updates a bundle efficiently as you change files<\/li>\n<li><a href=\"http:\/\/tapiov.net\/live-server\/\">live-server<\/a> &#8211; HTTP and <a href=\"http:\/\/livereload.com\/\">live-reload<\/a> server. Watches the filesystem and tells browser windows to refresh themselves when things change<\/li>\n<li><a href=\"https:\/\/github.com\/mysticatea\/npm-run-all\">npm-run-all<\/a> &#8211; cross-platform helper to run several <a href=\"https:\/\/docs.npmjs.com\/misc\/scripts\">npm scripts<\/a> in parallel; I have been using <a href=\"https:\/\/github.com\/keithamus\/parallelshell\">parallelshell<\/a>, but that&#8217;s been deprecated<\/li>\n<\/ul>\n<h2>Conclusion<\/h2>\n<p>All in all that&#8217;s <em>29 direct dependencies<\/em>, and probably hundreds or thousands of indirect dependencies. That still feels crazy to me, but I think that&#8217;s mostly because I have to hook it all up myself. Something like eclipse or visual studio has a ton of moving parts, but it&#8217;s one installer so I tend not to think about it. You can see some examples on some of my hobby projects: <a href=\"https:\/\/github.com\/ryepup\/c4-lab\/\">c4-lab<\/a>, <a href=\"https:\/\/github.com\/ryepup\/chore-cat\/\">chore-cat<\/a>, and <a href=\"https:\/\/github.com\/ryepup\/kpi-me\/\">kpi-me<\/a>.<\/p>\n<p>There&#8217;s room for improvement (e.g. debugging tests, speed), but this setup has worked out pretty well for me. In my <a href=\"http:\/\/ryepup.unwashedmeme.com\/blog\/2015\/07\/03\/angularjs-with-only-npm-and-browserify\/\">last post<\/a> I decided against it, but then found some more tools that really brought it all together.<\/p>\n<p>Most of these kinds of setups include <a href=\"http:\/\/gruntjs.com\/\">grunt<\/a> or <a href=\"http:\/\/gulpjs.com\/\">gulp<\/a>, but I haven&#8217;t really needed them. Between <a href=\"https:\/\/docs.npmjs.com\/misc\/scripts\">npm scripts<\/a>, <a href=\"http:\/\/browserify.org\/\">browserify<\/a>, endless plugins, and shell redirection I can accomplish the same results with one less dependency and config file. I feel like if I did adopt <a href=\"http:\/\/gruntjs.com\/\">grunt<\/a> or <a href=\"http:\/\/gulpjs.com\/\">gulp<\/a>, I&#8217;d basically have the same setup, but with their plugins instead of <a href=\"http:\/\/browserify.org\/\">browserify<\/a> plugins.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Javascript is an essential tool for building great user interfaces these days, and has some really mature tools for getting a great development environment. One problem I&#8217;ve had is there are many different ways to hooks these tools together. After a lot of trial and error, I found a set that seems to work pretty [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[54,38,53],"tags":[],"class_list":["post-308","post","type-post","status-publish","format-standard","hentry","category-browserify","category-javascript","category-npm"],"_links":{"self":[{"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/posts\/308","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/comments?post=308"}],"version-history":[{"count":7,"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/posts\/308\/revisions"}],"predecessor-version":[{"id":315,"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/posts\/308\/revisions\/315"}],"wp:attachment":[{"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/media?parent=308"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/categories?post=308"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/tags?post=308"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}