Running an Express server with Grunt and Yeoman: Part 2
Update: The grunt-express-workflow project has been updated to include a full front-end and back-end testing framework by leveraging Grunt, Karma, Mocha, and Istanbul. Read all about how to test your code easily in part 3 of this series.
In part 1 of this series I introduced a small number of tools that make a web engineer’s life easier: Grunt, Yeoman, Express, and Nodemon. In this second part of the series I’ll explain how to get started using them together. You’ll soon have a server running with LiveReload that’s capable of automatic browser and app server reboots for rapid development.
Getting the Right Tools
The first thing you should do is download and install Node.js if you haven’t already. It should come with a package manager called npm. You’ll need to install the proper tools with it:
$ npm install -g yo node-inspector nodemon grunt-cli bower karma istanbul
I recommend having a tool like nvm to manage multiple local installations of node with user privileges.
Quick Start
I’ve set up a repository of all the files you need to be up and running. Be sure to read the repository’s README.md
to get an idea of what it’s all about. As I fleshed out this project, I realized how much I had changed of the original Yeoman scaffold. If you’re interested to see what an initial Yeoman project directory would look like, type yo webapp
into a terminal that’s inside of an empty directory, follow the prompts, and poke around a bit.
What we’ll be referencing from here on are these repository files I’ve heavily modified from that initial Yeoman scaffold.
Start with:
$ git clone https://github.com/darvelo/grunt-express-workflow
Now you have a copy of the repository to play with.
$ cd grunt-express-workflow
$ npm install
$ bower install
These will install all your server-side and client-side dependencies. Then:
$ grunt server
I won’t be going into great detail about Grunt or the included Grunt plugins in this post — for that you can research the Grunt documentation and the documentation of each plugin — but by poking at the files in this project and reading Gruntfile.js
you can get a pretty good idea of how it all works together.
Now your Express server is running from server.js
with a minimalistic client-side app, and with tests! Expand on it to your heart’s content and watch happiness spread out across the land.
What’s Included?
Popular client-side libraries included and configured:
Server-side libraries included and configured:
Testing Libraries included and configured:
Grunt plugins I included in package.json
and configured in Gruntfile.js
are:
- grunt-nodemon
- grunt-contrib-handlebars - for precompiled client-side templates
- grunt-concurrent (~0.2.0 is important)
- grunt-contrib-watch (~0.5.1 is important)
- grunt-simple-mocha
- grunt-karma
Caveats
-
If you want to
grunt build
for a production-ready build of the project with Handlebars included, you’ll need to change the Handlebars runtime library code from the initialvar Handlebars = {};
tothis.Handlebars = {};
so that it’ll be attached to thewindow
and found by RequireJS modules. It seems this is being fixed upstream with the Handlebars developers at the time of this writing. -
There’s an issue in theThegrunt-contrib-watch
task where if two targets are watching the same file(s), only the last defined target will run if the file(s) change. Right now this means client-side tests will run and livereload will not when client-side scripts are saved. You can switch this behavior by altering the watch target order inGruntfile.js
. Watch grunt-contrib-watch issue #25 and upgrade your local copy of that library usingnpm
when it’s fixed.grunt-contrib-watch
package has been updated andgrunt-express-workflow
now uses the new version. Many thanks to the maintainers! -
If you want to test your media queries in old IE with
respond.js
, be sure to modify yourGruntfile.js
to switch yourcompass:server
options fromdebugInfo: true
todebugInfo: false
. This removes Compass’ ability to inject certain media queries into your CSS, which would make it easier for you to debug your styles in modern browser debuggers, but confuse the logic ofrespond.js
.
Differences to yo webapp
Since Yeoman is being worked on for a 1.0 release, there are a few things that we can upgrade in the meantime to make a better experience for ourselves. Here’s what I did:
- Bower’s
component.json
has since been standardized tobower.json
- grunt tasks
grunt-contrib-livereload
andgrunt-regarde
are replaced withgrunt-contrib-watch
~0.4.3, which does file watching and livereloading so that you never have to manually refresh your browser to see changes again - Client-side and server-side scripts and tests are watched for changes, which trigger a run of unit-tests and Istanbul code coverage output in the
coverage/
directory grunt server
will now compile any necessary files and launch nodemon tasks like Express andnode-inspector
grunt-concurrent
~0.2.0 installed, to be able to see nodemon terminal output and runnode-inspector
andwatch
concurrently with the Express server- the RequireJS task will concatenate and minify scripts in the dependency graph into one file for production.
The smaller almond loader will be used, and the built script will be wrapped in an IIFE so no script variables (evenThe project now usesrequire
anddefine
) are exposed globally.grunt-contrib-requirejs
, which does not yet have almond support. theThe useminPrepare and usemin tasks now produce a production build with file revisions for cache busting. It will copy and update your Jade views automatically into theuseminPrepare
task will now refer tousemin.html
to prepare script files in the usemin task.dist/views/
folder.grunt-nodemon
is installed, and its settings are set in a variable inGruntfile.js
The RequireJS. The usemin task now takes care of this, but the gruntpath
settings used inGruntfile.js
forgrunt build
have to be maintained in alignment with the client-side app’s main RequireJS config file and test config file, in this caseconfig.js
requirejs
task can be used to override any options you need to.- The
relativeAssets
option in thecompass
grunt task has been changed tofalse
, sincerelativeAssets: true
produces incorrect references to sprites in the final CSS. Images are now referenced relative to the definitions in the root filecompass.rb
.
How does it work?
At a very high level, the grunt server
command will start a bunch of tasks that do quite a few things:
- Compile and watch files for changes
- Implicitly set up intermediate folders for compiled files
- Run unit tests and code coverage analysis on front-end and back-end code after every change. Here’s an example of a coverage report with Istanbul
- Start up your Express server serving your local and implicit files and directories, with Jade templates replacing what would otherwise be static HTML files
- Start up node-inspector so that you can debug your Express server inside a WebKit-based browser like Chrome, Chromium, and Safari!
Client-side files
In the root directory is a folder called app/
. That’s where all your client-side files go. The app/scripts/
directory is for scripts, and has three folders:
app/scripts/app/
: all scripts related to your app are namespaced into this directory. The idea is that if you have multiple apps, or multiple pages using different scripts, they can all be grouped separately from one another, namespaced into their own folders. The RequireJS task in yourGruntfile.js
is the place to declare new namespaced app targets for the build step.app/scripts/misc/
: for scripts not intended for use with RequireJS. During a build, they’ll be wrapped in an IIFE, minified, and kept as separate files for use with the<script>
tag.app/scripts/vendor/
: scripts for use either with RequireJS or not - components you couldn’t otherwise get through Bower.
When you run grunt server
, your client-side files will automatically be watched for changes, with unit tests and code coverage run and the browser refreshed when they’re changed. In the case of SASS changes, the grunt watch
task will compile the CSS and inject it into the page without a refresh required. When edited and saved, any watched files that are compilable will automatically be compiled into .tmp/
with an identical file structure. The server is configured to also serve files out of .tmp/
(along with app/
) during development. Files watched are:
app/scripts/app/rawTemplates/
: your Handlebars templatesapp/styles/
: your SASS files. CSS files in this folder are also watched for changesapp/images/
: for your images of cats and/or kittens
The app/images/sprites/
folder is special. Each folder you create in it should represent a spritesheet you’d like to create for production that’s composed of the images inside. You can tell Compass to create these spritesheets using Compass’ sprite helpers. Once you do a grunt build
, what’s left inside the dist/app/images/sprites/
folder is your compiled spritesheets, named by the original folder name. An example of this is in the project. Try it out for yourself!
The dist/
folder is automatically regenerated every time you run grunt build
. It’ll hold all your usemin-updated Jade templates in dist/views/
, and in dist/app/
will hold your compiled SASS and Handlebars templates, and concatenated and minified scripts and libraries, along with efficiently-processed images. After a build you can freely delete the usemin.html
file in the dist/
folder, and if you’re using almond with RequireJS, the components/requirejs
folder. The cssmin
task in Gruntfile.js
determines which CSS files in .tmp/
are combined and placed in dist/styles/
.
Server-side files
With grunt server
, Express will run with the env
environment variable set to 'development'
by default, which you can detect in your server scripts with app.get('env') === 'development'
in order to branch into a different set of behaviors, such as setting certain variables in your views for more verbose error-handling. Files will be served out of app/
and .tmp/
.
The grunt nodemon
task generates a .nodemonignore
file in your project root and is configured to ignore your client-side files when watching for changes that will restart the server. You can edit which files are ignored in Gruntfile.js
.
After a build with grunt build
, you can invoke your node server directly for a production environment with $ node server.js production
or $ NODE_ENV=production node server.js
. The dist/
folder will be used for serving files, and not app/
or .tmp/
. If your server-side views with Jade are set up with usemin to process your css and js files, the switchover should be seamless.
The file node-inspector.js
exists so that node-inspector can be spawned by nodemon when running grunt server
.
If you prefer not to use Jade, simply remove the app.get
blocks in server.js
referencing res.render()
. All html files in the app/
directory will still be served as expected. You’ll be missing out on all sorts of wonderfulness, though!
Tests
Karma, Istanbul, and Mocha run automated tests and coverage for the frontend and backend. This happens when development files or test files are changed during a grunt server
run, or when the grunt test
command is run on the command line. See part 3 for an in-depth explanation.
Debugging the Server with node-inspector
Once you run grunt server
you can debug your node server on the fly by opening a new tab in a WebKit-based browser and navigating to http://127.0.0.1:8080/debug?port=5858. Since nodemon starts node in --debug
mode, grunt is configured to run a separate node-inspector process that will attach to that debug session and relay requests on that URL to a WebKit Inspector view, where you can use breakpoints and step debugging just like you’re used to on the client-side.
For instance, if you set a breakpoint in the app.get('/')
block and refresh another tab running your client-side app — http://localhost:9000 in this case — the response from the server will be paused while you switch to your node-inspector tab and step through the server code to squash bugs. Resuming script execution will finally send the response, even if the request has already timed out. This is all really amazingly cool and saves countless time debugging your server application, so I encourage you to read the node-inspector documentation and use it to amazing effect.
Conclusion
On to part 3 for how to work with unit tests and code coverage!
Comments