Skip to content
Browse files

* migrated to async compile

added es6-promise dependency
added es6-promises definitions
added source-map-support devDependency

implemented  es6-promise in tasks/ts.ts
- moved imports to top
- added asyncSeries helper to run each file
- diff is dirty because of trailing whitespace
- few test edits
- added some whitespace

moved to grunt.util.spawn()
- refactored compileAllFiles() helper
- added small node helper
- dropped shelljs dependency

reworked resolveTypeScriptBinPath()
- simplified logic
- non recursive
- added tsc version log

added simple compile test

hardened linting
- added rules to tslint.json
- added grunt-contrib-jshint dependency

dropped unused grunt-tasks

expanded Gruntfile.js
- added test_all task bundle
- annotated ts test tasks
- npm test / grunt test run all annotated tests
- added some comments
- linted (quotes & whitespace)

fixed error in gruntjs.d.ts:
- async callback is a function
  • Loading branch information...
1 parent 055bb9f commit 0307f4df20649df6df06179e17a052ae913b48b0 @Bartvds Bartvds committed
Showing with 614 additions and 265 deletions.
  1. +64 −21 Gruntfile.js
  2. +133 −0 defs/es6-promises/es6-promises.d.ts
  3. +4 −3 defs/gruntjs/gruntjs.d.ts
  4. +2 −0 defs/tsd.d.ts
  5. +9 −7 package.json
  6. +157 −98 tasks/ts.js
  7. +194 −135 tasks/ts.ts
  8. +15 −0 test/simple/js/zoo.d.ts
  9. +28 −0 test/simple/ts/zoo.ts
  10. +3 −0 tsd.json
  11. +5 −1 tslint.json
