You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.js 2.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. /**
  2. * Module dependencies.
  3. */
  4. var visit = require('rework-visit');
  5. /**
  6. * Define custom function.
  7. */
  8. module.exports = function(functions, args) {
  9. if (!functions) throw new Error('functions object required');
  10. return function(style){
  11. var functionMatcher = functionMatcherBuilder(Object.keys(functions).join('|'));
  12. visit(style, function(declarations){
  13. func(declarations, functions, functionMatcher, args);
  14. });
  15. }
  16. };
  17. /**
  18. * Visit declarations and apply functions.
  19. *
  20. * @param {Array} declarations
  21. * @param {Object} functions
  22. * @param {RegExp} functionMatcher
  23. * @param {Boolean} [parseArgs]
  24. * @api private
  25. */
  26. function func(declarations, functions, functionMatcher, parseArgs) {
  27. if (!declarations) return;
  28. if (false !== parseArgs) parseArgs = true;
  29. declarations.forEach(function(decl){
  30. if ('comment' == decl.type) return;
  31. var generatedFuncs = [], result, generatedFunc;
  32. while (decl.value.match(functionMatcher)) {
  33. decl.value = decl.value.replace(functionMatcher, function(_, name, args){
  34. if (parseArgs) {
  35. args = args.split(/\s*,\s*/).map(strip);
  36. } else {
  37. args = [strip(args)];
  38. }
  39. // Ensure result is string
  40. result = '' + functions[name].apply(decl, args);
  41. // Prevent fall into infinite loop like this:
  42. //
  43. // {
  44. // url: function(path) {
  45. // return 'url(' + '/some/prefix' + path + ')'
  46. // }
  47. // }
  48. //
  49. generatedFunc = {from: name, to: name + getRandomString()};
  50. result = result.replace(functionMatcherBuilder(name), generatedFunc.to + '($2)');
  51. generatedFuncs.push(generatedFunc);
  52. return result;
  53. });
  54. }
  55. generatedFuncs.forEach(function(func) {
  56. decl.value = decl.value.replace(func.to, func.from);
  57. })
  58. });
  59. }
  60. /**
  61. * Build function regexp
  62. *
  63. * @param {String} name
  64. * @api private
  65. */
  66. function functionMatcherBuilder(name) {
  67. // /(?!\W+)(\w+)\(([^()]+)\)/
  68. return new RegExp("(?!\\W+)(" + name + ")\\(([^\(\)]+)\\)");
  69. }
  70. /**
  71. * get random string
  72. *
  73. * @api private
  74. */
  75. function getRandomString() {
  76. return Math.random().toString(36).slice(2);
  77. }
  78. /**
  79. * strip quotes from string
  80. * @api private
  81. */
  82. function strip(str) {
  83. if ('"' == str[0] || "'" == str[0]) return str.slice(1, -1);
  84. return str;
  85. }