wishthis/node_modules/rework-plugin-function/index.js
2022-01-21 09:28:41 +01:00

102 lines
2.3 KiB
JavaScript

/**
* 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;
}