View
85 Gruntfile.js
@@ -1,25 +1,33 @@
module.exports = function (grunt) {
- "use strict";
+ 'use strict';
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
clean: {
test: [
- "test/**/*.js",
- "test/**/*.html.ts",
+ 'test/**/*.js',
+ 'test/**/*.js.map',
+ 'test/**/*.html.ts',
]
},
+ jshint: {
+ options: grunt.util._.extend(grunt.file.readJSON('.jshintrc'), {
+ reporter: './node_modules/jshint-path-reporter'
+ }),
+ support: ['Gruntfile.js']
+ },
tslint: {
+ options: {
+ configuration: grunt.file.readJSON('tslint.json'),
+ formatter: 'tslint-path-formatter'
+ },
source: {
- options: {
- configuration: grunt.file.readJSON('tslint.json'),
- formatter: 'tslint-path-formatter'
- },
src: ['tasks/**/*.ts']
}
},
- "ts-internal": {
+ 'ts-internal': {
options: {
+ target: 'es5',
module: 'commonjs',
comments: true,
sourcemap: true,
@@ -31,7 +39,7 @@ module.exports = function (grunt) {
}
},
ts: {
- options: {
+ options: { // override the main options, See : http://gruntjs.com/configuring-tasks#options
target: 'es3', // 'es3' (default) | 'es5'
module: 'commonjs', // use amd for asynchonous loading or commonjs 'amd' (default) | 'commonjs'
sourcemap: true, // generate a source map file for each result js file (true (default) | false)
@@ -41,19 +49,30 @@ module.exports = function (grunt) {
verbose: true // print the tsc command (true | false (default))
},
dev: { // a particular target
- src: ["test/work/**/*.ts"], // The source typescript files, See : http://gruntjs.com/configuring-tasks#files
+ src: ['test/work/**/*.ts'], // The source typescript files, See : http://gruntjs.com/configuring-tasks#files
out: 'test/work/out.js', // If specified, generate an out.js file which is the merged js file
options: { // override the main options, See : http://gruntjs.com/configuring-tasks#options
sourcemap: true,
declaration: true
},
},
+ simple: {
+ test: true,
+ options: {
+ sourcemap: true,
+ declaration: true
+ },
+ src: ['test/simple/ts/zoo.ts'],
+ outDir: 'test/simple/js/',
+ },
abtest: {
+ test: true,
src: ['test/abtest/**/*.ts'],
reference: 'test/abtest/reference.ts',
out: 'test/abtest/out.js',
},
amdloadersrc: {
+ test: true,
src: ['test/amdloader/ts/app/**/*.ts'],
html: ['test/amdloader/ts/app/**/*.html'],
reference: 'test/amdloader/ts/app/reference.ts',
@@ -62,6 +81,7 @@ module.exports = function (grunt) {
// watch: 'test/amdloader/app'
},
amdloadertest: {
+ test: true,
src: ['test/amdloader/ts/test/**/*.ts'],
html: ['test/amdloader/ts/test/**/*.html'],
reference: 'test/amdloader/ts/test/reference.ts',
@@ -69,12 +89,14 @@ module.exports = function (grunt) {
amdloader: 'test/amdloader/js/test/loader.js',
},
amdtest: {
+ test: true,
src: ['test/amdtest/**/*.ts'],
options: {
module: 'amd'
}
},
warnbothcomments: {
+ test: true,
src: ['test/abtest/**/*.ts'],
reference: 'test/abtest/reference.ts',
out: 'test/abtest/out.js',
@@ -85,18 +107,21 @@ module.exports = function (grunt) {
},
},
htmltest: {
+ test: true,
src: ['test/html/**/*.ts'],
html: ['test/html/**/*.tpl.html'],
reference: 'test/html/reference.ts',
out: 'test/html/out.js',
},
definitelyTypedTest: {
+ test: true,
src: ['test/definitelytypedtest/**/*.ts'],
html: ['test/definitelytypedtest/**/*.tpl.html'],
reference: 'test/definitelytypedtest/reference.ts',
out: 'test/definitelytypedtest/out.js',
},
nocompile: {
+ test: true,
src: ['test/nocompile/**/*.ts'],
reference: 'test/nocompile/reference.ts',
out: 'test/nocompile/out.js',
@@ -105,10 +130,12 @@ module.exports = function (grunt) {
}
},
outdirtest: {
+ test: true,
src: ['test/outdirtest/**/*.ts'],
outDir: 'test/outdirtest/js',
},
sourceroottest: {
+ test: true,
src: ['test/sourceroot/src/**/*.ts'],
html: ['test/sourceroot/src/**/*.html'],
reference: 'test/sourceroot/src/reference.ts',
@@ -119,6 +146,7 @@ module.exports = function (grunt) {
},
},
templatecache: {
+ test: true,
src: ['test/templatecache/**/*.ts'],
reference: 'test/templatecache/ts/reference.ts',
amdloader: 'test/templatecache/js/loader.js',
@@ -130,7 +158,7 @@ module.exports = function (grunt) {
},
},
fail: { // a designed to fail target
- src: ["test/fail/**/*.ts"],
+ src: ['test/fail/**/*.ts'],
// watch: 'test',
options: { // overide the main options for this target
sourcemap: false,
@@ -139,7 +167,9 @@ module.exports = function (grunt) {
}
});
- grunt.registerTask("upgrade", function () {
+ // Helper to upgrade internal compiler task (fresh dogfood)
+ // Only do this when stable!
+ grunt.registerTask('upgrade', function () {
var next = grunt.file.read('./tasks/ts.js');
var pattern = 'grunt.registerMultiTask(\'ts\',';
@@ -151,19 +181,32 @@ module.exports = function (grunt) {
}
next = next.replace(pattern, internal);
next = '// v' + grunt.config.get('pkg.version') + ' ' + new Date().toISOString() + '\r\n' + next;
- grunt.file.write('./tasks/ts-internal.js', next)
+ grunt.file.write('./tasks/ts-internal.js', next);
});
- // Loading it for testing since I have in a local "tasks" folder
- grunt.loadTasks("tasks");
+ // Collect test tasks
+ grunt.registerTask('test_all', grunt.util._.reduce(grunt.config.get('ts'), function (memo, task, name) {
+ if (task.test) {
+ memo.push('ts:' + name);
+ }
+ return memo;
+ }, []));
+
+ // Loading it for testing since we have in a local 'tasks' folder
+ grunt.loadTasks('tasks');
// in your configuration you would load this like:
- //grunt.loadNpmTasks("grunt-ts")
+ //grunt.loadNpmTasks('grunt-ts')
+
+ grunt.loadNpmTasks('grunt-contrib-clean');
+ grunt.loadNpmTasks('grunt-contrib-jshint');
+ grunt.loadNpmTasks('grunt-tslint');
- grunt.loadNpmTasks("grunt-contrib-clean");
- grunt.loadNpmTasks("grunt-tslint");
+ grunt.registerTask('prep', ['clean', 'jshint:support']);
+ grunt.registerTask('build', ['prep', 'ts-internal:build', 'tslint:source']);
+ grunt.registerTask('test', ['test_all']);
+ grunt.registerTask('default', ['test']);
- grunt.registerTask("build", ["clean", "ts-internal:build", "tslint:source"]);
- grunt.registerTask("test", ["build", "ts:htmltest", "ts:definitelyTypedTest"]);
- grunt.registerTask("default", ["test"]);
+ grunt.registerTask('run', ['ts:amdloadersrc']);
+ grunt.registerTask('dev', ['ts:simple']);
};
View
133 defs/es6-promises/es6-promises.d.ts
@@ -0,0 +1,133 @@
+// Type definitions for es6-promises
+// Project: https://github.com/jakearchibald/ES6-Promises
+// Definitions by: François de Campredon <https://github.com/fdecampredon/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+
+
+interface Thenable<R> {
+ then<U>(onFulfilled: (value: R) => Thenable<U>, onRejected: (error: any) => Thenable<U>): Thenable<U>;
+ then<U>(onFulfilled: (value: R) => Thenable<U>, onRejected?: (error: any) => U): Thenable<U>;
+ then<U>(onFulfilled: (value: R) => U, onRejected: (error: any) => Thenable<U>): Thenable<U>;
+ then<U>(onFulfilled?: (value: R) => U, onRejected?: (error: any) => U): Thenable<U>;
+
+}
+
+declare class Promise<R> implements Thenable<R> {
+ /**
+ * If you call resolve in the body of the callback passed to the constructor,
+ * your promise is fulfilled with result object passed to resolve.
+ * If you call reject your promise is rejected with the object passed to resolve.
+ * For consistency and debugging (eg stack traces), obj should be an instanceof Error.
+ * Any errors thrown in the constructor callback will be implicitly passed to reject().
+ */
+ constructor(callback: (resolve : (result: R) => void, reject: (error: any) => void) => void);
+ /**
+ * If you call resolve in the body of the callback passed to the constructor,
+ * your promise will be fulfilled/rejected with the outcome of thenable passed to resolve.
+ * If you call reject your promise is rejected with the object passed to resolve.
+ * For consistency and debugging (eg stack traces), obj should be an instanceof Error.
+ * Any errors thrown in the constructor callback will be implicitly passed to reject().
+ */
+ constructor(callback: (resolve : (thenable: Thenable<R>) => void, reject: (error: any) => void) => void);
+
+
+ /**
+ * onFulFill is called when/if "promise" resolves. onRejected is called when/if "promise" rejects.
+ * Both are optional, if either/both are omitted the next onFulfilled/onRejected in the chain is called.
+ * Both callbacks have a single parameter , the fulfillment value or rejection reason.
+ * "then" returns a new promise equivalent to the value you return from onFulfilled/onRejected after being passed through Promise.resolve.
+ * If an error is thrown in the callback, the returned promise rejects with that error.
+ *
+ * @param onFulFill called when/if "promise" resolves
+ * @param onReject called when/if "promise" rejects
+ */
+ then<U>(onFulfill: (value: R) => Thenable<U>, onReject: (error: any) => Thenable<U>): Promise<U>;
+ /**
+ * onFulFill is called when/if "promise" resolves. onRejected is called when/if "promise" rejects.
+ * Both are optional, if either/both are omitted the next onFulfilled/onRejected in the chain is called.
+ * Both callbacks have a single parameter , the fulfillment value or rejection reason.
+ * "then" returns a new promise equivalent to the value you return from onFulfilled/onRejected after being passed through Promise.resolve.
+ * If an error is thrown in the callback, the returned promise rejects with that error.
+ *
+ * @param onFulFill called when/if "promise" resolves
+ * @param onReject called when/if "promise" rejects
+ */
+ then<U>(onFulfill: (value: R) => Thenable<U>, onReject?: (error: any) => U): Promise<U>;
+ /**
+ * onFulFill is called when/if "promise" resolves. onRejected is called when/if "promise" rejects.
+ * Both are optional, if either/both are omitted the next onFulfilled/onRejected in the chain is called.
+ * Both callbacks have a single parameter , the fulfillment value or rejection reason.
+ * "then" returns a new promise equivalent to the value you return from onFulfilled/onRejected after being passed through Promise.resolve.
+ * If an error is thrown in the callback, the returned promise rejects with that error.
+ *
+ * @param onFulFill called when/if "promise" resolves
+ * @param onReject called when/if "promise" rejects
+ */
+ then<U>(onFulfill: (value: R) => U, onReject: (error: any) => Thenable<U>): Promise<U>;
+ /**
+ * onFulFill is called when/if "promise" resolves. onRejected is called when/if "promise" rejects.
+ * Both are optional, if either/both are omitted the next onFulfilled/onRejected in the chain is called.
+ * Both callbacks have a single parameter , the fulfillment value or rejection reason.
+ * "then" returns a new promise equivalent to the value you return from onFulfilled/onRejected after being passed through Promise.resolve.
+ * If an error is thrown in the callback, the returned promise rejects with that error.
+ *
+ * @param onFulFill called when/if "promise" resolves
+ * @param onReject called when/if "promise" rejects
+ */
+ then<U>(onFulfill?: (value: R) => U, onReject?: (error: any) => U): Promise<U>;
+
+
+ /**
+ * Sugar for promise.then(undefined, onRejected)
+ *
+ * @param onReject called when/if "promise" rejects
+ */
+ catch<U>(onReject?: (error: any) => Thenable<U>): Promise<U>;
+ /**
+ * Sugar for promise.then(undefined, onRejected)
+ *
+ * @param onReject called when/if "promise" rejects
+ */
+ catch<U>(onReject?: (error: any) => U): Promise<U>;
+}
+
+declare module Promise {
+
+ /**
+ * Returns promise (only if promise.constructor == Promise)
+ */
+ function cast<R>(promise: Promise<R>): Promise<R>;
+ /**
+ * Make a promise that fulfills to obj.
+ */
+ function cast<R>(object?: R): Promise<R>;
+
+
+ /**
+ * Make a new promise from the thenable.
+ * A thenable is promise-like in as far as it has a "then" method.
+ * This also creates a new promise if you pass it a genuine JavaScript promise, making it less efficient for casting than Promise.cast.
+ */
+ function resolve<R>(thenable: Thenable<R>): Promise<R>;
+ /**
+ * Make a promise that fulfills to obj. Same as Promise.cast(obj) in this situation.
+ */
+ function resolve<R>(object?: R): Promise<R>;
+
+ /**
+ * Make a promise that rejects to obj. For consistency and debugging (eg stack traces), obj should be an instanceof Error
+ */
+ function reject(error?: any): Promise<any>;
+
+ /**
+ * Make a promise that fulfills when every item in the array fulfills, and rejects if (and when) any item rejects.
+ * the array passed to all can be a mixture of promise-like objects and other objects.
+ * The fulfillment value is an array (in order) of fulfillment values. The rejection value is the first rejection value.
+ */
+ function all<R>(promises: Promise<R>[]): Promise<R[]>;
+
+ /**
+ * Make a Promise that fulfills when any item fulfills, and rejects if any item rejects.
+ */
+ function race<R>(promises: Promise<R>[]): Promise<R>;
+}
View
7 defs/gruntjs/gruntjs.d.ts
@@ -816,9 +816,10 @@ declare module grunt {
* Either false or an Error object may be passed to the done function
* to instruct Grunt that the task has failed.
*/
- done(success: boolean): void;
- done(error: Error): void;
- done(result: any): void;
+ (success: boolean): void;
+ (error: Error): void;
+ (result: any): void;
+ (): void;
}
/**
View
2 defs/tsd.d.ts
@@ -2,3 +2,5 @@
/// <reference path="underscore.string/underscore.string.d.ts" />
/// <reference path="underscore/underscore.d.ts" />
/// <reference path="gruntjs/gruntjs.d.ts" />
+/// <reference path="es6-promises/es6-promises.d.ts" />
+
View
16 package.json
@@ -41,24 +41,26 @@
"dependencies": {
"typescript": "0.9.5",
"chokidar": "0.6.2",
- "shelljs": "0.1.4",
"underscore": "1.5.1",
- "underscore.string": "2.3.3"
+ "underscore.string": "2.3.3",
+ "es6-promise": "~0.1.1"
},
"peerDependencies": {
"grunt": "~0.4.0"
},
"devDependencies": {
"grunt": "~0.4.0",
- "grunt-contrib-clean": "~0.4.0",
- "grunt-contrib-watch": "~0.5.1",
- "grunt-contrib-nodeunit": "~0.1.2",
"grunt-tslint": "~0.4.0",
- "tslint-path-formatter": "~0.1.1"
+ "grunt-contrib-clean": "~0.4.0",
+ "grunt-contrib-jshint": "~0.8.0",
+ "tslint-path-formatter": "~0.1.1",
+ "source-map-support": "~0.2.5",
+ "jshint-path-reporter": "~0.1.3"
},
"optionalDependencies": {},
"keywords": [
"gruntplugin",
- "typescript"
+ "typescript",
+ "compiler"
]
}
View
255 tasks/ts.js
@@ -1,7 +1,17 @@
-/// <reference path="../defs/node/node.d.ts"/>
-/// <reference path="../defs/grunt/gruntjs.d.ts"/>
-/// <reference path="../defs/underscore/underscore.d.ts"/>
-/// <reference path="../defs/underscore.string/underscore.string.d.ts"/>
+/// <reference path="../defs/tsd.d.ts"/>
+/*
+* grunt-ts
+* Licensed under the MIT license.
+*/
+// Typescript imports
+var _ = require('underscore');
+var _str = require('underscore.string');
+var path = require('path');
+var fs = require('fs');
+
+// plain vanilla imports
+var pathSeperator = path.sep;
+var Promise = require('es6-promise').Promise;
var ReferenceOrder;
(function (ReferenceOrder) {
@@ -84,98 +94,143 @@ function getTempFile(prefix, dir) {
throw 'Cannot create temp file in ' + dir;
}
-// Typescript imports
-var _ = require('underscore');
-var _str = require('underscore.string');
-var path = require('path');
-var fs = require('fs');
+/**
+* Run a map operation async in series (simplified)
+*/
+function asyncSeries(arr, iter) {
+ arr = arr.slice(0);
-// plain vanilla imports
-var shell = require('shelljs');
-var pathSeperator = path.sep;
+ var memo = [];
+
+ // Run one at a time
+ return new Promise(function (resolve, reject) {
+ var next = function () {
+ if (arr.length === 0) {
+ resolve(memo);
+ return;
+ }
+ Promise.cast(iter(arr.shift())).then(function (res) {
+ memo.push(res);
+ next();
+ }, reject);
+ };
+ next();
+ });
+}
function pluginFn(grunt) {
+ // Helper
+ function executeNode(args) {
+ return new Promise(function (resolve, reject) {
+ grunt.util.spawn({
+ cmd: 'node',
+ args: args
+ }, function (error, result, code) {
+ var ret = {
+ code: code,
+ output: String(result)
+ };
+ resolve(ret);
+ });
+ });
+ }
+
/////////////////////////////////////////////////////////////////////
// tsc handling.
////////////////////////////////////////////////////////////////////
- function resolveTypeScriptBinPath(currentPath, depth) {
- var targetPath = path.resolve(__dirname, (new Array(depth + 1)).join('../../'), '../node_modules/typescript/bin');
- if (path.resolve(currentPath, 'node_modules/typescript/bin').length > targetPath.length) {
- return null;
- }
- if (fs.existsSync(path.resolve(targetPath, 'typescript.js'))) {
- return targetPath;
+ function resolveTypeScriptBinPath() {
+ var ownRoot = path.resolve(path.dirname((module).filename), '..');
+ var userRoot = path.resolve(ownRoot, '..', '..');
+ var binSub = path.join('node_modules', 'typescript', 'bin');
+
+ if (fs.existsSync(path.join(userRoot, binSub))) {
+ // Using project override
+ return path.join(userRoot, binSub);
}
-
- return resolveTypeScriptBinPath(currentPath, ++depth);
+ return path.join(ownRoot, binSub);
}
+
function getTsc(binPath) {
- return '"' + binPath + '/' + 'tsc"';
+ var pkg = JSON.parse(fs.readFileSync(path.resolve(binPath, '..', 'package.json')).toString());
+ grunt.log.writeln('Using tsc v' + pkg.version);
+
+ return path.join(binPath, 'tsc');
}
+
var eol = grunt.util.linefeed;
- var cwd = path.resolve('.');
- var tsc = getTsc(resolveTypeScriptBinPath(cwd, 0));
// Blindly runs the tsc task using provided options
function compileAllFiles(files, target, task) {
- var cmd = files.join(' ');
+ var args = files.slice(0);
// boolean options
- if (task.sourceMap)
- cmd = cmd + ' --sourcemap';
- if (task.declaration)
- cmd = cmd + ' --declaration';
- if (task.removeComments)
- cmd = cmd + ' --removeComments';
- if (task.noImplicitAny)
- cmd = cmd + ' --noImplicitAny';
- if (task.noResolve)
- cmd = cmd + ' --noResolve';
+ if (task.sourceMap) {
+ args.push('--sourcemap');
+ }
+ if (task.declaration) {
+ args.push('--declaration');
+ }
+ if (task.removeComments) {
+ args.push('--removeComments');
+ }
+ if (task.noImplicitAny) {
+ args.push('--noImplicitAny');
+ }
+ if (task.noResolve) {
+ args.push('--noResolve');
+ }
// string options
- cmd = cmd + ' --target ' + task.target.toUpperCase();
- cmd = cmd + ' --module ' + task.module.toLowerCase();
+ args.push('--target', task.target.toUpperCase());
+ args.push('--module', task.module.toLowerCase());
// Target options:
if (target.out) {
- cmd = cmd + ' --out ' + target.out;
+ args.push('--out', target.out);
}
if (target.outDir) {
if (target.out) {
console.warn('WARNING: Option "out" and "outDir" should not be used together'.magenta);
}
- cmd = cmd + ' --outDir ' + target.outDir;
+ args.push('--outDir', target.outDir);
}
if (task.sourceRoot) {
- cmd = cmd + ' --sourceRoot ' + task.sourceRoot;
+ args.push('--sourceRoot', task.sourceRoot);
}
if (task.mapRoot) {
- cmd = cmd + ' --mapRoot ' + task.mapRoot;
+ args.push('--mapRoot', task.mapRoot);
}
+ // Locate a compiler
+ var tsc = getTsc(resolveTypeScriptBinPath());
+
// To debug the tsc command
if (task.verbose) {
- console.log(cmd.yellow);
+ console.log(args.join(' ').yellow);
} else {
- grunt.log.verbose.writeln(cmd.yellow);
+ grunt.log.verbose.writeln(args.join(' ').yellow);
}
// Create a temp last command file and use that to guide tsc.
// Reason: passing all the files on the command line causes TSC to go in an infinite loop.
var tempfilename = getTempFile('tscommand');
if (!tempfilename) {
- return null;
+ throw (new Error('cannot create temp file'));
}
- var tscExecCommand = 'node ' + tsc + ' @' + tempfilename;
- fs.writeFileSync(tempfilename, cmd);
+ fs.writeFileSync(tempfilename, args.join(' '));
- var result = shell.exec(tscExecCommand);
+ // Execute command
+ return executeNode([tsc, '@' + tempfilename]).then(function (result) {
+ fs.unlinkSync(tempfilename);
- // Cleanup
- fs.unlinkSync(tempfilename);
+ grunt.log.writeln(result.output);
- return result;
+ return Promise.cast(result);
+ }, function (err) {
+ fs.unlinkSync(tempfilename);
+ throw err;
+ });
}
/////////////////////////////////////////////////////////////////////
@@ -297,6 +352,7 @@ function pluginFn(grunt) {
};
var sortedGeneratedFiles = _.sortBy(generatedFiles);
+
function isGeneratedFile(filename) {
return _.indexOf(sortedGeneratedFiles, filename, true) !== -1;
}
@@ -306,8 +362,8 @@ function pluginFn(grunt) {
// When writing
var referenceIntro = '/// <reference path="';
- var referenceEnd = '" />';
+ // var referenceEnd = '" />';
// The section of unordered files
var ourSignatureStart = '//grunt-start';
var ourSignatureEnd = '//grunt-end';
@@ -479,6 +535,7 @@ function pluginFn(grunt) {
});
return files;
}
+
grunt.log.verbose.writeln('Making files relative to outDir...');
files.before = makeRelativeToOutDir(files.before);
files.generated = makeRelativeToOutDir(files.generated);
@@ -633,6 +690,11 @@ function pluginFn(grunt) {
grunt.registerMultiTask('ts', 'Compile TypeScript files', function () {
var currenttask = this;
+ // make async
+ var done = currenttask.async();
+
+ var watch;
+
// setup default options
var options = currenttask.options({
allowBool: false,
@@ -664,15 +726,11 @@ function pluginFn(grunt) {
if (options.removeComments === options.comments) {
console.warn('Either option will suffice (and removing the other will have no effect).'.magenta);
} else {
- console.warn(('The --removeComments value of "' + options.removeComments + '" ' + 'supercedes the --comments value of ' + options.comments + '"').magenta);
+ console.warn(('The --removeComments value of "' + options.removeComments + '" ' + 'supercedes the --comments value of "' + options.comments + '"').magenta);
}
}
options.removeComments = !!options.removeComments;
- // Was the whole process successful
- var success = true;
- var watch;
-
// Some interesting logs:
// http://gruntjs.com/api/inside-tasks#inside-multi-tasks
// console.log(this)
@@ -683,7 +741,8 @@ function pluginFn(grunt) {
// currenttaks.data as that is the raw (non interpolated) string that we reinterpolate ourselves,
// in case the file system as changed since this task was started
// this.files[0] is actually a single in our case as we gave examples of one source / out per target
- this.files.forEach(function (target) {
+ // Run compiler
+ asyncSeries(this.files, function (target) {
// Create a reference file?
var reference = target.reference;
var referenceFile;
@@ -717,18 +776,15 @@ function pluginFn(grunt) {
amdloaderPath = path.dirname(amdloaderFile);
}
+ var lastCompile = 0;
+
// Compiles all the files
// Uses the blind tsc compile task
// logs errors
- // Time the whole process
- var starttime;
- var endtime;
function runCompilation(files, target, options) {
+ // Don't run it yet
grunt.log.writeln('Compiling...'.yellow);
- // Time the task and go
- starttime = new Date().getTime();
-
// The files to compile
var filesToCompile = files;
@@ -743,22 +799,25 @@ function pluginFn(grunt) {
return '"' + item + '"';
});
+ // Time the compiler process
+ var starttime = new Date().getTime();
+ var endtime;
+
// Compile the files
- var result = compileAllFiles(filesToCompile, target, options);
-
- // End the timer
- endtime = new Date().getTime();
-
- // Evaluate the result
- if (!result || result.code !== 0) {
- var msg = 'Compilation failed';
- grunt.log.error(msg.red);
- return false;
- } else {
- var time = (endtime - starttime) / 1000;
- grunt.log.writeln(('Success: ' + time.toFixed(2) + 's for ' + files.length + ' typescript files').green);
- return true;
- }
+ return compileAllFiles(filesToCompile, target, options).then(function (result) {
+ // End the timer
+ lastCompile = endtime = new Date().getTime();
+
+ // Evaluate the result
+ if (!result || result.code) {
+ grunt.log.error('Compilation failed'.red);
+ return false;
+ } else {
+ var time = (endtime - starttime) / 1000;
+ grunt.log.writeln(('Success: ' + time.toFixed(2) + 's for ' + files.length + ' typescript files').green);
+ return true;
+ }
+ });
}
// Find out which files to compile
@@ -819,30 +878,28 @@ function pluginFn(grunt) {
}
}
- // compile, If there are any files to compile!
+ // Compile, if there are any files to compile!
if (files.length > 0) {
- success = runCompilation(files, target, options);
-
- // Create the loader if specified & compiliation succeeded
- if (success && !!amdloaderPath) {
- var referenceOrder = getReferencesInOrder(referenceFile, referencePath, generatedHtmlFiles);
- updateAmdLoader(referenceFile, referenceOrder, amdloaderFile, amdloaderPath, target.outDir);
- }
+ return runCompilation(files, target, options).then(function (success) {
+ // Create the loader if specified & compiliation succeeded
+ if (success && !!amdloaderPath) {
+ var referenceOrder = getReferencesInOrder(referenceFile, referencePath, generatedHtmlFiles);
+ updateAmdLoader(referenceFile, referenceOrder, amdloaderFile, amdloaderPath, target.outDir);
+ }
+ return success;
+ });
} else {
grunt.log.writeln('No files to compile'.red);
}
}
- }
- // Initial compilation:
- filterFilesAndCompile();
+ // Nothing to do
+ return Promise.resolve(true);
+ }
// Watch a folder?
watch = target.watch;
if (!!watch) {
- // make async
- var done = currenttask.async();
-
// A debounced version of compile
var debouncedCompile = _.debounce(filterFilesAndCompile, 150);
@@ -855,7 +912,7 @@ function pluginFn(grunt) {
// Do not run if just ran, behaviour same as grunt-watch
// These are the files our run modified
- if ((new Date().getTime() - endtime) <= 100) {
+ if ((new Date().getTime() - lastCompile) <= 100) {
// grunt.log.writeln((' ///' + ' >>' + filepath).grey);
return;
}
@@ -886,11 +943,13 @@ function pluginFn(grunt) {
console.error('Error happened in chokidar: ', error);
});
}
- });
-
- if (!watch) {
- return success;
- }
+ return filterFilesAndCompile();
+ }).then(function (res) {
+ // Ignore res? (either logs or throws)
+ if (!watch) {
+ done();
+ }
+ }, done);
});
}
module.exports = pluginFn;
View
329 tasks/ts.ts
@@ -5,6 +5,16 @@
* Licensed under the MIT license.
*/
+// Typescript imports
+import _ = require('underscore');
+import _str = require('underscore.string');
+import path = require('path');
+import fs = require('fs');
+
+// plain vanilla imports
+var pathSeperator = path.sep;
+var Promise: typeof Promise = require('es6-promise').Promise;
+
interface ICompileResult {
code: number;
output: string;
@@ -153,102 +163,147 @@ function getTempFile(prefix?: string, dir: string = ''): string {
throw 'Cannot create temp file in ' + dir;
}
-// Typescript imports
-import _ = require('underscore');
-import _str = require('underscore.string');
-import path = require('path');
-import fs = require('fs');
-// plain vanilla imports
-var shell: any = require('shelljs');
-var pathSeperator = path.sep;
+/**
+ * Run a map operation async in series (simplified)
+ */
+function asyncSeries<U, W>(arr: U[], iter: (item: U) => Promise<W>): Promise<W[]> {
+ arr = arr.slice(0);
+
+ var memo: W[] = [];
+
+ // Run one at a time
+ return new Promise((resolve, reject) => {
+ var next = () => {
+ if (arr.length === 0) {
+ resolve(memo);
+ return;
+ }
+ Promise.cast(iter(arr.shift())).then((res: W) => {
+ memo.push(res);
+ next();
+ }, reject);
+ };
+ next();
+ });
+}
function pluginFn(grunt: IGrunt) {
+ // Helper
+ function executeNode(args: string[]): Promise<ICompileResult> {
+ return new Promise((resolve, reject) => {
+ grunt.util.spawn({
+ cmd: 'node',
+ args: args
+ }, (error, result, code) => {
+ var ret: ICompileResult = {
+ code: code,
+ output: String(result)
+ };
+ resolve(ret);
+ });
+ });
+ }
+
/////////////////////////////////////////////////////////////////////
// tsc handling.
////////////////////////////////////////////////////////////////////
- function resolveTypeScriptBinPath(currentPath, depth): string {
- var targetPath = path.resolve(__dirname,
- (new Array(depth + 1)).join('../../'),
- '../node_modules/typescript/bin');
- if (path.resolve(currentPath, 'node_modules/typescript/bin').length > targetPath.length) {
- return null;
- }
- if (fs.existsSync(path.resolve(targetPath, 'typescript.js'))) {
- return targetPath;
- }
+ function resolveTypeScriptBinPath(): string {
+ var ownRoot = path.resolve(path.dirname((module).filename), '..');
+ var userRoot = path.resolve(ownRoot, '..', '..');
+ var binSub = path.join('node_modules', 'typescript', 'bin');
- return resolveTypeScriptBinPath(currentPath, ++depth);
+ if (fs.existsSync(path.join(userRoot, binSub))) {
+ // Using project override
+ return path.join(userRoot, binSub);
+ }
+ return path.join(ownRoot, binSub);
}
+
function getTsc(binPath: string): string {
- return '"' + binPath + '/' + 'tsc"';
+ var pkg = JSON.parse(fs.readFileSync(path.resolve(binPath, '..', 'package.json')).toString());
+ grunt.log.writeln('Using tsc v' + pkg.version);
+
+ return path.join(binPath, 'tsc');
}
+
var eol = grunt.util.linefeed;
- var cwd = path.resolve('.');
- var tsc = getTsc(resolveTypeScriptBinPath(cwd, 0));
// Blindly runs the tsc task using provided options
- function compileAllFiles(files: string[], target: ITargetOptions, task: ITaskOptions): ICompileResult {
+ function compileAllFiles(files: string[], target: ITargetOptions, task: ITaskOptions): Promise<ICompileResult> {
+
+ var args: string[] = files.slice(0);
- var cmd: string = files.join(' ');
// boolean options
- if (task.sourceMap)
- cmd = cmd + ' --sourcemap';
- if (task.declaration)
- cmd = cmd + ' --declaration';
- if (task.removeComments)
- cmd = cmd + ' --removeComments';
- if (task.noImplicitAny)
- cmd = cmd + ' --noImplicitAny';
- if (task.noResolve)
- cmd = cmd + ' --noResolve';
+ if (task.sourceMap) {
+ args.push('--sourcemap');
+ }
+ if (task.declaration) {
+ args.push('--declaration');
+ }
+ if (task.removeComments) {
+ args.push('--removeComments');
+ }
+ if (task.noImplicitAny) {
+ args.push('--noImplicitAny');
+ }
+ if (task.noResolve) {
+ args.push('--noResolve');
+ }
// string options
- cmd = cmd + ' --target ' + task.target.toUpperCase();
- cmd = cmd + ' --module ' + task.module.toLowerCase();
+ args.push('--target', task.target.toUpperCase());
+ args.push('--module', task.module.toLowerCase());
// Target options:
if (target.out) {
- cmd = cmd + ' --out ' + target.out;
+ args.push('--out', target.out);
}
if (target.outDir) {
if (target.out) {
console.warn('WARNING: Option "out" and "outDir" should not be used together'.magenta);
}
- cmd = cmd + ' --outDir ' + target.outDir;
+ args.push('--outDir', target.outDir);
}
if (task.sourceRoot) {
- cmd = cmd + ' --sourceRoot ' + task.sourceRoot;
+ args.push('--sourceRoot', task.sourceRoot);
}
if (task.mapRoot) {
- cmd = cmd + ' --mapRoot ' + task.mapRoot;
+ args.push('--mapRoot', task.mapRoot);
}
+ // Locate a compiler
+ var tsc = getTsc(resolveTypeScriptBinPath());
+
// To debug the tsc command
if (task.verbose) {
- console.log(cmd.yellow);
+ console.log(args.join(' ').yellow);
}
else {
- grunt.log.verbose.writeln(cmd.yellow);
+ grunt.log.verbose.writeln(args.join(' ').yellow);
}
// Create a temp last command file and use that to guide tsc.
// Reason: passing all the files on the command line causes TSC to go in an infinite loop.
var tempfilename = getTempFile('tscommand');
if (!tempfilename) {
- return null;
+ throw(new Error('cannot create temp file'));
}
- var tscExecCommand = 'node ' + tsc + ' @' + tempfilename;
- fs.writeFileSync(tempfilename, cmd);
+ fs.writeFileSync(tempfilename, args.join(' '));
- var result = shell.exec(tscExecCommand);
+ // Execute command
+ return executeNode([tsc , '@' + tempfilename]).then((result: ICompileResult) => {
+ fs.unlinkSync(tempfilename);
- // Cleanup
- fs.unlinkSync(tempfilename);
+ grunt.log.writeln(result.output);
- return result;
+ return Promise.cast(result);
+ }, (err) => {
+ fs.unlinkSync(tempfilename);
+ throw err;
+ });
}
/////////////////////////////////////////////////////////////////////
@@ -373,6 +428,7 @@ function pluginFn(grunt: IGrunt) {
};
var sortedGeneratedFiles = _.sortBy(generatedFiles);
+
function isGeneratedFile(filename: string): boolean {
return _.indexOf(sortedGeneratedFiles, filename, true) !== -1;
}
@@ -382,7 +438,7 @@ function pluginFn(grunt: IGrunt) {
// When writing
var referenceIntro = '/// <reference path="';
- var referenceEnd = '" />';
+ // var referenceEnd = '" />';
// The section of unordered files
var ourSignatureStart = '//grunt-start';
@@ -537,6 +593,7 @@ function pluginFn(grunt: IGrunt) {
});
return files;
}
+
grunt.log.verbose.writeln('Making files relative to outDir...');
files.before = makeRelativeToOutDir(files.before);
files.generated = makeRelativeToOutDir(files.generated);
@@ -610,11 +667,11 @@ function pluginFn(grunt: IGrunt) {
}
}
- /////////////////////////////////////////////////////////////////////
- // HTML -> TS
+ /////////////////////////////////////////////////////////////////////
+ // HTML -> TS
////////////////////////////////////////////////////////////////////
- // html -> js processing functions:
+ // html -> js processing functions:
// Originally from karma-html2js-preprocessor
// Refactored nicely in html2js grunt task
// https://github.com/karlgoldstein/grunt-html2js/blob/master/tasks/html2js.js
@@ -634,7 +691,7 @@ function pluginFn(grunt: IGrunt) {
var htmlTemplate = _.template('module <%= modulename %> { export var <%= varname %> = \'<%= content %>\' } ');
- // Compile an HTML file to a TS file
+ // Compile an HTML file to a TS file
// Return the filename. This filename will be required by reference.ts
function compileHTML(filename: string): string {
var htmlContent = escapeContent(fs.readFileSync(filename).toString());
@@ -645,14 +702,14 @@ function pluginFn(grunt: IGrunt) {
var extFreename = path.basename(filename, ext);
var fileContent = htmlTemplate({ modulename: extFreename, varname: ext.replace('.', ''), content: htmlContent });
- // Write the content to a file
+ // Write the content to a file
var outputfile = filename + '.ts';
fs.writeFileSync(outputfile, fileContent);
return outputfile;
}
- /////////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////
// AngularJS templateCache
////////////////////////////////////////////////////////////////////
@@ -663,7 +720,7 @@ function pluginFn(grunt: IGrunt) {
return;
}
- // Resolve the relative path from basePath to each src file
+ // Resolve the relative path from basePath to each src file
var relativePaths: string[] = _.map(src, (anHtmlFile) => 'text!' + makeReferencePath(basePath, anHtmlFile));
var fileNames: string[] = _.map(src, (anHtmlFile) => path.basename(anHtmlFile));
var fileVarialbeName = (anHtmlFile) => anHtmlFile.split('.').join('_').split('-').join('_');
@@ -693,17 +750,22 @@ function pluginFn(grunt: IGrunt) {
fs.writeFileSync(dest, fileContent);
}
- /////////////////////////////////////////////////////////////////////
- // The grunt task
+ /////////////////////////////////////////////////////////////////////
+ // The grunt task
////////////////////////////////////////////////////////////////////
// Note: this function is called once for each target
- // so task + target options are a bit blurred inside this function
+ // so task + target options are a bit blurred inside this function
grunt.registerMultiTask('ts', 'Compile TypeScript files', function() {
var currenttask: grunt.task.IMultiTask<ITargetOptions> = this;
- // setup default options
+ // make async
+ var done: grunt.task.AsyncResultCatcher = currenttask.async();
+
+ var watch;
+
+ // setup default options
var options = currenttask.options<ITaskOptions>({
allowBool: false,
allowImportModule: false,
@@ -736,31 +798,28 @@ function pluginFn(grunt: IGrunt) {
}
else {
console.warn(('The --removeComments value of "' + options.removeComments + '" ' +
- 'supercedes the --comments value of ' + options.comments + '"').magenta);
+ 'supercedes the --comments value of "' + options.comments + '"').magenta);
}
}
options.removeComments = !!options.removeComments;
- // Was the whole process successful
- var success = true;
- var watch;
-
- // Some interesting logs:
+ // Some interesting logs:
// http://gruntjs.com/api/inside-tasks#inside-multi-tasks
// console.log(this)
// console.log(this.files[0]); // An array of target files ( only one in our case )
- // console.log(this.files[0].src); // a getter for a resolved list of files
- // console.log(this.files[0].orig.src); // The original glob / array / !array / <% array %> for files. Can be very fancy :)
+ // console.log(this.files[0].src); // a getter for a resolved list of files
+ // console.log(this.files[0].orig.src); // The original glob / array / !array / <% array %> for files. Can be very fancy :)
// NOTE: to access the specified src files we use
// currenttaks.data as that is the raw (non interpolated) string that we reinterpolate ourselves,
// in case the file system as changed since this task was started
// this.files[0] is actually a single in our case as we gave examples of one source / out per target
- this.files.forEach(function (target: ITargetOptions) {
+ // Run compiler
+ asyncSeries(this.files, (target: ITargetOptions) => {
- // Create a reference file?
+ // Create a reference file?
var reference = target.reference;
var referenceFile;
var referencePath;
@@ -772,7 +831,7 @@ function pluginFn(grunt: IGrunt) {
return path.resolve(filename) === referenceFile;
}
- // Create an output file?
+ // Create an output file?
var out = target.out;
var outFile;
var outFile_d_ts;
@@ -784,7 +843,7 @@ function pluginFn(grunt: IGrunt) {
return path.resolve(filename) === outFile_d_ts;
}
- // Create an amd loader?
+ // Create an amd loader?
var amdloader = target.amdloader;
var amdloaderFile;
var amdloaderPath;
@@ -793,17 +852,15 @@ function pluginFn(grunt: IGrunt) {
amdloaderPath = path.dirname(amdloaderFile);
}
- // Compiles all the files
+ var lastCompile = 0;
+
+ // Compiles all the files
// Uses the blind tsc compile task
// logs errors
- // Time the whole process
- var starttime;
- var endtime;
- function runCompilation(files: string[], target: ITargetOptions, options: ITaskOptions) {
- grunt.log.writeln('Compiling...'.yellow);
- // Time the task and go
- starttime = new Date().getTime();
+ function runCompilation(files: string[], target: ITargetOptions, options: ITaskOptions): Promise<boolean> {
+ // Don't run it yet
+ grunt.log.writeln('Compiling...'.yellow);
// The files to compile
var filesToCompile = files;
@@ -817,41 +874,44 @@ function pluginFn(grunt: IGrunt) {
// Quote the files to compile
filesToCompile = _.map(filesToCompile, (item) => '"' + item + '"');
- // Compile the files
- var result = compileAllFiles(filesToCompile, target, options);
-
- // End the timer
- endtime = new Date().getTime();
+ // Time the compiler process
+ var starttime = new Date().getTime();
+ var endtime;
- // Evaluate the result
- if (!result || result.code !== 0) {
- var msg = 'Compilation failed';
- grunt.log.error(msg.red);
- return false;
- }
- else {
- var time = (endtime - starttime) / 1000;
- grunt.log.writeln(('Success: ' + time.toFixed(2) + 's for ' + files.length + ' typescript files').green);
- return true;
- }
+ // Compile the files
+ return compileAllFiles(filesToCompile, target, options).then((result: ICompileResult) => {
+ // End the timer
+ lastCompile = endtime = new Date().getTime();
+
+ // Evaluate the result
+ if (!result || result.code) {
+ grunt.log.error('Compilation failed'.red);
+ return false;
+ }
+ else {
+ var time = (endtime - starttime) / 1000;
+ grunt.log.writeln(('Success: ' + time.toFixed(2) + 's for ' + files.length + ' typescript files').green);
+ return true;
+ }
+ });
}
// Find out which files to compile
- // Then calls the compile function on those files
+ // Then calls the compile function on those files
// Also this funciton is debounced
- function filterFilesAndCompile() {
+ function filterFilesAndCompile(): Promise<boolean> {
- // Html files:
- // Note:
- // compile html files before reference file creation. Which is done in runCompilation
- // compile html files before globbing the file system again
+ // Html files:
+ // Note:
+ // compile html files before reference file creation. Which is done in runCompilation
+ // compile html files before globbing the file system again
var generatedHtmlFiles = [];
if (currenttask.data.html) {
var htmlFiles = grunt.file.expand(currenttask.data.html);
generatedHtmlFiles = _.map(htmlFiles, (filename) => compileHTML(filename));
}
- // The template cache files do not go into generated files.
- // You are free to generate a `ts OR js` file, both should just work
+ // The template cache files do not go into generated files.
+ // You are free to generate a `ts OR js` file, both should just work
if (currenttask.data.templateCache) {
if (!currenttask.data.templateCache.src || !currenttask.data.templateCache.dest || !currenttask.data.templateCache.baseUrl) {
grunt.log.writeln('templateCache : src, dest, baseUrl must be specified if templateCache option is used'.red);
@@ -893,62 +953,59 @@ function pluginFn(grunt: IGrunt) {
}
}
- // compile, If there are any files to compile!
+ // Compile, if there are any files to compile!
if (files.length > 0) {
- success = runCompilation(files, target, options);
-
- // Create the loader if specified & compiliation succeeded
- if (success && !!amdloaderPath) {
- var referenceOrder: IReferences = getReferencesInOrder(referenceFile, referencePath, generatedHtmlFiles);
- updateAmdLoader(referenceFile, referenceOrder, amdloaderFile, amdloaderPath, target.outDir);
- }
+ return runCompilation(files, target, options).then((success: boolean) => {
+ // Create the loader if specified & compiliation succeeded
+ if (success && !!amdloaderPath) {
+ var referenceOrder: IReferences = getReferencesInOrder(referenceFile, referencePath, generatedHtmlFiles);
+ updateAmdLoader(referenceFile, referenceOrder, amdloaderFile, amdloaderPath, target.outDir);
+ }
+ return success;
+ });
}
else {
grunt.log.writeln('No files to compile'.red);
}
}
+ // Nothing to do
+ return Promise.resolve(true);
}
- // Initial compilation:
- filterFilesAndCompile();
-
- // Watch a folder?
+ // Watch a folder?
watch = target.watch;
if (!!watch) {
- // make async
- var done: grunt.task.AsyncResultCatcher = currenttask.async();
-
- // A debounced version of compile
+ // A debounced version of compile
var debouncedCompile = _.debounce(filterFilesAndCompile, 150);
- // local event to handle file event
+ // local event to handle file event
function handleFileEvent(filepath: string, displaystr: string) {
- // Only ts and html :
+ // Only ts and html :
if (!endsWith(filepath.toLowerCase(), '.ts') && !endsWith(filepath.toLowerCase(), '.html')) {
return;
}
- // Do not run if just ran, behaviour same as grunt-watch
- // These are the files our run modified
- if ((new Date().getTime() - endtime) <= 100) {
+ // Do not run if just ran, behaviour same as grunt-watch
+ // These are the files our run modified
+ if ((new Date().getTime() - lastCompile) <= 100) {
// grunt.log.writeln((' ///' + ' >>' + filepath).grey);
return;
}
- // Log and run the debounced version.
+ // Log and run the debounced version.
grunt.log.writeln((displaystr + ' >>' + filepath).yellow);
debouncedCompile();
}
- // get path
+ // get path
var watchpath = path.resolve(watch);
- // create a file watcher for path
+ // create a file watcher for path
var chokidar = require('chokidar');
var watcher = chokidar.watch(watchpath, { ignoreInitial: true, persistent: true });
- // Log what we are doing
+ // Log what we are doing
grunt.log.writeln(('Watching all TypeScript / Html files under : ' + watchpath).cyan);
// A file has been added/changed/deleted has occurred
@@ -957,12 +1014,14 @@ function pluginFn(grunt: IGrunt) {
.on('unlink', function (path) { handleFileEvent(path, '--- removed '); })
.on('error', function (error) { console.error('Error happened in chokidar: ', error); });
}
+ return filterFilesAndCompile();
- });
-
- if (!watch) {
- return success;
- }
+ }).then((res: boolean[]) => {
+ // Ignore res? (either logs or throws)
+ if (!watch) {
+ done();
+ }
+ }, done);
});
}
export = pluginFn;
View
15 test/simple/js/zoo.d.ts
@@ -0,0 +1,15 @@
+declare class Animal {
+ public name: string;
+ constructor(name: string);
+ public move(meters: number): void;
+}
+declare class Snake extends Animal {
+ constructor(name: string);
+ public move(): void;
+}
+declare class Horse extends Animal {
+ constructor(name: string);
+ public move(): void;
+}
+declare var sam: Snake;
+declare var tom: Animal;
View
28 test/simple/ts/zoo.ts
@@ -0,0 +1,28 @@
+class Animal {
+ constructor(public name: string) { }
+ move(meters: number) {
+ alert(this.name + " moved " + meters + "m.");
+ }
+}
+
+class Snake extends Animal {
+ constructor(name: string) { super(name); }
+ move() {
+ alert("Slithering...");
+ super.move(5);
+ }
+}
+
+class Horse extends Animal {
+ constructor(name: string) { super(name); }
+ move() {
+ alert("Galloping...");
+ super.move(45);
+ }
+}
+
+var sam = new Snake("Sammy the Python");
+var tom: Animal = new Horse("Tommy the Palomino");
+
+sam.move();
+tom.move(34);
View
3 tsd.json
@@ -16,6 +16,9 @@
},
"gruntjs/gruntjs.d.ts": {
"commit": "2aff984fa86634866f70278406714d9fd7b9572d"
+ },
+ "es6-promises/es6-promises.d.ts": {
+ "commit": "62594ee84438dc6fb742f66e9e1c6f9f7fd29ec7"
}
}
}
View
6 tslint.json
@@ -5,7 +5,7 @@
"comment-format": [true,
"check-space"
],
- "curly": false,
+ "curly": true,
"eofline": true,
"forin": true,
"indent": [true, 4],
@@ -32,7 +32,11 @@
"no-string-literal": false,
"no-trailing-whitespace": true,
"no-unreachable": true,
+ "no-unused-variable": true,
+ "no-use-before-declare": true,
"one-line": [true,
+ "_check-catch",
+ "_check-else",
"check-open-brace",
"check-whitespace"
],

0 comments on commit 0307f4d

Please sign in to comment.
Something went wrong with that request. Please try again.