/** * Module dependencies. */ var visit = require('rework-visit'); /** * Define custom function. */ module.exports = function(functions, args) { if (!functions) throw new Error('functions object required'); return function(style){ var functionMatcher = functionMatcherBuilder(Object.keys(functions).join('|')); visit(style, function(declarations){ func(declarations, functions, functionMatcher, args); }); } }; /** * Visit declarations and apply functions. * * @param {Array} declarations * @param {Object} functions * @param {RegExp} functionMatcher * @param {Boolean} [parseArgs] * @api private */ function func(declarations, functions, functionMatcher, parseArgs) { if (!declarations) return; if (false !== parseArgs) parseArgs = true; declarations.forEach(function(decl){ if ('comment' == decl.type) return; var generatedFuncs = [], result, generatedFunc; while (decl.value.match(functionMatcher)) { decl.value = decl.value.replace(functionMatcher, function(_, name, args){ if (parseArgs) { args = args.split(/\s*,\s*/).map(strip); } else { args = [strip(args)]; } // Ensure result is string result = '' + functions[name].apply(decl, args); // Prevent fall into infinite loop like this: // // { // url: function(path) { // return 'url(' + '/some/prefix' + path + ')' // } // } // generatedFunc = {from: name, to: name + getRandomString()}; result = result.replace(functionMatcherBuilder(name), generatedFunc.to + '($2)'); generatedFuncs.push(generatedFunc); return result; }); } generatedFuncs.forEach(function(func) { decl.value = decl.value.replace(func.to, func.from); }) }); } /** * Build function regexp * * @param {String} name * @api private */ function functionMatcherBuilder(name) { // /(?!\W+)(\w+)\(([^()]+)\)/ return new RegExp("(?!\\W+)(" + name + ")\\(([^\(\)]+)\\)"); } /** * get random string * * @api private */ function getRandomString() { return Math.random().toString(36).slice(2); } /** * strip quotes from string * @api private */ function strip(str) { if ('"' == str[0] || "'" == str[0]) return str.slice(1, -1); return str; }