Advertisement
Guest User

Untitled

a guest
Aug 29th, 2015
86
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.81 KB | None | 0 0
  1. 'use strict';
  2.  
  3. var tsc = require('./tsc');
  4. var fs = require('fs');
  5. var path = require('path');
  6. var util = require('util');
  7. var _ = require('lodash');
  8. var async = require('async');
  9. var byline = require('byline');
  10. var temp = require('temp');
  11. var rimraf = require('rimraf');
  12. var through = require('through2');
  13. var fsSrc = require('vinyl-fs').src;
  14. var EventEmitter = require('events').EventEmitter;
  15.  
  16. module.exports = Compiler;
  17.  
  18. function Compiler(sourceFiles, options) {
  19. EventEmitter.call(this);
  20.  
  21. this.sourceFiles = sourceFiles || [];
  22.  
  23. this.options = _.extend({
  24. tscPath: null,
  25. tscSearch: null,
  26. module: 'commonjs',
  27. target: 'ES3',
  28. out: null,
  29. outDir: null,
  30. mapRoot: null,
  31. sourceRoot: null,
  32. allowbool: false,
  33. allowimportmodule: false,
  34. declaration: false,
  35. noImplicitAny: false,
  36. noResolve: false,
  37. removeComments: false,
  38. sourcemap: false,
  39. tmpDir: '',
  40. noLib: false,
  41. keepTree: true,
  42. pathFilter: null,
  43. safe: false,
  44. enableDecorators: false,
  45. emitDecoratorMetadata: false
  46. }, options);
  47.  
  48. this.tscOptions = {
  49. path: this.options.tscPath,
  50. search: this.options.tscSearch
  51. };
  52.  
  53. this.tempDestination = null;
  54. this.tscArgumentsFile = null;
  55. this.treeKeeperFile = null;
  56. }
  57. util.inherits(Compiler, EventEmitter);
  58.  
  59. Compiler.prototype.buildTscArguments = function () {
  60. var args = [];
  61. if (this.options.module) args.push('--module', this.options.module.toLowerCase());
  62. if (this.options.target) args.push('--target', this.options.target.toUpperCase());
  63. if (this.options.mapRoot) args.push('--mapRoot', this.options.mapRoot);
  64. if (this.options.sourceRoot) args.push('--sourceRoot', this.options.sourceRoot);
  65. if (this.options.allowbool) args.push('--allowbool');
  66. if (this.options.allowimportmodule) args.push('--allowimportmodule');
  67. if (this.options.declaration) args.push('--declaration');
  68. if (this.options.noImplicitAny) args.push('--noImplicitAny');
  69. if (this.options.noResolve) args.push('--noResolve');
  70. if (this.options.removeComments) args.push('--removeComments');
  71. if (this.options.sourcemap) args.push('--sourcemap');
  72. if (this.options.noLib) args.push('--noLib');
  73. if (this.options.enableDecorators) args.push('--experimentalDecorators');
  74. if (this.options.emitDecoratorMetadata ) args.push('--emitDecoratorMetadata');
  75.  
  76. if (this.tempDestination) {
  77. args.push('--outDir', this.tempDestination);
  78. if (this.options.out) {
  79. args.push('--out', path.resolve(this.tempDestination, this.options.out));
  80. }
  81. } else if (this.options.out) {
  82. args.push('--out', this.options.out);
  83. }
  84.  
  85. console.log("arguments:", args.join(" "));
  86.  
  87. this.sourceFiles.forEach(function (f) { args.push(f.path); });
  88. if (this.treeKeeperFile) {
  89. args.push(this.treeKeeperFile);
  90. }
  91.  
  92. return args;
  93. };
  94.  
  95. Compiler.prototype.getVersion = function (callback) {
  96. return tsc.version(this.tscOptions, callback);
  97. };
  98.  
  99. Compiler.prototype.compile = function (callback) {
  100. var _this = this;
  101. var checkAborted = this.checkAborted.bind(this);
  102.  
  103. this.emit('start');
  104. Compiler._start(this);
  105.  
  106. async.waterfall([
  107. checkAborted,
  108. this.makeTempDestinationDir.bind(this),
  109. checkAborted,
  110. this.makeTreeKeeperFile.bind(this),
  111. checkAborted,
  112. this.prepareTscArgumentsFile.bind(this),
  113. checkAborted,
  114. this.runTsc.bind(this),
  115. checkAborted
  116. ], function (err) {
  117. if (err && _this.options.safe) {
  118. finish(err);
  119. } else {
  120. _this.processOutputFiles(function (err2) {
  121. finish(err || err2);
  122. });
  123. }
  124. });
  125.  
  126. function finish(err) {
  127. _this.cleanup();
  128. _this.emit('end');
  129. callback(err);
  130. }
  131. };
  132.  
  133. Compiler.prototype.checkAborted = function (callback) {
  134. if (Compiler.isAborted()) {
  135. callback(new Error('aborted'));
  136. } else {
  137. callback(null);
  138. }
  139. };
  140.  
  141. Compiler.prototype.makeTempDestinationDir = function (callback) {
  142. var _this = this;
  143. temp.track();
  144. temp.mkdir({ dir: path.resolve(process.cwd(), this.options.tmpDir), prefix: 'gulp-tsc-tmp-' }, function (err, dirPath) {
  145. if (err) return callback(err);
  146. _this.tempDestination = dirPath;
  147.  
  148. callback(null);
  149. });
  150. };
  151.  
  152. Compiler.prototype.makeTreeKeeperFile = function (callback) {
  153. if (!this.options.keepTree || this.options.out || this.sourceFiles.length === 0) {
  154. return callback(null);
  155. }
  156.  
  157. var _this = this;
  158. temp.open({ dir: this.sourceFiles[0].base, prefix: '.gulp-tsc-tmp-', suffix: '.ts' }, function (err, file) {
  159. if (err) {
  160. return callback(new Error(
  161. 'Failed to create a temporary file on source directory: ' + (err.message || err) + ', ' +
  162. 'To skip creating it specify { keepTree: false } to your gulp-tsc.'
  163. ));
  164. }
  165.  
  166. _this.treeKeeperFile = file.path;
  167. try {
  168. fs.writeSync(file.fd, '// This is a temporary file by gulp-tsc for keeping directory tree.\n');
  169. fs.closeSync(file.fd);
  170. } catch (e) {
  171. return callback(e);
  172. }
  173. callback(null);
  174. });
  175. };
  176.  
  177. Compiler.prototype.prepareTscArgumentsFile = function(callback) {
  178. var tscArguments = this.buildTscArguments();
  179. var content = '"' + tscArguments.join('"\n"') + '"';
  180. this.tscArgumentsFile = path.join(this.tempDestination, 'tscArguments');
  181. fs.writeFile(this.tscArgumentsFile, content, callback);
  182. };
  183.  
  184. Compiler.prototype.runTsc = function (callback) {
  185. var _this = this;
  186. var proc = tsc.exec(['@' + this.tscArgumentsFile], this.tscOptions);
  187. var stdout = byline(proc.stdout);
  188. var stderr = byline(proc.stderr);
  189.  
  190. proc.on('exit', function (code) {
  191. if (code !== 0) {
  192. callback(new Error('tsc command has exited with code:' + code));
  193. } else {
  194. callback(null);
  195. }
  196. })
  197. proc.on('error', function (err) {
  198. _this.emit('error', err);
  199. });
  200. stdout.on('data', function (chunk) {
  201. _this.emit('stdout', chunk);
  202. });
  203. stderr.on('data', function (chunk) {
  204. _this.emit('stderr', chunk);
  205. });
  206.  
  207. return proc;
  208. };
  209.  
  210. Compiler.prototype.processOutputFiles = function (callback) {
  211. var _this = this;
  212. var options = { cwd: this.tempDestination, cwdbase: true };
  213. var patterns = ['**/*{.js,.js.map,.d.ts}'];
  214. if (this.treeKeeperFile) {
  215. patterns.push('!**/' + path.basename(this.treeKeeperFile, '.ts') + '.*');
  216. }
  217.  
  218. var stream = fsSrc(patterns, options);
  219. stream = stream.pipe(this.fixOutputFilePath());
  220. if (this.options.sourcemap && !this.options.sourceRoot) {
  221. stream = stream.pipe(this.fixSourcemapPath());
  222. }
  223. if (this.options.declaration && this.options.outDir) {
  224. stream = stream.pipe(this.fixReferencePath());
  225. }
  226. stream.on('data', function (file) {
  227. _this.emit('data', file);
  228. });
  229. stream.on('error', function (err) {
  230. callback(err);
  231. });
  232. stream.on('end', function () {
  233. callback();
  234. });
  235. };
  236.  
  237. Compiler.prototype.fixOutputFilePath = function () {
  238. var filter = this.options.pathFilter && this.filterOutput.bind(this);
  239. var outDir;
  240. if (this.options.outDir) {
  241. outDir = path.resolve(process.cwd(), this.options.outDir);
  242. } else {
  243. outDir = this.tempDestination;
  244. }
  245.  
  246. return through.obj(function (file, encoding, done) {
  247. file.originalPath = file.path;
  248. file.path = path.resolve(outDir, file.relative);
  249. file.cwd = file.base = outDir;
  250. if (filter) {
  251. try {
  252. file = filter(file);
  253. } catch (e) {
  254. return done(e);
  255. }
  256. }
  257. if (file) this.push(file);
  258. done();
  259. });
  260. };
  261.  
  262. Compiler.prototype.filterOutput = function (file) {
  263. if (!this.options.pathFilter) return file;
  264.  
  265. var filter = this.options.pathFilter;
  266. if (_.isFunction(filter)) {
  267. var ret = filter(file.relative, file);
  268. if (ret === true || _.isUndefined(ret)) {
  269. return file;
  270. } else if (ret === false) {
  271. return null;
  272. } else if (_.isString(ret)) {
  273. file.path = path.resolve(file.base, ret);
  274. return file;
  275. } else if (_.isObject(ret) && ret.path) {
  276. return ret;
  277. } else {
  278. throw new Error('Unknown return value from pathFilter function');
  279. }
  280. } else if (_.isPlainObject(filter)) {
  281. _.forOwn(filter, function (val, key) {
  282. if (_.isString(key) && _.isString(val)) {
  283. var src = path.normalize(key) + path.sep;
  284. if (file.relative.substr(0, src.length) === src) {
  285. file.path = path.resolve(file.base, val, file.relative.substr(src.length) || '.');
  286. return false;
  287. }
  288. }
  289. });
  290. return file;
  291. } else {
  292. throw new Error('Unknown type for pathFilter');
  293. }
  294. };
  295.  
  296. Compiler.prototype.fixSourcemapPath = function () {
  297. return through.obj(function (file, encoding, done) {
  298. if (!file.isBuffer() || !/\.js\.map/.test(file.path)) {
  299. this.push(file);
  300. return done();
  301. }
  302.  
  303. var map = JSON.parse(file.contents);
  304. if (map['sources'] && map['sources'].length > 0) {
  305. map['sources'] = map['sources'].map(function (sourcePath) {
  306. sourcePath = path.resolve(path.dirname(file.originalPath), sourcePath);
  307. sourcePath = path.relative(path.dirname(file.path), sourcePath);
  308. if (path.sep == '\\') sourcePath = sourcePath.replace(/\\/g, '/');
  309. return sourcePath;
  310. });
  311. file.contents = new Buffer(JSON.stringify(map));
  312. }
  313. this.push(file);
  314. done();
  315. });
  316. };
  317.  
  318. Compiler.prototype.fixReferencePath = function () {
  319. return through.obj(function (file, encoding, done) {
  320. if (!file.isBuffer() || !/\.d\.ts/.test(file.path)) {
  321. this.push(file);
  322. return done();
  323. }
  324.  
  325. var newContent = file.contents.toString().replace(
  326. /(\/\/\/\s*<reference\s*path\s*=\s*)(["'])(.+?)\2/g,
  327. function (entire, prefix, quote, refPath) {
  328. refPath = path.resolve(path.dirname(file.originalPath), refPath);
  329. refPath = path.relative(path.dirname(file.path), refPath);
  330. if (path.sep == '\\') refPath = refPath.replace(/\\/g, '/');
  331. return prefix + quote + refPath + quote;
  332. }
  333. );
  334. file.contents = new Buffer(newContent);
  335. this.push(file);
  336. done();
  337. });
  338. };
  339.  
  340. Compiler.prototype.cleanup = function () {
  341. try { rimraf.sync(this.tempDestination); } catch(e) {}
  342. try { fs.unlinkSync(this.treeKeeperFile); } catch(e) {}
  343. };
  344.  
  345. Compiler.running = 0;
  346. Compiler.aborted = false;
  347. Compiler.abortCallbacks = [];
  348.  
  349. Compiler.abortAll = function (callback) {
  350. Compiler.aborted = true;
  351. callback && Compiler.abortCallbacks.push(callback);
  352. if (Compiler.running == 0) {
  353. Compiler._allAborted();
  354. }
  355. };
  356.  
  357. Compiler.isAborted = function () {
  358. return Compiler.aborted;
  359. };
  360.  
  361. Compiler._start = function (compiler) {
  362. Compiler.running++;
  363. compiler.once('end', function () {
  364. Compiler.running--;
  365. if (Compiler.running == 0 && Compiler.aborted) {
  366. Compiler._allAborted();
  367. }
  368. });
  369. };
  370.  
  371. Compiler._allAborted = function () {
  372. var callbacks = Compiler.abortCallbacks;
  373. Compiler.aborted = false;
  374. Compiler.abortCallbacks = [];
  375. callbacks.forEach(function (fn) {
  376. fn.call(null);
  377. });
  378. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement