index.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. var coffee = require('coffeescript');
  2. var convert = require('convert-source-map');
  3. var path = require('path');
  4. var through = require('through2');
  5. var filePattern = /\.((lit)?coffee|coffee\.md)$/;
  6. function isCoffee (file) {
  7. return filePattern.test(file);
  8. }
  9. function isLiterate (file) {
  10. return (/\.(litcoffee|coffee\.md)$/).test(file);
  11. }
  12. function ParseError(error, src, file) {
  13. /* Creates a ParseError from a CoffeeScript SyntaxError
  14. modeled after substack's syntax-error module */
  15. SyntaxError.call(this);
  16. this.message = error.message;
  17. this.line = error.location.first_line + 1; // cs linenums are 0-indexed
  18. this.column = error.location.first_column + 1; // same with columns
  19. var markerLen = 2;
  20. if(error.location.first_line === error.location.last_line) {
  21. markerLen += error.location.last_column - error.location.first_column;
  22. }
  23. this.annotated = [
  24. file + ':' + this.line,
  25. src.split('\n')[this.line - 1],
  26. Array(this.column).join(' ') + Array(markerLen).join('^'),
  27. 'ParseError: ' + this.message
  28. ].join('\n');
  29. }
  30. ParseError.prototype = Object.create(SyntaxError.prototype);
  31. ParseError.prototype.toString = function () {
  32. return this.annotated;
  33. };
  34. ParseError.prototype.inspect = function () {
  35. return this.annotated;
  36. };
  37. function compile(filename, source, options, callback) {
  38. var compiled;
  39. try {
  40. compiled = coffee.compile(source, Object.assign({
  41. inline: true,
  42. literate: isLiterate(filename)
  43. }, options));
  44. } catch (e) {
  45. var error = e;
  46. if (e.location) {
  47. error = new ParseError(e, source, filename);
  48. }
  49. callback(error);
  50. return;
  51. }
  52. if (options.sourceMap) {
  53. var map = convert.fromJSON(compiled.v3SourceMap);
  54. var basename = path.basename(filename);
  55. map.setProperty('file', basename.replace(filePattern, '.js'));
  56. map.setProperty('sources', [basename]);
  57. map.setProperty('sourcesContent', [source]);
  58. callback(null, compiled.js + '\n' + map.toComment() + '\n');
  59. } else {
  60. callback(null, compiled + '\n');
  61. }
  62. }
  63. function coffeeify(filename, options) {
  64. if (!isCoffee(filename)) return through();
  65. if (typeof options === 'undefined' || options === null) options = {};
  66. var compileOptions = {
  67. sourceMap: (options._flags && options._flags.debug),
  68. bare: true,
  69. header: false
  70. };
  71. for (var i = 0, keys = Object.keys(options); i < keys.length; i++) {
  72. var key = keys[i], option = options[key];
  73. if (typeof option !== 'undefined' && option !== null) {
  74. if (option === 'false' || option === 'no' || option === '0') {
  75. option = false;
  76. }
  77. compileOptions[key] = option;
  78. }
  79. }
  80. var chunks = [];
  81. function transform(chunk, encoding, callback) {
  82. chunks.push(chunk);
  83. callback();
  84. }
  85. function flush(callback) {
  86. var stream = this;
  87. var source = Buffer.concat(chunks).toString();
  88. compile(filename, source, compileOptions, function(error, result) {
  89. if (!error) stream.push(result);
  90. callback(error);
  91. });
  92. }
  93. return through(transform, flush);
  94. }
  95. coffeeify.compile = compile;
  96. coffeeify.isCoffee = isCoffee;
  97. coffeeify.isLiterate = isLiterate;
  98. module.exports = coffeeify;