From daec7dff1c5d533bfaaee5315aa20c9bf56f1873 Mon Sep 17 00:00:00 2001 From: Christian Hammond Date: Wed, 13 Nov 2013 22:44:42 -0800 Subject: [PATCH] Support specifying custom variables when calling lessc and less.js. Both lessc and less.js can now be provided with global variables that all .less files will have immediate access to. This can be used to provide, for example, a base path for an @import, signed URLs offering temporary access to an image on S3, or anything else. lessc has two new parameters, --global-var and --modify-var. Both take a value of the form "varname=value". --global-var declares variables immediately before the content of the .less files, and --modify-var declares them after. --global-var is used when rules, imports, or other variables will depend on the provided variable. --modify-var is used to override a variable declared within the .less file. less.js's equivalent for global variables is less.globalVars. This can be set before loading less.js. There is no new requivalent to --modify-var, as less.modifyVars can be used for that purpose. --- Gruntfile.js | 8 +++++ bin/lessc | 23 +++++++++++-- lib/less/browser.js | 38 +++++++++++++++++----- lib/less/lessc_helper.js | 2 ++ test/browser/css/global-vars/simple.css | 3 ++ test/browser/less/global-vars/simple.less | 3 ++ test/browser/runner-global-vars-options.js | 4 +++ test/browser/runner-global-vars-spec.js | 3 ++ 8 files changed, 73 insertions(+), 11 deletions(-) create mode 100644 test/browser/css/global-vars/simple.css create mode 100644 test/browser/less/global-vars/simple.less create mode 100644 test/browser/runner-global-vars-options.js create mode 100644 test/browser/runner-global-vars-spec.js diff --git a/Gruntfile.js b/Gruntfile.js index 8ffcaeed4..cf8c96242 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -195,6 +195,14 @@ module.exports = function(grunt) { specs: 'test/browser/runner-modify-vars-spec.js', outfile: 'tmp/browser/test-runner-modify-vars.html' } + }, + globalVars: { + src: ['test/browser/less/global-vars/*.less'], + options: { + helpers: 'test/browser/runner-global-vars-options.js', + specs: 'test/browser/runner-global-vars-spec.js', + outfile: 'tmp/browser/test-runner-global-vars.html' + } } }, diff --git a/bin/lessc b/bin/lessc index 9ee3609a3..9b8b04ce9 100755 --- a/bin/lessc +++ b/bin/lessc @@ -25,7 +25,9 @@ var options = { relativeUrls: false, ieCompat: true, strictMath: false, - strictUnits: false + strictUnits: false, + globalVariables: '', + modifyVariables: '' }; var continueProcessing = true, currentErrorcode; @@ -53,6 +55,11 @@ var checkBooleanArg = function(arg) { return Boolean(onOff[2]); }; +var parseVariableOption = function(option) { + var parts = option.split('=', 2); + return '@' + parts[0] + ': ' + parts[1] + ';\n'; +}; + var warningMessages = ""; var sourceMapFileInline = false; @@ -64,7 +71,7 @@ args = args.filter(function (arg) { return false; } - if (match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=([^\s]*))?$/i)) { arg = match[1] } + if (match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=(.*))?$/i)) { arg = match[1] } else { return arg } switch (arg) { @@ -189,6 +196,16 @@ args = args.filter(function (arg) { options.strictUnits = checkBooleanArg(match[2]); } break; + case "global-var": + if (checkArgFunc(arg, match[2])) { + options.globalVariables += parseVariableOption(match[2]); + } + break; + case "modify-var": + if (checkArgFunc(arg, match[2])) { + options.modifyVariables += parseVariableOption(match[2]); + } + break; } }); @@ -267,6 +284,8 @@ var parseLessFile = function (e, data) { return; } + data = options.globalVariables + data + options.modifyVariables; + options.paths = [path.dirname(input)].concat(options.paths); options.filename = input; diff --git a/lib/less/browser.js b/lib/less/browser.js index 4e1c68cdd..e5a1dce2b 100644 --- a/lib/less/browser.js +++ b/lib/less/browser.js @@ -52,6 +52,7 @@ if (dumpLineNumbers) { var typePattern = /^text\/(x-)?less$/; var cache = null; var fileCache = {}; +var varsPre = ""; function log(str, level) { if (less.env == 'development' && typeof(console) !== 'undefined' && less.logLevel >= level) { @@ -294,9 +295,15 @@ function loadStyles(newVars) { var env = new less.tree.parseEnv(less), lessText = style.innerHTML || ''; env.filename = document.location.href.replace(/#.*$/, ''); - if (newVars) { + + if (newVars || varsPre) { env.useFileCache = true; - lessText += "\n" + newVars; + + lessText = varsPre + lessText; + + if (newVars) { + lessText += "\n" + newVars; + } } /*jshint loopfunc:true */ @@ -499,6 +506,8 @@ function loadFile(originalHref, currentFileInfo, callback, env, newVars) { } doXHR(href, env.mime, function (data, lastModified) { + data = varsPre + data; + // per file cache fileCache[href] = data; @@ -518,7 +527,7 @@ function loadStyleSheet(sheet, callback, reload, remaining, newVars) { var env = new less.tree.parseEnv(less); env.mime = sheet.type; - if (newVars) { + if (newVars || varsPre) { env.useFileCache = true; } @@ -585,6 +594,18 @@ function initRunningMode(){ } } +function serializeVars(vars) { + var s = ""; + + for (var name in vars) { + s += ((name.slice(0,1) === '@')? '' : '@') + name +': '+ + ((vars[name].slice(-1) === ';')? vars[name] : vars[name] +';'); + } + + return s; +} + + // // Watch mode // @@ -627,12 +648,7 @@ for (var i = 0; i < links.length; i++) { // CSS without reloading less-files // less.modifyVars = function(record) { - var newVars = ""; - for (var name in record) { - newVars += ((name.slice(0,1) === '@')? '' : '@') + name +': '+ - ((record[name].slice(-1) === ';')? record[name] : record[name] +';'); - } - less.refresh(false, newVars); + less.refresh(false, serializeVars(record)); }; less.refresh = function (reload, newVars) { @@ -659,6 +675,10 @@ less.refresh = function (reload, newVars) { loadStyles(newVars); }; +if (less.globalVars) { + varsPre = serializeVars(less.globalVars) + "\n"; +} + less.refreshStyles = loadStyles; less.Parser.fileLoader = loadFile; diff --git a/lib/less/lessc_helper.js b/lib/less/lessc_helper.js index 1581f3e82..be1e5a5c9 100644 --- a/lib/less/lessc_helper.js +++ b/lib/less/lessc_helper.js @@ -52,6 +52,8 @@ var lessc_helper = { console.log(" be removed in the future."); console.log(" -su=on|off Allow 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."); + console.log(" --modify-var='VAR=VALUE' Modifies a variable already declared in the file."); console.log(""); console.log("-------------------------- Deprecated ----------------"); console.log(" -O0, -O1, -O2 Set the parser's optimization level. The lower"); diff --git a/test/browser/css/global-vars/simple.css b/test/browser/css/global-vars/simple.css new file mode 100644 index 000000000..05b9fb02f --- /dev/null +++ b/test/browser/css/global-vars/simple.css @@ -0,0 +1,3 @@ +.test { + color: #ff0000; +} diff --git a/test/browser/less/global-vars/simple.less b/test/browser/less/global-vars/simple.less new file mode 100644 index 000000000..00545b164 --- /dev/null +++ b/test/browser/less/global-vars/simple.less @@ -0,0 +1,3 @@ +.test { + color: @global-var; +} diff --git a/test/browser/runner-global-vars-options.js b/test/browser/runner-global-vars-options.js new file mode 100644 index 000000000..f313b2f77 --- /dev/null +++ b/test/browser/runner-global-vars-options.js @@ -0,0 +1,4 @@ +var less = {}; +less.globalVars = { + "@global-var": "red" +}; diff --git a/test/browser/runner-global-vars-spec.js b/test/browser/runner-global-vars-spec.js new file mode 100644 index 000000000..6dba89170 --- /dev/null +++ b/test/browser/runner-global-vars-spec.js @@ -0,0 +1,3 @@ +describe("less.js global vars", function() { + testLessEqualsInDocument(); +});