Skip to content

Commit

Permalink
Merge pull request #3274 from matthew-dean/strict-math-division
Browse files Browse the repository at this point in the history
Fixes #1880 - Adds two new math modes and deprecates strictMath
  • Loading branch information
matthew-dean committed Jul 11, 2018
2 parents d99a1b8 + 78c38e4 commit 8938e98
Show file tree
Hide file tree
Showing 49 changed files with 1,501 additions and 215 deletions.
12 changes: 11 additions & 1 deletion bin/lessc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ var path = require('path'),
fs = require('../lib/less-node/fs'),
os = require('os'),
utils = require('../lib/less/utils'),
MATH = require('../lib/less/math-constants'),
errno,
mkdirp;

Expand Down Expand Up @@ -481,8 +482,17 @@ function processPluginQueue() {
break;
case 'sm':
case 'strict-math':
console.warning('--strict-math is deprecated. Use --math=strict');
if (checkArgFunc(arg, match[2])) {
options.strictMath = checkBooleanArg(match[2]);
if (checkBooleanArg(match[2])) {
options.math = MATH.STRICT_LEGACY;
}
}
break;
case 'm':
case 'math':
if (checkArgFunc(arg, match[2])) {
options.math = match[2];
}
break;
case 'su':
Expand Down
4 changes: 0 additions & 4 deletions lib/less-browser/add-default-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,4 @@ module.exports = function(window, options) {
if (options.onReady === undefined) {
options.onReady = true;
}

// TODO: deprecate and remove 'inlineJavaScript' thing - where it came from at all?
options.javascriptEnabled = (options.javascriptEnabled || options.inlineJavaScript) ? true : false;

};
13 changes: 10 additions & 3 deletions lib/less-node/lessc-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,13 @@ var lessc_helper = {
console.log(' -rp, --rootpath=URL Sets rootpath for url rewriting in relative imports and urls');
console.log(' Works with or without the relative-urls option.');
console.log(' -ru, --relative-urls Re-writes relative urls to the base less file.');
console.log(' -sm=on|off Turns on or off strict math, where in strict mode, math.');
console.log(' --strict-math=on|off Requires brackets. This option may default to on and then');
console.log(' be removed in the future.');
console.log('');
console.log(' -m=, --math=');
console.log(' always Less will eagerly perform math operations always.');
console.log(' parens-division Math performed except for division (/) operator');
console.log(' parens | strict Math only performed inside parentheses');
console.log(' strict-legacy Parens required in very strict terms (legacy --strict-math)');
console.log('');
console.log(' -su=on|off Allows mixed units, e.g. 1px+1em or 1px*1px which have units');
console.log(' --strict-units=on|off that cannot be represented.');
console.log(' --global-var=\'VAR=VALUE\' Defines a variable that can be referenced by the file.');
Expand All @@ -64,6 +68,9 @@ var lessc_helper = {
console.log(' or --clean-css="advanced"');
console.log('');
console.log('-------------------------- Deprecated ----------------');
console.log(' -sm=on|off Legacy parens-only math. Use --math');
console.log(' --strict-math=on|off ');
console.log('');
console.log(' --line-numbers=TYPE Outputs filename and line numbers.');
console.log(' TYPE can be either \'comments\', which will output');
console.log(' the debug info within comments, \'mediaquery\'');
Expand Down
13 changes: 10 additions & 3 deletions lib/less/contexts.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
var contexts = {};
module.exports = contexts;
var MATH = require('./math-constants');

var copyFromOriginal = function copyFromOriginal(original, destination, propertiesToCopy) {
if (!original) { return; }
Expand Down Expand Up @@ -43,7 +44,7 @@ var evalCopyProperties = [
'paths', // additional include paths
'compress', // whether to compress
'ieCompat', // whether to enforce IE compatibility (IE8 data-uri)
'strictMath', // whether math has to be within parenthesis
'math', // whether math has to be within parenthesis
'strictUnits', // whether units need to evaluate correctly
'sourceMap', // whether to output a source map
'importMultiple', // whether we are currently importing multiple copies
Expand Down Expand Up @@ -90,11 +91,17 @@ contexts.Eval.prototype.outOfParenthesis = function () {

contexts.Eval.prototype.inCalc = false;
contexts.Eval.prototype.mathOn = true;
contexts.Eval.prototype.isMathOn = function () {
contexts.Eval.prototype.isMathOn = function (op) {
if (!this.mathOn) {
return false;
}
return this.strictMath ? (this.parensStack && this.parensStack.length) : true;
if (op === '/' && this.math !== MATH.ALWAYS && (!this.parensStack || !this.parensStack.length)) {
return false;
}
if (this.math > MATH.PARENS_DIVISION) {
return this.parensStack && this.parensStack.length;
}
return true;
};

contexts.Eval.prototype.isPathRelative = function (path) {
Expand Down
14 changes: 11 additions & 3 deletions lib/less/default-options.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
// Export a new default each time
module.exports = function() {
return {
/* Inline Javascript - @plugin still allowed */
javascriptEnabled: false,

/* Outputs a makefile import dependency list to stdout. */
depends: false,

/* Compress using less built-in compression.
/* (DEPRECATED) Compress using less built-in compression.
* This does an okay job but does not utilise all the tricks of
* dedicated css compression. */
compress: false,
Expand Down Expand Up @@ -44,8 +47,13 @@ module.exports = function() {
/* Compatibility with IE8. Used for limiting data-uri length */
ieCompat: false, // true until 3.0

/* Without this option on, Less will try and process all math in your css */
strictMath: false,
/* How to process math
* 0 always - eagerly try to solve all operations
* 1 parens-division - require parens for division "/"
* 2 parens | strict - require parens for all operations
* 3 strict-legacy - legacy strict behavior (super-strict)
*/
math: 0,

/* Without this option, less attempts to guess at the output unit when it does maths. */
strictUnits: false,
Expand Down
6 changes: 6 additions & 0 deletions lib/less/math-constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
ALWAYS: 0,
PARENS_DIVISION: 1,
PARENS: 2,
STRICT_LEGACY: 3
};
4 changes: 2 additions & 2 deletions lib/less/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ module.exports = function(environment, ParseTree, ImportManager) {

if (typeof options === 'function') {
callback = options;
options = utils.defaults(this.options, {});
options = utils.copyOptions(this.options, {});
}
else {
options = utils.defaults(this.options, options || {});
options = utils.copyOptions(this.options, options || {});
}

if (!callback) {
Expand Down
2 changes: 1 addition & 1 deletion lib/less/parser/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -1947,7 +1947,7 @@ var Parser = function Parser(context, imports, fileInfo) {

parserInput.save();

op = parserInput.$char('/') || parserInput.$char('*');
op = parserInput.$char('/') || parserInput.$char('*') || parserInput.$str('./');

if (!op) { parserInput.forget(); break; }

Expand Down
4 changes: 2 additions & 2 deletions lib/less/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ module.exports = function(environment, ParseTree, ImportManager) {
var render = function (input, options, callback) {
if (typeof options === 'function') {
callback = options;
options = utils.defaults(this.options, {});
options = utils.copyOptions(this.options, {});
}
else {
options = utils.defaults(this.options, options || {});
options = utils.copyOptions(this.options, options || {});
}

if (!callback) {
Expand Down
18 changes: 11 additions & 7 deletions lib/less/tree/declaration.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
var Node = require('./node'),
Value = require('./value'),
Keyword = require('./keyword'),
Anonymous = require('./anonymous');
Anonymous = require('./anonymous'),
MATH = require('../math-constants');

var Declaration = function (name, value, important, merge, index, currentFileInfo, inline, variable) {
this.name = name;
Expand Down Expand Up @@ -41,17 +42,20 @@ Declaration.prototype.genCSS = function (context, output) {
output.add(this.important + ((this.inline || (context.lastRule && context.compress)) ? '' : ';'), this._fileInfo, this._index);
};
Declaration.prototype.eval = function (context) {
var strictMathBypass = false, name = this.name, evaldValue, variable = this.variable;
var mathBypass = false, prevMath, name = this.name, evaldValue, variable = this.variable;
if (typeof name !== 'string') {
// expand 'primitive' name directly to get
// things faster (~10% for benchmark.less):
name = (name.length === 1) && (name[0] instanceof Keyword) ?
name[0].value : evalName(context, name);
variable = false; // never treat expanded interpolation as new variable name
}
if (name === 'font' && !context.strictMath) {
strictMathBypass = true;
context.strictMath = true;

// @todo remove when parens-division is default
if (name === 'font' && context.math === MATH.ALWAYS) {
mathBypass = true;
prevMath = context.math;
context.math = MATH.PARENS_DIVISION;
}
try {
context.importantScope.push({});
Expand Down Expand Up @@ -82,8 +86,8 @@ Declaration.prototype.eval = function (context) {
throw e;
}
finally {
if (strictMathBypass) {
context.strictMath = false;
if (mathBypass) {
context.math = prevMath;
}
}
};
Expand Down
10 changes: 7 additions & 3 deletions lib/less/tree/expression.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
var Node = require('./node'),
Paren = require('./paren'),
Comment = require('./comment');
Comment = require('./comment'),
Dimension = require('./dimension'),
MATH = require('../math-constants');

var Expression = function (value, noSpacing) {
this.value = value;
Expand All @@ -17,7 +19,8 @@ Expression.prototype.accept = function (visitor) {
Expression.prototype.eval = function (context) {
var returnValue,
mathOn = context.isMathOn(),
inParenthesis = this.parens && !this.parensInOp,
inParenthesis = this.parens &&
(context.math !== MATH.STRICT_LEGACY || !this.parensInOp),
doubleParen = false;
if (inParenthesis) {
context.inParenthesis();
Expand All @@ -40,7 +43,8 @@ Expression.prototype.eval = function (context) {
if (inParenthesis) {
context.outOfParenthesis();
}
if (this.parens && this.parensInOp && !mathOn && !doubleParen) {
if (this.parens && this.parensInOp && !mathOn && !doubleParen
&& (!(returnValue instanceof Dimension))) {
returnValue = new Paren(returnValue);
}
return returnValue;
Expand Down
14 changes: 10 additions & 4 deletions lib/less/tree/operation.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
var Node = require('./node'),
Color = require('./color'),
Dimension = require('./dimension');
Dimension = require('./dimension'),
MATH = require('../math-constants');

var Operation = function (op, operands, isSpaced) {
this.op = op.trim();
Expand All @@ -14,21 +15,26 @@ Operation.prototype.accept = function (visitor) {
};
Operation.prototype.eval = function (context) {
var a = this.operands[0].eval(context),
b = this.operands[1].eval(context);
b = this.operands[1].eval(context),
op;

if (context.isMathOn()) {
if (context.isMathOn(this.op)) {
op = this.op === './' ? '/' : this.op;
if (a instanceof Dimension && b instanceof Color) {
a = a.toColor();
}
if (b instanceof Dimension && a instanceof Color) {
b = b.toColor();
}
if (!a.operate) {
if (a instanceof Operation && a.op === '/' && context.math === MATH.PARENS_DIVISION) {
return new Operation(this.op, [a, b], this.isSpaced);
}
throw { type: 'Operation',
message: 'Operation on an invalid type' };
}

return a.operate(context, this.op, b);
return a.operate(context, op, b);
} else {
return new Operation(this.op, [a, b], this.isSpaced);
}
Expand Down
25 changes: 25 additions & 0 deletions lib/less/utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/* jshint proto: true */
var MATH = require('./math-constants');

var utils = {
getLocation: function(index, inputStream) {
var n = index + 1,
Expand Down Expand Up @@ -36,6 +38,29 @@ var utils = {
}
return cloned;
},
copyOptions: function(obj1, obj2) {
var opts = utils.defaults(obj1, obj2);
if (opts.strictMath) {
opts.math = MATH.STRICT_LEGACY;
}
if (opts.hasOwnProperty('math') && typeof opts.math === 'string') {
switch (opts.math.toLowerCase()) {
case 'always':
opts.math = MATH.ALWAYS;
break;
case 'parens-division':
opts.math = MATH.PARENS_DIVISION;
break;
case 'strict':
case 'parens':
opts.math = MATH.PARENS;
break;
case 'strict-legacy':
opts.math = MATH.STRICT_LEGACY;
}
}
return opts;
},
defaults: function(obj1, obj2) {
if (!obj2._defaults || obj2._defaults !== obj1) {
for (var prop in obj1) {
Expand Down

0 comments on commit 8938e98

Please sign in to comment.