grunt-ts
Version:
Compile and manage your TypeScript project
311 lines • 13 kB
JavaScript
/// <reference path="../../defs/tsd.d.ts"/>
/// <reference path="./interfaces.d.ts"/>
;
var path = require('path');
var fs = require('fs');
var _ = require('lodash');
var utils = require('./utils');
var cache = require('./cacheUtils');
var es6_promise_1 = require('es6-promise');
exports.grunt = require('grunt');
///////////////////////////
// Helper
///////////////////////////
var executeNode;
var executeNodeDefault = function (args, optionalInfo) {
return new es6_promise_1.Promise(function (resolve, reject) {
exports.grunt.util.spawn({
cmd: process.execPath,
args: args
}, function (error, result, code) {
var ret = {
code: code,
// New TypeScript compiler uses stdout for user code errors. Old one used stderr.
output: result.stdout || result.stderr
};
resolve(ret);
});
});
};
/////////////////////////////////////////////////////////////////
// Fast Compilation
/////////////////////////////////////////////////////////////////
// Map to store if the cache was cleared after the gruntfile was parsed
var cacheClearedOnce = {};
function getChangedFiles(files, targetName) {
files = cache.getNewFilesForTarget(files, targetName);
_.forEach(files, function (file) {
exports.grunt.log.writeln(('### Fast Compile >>' + file).cyan);
});
return files;
}
function resetChangedFiles(files, targetName) {
cache.compileSuccessfull(files, targetName);
}
function clearCache(targetName) {
cache.clearCache(targetName);
cacheClearedOnce[targetName] = true;
}
/////////////////////////////////////////////////////////////////////
// tsc handling
////////////////////////////////////////////////////////////////////
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 path.join(ownRoot, binSub);
}
function getTsc(binPath) {
var pkg = JSON.parse(fs.readFileSync(path.resolve(binPath, '..', 'package.json')).toString());
exports.grunt.log.writeln('Using tsc v' + pkg.version);
return path.join(binPath, 'tsc');
}
function compileAllFiles(options, compilationInfo) {
var targetFiles = compilationInfo.src;
// Make a local copy so we can modify files without having external side effects
var files = _.map(targetFiles, function (file) { return file; });
var newFiles = files;
if (options.fast === 'watch') {
// if this is the first time its running after this file was loaded
if (cacheClearedOnce[exports.grunt.task.current.target] === undefined) {
// Then clear the cache for this target
clearCache(options.targetName);
}
}
if (options.fast !== 'never') {
if (compilationInfo.out) {
exports.grunt.log.writeln('Fast compile will not work when --out is specified. Ignoring fast compilation'.cyan);
}
else {
newFiles = getChangedFiles(files, options.targetName);
if (newFiles.length !== 0 || options.testExecute || utils.shouldPassThrough(options)) {
files = newFiles;
// If outDir is specified but no baseDir is specified we need to determine one
if (compilationInfo.outDir && !options.baseDir) {
options.baseDir = utils.findCommonPath(files, '/');
}
}
else {
exports.grunt.log.writeln('No file changes were detected. Skipping Compile'.green);
return new es6_promise_1.Promise(function (resolve) {
var ret = {
code: 0,
fileCount: 0,
output: 'No files compiled as no change detected'
};
resolve(ret);
});
}
}
}
// Transform files as needed. Currently all of this logic in is one module
// transformers.transformFiles(newFiles, targetFiles, target, task);
// If baseDir is specified create a temp tsc file to make sure that `--outDir` works fine
// see https://github.com/grunt-ts/grunt-ts/issues/77
var baseDirFile = '.baseDir.ts';
var baseDirFilePath;
if (compilationInfo.outDir && options.baseDir && files.length > 0) {
baseDirFilePath = path.join(options.baseDir, baseDirFile);
if (!fs.existsSync(baseDirFilePath)) {
exports.grunt.file.write(baseDirFilePath, '// Ignore this file. See https://github.com/grunt-ts/grunt-ts/issues/77');
}
files.push(baseDirFilePath);
}
// If reference and out are both specified.
// Then only compile the updated reference file as that contains the correct order
if (options.reference && compilationInfo.out) {
var referenceFile = path.resolve(options.reference);
files = [referenceFile];
}
// Quote the files to compile. Needed for command line parsing by tsc
files = _.map(files, function (item) { return ("\"" + path.resolve(item) + "\""); });
// if (outFile) {
// outFile = `"${path.resolve(outFile)}"`;
// }
var args = files.slice(0);
var tsconfig = options.tsconfig;
if (tsconfig && tsconfig.passThrough) {
args.push('--project', tsconfig.tsconfig);
}
else {
if (options.sourceMap) {
args.push('--sourcemap');
}
if (options.emitDecoratorMetadata) {
args.push('--emitDecoratorMetadata');
}
if (options.declaration) {
args.push('--declaration');
}
if (options.removeComments) {
args.push('--removeComments');
}
if (options.noImplicitAny) {
args.push('--noImplicitAny');
}
if (options.noResolve) {
args.push('--noResolve');
}
if (options.noEmitOnError) {
args.push('--noEmitOnError');
}
if (options.preserveConstEnums) {
args.push('--preserveConstEnums');
}
if (options.suppressImplicitAnyIndexErrors) {
args.push('--suppressImplicitAnyIndexErrors');
}
if (options.noEmit) {
args.push('--noEmit');
}
if (options.inlineSources) {
args.push('--inlineSources');
}
if (options.inlineSourceMap) {
args.push('--inlineSourceMap');
}
if (options.newLine && !utils.newLineIsRedundant(options.newLine)) {
args.push('--newLine', options.newLine);
}
if (options.isolatedModules) {
args.push('--isolatedModules');
}
if (options.noEmitHelpers) {
args.push('--noEmitHelpers');
}
if (options.experimentalDecorators) {
args.push('--experimentalDecorators');
}
if (options.experimentalAsyncFunctions) {
args.push('--experimentalAsyncFunctions');
}
if (options.jsx) {
args.push('--jsx', options.jsx.toLocaleLowerCase());
}
if (options.moduleResolution) {
args.push('--moduleResolution', options.moduleResolution.toLocaleLowerCase());
}
if (options.rootDir) {
args.push('--rootDir', options.rootDir);
}
args.push('--target', options.target.toUpperCase());
if (options.module) {
var moduleOptionString = ('' + options.module).toLowerCase();
if ('amd|commonjs|system|umd'.indexOf(moduleOptionString) > -1) {
args.push('--module', moduleOptionString);
}
else {
console.warn('WARNING: Option "module" only supports "amd" | "commonjs" | "system" | "umd" '.magenta);
}
}
if (compilationInfo.outDir) {
if (compilationInfo.out) {
console.warn('WARNING: Option "out" and "outDir" should not be used together'.magenta);
}
args.push('--outDir', compilationInfo.outDir);
}
if (compilationInfo.out) {
args.push('--out', compilationInfo.out);
}
if (compilationInfo.dest && (!compilationInfo.out) && (!compilationInfo.outDir)) {
if (utils.isJavaScriptFile(compilationInfo.dest)) {
args.push('--out', compilationInfo.dest);
}
else {
if (compilationInfo.dest === 'src') {
console.warn(('WARNING: Destination for target "' + options.targetName + '" is "src", which is the default. If you have' +
' forgotten to specify a "dest" parameter, please add it. If this is correct, you may wish' +
' to change the "dest" parameter to "src/" or just ignore this warning.').magenta);
}
if (Array.isArray(compilationInfo.dest)) {
if (compilationInfo.dest.length === 0) {
}
else if (compilationInfo.dest.length > 0) {
console.warn((('WARNING: "dest" for target "' + options.targetName + '" is an array. This is not supported by the' +
' TypeScript compiler or grunt-ts.' +
((compilationInfo.dest.length > 1) ? ' Only the first "dest" will be used. The' +
' remaining items will be truncated.' : ''))).magenta);
args.push('--outDir', compilationInfo.dest[0]);
}
}
else {
args.push('--outDir', compilationInfo.dest);
}
}
}
if (args.indexOf('--out') > -1 && args.indexOf('--module') > -1) {
console.warn(('WARNING: TypeScript does not allow external modules to be concatenated with' +
' --out. Any exported code may be truncated. See TypeScript issue #1544 for' +
' more details.').magenta);
}
if (options.sourceRoot) {
args.push('--sourceRoot', options.sourceRoot);
}
if (options.mapRoot) {
args.push('--mapRoot', options.mapRoot);
}
}
if (options.additionalFlags) {
args.push(options.additionalFlags);
}
// Locate a compiler
var tsc;
if (options.compiler) {
exports.grunt.log.writeln('Using the custom compiler : ' + options.compiler);
tsc = options.compiler;
}
else {
tsc = getTsc(resolveTypeScriptBinPath());
}
// To debug the tsc command
if (options.verbose) {
console.log(args.join(' ').yellow);
}
else {
exports.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 = utils.getTempFile('tscommand');
if (!tempfilename) {
throw (new Error('cannot create temp file'));
}
fs.writeFileSync(tempfilename, args.join(' '));
var command;
// Switch implementation if a test version of executeNode exists.
if ('testExecute' in options) {
if (_.isFunction(options.testExecute)) {
command = [tsc, args.join(' ')];
executeNode = options.testExecute;
}
else {
var invalidTestExecuteError = 'Invalid testExecute node present on target "' +
options.targetName + '". Value of testExecute must be a function.';
throw (new Error(invalidTestExecuteError));
}
}
else {
// this is the normal path.
command = [tsc, '@' + tempfilename];
executeNode = executeNodeDefault;
}
// Execute command
return executeNode(command, options).then(function (result) {
if (options.fast !== 'never' && result.code === 0) {
resetChangedFiles(newFiles, options.targetName);
}
result.fileCount = files.length;
fs.unlinkSync(tempfilename);
exports.grunt.log.writeln(result.output);
return es6_promise_1.Promise.cast(result);
}, function (err) {
fs.unlinkSync(tempfilename);
throw err;
});
}
exports.compileAllFiles = compileAllFiles;
//# sourceMappingURL=compile.js.map