{"id":303,"date":"2015-07-03T15:37:44","date_gmt":"2015-07-03T19:37:44","guid":{"rendered":"http:\/\/ryepup.unwashedmeme.com\/blog\/?p=303"},"modified":"2015-07-03T15:37:44","modified_gmt":"2015-07-03T19:37:44","slug":"angularjs-with-only-npm-and-browserify","status":"publish","type":"post","link":"http:\/\/ryepup.unwashedmeme.com\/blog\/2015\/07\/03\/angularjs-with-only-npm-and-browserify\/","title":{"rendered":"AngularJS with ONLY npm and browserify"},"content":{"rendered":"<p>I&#8217;ve been working with javascript and <a href=\"https:\/\/angularjs.org\/\">AngularJS<\/a> a lot recently, both at work and in hobby projects. I&#8217;m a big fan of the framework, but like most non-trivial javascript frameworks, it really wants to have a build\/compile step. There are a lot of options for javascript build tools. I identified some main contenders: <a href=\"https:\/\/www.npmjs.com\">npm<\/a>, <a href=\"http:\/\/gruntjs.com\">grunt<\/a>, <a href=\"http:\/\/gulpjs.com\">gulp<\/a>, <a href=\"http:\/\/browserify.org\/\">browserify<\/a>, <a href=\"http:\/\/webpack.github.io\/\">webpack<\/a>, <a href=\"http:\/\/bower.io\">bower<\/a>, <a href=\"http:\/\/requirejs.org\/\">requirejs<\/a>.<\/p>\n<p>Recently I&#8217;ve been experimenting with just using <a href=\"https:\/\/www.npmjs.com\">npm<\/a> and <a href=\"http:\/\/browserify.org\/\">browserify<\/a>, and wanted to summarize my results.<\/p>\n<blockquote><p>\n  TL;DR: works well for small projects, but I think I need to add something like <a href=\"http:\/\/gruntjs.com\">grunt<\/a> or <a href=\"http:\/\/gulpjs.com\">gulp<\/a> (<code>g(runt|ulp)<\/code>).\n<\/p><\/blockquote>\n<h2>Goals<\/h2>\n<p>The benefits I&#8217;m looking to get from my build tools:<\/p>\n<ul>\n<li>can use many small files: angularjs code is easier for me to write and test if I have many small js\/html files. Serving many small files to users is bad for performance (lots of HTTP requests), and I&#8217;m bad at maintaining a curated list of script tags for what to serve. Ideally I&#8217;m serving one or two files that contain everything my app needs<\/li>\n<li>can use third-party libs: there are lots of good open source libraries that I want to use. I&#8217;m bad at managing those dependencies (and their dependencies) by hand, and I feel bad committing a minified library into source control<\/li>\n<\/ul>\n<p>My test was a small web app to track when the last time I did a house chore: <a href=\"http:\/\/ryepup.github.io\/when-did-i\/\">when-did-i<\/a>. The source is up at <a href=\"https:\/\/github.com\/ryepup\/when-did-i\/tree\/material-design\">ryepup\/when-did-i<\/a>. I&#8217;m actually experimenting with a bunch of different stuff, but I&#8217;m only going to consider <a href=\"https:\/\/www.npmjs.com\">npm<\/a> and <a href=\"http:\/\/browserify.org\/\">browserify<\/a> here.<\/p>\n<h2>Using <a href=\"https:\/\/www.npmjs.com\">npm<\/a> for third-party dependencies<\/h2>\n<p>Most libraries are published to <a href=\"https:\/\/www.npmjs.com\">npm<\/a>, and I never had an issue with missing a library, and was able to keep all external libs out of my repo.<\/p>\n<p>The only weird thing is the version specifier in package.json. By default, if you install package X (<code>npm install --save X<\/code>), it&#8217;ll find the current version (say 1.2.3), and then add it to package.json with a version specifier like <code>^1.2.3<\/code>. This basically means &#8220;1.2.3&#8221; or <em>anything newer with a 1.x.y version<\/em>. This can cause some surprises, especially if you have a continuous integration setup. Your CI robot might be testing different versions than what you are developing against.<\/p>\n<p>The solution to this is <a href=\"https:\/\/docs.npmjs.com\/cli\/shrinkwrap\">npm shrinkwrap<\/a>, to specify precisely every version of every piece of software you want. It&#8217;s basically the equivalivent of python&#8217;s <code>pip freeze<\/code> and <code>requirements.txt<\/code>.<\/p>\n<h2>Using <a href=\"https:\/\/docs.npmjs.com\/misc\/scripts\">npm scripts<\/a> for build actions<\/h2>\n<p>This worked out well&#8230; up to a point. Using <a href=\"https:\/\/docs.npmjs.com\/misc\/scripts\">npm scripts<\/a> gave me easy access to a lot of npm installed command line programs, without needing to install them globally on my system or muck about with my path. I like keeping the project&#8217;s needs self-contained. There are a ton of small tools available on npm to do just about anything.<\/p>\n<p>I like the simplicity; there&#8217;s no explicit &#8220;target&#8221; like other build tools, you just have a name, and what command you want to run with node&#8217;s path all setup. Then you can say <code>npm run $NAME<\/code> and it&#8217;ll go. You can add a &#8220;pre&#8221; or &#8220;post&#8221; prefix to the name to run other commands before\/after. If you want to call your other scripts, you just use <code>npm run my-other-script<\/code> as part of your command. Pretty easy, pretty basic.<\/p>\n<p>The problem arises when you want to do something more complex. The worst one I had was <code>start<\/code>:<\/p>\n<pre><code>\"prestart\": \"npm install\",\n\"start\": \"watch 'npm run build' src\/ &amp; live-reload --port 9091 .\/build\/* &amp; ws -d .\/build\",\n<\/code><\/pre>\n<p>Let&#8217;s break it down:<\/p>\n<ol>\n<li>a &#8220;pre&#8221; script to ensure packages are installed first if someone runs <code>npm run start<\/code><\/li>\n<li>start the npm-installed <code>watch<\/code> command to look file changes in <code>src\/<\/code>, and run <code>npm run build<\/code> when something changes (this is an example of one command calling another), then we have an <code>&amp;<\/code> to run <code>watch<\/code> in the background &#8211; this means our &#8220;start&#8221; script doesn&#8217;t work on windows<\/li>\n<li>start the npm-installed <code>live-reload<\/code> to run a <a href=\"http:\/\/livereload.com\/\">LiveReload<\/a> server on localhost:9091 to refresh my browser when something changes in <code>build\/<\/code>, and another <code>&amp;<\/code> to run this in the background<\/li>\n<li>start the npm-installed <code>ws<\/code> web server to serve the files in my build folder at localhost:8000<\/li>\n<\/ol>\n<p>With that combination, as I edit my files they get rebuilt and my browser refreshes.<\/p>\n<p>This is a pretty standard frontend development workflow, and I feel like it&#8217;s too much to squeeze into a one liner. I could make some short nodejs scripts that launch these services, but at that point I feel like I&#8217;m reinventing a wheel and I should just pull in <code>g(runt|ulp)<\/code>.<\/p>\n<h2>Using <a href=\"http:\/\/browserify.org\/\">browserify<\/a> to combine files<\/h2>\n<p>I think this worked out pretty well, but also had some quirks. By using <code>require<\/code> statements, I was able to centralize all my angularjs registrations into one file, which felt really nice and reduced some boilerplate. Each of my javascript files was basically defining one function. I really liked not having to manually specify an <a href=\"https:\/\/en.wikipedia.org\/wiki\/Immediately-invoked_function_expression\">IIFE<\/a> in each file. It also generates all the source maps, so debugging in the browser is referring to my small files, not whatever the bundle produces.<\/p>\n<p><a href=\"http:\/\/browserify.org\/\">browserify<\/a> has a pretty rich plugin system, and I used <a href=\"https:\/\/github.com\/javoire\/browserify-ng-html2js\">browserify-ng-html2js<\/a> to support keeping my templates in separate html files. This is another place where <a href=\"https:\/\/docs.npmjs.com\/misc\/scripts\">npm scripts<\/a> broke down a little. By default <a href=\"https:\/\/github.com\/javoire\/browserify-ng-html2js\">browserify-ng-html2js<\/a> puts each html file into it&#8217;s own angular module, and then I need to make my main angular module depend on each individual template. This is back to a manually curated list that I&#8217;m going to screw up. <a href=\"https:\/\/github.com\/javoire\/browserify-ng-html2js\">browserify-ng-html2js<\/a> has an option to put all the templates into one module, but that only seems to be available if you use <code>g(runt|ulp)<\/code>.<\/p>\n<p>Pulling everything in via <a href=\"https:\/\/www.npmjs.com\">npm<\/a> means I could have one bundled file that contains my code and all it&#8217;s dependencies. This gets to be kinda a big file. I added some machinery to reference some angularjs libs from a CDN, but the easist path with <a href=\"http:\/\/browserify.org\/\">browserify<\/a> is to have everything just included. I guess if you&#8217;re using cache headers well and versioning in the URL this might be alright. Rigth now I&#8217;m at 264KB (73.4KB over the wire) which does include some dependencies. Letting <a href=\"http:\/\/browserify.org\/\">browserify<\/a> combine ALL my dependencies would more than double the file size. I&#8217;m really not sure if that matters, but it makes me nervous.<\/p>\n<p>In the past I&#8217;ve used some <a href=\"http:\/\/gruntjs.com\">grunt<\/a> machinery to maintain the list of scripts to load; I liked this a little better because what I was developing with was closer to what I&#8217;m deploying.<\/p>\n<h2>Conclusion<\/h2>\n<p>I like the <a href=\"http:\/\/browserify.org\/\">browserify<\/a> and <a href=\"https:\/\/www.npmjs.com\">npm<\/a> combination, but <a href=\"https:\/\/docs.npmjs.com\/misc\/scripts\">npm scripts<\/a> are too limited, and another build tool is required. I think <a href=\"https:\/\/docs.npmjs.com\/misc\/scripts\">npm scripts<\/a> are good enough as a task runner for simple dev or CI, but build steps just need more configration. It&#8217;s possible that maybe the specific build libraries could better support looking at package.json for configuration, but there&#8217;s just a lot more momentum behind using <code>g(runt|ulp)<\/code>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve been working with javascript and AngularJS a lot recently, both at work and in hobby projects. I&#8217;m a big fan of the framework, but like most non-trivial javascript frameworks, it really wants to have a build\/compile step. There are a lot of options for javascript build tools. I identified some main contenders: npm, grunt, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[48,54,38,53],"tags":[],"class_list":["post-303","post","type-post","status-publish","format-standard","hentry","category-angularjs","category-browserify","category-javascript","category-npm"],"_links":{"self":[{"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/posts\/303","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=303"}],"version-history":[{"count":1,"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/posts\/303\/revisions"}],"predecessor-version":[{"id":304,"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/posts\/303\/revisions\/304"}],"wp:attachment":[{"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/media?parent=303"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/categories?post=303"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/ryepup.unwashedmeme.com\/blog\/wp-json\/wp\/v2\/tags?post=303"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}