/grunt-ts

A grunt task to manage your complete typescript development to production workflow

Primary LanguageJavaScriptMIT LicenseMIT

grunt-ts

Build Status NPM version

TypeScript Compilation Task for GruntJS

Grunt-ts is an npm package that handles TypeScript compilation work in GruntJS build scripts. It provides a Grunt-compatible wrapper for the tsc command-line compiler, and provides some additional functionality that improves the TypeScript development workflow. Grunt-ts is itself written in TypeScript.

Getting Started

If you've never used GruntJS on your computer, you should follow the detailed instructions here to get Node.js and the grunt-cli working. If you're a Grunt expert, simply follow these steps:

  • Run npm install grunt-ts in your project directory; this will install grunt-ts, TypeScript, and GruntJS.
  • Add the ts task in your gruntfile.js (see below for a minimalist one).
  • Run grunt at the command line in your project folder to compile your TypeScript code.

This minimalist gruntfile.js will compile *.ts files in all subdirectories of the project folder, excluding anything under node_modules:

module.exports = function(grunt) {
  grunt.initConfig({
    ts: {
      default : {
        src: ["**/*.ts", "!node_modules/**/*.ts"]
      }
    }
  });
  grunt.loadNpmTasks("grunt-ts");
  grunt.registerTask("default", ["ts"]);
};

A more extensive sample gruntfile.js is available here.

Grunt-ts Features

  • Allows use of all standard GruntJS functionality such as use of customizable task targets, globbing, use of the files object (for instantiating multiple independent tsc runs in a single target), etc.
  • Allows the developer to select a custom TypeScript compiler version for their project, or even use a custom (in-house) version.
  • Supports most switches of the tsc TypeScript Compiler via options in the gruntfile ts task, and also supports switch overrides per-target.
  • Provides a transforms feature that eases code refactoring by taking the burden of relative path maintenance off the developer. If the paths to a set of files changes, grunt-ts will regenerate the relevant sections. This feature supports:
  • Allows concatenation where supported by the TypeScript compiler's --out switch
  • Encodes HTML files as TypeScript variables (for HTML templating engines)
  • Performs live file watching (compile on save)
  • Enables "Fast" compile when using external modules

Support for tsc Switches

Grunt-ts supports most tsc switches. Click the link to cross-reference to the grunt-ts option.

tsc switch grunt-ts analogue description
--declaration declaration Generates a .d.ts definitions file for compiled TypeScript files
--mapRoot LOCATION mapRoot Specifies the location where debugger should locate map files instead of generated locations.
--module KIND module Specify module style for code generation
--noImplicitAny noImplicitAny Warn on expressions and declarations with an implied any type.
--noResolve noResolve Skip resolution and preprocessing (deprecated)
--removeComments removeComments Configures if comments should be included in the output
--sourceMap sourceMap Generates corresponding .map file
--sourceRoot LOCATION sourceRoot Specifies the location where debugger should locate TypeScript files instead of source locations.
--target VERSION target Specify ECMAScript target version: 'es3' or 'es5'
--out FILE out Concatenate and emit output to a single file.
--outDir DIRECTORY outDir Redirect output structure to the directory.

For file ordering, look at JavaScript Generation.

grunt-ts gruntfile.js options

property where to define description
comments option true, false (default) - include comments in emitted JS.
compile option true (default), false - compile TypeScript code.
compiler option string - path to custom compiler
declaration option true, false (default) - indicates that definition files should be emitted.
failOnTypeErrors option true, false (default) - fail Grunt pipeline if there is a type error
fast option 'watch' (default), 'always', 'never' - how to decide on a "fast" grunt-ts compile.
files target Sets of files to compile and optional output destination
html target string or string[] - glob to HTML templates
htmlModuleTemplate option string - HTML template namespace
htmlVarTemplate option string - HTML property name
mapRoot option string - root for referencing .js.map files in JS
module option 'amd' (default) or 'commonjs' - specifies external module style
noImplicitAny option true, false (default) - enable for stricter type checking
noResolve option true, false (default) - for deprecated version of TypeScript
options target
out target string - instruct tsc to concatenate output to this file.
outDir target string - instruct tsc to emit JS to this directory.
reference target string - tells grunt-ts which file to use for maintaining references
removeComments option true (default), false - removes comments in emitted JS
sourceRoot option string - root for referencing TS files in .js.map
sourceMap option true (default), false - indicates if source maps should be generated (.js.map)
src target string or string[] - glob of TypeScript files to compile.
target option 'es5' (default) or 'es3' - targeted ECMAScript version
verbose option true, false (default) - logs tsc command-line options to console
watch target string - will watch for changes in the specified directory or below

