Grunt configuration for your Node.JS server in TypeScript
By following the steps of this article, you will have a powerful automation tool that can lint and compile your TypeScript files and restart your node server each time you make a change to your code.
If you are in a hurry or just don't need explanations on how to make it, you can directly grab the full working project.
I don't think that I need to introduce Node.JS. Over the past few years, the adoption rate of this technology just sky rocketed. Big companies like Paypal, Uber or Groupon are already using it in their production environment.
TypeScript is a powerful typed superset of JavaScript that compiles to plain JavaScript. The language is still pretty young but has already been adopted by a large community and The Angular team is already working with it for their 2.0 version.
Enough with the blabla. Let's make a kickass Grunt config file to get the best of these two worlds.
If you don’t know what Grunt is or never used it, I encourage you to first read my previous article to get you started with Grunt.
Prerequesities
If you haven’t done it yet, to be able to run Grunt, you’ll have to first install Node.js. Then, install the Grunt CLI using npm (installed with NodeJS) by running this command:
npm install –g grunt-cli
It's as easy to install TypeScript on your machine:
npm install –g typescript
Typescript is a typed language. It needs definitions files for the libraries you're using to be able to do its work. There is an opensource repository full of TypeScript definitions for known libraries. Meaning someone has already done the job for us!
There's a manager using this repository to make our work even simpler. So don't forget to install it:
npm install –g tsd@next
Setting up the Grunt project
First, let’s create the default necessary files to have Grunt run correctly.
Create the package.json
file:
{
"name": "introduction-to-grunt",
"version": "0.1.0"
}
Install Grunt and save it to package.json
file:
npm install grunt --save-dev
Your package.json
should look like this:
{
"name": "introduction-to-grunt",
"version": "0.1.0",
"devDependencies": {
"grunt": "^0.4.5"
}
}
Create a basic gruntfile.js
:
module.exports = function (grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json')
});
// Default tasks.
grunt.registerTask('default', []);
};
OK, everything is in place, so let's make the magic happen.
Creating a simple Node.JS server
I will not enter in the details of this part, as it's not the subject here. I got this piece of code from this great article. Create a file named server.ts
and write this in it:
var sys = require("sys"),
my_http = require("http");
my_http.createServer(function(request,response){
sys.puts("I got kicked");
response.writeHeader(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}).listen(8080);
sys.puts("Server Running on 8080");
Setting up the TypeScript definitions
We only need the TypeScript definitions for Node.js. So let's get it:
tsd install node --save
This will create a tsd.json
file and put the typings definitions inside a typings
folder. Yes, it looks a lot like npm, but after all, it's a manager right?
Don't add any file under the folder typings
to your source controller. Just add tsd.json
.
To install all the definitions listed in tsd.json
just run this command:
tsd reinstall -s
Yes, I know, it's a bit weird. I actually created an issue in their github asking to behave like npm or bower: https://github.com/DefinitelyTyped/tsd/issues/116
Reference the created typing file created by TSD so that TypeScript recognize all the node definitions. Put this line at the begining of server.ts
:
///<reference path="typings/tsd.d.ts" />
Ok, so the node server part is now ready. Let's Grunt!
Adding necessary grunt plugins
TypeScript
Well, you figured it out, the first thing we need to get our server to run is to compile our TypeScript files. For that, we will use grunt-ts.
npm install grunt-ts --save-dev
Let's add our typescript task to our Grunt initConfig:
ts: {
build: {
src: ["server.ts", "!node_modules/**/*.ts"],
// Avoid compiling TypeScript files in node_modules
options: {
module: 'commonjs',
// To compile TypeScript using external modules like NodeJS
fast: 'never'
// You'll need to recompile all the files each time for NodeJS
}
}
}
You don't really need the "!node_modules/**/*.ts"
now. But when you'll have several folders to compile and will add "**/*.ts"
to your src parameters, you'll need it.
The fast option of grunt-ts allows you to only recompile changed files (way faster). But unfortunately, that won't work with NodeJS. Typescript won't be able to resolve the imported external modules and you will end up with a compilation error.
Load the task:
grunt.loadNpmTasks("grunt-ts");
Put it in the default task:
grunt.registerTask("default", ["ts:build"]);
Here is the complete Gruntfile:
module.exports = function (grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
ts: {
build: {
src: ["server.ts", "!node_modules/**/*.ts"],
// Avoid compiling TypeScript files in node_modules
options: {
module: 'commonjs',
// To compile TypeScript using external modules like NodeJS
fast: 'never'
// You'll need to recompile all the files each time for NodeJS
}
}
}
});
grunt.loadNpmTasks("grunt-ts");
// Default tasks.
grunt.registerTask('default', ["ts:build"]);
};
TSLint
Like in JavaScript, you need your code to be lint free. I suppose you are using JSLint to do so. Don't worry, there's an equivalent for TypeScript: TSLint.
First of all, you'll need a configuration file named tslint.json
to activate or deactivate linting options. You can grab the one in the tslint repository and tweak the options as you like: https://github.com/palantir/tslint/blob/master/tslint.json
Hopefully, there's already a Grunt plugin for TSLint. Here it is: grunt-tslint.
Install it:
npm install grunt-tslint --save-dev
Configure it:
tslint: {
options: {
configuration: grunt.file.readJSON("tslint.json")
},
all: {
src: ["server.ts", "!node_modules/**/*.ts", "!obj/**/*.ts", "!typings/**/*.ts"]
// avoid linting typings files and node_modules files
}
}
Load it:
grunt.loadNpmTasks("grunt-tslint");
Yes, it looks like I'm singing a Daft Punk song... "Buy it, use it, break it, fix it". But let's stay focused everyone!
Add it to the default task:
grunt.registerTask('default', ["tslint:all", "ts:build"]);
If you run grunt
in command line, you'll see tslint and ts running against your server.ts
file.
Now, we have a grunt task that lint and compile your TypeScript. That's a good start! But we need to run it manually every time and it's a bit of a hassle. So let's add the automation part thanks to watch.
Automate linting and compilation
One of the greatest feature of Grunt is its automation plugins. Meaning that everytime you will change a file, it will automatically lint and recompile your files.
What we need for that is a well-know Grunt plugin: grunt-contrib-watch. So let's go Daft Punk on it, shall we?
Install it:
npm install grunt-contrib-watch --save-dev
Configure it:
watch: {
scripts: {
files: ['server.ts', '!node_modules/**/*.ts'], // the watched files
tasks: ["tslint:all", "ts:build"], // the task to run
options: {
spawn: false // makes the watch task faster
}
}
}
Load it:
grunt.loadNpmTasks("grunt-contrib-watch");
Test it:
Run grunt watch
and modify your server.ts
file. You'll get something looking like this:
Running "watch" task
Waiting...
>> File "server.ts" changed.
Running "tslint:all" (tslint) task
>> 1 file lint free.
Running "ts:build" (ts) task
Compiling...
Using tsc v1.0.1
TypeScript compilation complete: 1.83s for 1 typescript files
Running "watch" task
Completed in 2.097s at Wed Jan 14 2015 12:33:03 GMT+0100 (CET) - Waiting...
Love it!
There's one more little tweak we can add to make it ever cooler. When you'll have more than one TypeScript file to compile, you'll notice that TSLint will run on every file each time you modify one. That's precious time lost and we don't want that. Hopefully for us, there is a Grunt task for that! Ladies and gentlemen, let me introduce grunt-newer. This task will tell TSLint to run only against newly modified files.
Install it:
npm install grunt-newer --save-dev
Load it:
grunt.loadNpmTasks("grunt-newer");
Change your watch task to add newer before the tslint task like that:
watch: {
scripts: {
files: ['server.ts', '!node_modules/**/*.ts'], // the watched files
tasks: ["newer:tslint:all", "ts:build"], // the task to run
options: {
spawn: false // makes the watch task faster
}
}
}
Now TSLint will only run against changed files.
That's great, but if your node server was already running, it won't take your changes into account until it restarts.
So let's take it a step further and automate the launch of the node server.
Automate the node server
After some research, I found that using nodemon to do that was the easiest way. Nodemon has its own watcher and will automatically restart the node server without any additional configuration.
Install Nodemon:
npm install -g nodemon
The Grunt plugin to use with it is... grunt-nodemon.What a surprise!
Install the plugin (oh no! I broke the song!):
npm install grunt-nodemon --save-dev
Configure the plugin:
nodemon: {
dev: {
script: 'server.js'
},
options: {
ignore: ['node_modules/**', 'Gruntfile.js'],
env: {
PORT: '8181'
}
}
}
Load the plugin:
grunt.loadNpmTasks("grunt-nodemon");
Create a serve
task that will call watch and then nodemon:
grunt.registerTask("serve", ["watch", "nodemon"]);
And they lived happily ever after... Not really. If you run the serve command, you'll notice that nodemon will run properly but the watch won't work. That's because both tasks are blocking ones and the last one called kills the other, Highlander style. There can be only one!
The solution is to use another great Grunt plugin: grunt-concurrent. Christophe Lambert would have loved it!
grunt-concurent lets you run 2 blocking tasks at the same time and log both outputs. It can also improve your build time significantly by running slow tasks concurrently, but that's not our goal here.
I think you know the drill (or should I say song?) by now, but let's be thorough:
Install it:
npm install grunt-concurrent --save-dev
Configure it:
concurrent: {
watchers: {
tasks: ['nodemon', 'watch'],
options: {
logConcurrentOutput: true
}
}
}
Load it:
grunt.loadNpmTasks("grunt-concurrent");
Change the serve task like that:
grunt.registerTask("serve", ["concurrent:watchers"]);
Let's give it a test run. Run grunt serve
in you command line. You should see something like this:
Running "concurrent:watchers" (concurrent) task
Running "nodemon:dev" (nodemon) task
Running "watch" task
Waiting...
[nodemon] v1.2.1
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: *.*
[nodemon] starting `node server.js`
Server Running on 8080
It's working! Both watch and nodemon tasks are running concurrently. If you change a file, watch will first lint and compile the TypeScript files. Then Nodemon will see the changes in the js files and restart your node server. Magic!
Conclusion
Now you have a fully functional Grunt configuration for your NodeJS app written in TypeScript. And on top of that, you got a really good grasp of how to configure Grunt for your project. Now it's your turn to play with it and add even more awesomeness in it.
You can download the full working project done following this article instructions.
I hope you enjoyed this tutorial and found it useful. I’d love to hear about your thought about it in the comment section. Thanks for reading! And may code be with you.