source: Dev/trunk/node_modules/grunt-contrib-coffee/tasks/coffee.js @ 484

Last change on this file since 484 was 484, checked in by hendrikvanantwerpen, 11 years ago

Commit node_modules, to make checkouts and builds more deterministic.

File size: 6.7 KB
Line 
1/*
2 * grunt-contrib-coffee
3 * http://gruntjs.com/
4 *
5 * Copyright (c) 2012 Eric Woroshow, contributors
6 * Licensed under the MIT license.
7 */
8
9module.exports = function(grunt) {
10  'use strict';
11
12  var path = require('path');
13  var _ = grunt.util._;
14
15  grunt.registerMultiTask('coffee', 'Compile CoffeeScript files into JavaScript', function() {
16
17    var options = this.options({
18      bare: false,
19      join: false,
20      sourceMap: false,
21      separator: grunt.util.linefeed
22    });
23
24    grunt.verbose.writeflags(options, 'Options');
25
26    this.files.forEach(function (f) {
27      var validFiles = removeInvalidFiles(f);
28
29      if (options.sourceMap === true) {
30        var paths = createOutputPaths(f.dest);
31        writeFileAndMap(paths, compileWithMaps(validFiles, options, paths));
32      } else if (options.join === true) {
33        writeFile(f.dest, concatInput(validFiles, options));
34      } else {
35        writeFile(f.dest, concatOutput(validFiles, options));
36      }
37    });
38  });
39
40  var isLiterate = function (ext) {
41    return (ext === ".litcoffee" || ext === ".md");
42  };
43
44  var removeInvalidFiles = function(files) {
45    return files.src.filter(function(filepath) {
46      if (!grunt.file.exists(filepath)) {
47        grunt.log.warn('Source file "' + filepath + '" not found.');
48        return false;
49      } else {
50        return true;
51      }
52    });
53  };
54
55  var createOutputPaths = function (destination) {
56    var fileName = path.basename(destination, path.extname(destination));
57    return {
58      dest: destination,
59      destName: fileName,
60      destDir: appendTrailingSlash(path.dirname(destination)),
61      mapFileName: fileName + '.js.map'
62    };
63  };
64
65  var appendTrailingSlash = function (path) {
66    if (path.length > 0) {
67      return path + '/';
68    } else {
69      return path;
70    }
71  };
72
73  var compileWithMaps = function (files, options, paths) {
74    if (!hasUniformExtensions(files)) {
75      return;
76    }
77
78    var mapOptions, filepath;
79
80    if (files.length > 1) {
81      mapOptions = createOptionsForJoin(files, paths, options.separator);
82    } else {
83      mapOptions = createOptionsForFile(files[0], paths);
84      filepath = files[0];
85    }
86
87    options = _.extend({
88        generatedFile: path.basename(paths.dest),
89        sourceRoot: mapOptions.sourceRoot,
90        sourceFiles: mapOptions.sourceFiles
91      }, options);
92
93    var output = compileCoffee(mapOptions.code, options, filepath);
94    appendFooter(output, paths);
95    return output;
96  };
97
98  var hasUniformExtensions = function(files) {
99    // get all extensions for input files
100    var ext = files.map(function (f) {
101      return path.extname(f);
102    });
103
104    if(_.uniq(ext).length > 1) {
105      grunt.fail.warn('Join and sourceMap options require input files share the same extension (found '+_.uniq(ext).join(', ')+').');
106      return false;
107    } else {
108      return true;
109    }
110  };
111
112  var createOptionsForJoin = function (files, paths, separator) {
113    var code = concatFiles(files, separator);
114    var targetFileName = paths.destName + '.src.coffee';
115    grunt.file.write(paths.destDir + targetFileName, code);
116
117    return {
118      code: code,
119      sourceFiles: [targetFileName],
120      sourceRoot: ''
121    };
122  };
123
124  var concatFiles = function (files, separator) {
125    return files.map(function (filePath) {
126      return grunt.file.read(filePath);
127    }).join(grunt.util.normalizelf(separator));
128  };
129
130  var createOptionsForFile = function (file, paths) {
131    return {
132      code: grunt.file.read(file),
133      sourceFiles: [path.basename(file)],
134      sourceRoot: appendTrailingSlash(path.relative(paths.destDir, path.dirname(file)))
135    };
136  };
137
138  var appendFooter = function (output, paths) {
139    // Add sourceMappingURL to file footer
140    output.js = output.js + '\n/*\n//@ sourceMappingURL=' + paths.mapFileName + '\n*/';
141  };
142
143  var concatInput = function (files, options) {
144    if (!hasUniformExtensions(files)) {
145      return;
146    }
147
148    var code = concatFiles(files, options.separator);
149    return compileCoffee(code, options);
150  };
151
152  var concatOutput = function(files, options) {
153    return files.map(function(filepath) {
154      var code = grunt.file.read(filepath);
155      return compileCoffee(code, options, filepath);
156    }).join(grunt.util.normalizelf(options.separator));
157  };
158
159  var compileCoffee = function(code, options, filepath) {
160    options = _.clone(options);
161    if(filepath) {
162      options.filename = filepath;
163      options.literate = isLiterate(path.extname(filepath));
164    }
165
166    try {
167      return require('coffee-script').compile(code, options);
168    } catch (e) {
169      if (e.location == null ||
170          e.location.first_column == null ||
171          e.location.first_line == null) {
172        grunt.log.error('Got an unexpected exception ' +
173                        'from the coffee-script compiler. ' +
174                        'The original exception was: ' +
175                        e);
176        grunt.log.error('(The coffee-script compiler should not raise *unexpected* exceptions. ' +
177                        'You can file this error as an issue of the coffee-script compiler: ' +
178                        'https://github.com/jashkenas/coffee-script/issues)');
179      } else {
180        var firstColumn = e.location.first_column;
181        var firstLine = e.location.first_line;
182        var codeLine = code.split('\n')[firstLine];
183        var errorArrows = '\x1B[31m>>\x1B[39m ';
184        var offendingCharacter;
185 
186        if (firstColumn < codeLine.length) {
187          offendingCharacter = '\x1B[31m' + codeLine[firstColumn] + '\x1B[39m';
188        } else {
189          offendingCharacter = '';
190        }
191 
192        grunt.log.error(e);
193        grunt.log.error('In file: ' + filepath);
194        grunt.log.error('On line: ' + firstLine);
195        // Log erroneous line and highlight offending character
196        // grunt.log.error trims whitespace so we have to use grunt.log.writeln
197        grunt.log.writeln(errorArrows + codeLine.substring(0, firstColumn) +
198                          offendingCharacter + codeLine.substring(firstColumn + 1));
199        grunt.log.writeln(errorArrows + grunt.util.repeat(firstColumn, ' ') +
200                          '\x1B[31m^\x1B[39m ');
201      }
202      grunt.fail.warn('CoffeeScript failed to compile.');
203    }
204  };
205
206  var writeFileAndMap = function(paths, output) {
207    if (!output || output.js.length === 0) {
208      warnOnEmptyFile(paths.dest);
209      return;
210    }
211
212    writeFile(paths.dest, output.js);
213    writeFile(paths.destDir + paths.mapFileName, output.v3SourceMap);
214  };
215
216  var warnOnEmptyFile = function (path) {
217    grunt.log.warn('Destination (' + path + ') not written because compiled files were empty.');
218  };
219
220  var writeFile = function (path, output) {
221    if (output.length < 1) {
222      warnOnEmptyFile(path);
223    } else {
224      grunt.file.write(path, output);
225      grunt.log.writeln('File ' + path + ' created.');
226    }
227  };
228};
Note: See TracBrowser for help on using the repository browser.