Note: In the above chart, if "where to define" is "target", the property must be defined on a target or on the ts object directly. If "where to define" is "options", then the property must be defined on an options object on ts or on a target under ts.

grunt-ts target properties

dest

Grunt-ts does not support the GruntJS standard dest target property. Instead, you should use files, out, or outDir.

files

Grunt-ts supports use of the GruntJS-centric files property on a target as an alternative to the tsc-centric use of src and out/outDir.

Notes:

  • The fast grunt-ts option is not supported in this configuration. You should specify fast: 'never' to avoid warnings when files is used.
  • It is not supported to specify an array of values for dest with grunt-ts. A warning will be issued to the console. If a non-empty array is passed, the first element will be used and the rest will be truncated.
  • If the dest parameter ends with ".js", the value will be passed to the --out parameter of the TypeScript compiler. Otherwise, if there is a non-blank value, it will be passed to the --outDir parameter.
  • If you intend to pass the specific value "src" to the TypeScript --outDir parameter, specify it as "src/" in the dest parameter to avoid grunt-ts warnings.

Here are some examples of using the target files property with grunt-ts:

grunt.initConfig({
  ts: {
    compileTwoSetsOfFilesUsingArrayStyle: {
      // This will run tsc twice.  The first time, the result of the 'files1/**/*.ts' glob will be
      // passed to tsc with the --out switch as 'out/ArrayStyle/1.js'.
      // see https://github.com/gruntjs/grunt-docs/blob/master/Configuring-tasks.md#files-array-format
      files: [{ src: ['files1/**/*.ts'], dest: 'out/ArrayStyle/1.js' },
              { src: ['files2/**/*.ts'], dest: 'out/ArrayStyle/2.js' }],
      options: {
        fast: 'never'
      }
    },
    compileTwoSetsOfFilesToDirUsingArrayStyle: {
      // This will run tsc twice.  The first time, the result of the 'files1/**/*.ts' glob will be
      // passed to tsc with the --outDir switch as 'out/ArrayStyle'.
      // see https://github.com/gruntjs/grunt-docs/blob/master/Configuring-tasks.md#files-array-format
      files: [{ src: ['files1/**/*.ts'], dest: 'out/ArrayStyle' },
              { src: ['files2/**/*.ts'], dest: 'out/ArrayStyle' }],
      options: {
        fast: 'never'
      }
    },
    compileTwoSetsOfFilesUsingObjectStyle: {
      // This will run tsc twice.  The first time, the result of the 'files1/**/*.ts' glob will be
      // passed to tsc with the --out switch as 'out/ObjectStyle/1.js'.
      // see https://github.com/gruntjs/grunt-docs/blob/master/Configuring-tasks.md#files-object-format
      files: {
        'out/ObjectStyle/1.js': ['files1/**/*.ts'],
        'out/ObjectStyle/2.js': ['files2/**/*.ts']
      },
      options: {
        fast: 'never'
      }
    },
    compileTwoSetsOfFilesToDirUsingObjectStyle: {
      // This will run tsc once.  The result of the globs will be passed to tsc with the
      // --outDir switch as 'out/ObjectStyle'.
      // see https://github.com/gruntjs/grunt-docs/blob/master/Configuring-tasks.md#files-object-format
      files: {
        'out/ObjectStyle': ['files1/**/*.ts','files2/**/*.ts']
        },
        options: {
          fast: 'never'
        }
      }
    }
});

html

Grunt-ts supports compilation of .html file content to TypeScript variables which is explained in detail here. The html target property acts similarly to src, except that it searches for html files to convert to TypeScript variables. See also htmlModuleTemplate and htmlVarTemplate.

// How to use the html target property (incomplete example)
grunt.initConfig({
  ts: {
    default: {
      html: ["templates/**/*.html"]
    }
  }
});

Note: the html compilation functionality will not fire if the src property is not specified. If you wish to only have the HTML compile to TypeScript without compiling the resulting .ts files to JavaScript, make sure they're excluded from the src globs, or else specify an empty src array alongside the html task property, and set the target compile option to false:

