Quantcast
Channel: B-Line Medical » TypeScript
Viewing all articles
Browse latest Browse all 2

Compiling TypeScript With Grunt

$
0
0

As Sam introduced recently, B-Line Medical is now using TypeScript, which is a superset of JavaScript. TypeScript lets us have our JavaScript and type it too, but it comes at the cost of having to compile the TypeScript into JavaScript before we can execute anything. This post will show how we set up Grunt—a JavaScript task runner—to do this compilation for us.

Why Use Grunt?

We’re currently developing using WebStorm 8, an IDE by JetBrains. WebStorm actually comes with a TypeScript file watcher, which watches your TypeScript files and automatically compiles them into JavaScript whenever you change anything. This is pretty awesome in theory, but had a few problems in practice:

  • The watcher would trigger a compilation for literally every change, so when you’re in the middle of writing a long statement it attempts to compile when you’ve only written half a line. This results in a lot of annoying error messages until you finish what you’re working on.
  • The watcher would sometimes silently fail, so everything would seem to be broken because it just wasn’t getting compiled. It often took many iterations of adding and removing the watcher and restarting WebStorm to get the watcher to reactivate.

Running the TypeScript compilation process through Grunt took care of both of these issues, and since we were already using Grunt to handle other things like running tests, this process worked out perfectly.

Installing Grunt

Note: the following assumes a basic familiarity with the command prompt and that you already have node.js installed and have a node.js project to play with. Run all commands from the root directory of a node.js project—all you need to have is a package.json file, but some uncompiled TypeScript files will be helpful, too! If you’d rather look at the code than try it yourself, feel free to clone this repository in GitHub: https://github.com/blinemedical/GruntTypeScriptWatchExample.

The first step is to install the Grunt Command Line Interface (grunt-cli). You can do this with:

npm install -g grunt-cli

Make sure to include the -g flag so the tool will be available from any directory. You should now be able to run Grunt from any directory, and you should see the following output:

grunt-cli: The grunt command line interface. (v0.1.13)

Fatal error: Unable to find local grunt.

If you’re seeing this message, either a Gruntfile wasn’t found or grunt hasn’t been installed locally to your project. For more information about installing and configuring grunt, please see the Getting Started guide:

http://gruntjs.com/getting-started

You’ll notice it said “Unable to find local grunt”. To remedy this, run:

npm install grunt --save-dev

The --save-dev flag will update your package.json file so anyone else who checks out your project and does an npm install will get the Grunt module. Now when you run grunt you should see:

A valid Gruntfile could not be found. Please see the getting started guide for more information on how to configure grunt: http://gruntjs.com/getting-started

Fatal error: Unable to find Gruntfile.

We’re still failing, but that’s fine—we’re learning how to work through these issues and why. For example, the Gruntfile that’s missing is the file that contains the tasks that Grunt will run for you. For now, create an empty file in the same directory as package.json named Gruntfile.js. Running grunt will now show you:

Warning: Task “default” not found. Use –force to continue.

Aborted due to warnings.

Don’t worry about this warning for now—we will add the default task in the last section of this post.

You will need a few more local node modules to use Grunt to compile TypeScript. Run the following commands:

npm install grunt-typescript --save-dev

and then

npm install grunt-contrib-watch --save-dev

We’ll use the grunt-typescript module to compile our TypeScript into JavaScript. The grunt-contrib-watch module will watch our TypeScript files for changes so we can get continuous compilation. Fun fact: modules that start with grunt-contrib come from the official Grunt development team.

That’s all the setup we need! Now let’s make Grunt do something.

Compiling TypeScript

To use Grunt to compile TypeScript, we’re going to make use of the grunt-typescript module. Open up your Gruntfile and add the following lines:

module.exports = function (grunt) {
   grunt.loadNpmTasks('grunt-typescript');
};

This will load the node.js module called grunt-typescript so Grunt can make use of it.

Now we want to configure the TypeScript task.

module.exports = function (grunt) {
   grunt.loadNpmTasks('grunt-typescript');

   grunt.initConfig({
      typescript: {
         options: {
            sourceMap: true
         },
        examples: {
            src: ['examples/**/*.ts']
        }
      }
   });
};

If you already have some TypeScript in your project, change the filepath in the src array to point to your TypeScript files. Otherwise, make a directory named examples at the same level as package.json and your Gruntfile. Put a TypeScript file that you want to compile in that directory.

Now run:

grunt typescript

You should see the following output:

Running “typescript:example_files” (typescript) task
2 files created. js: 1 file, map: 1 file, declaration: 0 files (706ms)

Done, without errors.

And now there should be a compiled JavaScript file alongside your TypeScript file!

Tasks And Targets

Let’s take a closer look at what we did here. First, grunt.initConfig sets ups tasks and targets. A task is something like “typescript”: it’s a task that you want Grunt to perform. You can use tasks by getting node.js modules for Grunt tasks, such as grunt-typescript, or by writing your own tasks (unfortunately, those are beyond the scope of this post).

