Skip to content
Browse files

Support nested function

  • Loading branch information...
1 parent 7b21c70 commit 7caed684c3916ddf55025f72106fce0c9463b6f1 @bolasblack bolasblack committed
View
29 Readme.md
@@ -392,6 +392,35 @@ h1 {
}
```
+ Nested functions works well too:
+
+```css
+input {
+ top: divide(subtract(30, floor(multiply(20, 10))), 2);
+}
+```
+
+```javascript
+var functions = {
+}
+
+rework(css)
+ .use(rework.function(
+ subtract: function(a, b) { return a - b },
+ multiply: function(a, b) { return a * b },
+ divide: function(a, b) { return a / b },
+ floor: Math.floor
+ )).toString()
+```
+
+ Would yield:
+
+```css
+input {
+ top: -85;
+}
+```
+
You may also return array values to expand to several definitions of the property:
```
View
70 lib/plugins/function.js
@@ -14,10 +14,10 @@ var strip = utils.stripQuotes;
module.exports = function(functions, args) {
if (!functions) throw new Error('functions object required');
return function(style, rework){
+ var functionMatcher = functionMatcherBuilder(Object.keys(functions).join('|'));
+
visit.declarations(style, function(declarations){
- for (var name in functions) {
- func(declarations, name, functions[name], args);
- }
+ func(declarations, functions, functionMatcher, args);
});
}
};
@@ -38,20 +38,68 @@ function escape(s) {
*
* @param {Array} declarations
* @param {Object} functions
+ * @param {RegExp} functionMatcher
* @param {Boolean} [parseArgs]
* @api private
*/
-function func(declarations, name, func, parseArgs) {
+function func(declarations, functions, functionMatcher, parseArgs) {
if (false !== parseArgs) parseArgs = true;
- var regexp = new RegExp(escape(name) + '\\(([^\)]+)\\)', 'g');
+
declarations.forEach(function(decl){
if ('comment' == decl.type) return;
- if (!~decl.value.indexOf(name + '(')) return;
- decl.value = decl.value.replace(regexp, function(_, args){
- if (!parseArgs) return func.call(decl, strip(args));
- args = args.split(/,\s*/).map(strip);
- return func.apply(decl, args);
- });
+ 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);
+}
+
View
4 test/fixtures/function.infinite-loop.css
@@ -0,0 +1,4 @@
+.selector {
+ background-image: url(/path/to/some/image.png);
+}
+
View
4 test/fixtures/function.infinite-loop.out.css
@@ -0,0 +1,4 @@
+.selector {
+ background-image: url(/some/prefix/path/to/some/image.png);
+}
+
View
8 test/fixtures/function.nested.css
@@ -0,0 +1,8 @@
+.rule-with-all-defined-functions {
+ attr: divide(subtract(30, floor(multiply(20, 10))), 2);
+}
+
+.rule-with-some-undefined-functions {
+ attr: subtract(30, user-need-handle-this-case-by-self(multiply(20, 10)))
+}
+
View
8 test/fixtures/function.nested.out.css
@@ -0,0 +1,8 @@
+.rule-with-all-defined-functions {
+ attr: -85;
+}
+
+.rule-with-some-undefined-functions {
+ attr: subtract(30, user-need-handle-this-case-by-self(200));
+}
+
View
25 test/rework.js
@@ -201,6 +201,31 @@ describe('rework', function(){
}).join(', ');
}
})
+
+ it('should support nested function', function() {
+ var functions = {
+ subtract: function(a, b) { return a - b },
+ multiply: function(a, b) { return a * b },
+ divide: function(a, b) { return a / b },
+ floor: Math.floor
+ }
+
+ rework(fixture('function.nested'))
+ .use(rework.function(functions))
+ .toString()
+ .should.equal(fixture('function.nested.out'));
+ })
+
+ it('should prevent infinite loop', function() {
+ rework(fixture('function.infinite-loop'))
+ .use(rework.function({url: prefixurl}))
+ .toString()
+ .should.equal(fixture('function.infinite-loop.out'));
+
+ function prefixurl(path) {
+ return 'url(' + '/some/prefix' + path + ')';
+ }
+ })
})
describe('.references()', function(){

0 comments on commit 7caed68

Please sign in to comment.
Something went wrong with that request. Please try again.