From 2268fd663644f2b8648dd7a0fd0a08a97971857c Mon Sep 17 00:00:00 2001 From: Maxime Thirouin Date: Mon, 26 May 2014 08:16:00 +0200 Subject: [PATCH] Add `preserve` option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Setting `preserve` to `true` will preserve `calc()` in the output, so that they can be used by supporting browsers. Useful now that browsers are starting to implement vars (eg Firefox 31+) & it’s even better for debug [to see real rules](http://cl.ly/image/3W3O0E41173X). I’ve refactored the code a little bit to make it easier to use the `options.preserve` in the main function (& also use rework-visit which is more bullet proof). This should be a major update since now calc need to be called as a function (like rework-vars for example) to be able to pass options. So this should be tagged as 0.3 (or maybe it’s time to ship 1.0 :) ?) I also quickly refactor tests to make it more understandable (plugins.js near preserve.css was confusing imo). I hope it’s ok. FWI, I did this since I’m more seeing rework-vars & rework-calc as fallback than replacement. See https://github.com/reworkcss/rework-vars/issues/28 https://github.com/reworkcss/rework-vars/issues/29 & https://github.com/segmentio/myth/issues/64 --- README.md | 8 ++- lib/calc.js | 70 +++++++++++++----------- package.json | 3 + test/{ => fixtures}/calc-complex.in.css | 0 test/{ => fixtures}/calc-complex.out.css | 0 test/{ => fixtures}/calc-percent.in.css | 0 test/{ => fixtures}/calc-percent.out.css | 0 test/{ => fixtures}/calc-prefix.in.css | 0 test/{ => fixtures}/calc-prefix.out.css | 0 test/{ => fixtures}/calc.in.css | 0 test/{ => fixtures}/calc.out.css | 0 test/fixtures/preserve.in.css | 9 +++ test/fixtures/preserve.out.css | 11 ++++ test/plugins.js | 47 ---------------- test/test.js | 38 +++++++++++++ 15 files changed, 105 insertions(+), 81 deletions(-) rename test/{ => fixtures}/calc-complex.in.css (100%) rename test/{ => fixtures}/calc-complex.out.css (100%) rename test/{ => fixtures}/calc-percent.in.css (100%) rename test/{ => fixtures}/calc-percent.out.css (100%) rename test/{ => fixtures}/calc-prefix.in.css (100%) rename test/{ => fixtures}/calc-prefix.out.css (100%) rename test/{ => fixtures}/calc.in.css (100%) rename test/{ => fixtures}/calc.out.css (100%) create mode 100644 test/fixtures/preserve.in.css create mode 100644 test/fixtures/preserve.out.css delete mode 100644 test/plugins.js create mode 100644 test/test.js diff --git a/README.md b/README.md index cf98322..f77c599 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ particularly useful together with the [rework-vars](https://npmjs.org/package/re When multiple units are mixed together in the same expression, the calc() statement is left as is, to fallback to the CSS3 Calc feature. -**Example** (with rework-vars enabled as well): +**Example** (with [rework-vars](https://github.com/reworkcss/rework-vars) enabled as well): ```css :root { @@ -62,6 +62,12 @@ h1 { See unit tests for another example. +### Options + +#### `preserve` (default: `false`) + +Setting `preserve` to `true` will preserve `calc()` in the output, so that they can be used by supporting browsers. + ## Unit tests Make sure the dev-dependencies are installed, and then run: diff --git a/lib/calc.js b/lib/calc.js index 979495a..8517ef1 100644 --- a/lib/calc.js +++ b/lib/calc.js @@ -1,24 +1,20 @@ - /** * Calculation Plugin * * Useful in combination with the [rework-vars](https://npmjs.org/package/rework-vars) plugin, e.g: * * :root { - * var-base-font-size: 16px; + * --base-font-size: 16px; * } * body { - * font-size: var(base-font-size); + * font-size: var(--base-font-size); * } * h1 { - * font-size: calc(var(base-font-size) * 2); + * font-size: calc(var(--base-font-size) * 2); * } * * Yields: * - * :root { - * var-base-font-size: 16px; - * } * body { * font-size: 16px; * } @@ -28,8 +24,35 @@ * */ -module.exports = function (style) { - rules(style.rules); +var visit = require('rework-visit'); + +module.exports = function (options) { + options = options || {}; + options.preserve = options.preserve !== undefined ? options.preserve : false; + + return function calc(style) { + visit(style, !options.preserve ? + function(declarations, node) { + declarations.forEach(function (decl) { + if (!hasExpressions(decl.value)) return; + + decl.value = evaluateValue(decl.value); + }); + } : + function(declarations, node) { + for (var i = 0; i < declarations.length; i++) { + var decl = declarations[i] + if (!hasExpressions(decl.value)) return; + + declarations.splice(i++, 0, { + type: decl.type, + property: decl.property, + value: evaluateValue(decl.value) + }); + } + } + ); + } }; /** @@ -41,34 +64,13 @@ var DEFAULT_UNIT = 'px', EXPRESSION_METHOD_REGEXP = EXPRESSION_OPT_VENDOR_PREFIX + EXPRESSION_METHOD_NAME, EXPRESSION_REGEXP = '\\b' + EXPRESSION_METHOD_REGEXP + '\\('; -/** - * Visit all rules - * - * @param {Array} arr Array with css rules - * @api private - */ -function rules(arr) { - arr.forEach(function (rule) { - if (rule.rules) rules(rule.rules); - if (rule.declarations) visit(rule.declarations); - }); -} - /** * Visit all declarations (in a rule) * * @param {Array} declarations * @api private */ -function visit(declarations) { - declarations.forEach(function (decl) { - if (!hasExpressions(decl.value)) return; - var expressions = getExpressionsFromValue(decl.value); - - evaluateAndApplyExpressions(expressions, decl); - }); -} /** * Checks if a value contains an expression @@ -118,8 +120,8 @@ function getExpressionsFromValue(value) { * @param {Object} declaration * @api private */ -function evaluateAndApplyExpressions(expressions, declaration) { - expressions.forEach(function (expression) { +function evaluateValue(value) { + getExpressionsFromValue(value).forEach(function (expression) { var result = evaluateExpression(expression); if (!result) return; @@ -129,8 +131,10 @@ function evaluateAndApplyExpressions(expressions, declaration) { EXPRESSION_METHOD_REGEXP + '\\(' + escapeExp(expression) + '\\)' ); - declaration.value = declaration.value.replace(expRegexp, result); + value = value.replace(expRegexp, result); }); + + return value } /** diff --git a/package.json b/package.json index 35bc87d..efda46e 100644 --- a/package.json +++ b/package.json @@ -19,5 +19,8 @@ "mocha": "~1.15.1", "rework": "~0.18.3", "chai": "~1.8.1" + }, + "dependencies": { + "rework-visit": "^1.0.0" } } diff --git a/test/calc-complex.in.css b/test/fixtures/calc-complex.in.css similarity index 100% rename from test/calc-complex.in.css rename to test/fixtures/calc-complex.in.css diff --git a/test/calc-complex.out.css b/test/fixtures/calc-complex.out.css similarity index 100% rename from test/calc-complex.out.css rename to test/fixtures/calc-complex.out.css diff --git a/test/calc-percent.in.css b/test/fixtures/calc-percent.in.css similarity index 100% rename from test/calc-percent.in.css rename to test/fixtures/calc-percent.in.css diff --git a/test/calc-percent.out.css b/test/fixtures/calc-percent.out.css similarity index 100% rename from test/calc-percent.out.css rename to test/fixtures/calc-percent.out.css diff --git a/test/calc-prefix.in.css b/test/fixtures/calc-prefix.in.css similarity index 100% rename from test/calc-prefix.in.css rename to test/fixtures/calc-prefix.in.css diff --git a/test/calc-prefix.out.css b/test/fixtures/calc-prefix.out.css similarity index 100% rename from test/calc-prefix.out.css rename to test/fixtures/calc-prefix.out.css diff --git a/test/calc.in.css b/test/fixtures/calc.in.css similarity index 100% rename from test/calc.in.css rename to test/fixtures/calc.in.css diff --git a/test/calc.out.css b/test/fixtures/calc.out.css similarity index 100% rename from test/calc.out.css rename to test/fixtures/calc.out.css diff --git a/test/fixtures/preserve.in.css b/test/fixtures/preserve.in.css new file mode 100644 index 0000000..42b4e6a --- /dev/null +++ b/test/fixtures/preserve.in.css @@ -0,0 +1,9 @@ + +body { + width: 100%; +} + +body > header { + height: calc(3em * 2); + font-size: calc(6em / 2); +} diff --git a/test/fixtures/preserve.out.css b/test/fixtures/preserve.out.css new file mode 100644 index 0000000..41c2720 --- /dev/null +++ b/test/fixtures/preserve.out.css @@ -0,0 +1,11 @@ + +body { + width: 100%; +} + +body > header { + height: 6em; + height: calc(3em * 2); + font-size: 3em; + font-size: calc(6em / 2); +} diff --git a/test/plugins.js b/test/plugins.js deleted file mode 100644 index a86700a..0000000 --- a/test/plugins.js +++ /dev/null @@ -1,47 +0,0 @@ -var calc = require('../index'), - rework = require('rework'), - should = require('chai').Should(), - read = require('fs').readFileSync; - -var css = { - in: function (name) { - return this._read(name, 'in'); - }, - out: function (name) { - return this._read(name, 'out'); - }, - _read: function (name, type) { - return read(__dirname + '/' + name + '.' + type + '.css', 'utf8'); - } -}; - -describe('rework-calc', function() { - it('should calculate expressions with only one unit involved', function() { - rework(css.in('calc')) - .use(calc) - .toString() - .should.equal(css.out('calc')); - }); - - it('should calculate expressions with percents correctly', function () { - rework(css.in('calc-percent')) - .use(calc) - .toString() - .should.equal(css.out('calc-percent')); - }); - - it('should use CSS3 Calc function as fallback for expressions with multiple units', function () { - rework(css.in('calc-complex')) - .use(calc) - .toString() - .should.equal(css.out('calc-complex')); - }); - - it('should handle vendor prefixed expressions', function () { - rework(css.in('calc-prefix')) - .use(calc) - .toString() - .should.equal(css.out('calc-prefix')); - }); - -}); diff --git a/test/test.js b/test/test.js new file mode 100644 index 0000000..978c9dc --- /dev/null +++ b/test/test.js @@ -0,0 +1,38 @@ +var calc = require('../index'), + rework = require('rework'), + expect = require('chai').expect, + read = require('fs').readFileSync; + +function fixture(name){ + return read('test/fixtures/' + name + '.css', 'utf8').trim(); +} + +function compareFixtures(name, options){ + return expect( + rework(fixture(name + '.in')) + .use(calc(options)) + .toString().trim() + ).to.equal(fixture(name + '.out')); +} + +describe('rework-calc', function() { + it('should calculate expressions with only one unit involved', function() { + compareFixtures('calc'); + }); + + it('should calculate expressions with percents correctly', function () { + compareFixtures('calc-percent'); + }); + + it('should use CSS3 Calc function as fallback for expressions with multiple units', function () { + compareFixtures('calc-complex'); + }); + + it('should handle vendor prefixed expressions', function () { + compareFixtures('calc-prefix'); + }); + + it('should preserves calc() when `preserve` is `true`', function() { + compareFixtures('preserve', {preserve: true}); + }); +});