3 exports.init = function (grunt) {
4 var fs = require('fs');
5 var tmp = require('tmp');
6 var dargs = require('dargs');
7 var path = require('path');
8 var async = require('async');
9 var onetime = require('onetime');
10 var whichSync = require('which').sync;
15 function camelCaseToUnderscore(str) {
17 .replace(/([a-z])([A-Z])/g, '$1_$2')
21 // Extracts the options that cannot be used as CLI parameter but only
22 // as 'raw' arguments.
23 // Returns an object: {raw: str, options: []} with the raw string to be
24 // used to generate a config and the list of used options.
25 exports.extractRawOptions = function extractRawOptions(options) {
26 var raw = options.raw || '';
27 var supportedOptions = [
29 'http_stylesheets_path',
33 'generated_images_dir',
34 'generated_images_path',
35 'http_generated_images_path',
37 'http_javascripts_path',
46 var usedOptions = Object.keys(options).filter(function (option) {
47 var underscoredOption = camelCaseToUnderscore(option);
48 if (supportedOptions.indexOf(underscoredOption) >= 0) {
49 // naively escape single-quotes in the value
50 var value = options[option].replace(/'/g, '\\\'');
51 raw += underscoredOption + ' = \'' + value + '\'\n';
52 delete options[option];
55 } else if (underscoredOption === 'asset_cache_buster') {
56 // Special handling for asset_cache_buster as it doesn't take
57 // a string as argument, but either an inline-ruby block (which we don't
58 // support) or a `:none` symbol to disable it.
59 if (options[option] === false) {
60 raw += underscoredOption + ' :none' + '\n';
62 delete options[option];
64 } else if (underscoredOption === 'sprite_load_path') {
65 // Special handling for sprite_load_path as it doesn't take
66 // a string as argument, but an array or a string.
67 // Append the load paths in ruby via <<
68 // http://compass-style.org/blog/2012/02/01/compass-0-12-is-released/
69 var loadPath = options[option];
71 loadPath = Array.isArray(loadPath) ? loadPath : [loadPath];
72 loadPath.forEach(function (path) {
73 // naively escape double-quotes in the value
74 path = path.replace(/"/g, '\\"');
75 raw += underscoredOption + ' << "' + path + '"\n';
78 delete options[option];
83 return {raw: raw, options: usedOptions};
86 // Create a function to add a banner, if requested through the options.
87 exports.buildBannerCallback = function (grunt, options) {
88 if (!options.specify || !options.banner) {
89 if (options.banner && !options.specify) {
90 grunt.warn('You can only use the `banner` option in combination with `specify.`');
92 // Return a no-op if specify or banner aren't set.
93 return function () {};
96 var srcFiles = grunt.file.expand({
97 filter: function (filePath) {
98 return path.basename(filePath)[0] !== '_';
102 var banner = options.banner;
103 delete options.banner;
105 var destFiles = srcFiles.map(function (filename) {
106 return filename.replace(options.sassDir, options.cssDir).replace(/\.(css\.)?(scss|sass)$/i, '.css');
110 grunt.log.verbose.writeln('Writing CSS banners.');
111 async.map(destFiles, function (filename) {
112 grunt.log.verbose.writeln('Writing CSS banner for ' + filename);
113 var content = grunt.file.read(filename);
114 grunt.file.write(filename, banner + grunt.util.linefeed + content);
119 // Create a config file on the fly if there are arguments not supported as
120 // CLI, returns a function that runs within the temprorary context.
121 exports.buildConfigContext = function (options) {
122 var rawOptions = exports.extractRawOptions(options);
123 if (options.raw && options.config) {
124 grunt.warn('The options `raw` and `config` are mutually exclusive');
127 if (rawOptions.options.length > 0 && options.config) {
128 grunt.warn('The option `config` cannot be combined with ' +
129 'these options: ' + rawOptions.options.join(', ') + '.');
132 return function configContext(cb) {
133 if (rawOptions.raw) {
134 tmp.setGracefulCleanup();
135 tmp.file(function (err, path, fd) {
140 // Dynamically create config.rb as a tmp file for the `raw` content
141 fs.writeSync(fd, new Buffer(rawOptions.raw), 0, rawOptions.raw.length);
150 // build the array of arguments to build the compass command
151 exports.buildArgsArray = function (options) {
152 var args = ['compile'];
155 } else if (options.watch) {
159 if (options.bundleExec) {
160 args.unshift(path.basename(whichSync('bundle')), 'exec', 'compass');
162 // Find the compass binary
163 args.unshift(path.basename(whichSync('compass')));
166 // add converted options
167 [].push.apply(args, dargs(options, [
176 if (grunt.option('no-color')) {
177 args.push('--boring');
180 var pushDoubleDash = onetime(function () {
184 if (options.basePath) {
186 args.push(options.basePath);
189 if (options.specify) {
191 var files = grunt.file.expand({
192 filter: function (filePath) {
193 return path.basename(filePath)[0] !== '_';
197 if (files.length > 0) {
198 [].push.apply(args, files);
200 return grunt.log.writeln('`specify` option used, but no files were found.');