module.exports = function (object, data) { var out = grunt.util.kindOf(object) === 'array' ? [] : {}; data.self = out; function recurse(input, output) { Object.keys(input).forEach(function (key) { var kind = grunt.util.kindOf(input[key]); if (kind === 'array') { output[key] = []; recurse(input[key], output[key]); } else if (kind === 'object') { output[key] = {}; recurse(input[key], output[key]); } else if (kind === 'string') { output[key] = grunt.template.process(input[key], { data : data, delimiters : 'ipsum-delimiters' }); } else { output[key] = input[key]; } }); } recurse(object, out); return out; };
Utils.extension = function(filename) { grunt.verbose.writeln('extension'); grunt.verbose.writeln(filename); if(grunt.util.kindOf(filename) === 'array' && filename.length > 0) { filename = filename[0]; } return _(filename.match(/[^.]*$/)).last(); };
this.options = function() { var targetObj = grunt.config([name, target]); var args = [{}].concat(grunt.util.toArray(arguments)).concat([ grunt.config([name, 'options']), grunt.util.kindOf(targetObj) === 'object' ? targetObj.options : {} ]); var options = grunt.util._.extend.apply(null, args); grunt.verbose.writeflags(options, 'Options'); return options; };
Utils.findBasePath = function(srcFiles, basePath) { if (basePath === false) {return '';} if (grunt.util.kindOf(basePath) === 'string' && basePath.length >= 1) { return _(path.normalize(basePath)).trim(path.sep); } var foundPath, basePaths = [], dirName; srcFiles.forEach(function(srcFile) { srcFile = path.normalize(srcFile); dirName = path.dirname(srcFile); basePaths.push(dirName.split(path.sep)); }); basePaths = _.intersection.apply([], basePaths); foundPath = path.join.apply(path, basePaths); if (foundPath === '.') {foundPath = '';} return foundPath; };
Object.keys(input).forEach(function (key) { var kind = grunt.util.kindOf(input[key]); if (kind === 'array') { output[key] = []; recurse(input[key], output[key]); } else if (kind === 'object') { output[key] = {}; recurse(input[key], output[key]); } else if (kind === 'string') { output[key] = grunt.template.process(input[key], { data : data, delimiters : 'ipsum-delimiters' }); } else { output[key] = input[key]; } });
data.map(function(val) { grunt.verbose.ok('Type:'.yellow, grunt.util.kindOf(val)); // Skip empty data files to avoid compiler errors if (_.isString(val)) { grunt.file.expand(val).forEach(function(filepath) { var checkForContent = grunt.file.read(filepath); if (checkForContent.length === 0 || checkForContent === '') { grunt.verbose.warn('Skipping empty path:'.yellow, val); } else { var parsedData = exports.dataFileReaderFactory(filepath); metadata = grunt.config.process(_.extend({}, metadata, parsedData)); grunt.verbose.ok('metadata:'.yellow, metadata); } }); } if (_.isObject(val)) { metadata = grunt.config.process(_.extend({}, metadata, val)); grunt.verbose.ok('metadata:'.yellow, metadata); } });
/** * Converts stupid grunt tasks to a consistent format * * @param {*} data * @return {Array.<{src:Array.<string>,dest:string}>} */ function normalizeMultiTaskFiles(data) { var prop, obj; var files = []; if (grunt.util.kindOf(data) === 'object') { if ('src' in data || 'dest' in data) { obj = {}; for (prop in data) { if (prop !== 'options') { obj[prop] = data[prop]; } } files.push(obj); } else if (grunt.util.kindOf(data.files) === 'object') { for (prop in data.files) { files.push({ src: data.files[prop], dest: grunt.config.process(prop) }); } } else if (Array.isArray(data.files)) { grunt.util._.flatten(data.files).forEach(function(obj) { var prop; if ('src' in obj || 'dest' in obj) { files.push(obj); } else { for (prop in obj) { files.push({ src: obj[prop], dest: grunt.config.process(prop) }); } } }); } } // If no src/dest or files were specified, return an empty files array. if (files.length === 0) { grunt.verbose.writeln('File: ' + '[no files]'.yellow); return []; } // Process all normalized file objects. files = grunt.util._(files).chain().forEach(function(obj) { if (!('src' in obj) || !obj.src) { return; } // Normalize .src properties to flattened array. if (Array.isArray(obj.src)) { obj.src = grunt.util._.flatten(obj.src); } else { obj.src = [obj.src]; } }).map(function(obj) { // Build options object, removing unwanted properties. var expandOptions = grunt.util._.extend({}, obj); delete expandOptions.src; delete expandOptions.dest; // Expand file mappings. if (obj.expand) { var newObj = {}; newObj.src = []; newObj.dest = obj.dest; if (obj.src) { for (var i = 0; i < obj.src.length; i++) { newObj.src.push(obj.cwd ? path.join(obj.cwd, obj.src[i]) : obj.src[i]); } } obj = newObj; } // Copy obj properties to result, adding an .orig property. var result = grunt.util._.extend({}, obj); if ('src' in result) { // Expose an expand-on-demand getter method as .src. Object.defineProperty(result, 'src', { enumerable: true, get: function fn() { var src; if (!('result' in fn)) { src = obj.src; // If src is an array, flatten it. Otherwise, make it into an array. src = Array.isArray(src) ? grunt.util._.flatten( src) : [src]; } return src; } }); } if ('dest' in result) { result.dest = obj.dest; } return result; }).flatten().value(); return files; }
module.exports.stylesheet = function(srcFile, opts, done) { opts = opts || {}; // Regular expressions to check inclusion or exclusion of embedded files var rInclude = opts.regexInclude || /.*/g; var rExclude = opts.regexExclude || /$^/g; // Cache of already converted images var cache = {}; // Shift args if no options object is specified if(grunt.util.kindOf(opts) === 'function') { done = opts; opts = {}; } var src = grunt.file.read(srcFile); var result = ''; var img, group; grunt.util.async.whilst(function() { group = rImages.exec(src); return group != null; }, function(complete) { // if there is another url to be processed, then: // group[1] will hold everything up to the url declaration // group[2] will hold the complete url declaration (useful if no encoding will take place) // group[3] will hold the contents of the url declaration // group[4] will be undefined // if there is no other url to be processed, then group[1-3] will be undefined // group[4] will hold the entire string // Will skip processing if file is not included or is excluded var process = group[3] && (group[3].match(rInclude) !== null || group[3].match(rExclude) === null); if(group[4] == null && process) { result += group[1]; img = group[3].trim() .replace(rQuotes, '') // remove quotation marks .replace(rParams, ''); // remove query string/hash parmams in the filename, like foo.png?bar or foo.png#bar // Throw a warning if this image has already been encoded elsewhere // in the stylesheet if(cache[img]) { grunt.log.warn('The image ' + img + ' has already been encoded elsewhere in your stylesheet. I\'m going to do it again, but it\'s going to make your stylesheet a lot larger than it needs to be.'); result = result += cache[img]; return complete(); } // process it and put it into the cache var loc = img; var isLocalFile = !rData.test(img) && !rExternal.test(img) && !rSchemeless.test(img); // Resolve the image path relative to the CSS file if(isLocalFile) { // local file system.. fix up the path loc = img.charAt(0) === '/' ? (opts.baseDir || '') + loc : path.join(path.dirname(srcFile), (opts.baseDir || '') + img); // If that didn't work, try finding the image relative to // the current file instead. if(!fs.existsSync(loc)) { loc = path.resolve(__dirname + img); } } // Test for scheme less URLs => "//example.com/image.png" if(!isLocalFile && rSchemeless.test(loc)) { loc = 'http:' + loc; } // Encode the image exports.image(loc, opts, function(err, resp, cacheable) { if(err == null) { var url = 'url(' + resp + ')'; result += url; if(cacheable !== false) { cache[img] = url; } if(opts.deleteAfterEncoding && isLocalFile) { grunt.log.writeln('Deleting file ' + loc); fs.unlinkSync(loc); } } else { result += group[2]; } complete(); }); } else if (group[4] === undefined && !process) { result += group[1] + group[2]; complete(); } else { result += group[4]; complete(); } }, function(err) { done(err, result); }); };
exports.image = function(img, opts, done) { // Shift args if(grunt.util.kindOf(opts) === 'function') { done = opts; opts = {}; } // Set default, helper-specific options opts = _.extend({ maxImageSize: 32768 }, opts); /** * Callback when the image has been base64 encoded. */ var complete = function(err, encoded, cacheable) { // Did the dataURI exceed the max length? if(cacheable && encoded && opts.maxImageSize && encoded.length > opts.maxImageSize) { err = new Error("Skipping " + img + " (greater than " + opts.maxImageSize + " bytes)"); } // Return the original source if an error occurred if(err) { return done(err, img, false); } done(null, encoded, cacheable); }; /** * Function to base64 encode the image */ var process = function() { // If the image is already base64 encoded, pass it directly // through to the callback. if(rData.test(img)) { return complete(null, img, false); } // External URL? if(rExternal.test(img)) { grunt.log.writeln('Encoding file: ' + img); return fetch.image(img, function(err, src, cacheable) { if(err) { return complete(err); } var type = mime.lookup(img); var encoded = getDataURI(type, src); complete(null, encoded, cacheable); }); } // If we get this far we can assume the image is a local file. // Does the image actually exist? if(!fs.existsSync(img)) { return complete(new Error('File ' + img + ' does not exit')); } // Read the local file, and convert it, and cache it. grunt.log.writeln('Encoding file: ' + img); var src = fs.readFileSync(img); var type = mime.lookup(img); var encoded = getDataURI(type, src); complete(null, encoded, true); }; // Ask optional callback what to do with the image // return values // false: do not encode // true: process images as ususal // String: replace the image with the returned string if(opts.preEncodeCallback) { var rv = opts.preEncodeCallback(img); // do not encode if(rv === false) { return complete(new Error('Image encoding is declined by callback')); } // continue as usual if(rv === true) { return process(); } // replace the image with the callback-provided value complete(null, rv, false); // false == non-cacheable } else { process(); } };
module.exports.is = function (thing, type) { return grunt.util.kindOf(thing) === type; };
task.normalizeMultiTaskFiles = function(data, target) { var prop, obj; var files = []; if (grunt.util.kindOf(data) === 'object') { if ('src' in data || 'dest' in data) { obj = {}; for (prop in data) { if (prop !== 'options') { obj[prop] = data[prop]; } } files.push(obj); } else if (grunt.util.kindOf(data.files) === 'object') { for (prop in data.files) { files.push({src: data.files[prop], dest: grunt.config.process(prop)}); } } else if (Array.isArray(data.files)) { grunt.util._.flatten(data.files).forEach(function(obj) { var prop; if ('src' in obj || 'dest' in obj) { files.push(obj); } else { for (prop in obj) { files.push({src: obj[prop], dest: grunt.config.process(prop)}); } } }); } } else { files.push({src: data, dest: grunt.config.process(target)}); } // If no src/dest or files were specified, return an empty files array. if (files.length === 0) { grunt.verbose.writeln('File: ' + '[no files]'.yellow); return []; } // Process all normalized file objects. files = grunt.util._(files).chain().forEach(function(obj) { if (!('src' in obj) || !obj.src) { return; } // Normalize .src properties to flattened array. if (Array.isArray(obj.src)) { obj.src = grunt.util._.flatten(obj.src); } else { obj.src = [obj.src]; } }).map(function(obj) { // Build options object, removing unwanted properties. var expandOptions = grunt.util._.extend({}, obj); delete expandOptions.src; delete expandOptions.dest; // Expand file mappings. if (obj.expand) { return grunt.file.expandMapping(obj.src, obj.dest, expandOptions).map(function(mapObj) { // Copy obj properties to result. var result = grunt.util._.extend({}, obj); // Make a clone of the orig obj available. result.orig = grunt.util._.extend({}, obj); // Set .src and .dest, processing both as templates. result.src = grunt.config.process(mapObj.src); result.dest = grunt.config.process(mapObj.dest); // Remove unwanted properties. ['expand', 'cwd', 'flatten', 'rename', 'ext'].forEach(function(prop) { delete result[prop]; }); return result; }); } // Copy obj properties to result, adding an .orig property. var result = grunt.util._.extend({}, obj); // Make a clone of the orig obj available. result.orig = grunt.util._.extend({}, obj); if ('src' in result) { // Expose an expand-on-demand getter method as .src. Object.defineProperty(result, 'src', { enumerable: true, get: function fn() { var src; if (!('result' in fn)) { src = obj.src; // If src is an array, flatten it. Otherwise, make it into an array. src = Array.isArray(src) ? grunt.util._.flatten(src) : [src]; // Expand src files, memoizing result. fn.result = grunt.file.expand(expandOptions, src); } return fn.result; } }); } if ('dest' in result) { result.dest = obj.dest; } return result; }).flatten().value(); // Log this.file src and dest properties when --verbose is specified. if (grunt.option('verbose')) { files.forEach(function(obj) { var output = []; if ('src' in obj) { output.push(obj.src.length > 0 ? grunt.log.wordlist(obj.src) : '[no src]'.yellow); } if ('dest' in obj) { output.push('-> ' + (obj.dest ? String(obj.dest).cyan : '[no dest]'.yellow)); } if (output.length > 0) { grunt.verbose.writeln('Files: ' + output.join(' ')); } }); } return files; };