Browserify handbook
github.com
github.com
I auditioned Component and RequireJS but both of those felt like a kludge. Browserify lets you do node style includes as well as compile your JS into separate bundles.
I wrote a very small structure and build system [1] for our front-end projects at work that uses Browserify / Gulp and can compile everything into separate modules. It uses nginx/apache for routing and since it's all HTML/CSS and compiled JS, it's lightning fast to deliver.
I think this guide is fantastic; we just need more things like this to lower the barrier to entry.
Also, though I found bower useful I disliked having yet another package manager, yet another manifest and yet another install step included in my development workflow. Using npm for both server and client side modules has been a dream.
That's why I prefer client-side loaders. All you have to do is copy the loader into a folder and create a .html file. I have my dev folder hosted by apache so I don't ever have to worry about starting some process, running npm install and watching it install the entire internet, etc.
Well, that's the beauty of it, for me. I run everything through gulp, so I don't need to remember to run the watch thingy - I type:
gulp watch
which starts up my dev web server, complete with LiveReload shims enabled. Then when I'm ready to build I do gulp dist
and it packages everything up.I understand that some people like this workflow, what I don't like about it is when I start working on a project I don't want to spend 15 minutes setting up build scripts, downloading the entirety of npm, etc. I just want to start hacking in the browser. Client-side loaders make that really easy, just drop in the .js file and go.
Again, I know some people like that workflow and I'm not saying you're wrong, just pointing out that there is a tradeoff, it's not a clear win by any stretch.
Well you have to type something in order to start your project unless you like using file:/// URLs. "gulp watch" doesn't just run the watch task - it runs the entire dev environment with watch. How do you run multiple projects with Apache?
And while setting up these modules is a process, I only really have to do it once - I have a template directory I just copy into a new project. Then everything in the src/ directory gets processed accordingly once I type 'gulp watch'.
Apache serves everything under ~/dev and that's where I stick new client-side projects.
It's nice to be able to just start coding without the friction of setting up every project as though it were some large thing that would include production builds, automated tests, and many other developers. Of course you can work on those types of projects with client-side loaders just as easily.
I recommend trying out jspm: http://jspm.io/ It is all about removing the friction that people often have with client-side loaders (I have to maintain another config file, the horror! ;) but is also forward-compatible as it implements the upcoming ES6 module loading stuff. You can use CommonJS, AMD, or ES6, and mix and match the three.
Client side loaders (et al AMD based RequireJS) are dumb. I've been there, done that. I'll minify my js code and then "just drop the .js file" into my page, and let gulp watch for changes.
Hell, you could even use a client-side loader exactly as you are doing with Browserify/Webpack. Just set up a watch task that builds unminified and there you go! It's just nice that they aren't dependent on a cli process. But unlike you, I'm not trying to convert anyone here, just pointing out that there are advantages to using client-side loaders. For example, it promotes separation of client-side code from server-side code. Using the same package.json file for both promotes bad practices like developing with the server running and writing code that's not easily testable without the server running.
Steps like these allow you to systematically control every aspect of the development process, and adapt. In the long run, these steps work in your favor.
They also allow you to hook into the more modern aspects of development. I mean, you don't want to type `npm install` --that's fine; what you're essentially saying is that this incredibly useful ecosystem is irrelevant to your needs. I find that very hard to believe.
1. git clone https://github.com/WINTR/grunt-frontend-scaffold
2. npm install; and then
3. grunt dev
...which watches everything on localhost:3000, including my unit tests and source code, and reloads on change. Dead simple, fast and consistent stuff and everything I could possibly need set up in less than 1 minute, every time.
Most teams, I imagine, work in a similar way.
Not to mention, if I wanted to pass over the project to another developer, I would just have to tell him to clone the repo and hit `npm install`.
app.use('/js', browserify('./app/js', {
transform: ['reactify', 'envify'],
extensions: ['.jsx'],
cache: app.get('env') !== 'development',
minify: app.get('env') !== 'development',
gzip: app.get('env') !== 'development',
debug: app.get('env') === 'development',
precompile: ['./app/app.js']
}));
The server will dynamically bundle the JS for you; all you need is to run the server. In our apps, we do this for development, but in the production environment we do the packaging at deploy time (via Google Closure to minify) to create static files that can be served by Nginx.Why the node community is so stubborn about this point is a mystery to me and it makes me wary of node in general, because who wants to be locked into an environment where such an obvious pain point is ignored due to stubbornness?
Basically, what would be your ideal solution?
My preference is to add structure with file names instead of directories: app.foo.bar.js instead of app/foo/bar.js
Unfortunately, some of us need to live uphill.
I find the tiny module style works pretty well for me, but I need private code. I haven't yet found a private registry solution I like. NPM's lack of support for any "get this module from this registry" directive in package.json forces them to proxy the main registry. They're not good at it. I'm still declaring my dependencies by tarball URL, losing semantic version specs, e.g. "most recent compatible with 4.x".
It strikes me as unlikely now to change in any way other than "here, use my paid-for private registry", because shareholders. That makes me unhappy, but not unhappy enough to ditch the public Node ecosystem and go back to what I was using before. It had its own uphill, and living there was harder.
From the most popular answer on the stackoverflow link: "If you find yourself loading common files from the root of your project (perhaps because they are common utility functions), then that is a big clue that it's time to make a package."
I'm sorry, but no, that is not a big clue that it's time to make a package. Every project I've ever worked on contained internal modules which were nicely organized and isolated, and were consumed by the rest of the project as black box "libraries" for good design's sake, but breaking them out into separately installable modules would have been sheer, unproductive bureaucracy. Oops, you made your code modular! Get ready to start managing packages!
It's as if npm no longer wants to be merely a package manager, but also have a say in how your project internals are laid out as well. Perhaps it's a self-perpetuation strategy.
Dictating that the only way to control dependency-loading is by manipulating the filesystem is really braindead.
It doesn't work with browserify but it wouldn't be too hard to write a transform that does it.
Most substantial applications are going to have at least some small amount of internal coupling around their business logic and configuration, and as long as you limit knowledge of the custom NODE_PATH to that code you should be alright.
eg. you could have
# assuming git, find repo root
APP_ROOT=$(git rev-parse --show-toplevel)
# /app is for private, checked in modules
# `npm root` will find your root node_modules dir
NODE_PATH="$APP_ROOT/app:`npm root`"
and then all of your app-coupled private modules can live in /app, and get checked into your git repo, while all the dependencies would be referenced in the package.json at the root of your app, and installed to node_modules (eg. npm root for the app package).I should emphasise that taking this approach requires quite a bit of developer responsibility to avoid coupling between modules when it's not required, and it's really overkill for small apps.
// App Controller
var AppModel = require('./models/AppModel');
var NavView = require('./views/NavView');
var HomeView = require('./views/HomeView');
// Do stuff
You can immediately know whats going on inside of your class or module simply by looking at the imports, the LOC is greatly reduced, and refactoring, via imports, makes your code about a thousand times more maintainable.
One tip I can give is that I ended up organizing each of my React components and mixins as a module in their own folders and files and my gulpfile adds the parent source path to the NODE_PATH environment variable. By adding the coffeeify transform and .coffee extension to the browserify gulp task I can just do this in my code:
SomeComponent = require 'react-components/some-component'
SomeMixin = require 'react-mixins/some-mixin'
No need to worry about relative paths or if it is CoffeeScript code or plain Javascript.The other tip is to NOT require react within your browserfied code. Just load it as a script tag before your browserfied code and use window.React to get a reference.
window.$ = jQuery
Global variables are a bad thing until they're a really convenient thing.I also didn't give the main reason for using the NODE_PATH method. Browserify doesn't resolve duplicate relative paths so if you have a structure like this:
/src
main.js
components/a.js
components/foo/b.js
components/foo/c.js
and a.js requires c.js via relative paths: require('./foo/c.js')
and b.js also requires c.js via require('./c.js')
then c.js is included twice in your bundled code since Browserify uses the pure string path as the key. If instead you put /src in your NODE_PATH you can do this in a.js: require('components/foo/c.js')
and this in b.js require('components/foo/c.js')
and c.js will only be bundled once since it has the same path.Also, if you're requiring large files that you know won't need processing by browserify (like react), you can use the 'noparse' option to prevent the performance hit during bundling. (Of course a cdn is potentially a better option.)
The script
- Creates multiple bundles, with Underscore template precompilation and source code minification (for some bundles).
- Converts every vendor library into shared modules (you don't want Underscore included in every bundle that needs it, but you want to be able to require() it in those bundles).
- Monitors your files for changes and recompiles bundles as needed when run with --watch.
I found working with Browserify a pleasant experience after RequireJS and r.js (its build tool).
https://github.com/bcoe/npm-typeahead
Both the libraries that the app depended on (typeahead.js, and jquery) had been published to npm. I found it really straight-forward to setup an asset-pipeline using Browserify.
I'm a big fan :)
What I ended up with was a seperate ts file that is just the module, and variables I export. See this:
http://jsbin.com/gidaneja/1/edit?js,output
So I do a "<reference path=" for the ambient paths of variables. Then I declare require so I can call browserify's require. Then I create a function with it's return type being used based on ambient files I included. Then I create an export variable and call the function to set it's value. Then I can use that variable without a problem everywhere.
I tried using the whole "import lodash = require('lodash.d.ts') thing and other variations and failed miserably. This is the only thing I got to work, and its working perfectly. Email me (email should be on my profile) if it's not clear, I know the explanation was kinda vague.
how can I develop a library with different modules, say lib/module/func, lib/module/other, and compile the library as static, then import it in another project and be able to require lib/module/other from the other project using only the compiled lib?
I can understand bundling a bunch of modules together for UMD builds, but if you need to bundle all of your NPM/browserifiable modules for other browserify apps, you are probably going about it the wrong way..
Advantages:
- requiring and pre-prosessing files other than js is easier
- ability to use AMD modules if you have to
- code splitting