// Example of how to compile html files to TypeScript without compiling the resulting
// .ts files to JavaScript.
grunt.initConfig({
  ts: {
    default: {
      html: ["templates/**/*.html"],
      src: [],
      options: {
        compile: false
      }
    }
  }
});

options

This section allows global configuration for the grunt-ts task. All target-specific options are supported. If a target also has options set, the target's options override the global task options.

out

Passes the --out switch to tsc. This will cause the emitted JavaScript to be concatenated to a single file if your code allows for that.

grunt.initConfig({
  ts: {
    default: {
      out: "dist/myscript.js"
    }
  }
});

Warning: Using the compiler with out and reference will prevent grunt-ts from using its fast compile feature. Consider using external modules with transforms instead.

outDir

Passes the --outDir switch to tsc. This will redirect the emitted JavaScript to the specified directory and subdirectories.

grunt.initConfig({
  ts: {
    default: {
      outDir: "dist"
    }
  }
});

reference

Grunt-ts can generate a reference TypeScript file which will contains a reference to all other found .ts files.

This means that the developer will not need to cross-reference files manually; instead they can just reference reference.ts.

grunt.initConfig({
  ts: {
    default: {
      reference: "references.ts"
    }
  }
});

Warning: Using the compiler with out and reference will prevent grunt-ts from using its fast compile feature. Consider using external modules with transforms instead.

src