Targets are subdivisions of tasks, and are frequently used to run the same task with different parameters. For example, we could change our configuration to add another target:

   grunt.initConfig({
      typescript: {
         options: {
            sourceMap: true
         },
        examples: {
            src: ['examples/**/*.ts']
        },
        moar_examples: {
            src: ['moar_examples/**/*.ts']
        }
      }
   });

Now if we run grunt typescript we see this output:

Running “typescript:example_files” (typescript) task
2 files created. js: 1 file, map: 1 file, declaration: 0 files (753ms)

Running “typescript:moar_example_files” (typescript) task
0 files created. js: 0 files, map: 0 files, declaration: 0 files (4ms)

Done, without errors.

Grunt ran the typescript task, and since we didn’t specify which target we wanted, it ran both of them. In order to only run one target, you specify it with a colon like so:

grunt typescript:examples

TypeScript Options

You may notice that our options target looks different from the others. The options target lets us tell grunt-typescript how we want it to compile our files. In this example, we only specify one option: sourceMap. This option tells the compiler to also generate *.js.map files, so our IDE and browser can map the generated JavaScript to the original TypeScript (this is really helpful for debugging).

Options can be specified as its own target, like we did here. That results in the options applying to all of the targets. You can also specify options on a per-target basis, such as:

examples: {
   options: {
      sourceMap: true
   },
   src: ['examples/**/*.ts']
}

You can see all the available options at https://github.com/k-maru/grunt-typescript, which is the GitHub repository for the grunt-typescript module.

Note that grunt-typescript automatically places the compiled files in the same directory as the originals. If you want to change where the compiled files go, or put all the compiled files in the same file, you can specify a destination for the compilation in a target with the dest property, for example:

examples: {
  src: ['examples/**/*.ts'],
  dest: 'examples/compiled.js'
}

Continuous Compilation

So far we’ve made it so Grunt will compile our files whenever we run the grunt typescript command. This can get inconvenient when you’re actively developing and testing frequently. We will now set up a watcher so Grunt will compile our TypeScript whenever it detects a change in a TypeScript file.

Add the following to the top of your Gruntfile:

grunt.loadNpmTasks('grunt-contrib-watch');

And add another task to your initial configuration:

grunt.initConfig({
   typescript: {
     ...
   },
   watch: {
      files: ['./**/*.ts'],
      tasks: ['typescript']
   }
});

Now you can run grunt watch and you should see the following:

Running “watch” task
Waiting…

If you change one of your TypeScript files, you should see something like:

>> File “moar_examples\another_example_file.ts” changed.
Running “typescript:example_files” (typescript) task
File C:\Users\lily.seropian\Documents\GruntTypeScriptWatchExample\examples\compiled.js created.
js: 1 file, map: 1 file, declaration: 0 files (746ms)

Running “typescript:moar_example_files” (typescript) task
2 files created. js: 1 file, map: 1 file, declaration: 0 files (683ms)

Done, without errors.
Completed in 2.118s at Fri Jul 18 2014 11:23:11 GMT-0400 (Eastern Daylight Time) – Waiting…

Note: unless you’re working with an editor that saves your work automatically, like WebStorm, you will have to save your TypeScript file before Grunt will notice it changed.

In the example above, the files target specifies which files Grunt should watch for changes. The tasks target tells Grunt what to do when a file Grunt is watching changes. So, when Grunt sees a change on any file ending in .ts, it runs the typescript task and compiles the TypeScript into JavaScript.

To end the watch task, hit Ctrl-C or exit the command prompt.

The Default Task

The default task is great if you want Grunt to always run multiple things at once, or if you’re just lazy (editor’s note: “efficient” would also work here!). The default task is what runs when you run the grunt command without any arguments. To set up the default task, add the following to the end of your Gruntfile:

grunt.registerTask('default', 'compiles typescript', [
   'typescript',
   'watch'
]);

This is just a preview of how to make custom tasks. The registerTask method registers a new task with Grunt. The first parameter is the name of the task; default is a special value that instructs the task should be run when Grunt is invoked without any arguments. The next parameter is a simple description of what the task does. The last parameter is a list of Grunt tasks that Grunt should run, in that order.

In our example, Grunt will first compile all the existing TypeScript into JavaScript, then start watching for changes so it can recompile when necessary. Now when you run grunt, you should see:

Running “typescript:example_files” (typescript) task
File C:\Users\lily.seropian\Documents\GruntTypeScriptWatchExample\examples\compiled.js created.
js: 1 file, map: 1 file, declaration: 0 files (757ms)

Running “typescript:moar_example_files” (typescript) task
2 files created. js: 1 file, map: 1 file, declaration: 0 files (703ms)

Running “watch” task
Waiting…

That’s everything! Now we’ve set up Grunt to do one-time compilation, continuous compilation, and both. If you’re curious about how to make custom tasks, poke around http://gruntjs.com/creating-tasks. Again, if you want to see this in action, look at or clone https://github.com/blinemedical/GruntTypeScriptWatchExample.


Viewing all articles
Browse latest Browse all 2

Trending Articles