Allows you to specify the TypeScript files that will be passed to the compiler. Supports standard GruntJS functionality such as globbing. More info at Configuring GruntJS Tasks](http://gruntjs.com/configuring-tasks#files).

grunt.initConfig({
  ts: {
    default: {
      src: ["app/**/*.ts"]
    }
  }
});

watch

Grunt-ts can watch a directory and recompile TypeScript files when any TypeScript or HTML file is changed, added, or removed. Use the watch target option specifying a target directory that will be watched. All subdirectories are automatically included.

Note: this feature does not allow for additional tasks to run after the compilation step is done - for that you should use grunt-contrib-watch.

grunt.initConfig({
  ts: {
    default: {
      watch: "."  //will re-run this task if any .ts or .html file is changed.
    }
  }
});

grunt-ts target options

compile

true (default)| false

Indicates if the TypeScript compilation should be attempted. Turn this off if you wish to just run transforms.

grunt.initConfig({
  ts: {
    default: {
      options: {
        compile: false
      }
    }
  }
});

compiler

This target option allows the developer to select an alternate TypeScript compiler.

To use the alternate compiler that is included with grunt-ts, update your gruntfile.js file with this code:

grunt.initConfig({
  ts: {
    options: {
      compiler: './node_modules/grunt-ts/customcompiler/tsc'
    }
  }
});

To use another compiler version, download it from the current TypeScript repository on GitHub or the old TypeScript repository on CodePlex and extract it to a folder in your project. The compiler will be in the bin folder. Copy all of the files to your project folder and then reference tsc using the compiler task option. For example, if you extracted everything to a mycompiler folder in your project, you'd set the grunt-ts compiler property to './mycompiler/tsc'.

noResolve

true | false (default)

Deprecated: Grunt-ts supports passing this parameter to legacy versions of tsc. It will pass --noResolve on the command line.

comments

true | false (default)

Retains comments in the emitted JavaScript if set to true. Removes comments if set to false. Note that if comments and removeComments are both used, the value of removeComments will win; regardless, please don't do this as it is just confusing to everyone.

grunt.initConfig({
  ts: {
    options: {
      comments: true //preserves comments in output.
    }
  }
});

removeComments

true (default)| false

Removes comments in the emitted JavaScript if set to true. Preserves comments if set to false. Note that if comments and removeComments are both used, the value of removeComments will win; regardless, please don't do this as it is just confusing to everyone.

grunt.initConfig({
  ts: {
    options: {
      removeComments: false //preserves comments in output.
    }
  }
});

declaration

true | false (default)

Generates corresponding .d.ts file(s) for compiled TypeScript files.

grunt.initConfig({
  ts: {
    options: {
      declaration: true
    }
  }
});

failOnTypeErrors

true | false (default)

TypeScript has two types of errors: emit preventing and non-emit preventing. Generally, type errors do not prevent the JavaScript emit. Therefore, it can be useful to allow the Grunt pipeline to continue even if there are type errors because tsc will still generate JavaScript.

If failOnTypeErrors is enabled, grunt-ts will halt the Grunt pipeline if there is a TypeScript type error even if it wouldn't have prevented the emit. Note that syntax errors or other general tsc errors will always halt the pipeline.

grunt.initConfig({
  ts: {
    options: {
      failOnTypeErrors: true
    }
  }
});

fast

"watch" (default) | "always" | "never"

If you are using external modules, grunt-ts will try to do a fast compile by default, basically only compiling what's changed. It should "just work" with the built-in file watching as well as with external tools like grunt-contrib-watch.

To do a fast compile, grunt-ts maintains a cache of hashes for TypeScript files in the .tscache folder to detect changes (needed for external watch tool support). It also creates a .baseDir.ts file at the root, passing it to the compiler to make sure that --outDir is always respected in the generated JavaScript.

You can customize the behaviour of grunt-ts fast.

If you are using files, grunt-ts can't do a fast compile. You should set fast to 'never'.

grunt.initConfig({
  ts: {
    options: {
      // disable the grunt-ts fast feature
      fast: 'never'
    }
  }
});

htmlModuleTemplate

Grunt-ts supports compilation of .html file content to TypeScript variables which is explained in detail here. The htmlModuleTemplate target property allows the developer to define a namespace for the templates. See also html and htmlVarTemplate.

//Note: incomplete - combine with html and htmlVarTemplate
grunt.initConfig({
  ts: {
    default: {
      options: {
        //MyTemplate.html will be accessible as HtmlTemplates.MyTemplate
        htmlModuleTemplate: 'HtmlTemplates.<%= filename %>'
      }
    }
  }
});

htmlVarTemplate

Grunt-ts supports compilation of .html file content to TypeScript variables which is explained in detail here. The htmlVarTemplate target property allows the developer to define a property name for the template contents. See also html and htmlModuleTemplate.

//Note: incomplete - combine with html and htmlModuleTemplate
grunt.initConfig({
  ts: {
    default: {
      options: {
        //HTML template objects will expose their content via a property called markup.
        htmlVarTemplate: 'markup'
      }
    }
  }
});

mapRoot

Specifies the root for where .js.map sourcemap files should be referenced. This is useful if you intend to move your .js.map files to a different location. Leave this blank or omit entirely if the .js.map files will be deployed to the same folder as the corresponding .js files. See also sourceRoot.

grunt.initConfig({
  ts: {
    default: {
      options: {
        //When abc.ts is compiled to abc.js, it will reference /maps/abc.js.map
        mapRoot: "/maps"
      }
    }
  }
});

module

"amd" (default) | "commonjs" | ""

Specifies if TypeScript should emit AMD or CommonJS-style external modules. Has no effect if internal modules are used.

grunt.initConfig({
  ts: {
    default: {
      options: {
        module: "amd"
      }
    }
  }
});

noImplicitAny

true | false (default)

Set to true to pass --noImplicitAny to the compiler. Requires more strict type checking. If noImplicitAny is enabled, TypeScript will raise a type error whenever it is unable to infer the type of a variable.

grunt.initConfig({
  ts: {
    default: {
      options: {
        noImplicitAny: true
      }
    }
  }
});

sourceMap

true (default) | false

If true, grunt-ts will instruct tsc to emit source maps (.js.map files).

grunt.initConfig({
  ts: {
    default: {
      options: {
        sourceMap: true
      }
    }
  }
});

sourceRoot

The sourceRoot to use in the emitted source map files. Allows mapping moved .js.map files back to the original TypeScript files. See also mapRoot.

grunt.initConfig({
  ts: {
    default: {
      options: {
        sourceRoot: "/dev"
      }
    }
  }
});

target

"es5" (default) | "es3"

Allows the developer to specify if they are targeting ECMAScript version 3 or 5. Only select ES3 if you are targeting old browsers (IE8 or below). The default for grunt-ts (es5) is different than the default for tsc (es3).

grunt.initConfig({
  ts: {
    default: {
      options: {
        target: "es3" //for IE8 and below
      }
    }
  }
});

verbose

false (default) | true

Will print the switches passed to tsc on the console. Helpful for debugging.

grunt.initConfig({
  ts: {
    default: {
      options: {
        verbose: true
      }
    }
  }
});

Transforms

Objective : To allow easier code refactoring by taking the relative path maintenance burden off the developer. If the paths to the files changes grunt-ts will regenerate the relevant sections.

Transforms begin with a three-slash comment /// and are prefixed with ts:

You can also run transforms without compiling your code by setting compile: false in your config. For example:

grunt.initConfig({
  ts: {
    "transforms-only": {
      options: {
        compile: false
      },
      // in addition to your standard settings:
      // src: ...
      // outDir: ...
    },
    // ...
  }
} );

Import Transform

///ts:import=<fileOrDirectoryName>[,<variableName>]

This will generate the relevant import foo = require('./path/to/foo'); code without you having to figure out the relative path.

If a directory is provided, the entire contents of the directory will be imported. However if a directory has a file index.ts inside of it, then instead of importing the entire folder only index.ts is imported.

Examples

Import file:

///ts:import=filename
import filename = require('../path/to/filename'); ///ts:import:generated

Import file with an alternate name:

///ts:import=BigLongClassName,foo
import foo = require('../path/to/BigLongClassName'); ///ts:import:generated

Import directory:

///ts:import=directoryName
import filename = require('../path/to/directoryName/filename'); ///ts:import:generated
import anotherfile = require('../path/to/directoryName/deeper/anotherfile'); ///ts:import:generated
...

Import directory that has an index.ts file in it:

///ts:import=directoryName
import directoryName = require('../path/to/directoryName/index'); ///ts:import:generated

See Exports for examples of how grunt-ts can generate an index.ts file for you

Export Transform

///ts:export=<fileOrDirectoryName>[,<variableName>]

This is similar to ///ts:import but will generate export import foo = require('./path/to/foo'); and is very useful for generating indexes of entire module directories when using external modules (which you should always be using).

Examples

Export file:

///ts:export=filename
export import filename = require('../path/to/filename'); ///ts:export:generated

Export file with an alternate name:

///ts:export=filename,foo
export import foo = require('../path/to/filename'); ///ts:export:generated

Export directory:

///ts:export=dirName
export import filename = require('../path/to/dirName/filename'); ///ts:export:generated
export import anotherfile = require('../path/to/dirName/deeper/anotherfile'); ///ts:export:generated
...

References

///ts:ref=<fileName>

This will generate the relevant /// <references path="./path/to/foo" /> code without you having to figure out the relative path.

Examples

Reference file:

///ts:ref=filename
/// <reference path='../path/to/filename'/> ///ts:ref:generated

JavaScript Generation

When a output file is specified via out in combination with a reference file via reference then grunt-ts uses the generated reference file to order the code in the generated JavaScript.

Use reference.ts to specify the order for the few files the build really cares about and leave the rest to be maintained by grunt-ts.

E.g. in the following case the generated JavaScript for someBaseClass.ts is guaranteed to be at the top, and the generated JavaScript for main.ts is guaranteed to be at the bottom of the single merged js file.

Everything between grunt-start and grunt-end is generated and maintained by grunt-ts. If there is no grunt-start section found, it is created. If reference.ts does not exist originally, it is also created.

/// <reference path="someBaseClass.ts" />

// Put comments here and they are preserved

//grunt-start
/// <reference path="autoreference.ts" />
/// <reference path="someOtherFile.ts" />
//grunt-end


/// <reference path="main.ts" />

Video Examples

TypeScript programming using grunt-ts (YouTube):

AngularJS + TypeScript : Workflow with grunt-ts (YouTube)

Contributing

With npm and grunt-cli installed, run the following from the root of the repository:

$ npm install

Building the project:

To build all

$ grunt build

Running the tests:

To test all

$ grunt test

Before PR

$ grunt release

It runs build followed by test. This is also the default task. You should run this before sending a PR.

Development

You will probably be working and testing a particular feature. Modify tasksToTest in our Gruntfile.js and run:

$ grunt dev

It will watch your changes (to grunt-ts task as well as examples) and run your tasksToTest after updating the task (if any changes detected).

Additional commands

Update the current grunt-ts to be the last known good version (dogfood). Commit message should be Update LKG.

$ grunt upgrade

Publishing Checklist

  • Update the version in package.json.
  • Update CHANGELOG.md.
  • Commit to master.
  • Push to npm.
  • Push version tag to GitHub.

License

Licensed under the MIT License.