Merge https://github.com/less/less.js into nested-parent-selector-2026-v1

Conflicts:
	lib/less/parser/parser.js
	lib/less/tree/ruleset.js
This commit is contained in:
jurcovicovam
2015-01-28 10:46:49 +01:00
85 changed files with 1386 additions and 846 deletions

1
.gitignore vendored
View File

@@ -16,6 +16,7 @@ test/browser/less.js
test/sourcemaps/**/*.map
test/sourcemaps/*.map
test/sourcemaps/*.css
test/less-bom
# grunt
.grunt

38
.jscsrc Normal file
View File

@@ -0,0 +1,38 @@
{
"disallowImplicitTypeConversion": ["numeric", "boolean", "binary", "string"],
"disallowKeywords": ["with"],
"disallowMixedSpacesAndTabs": true,
"disallowMultipleLineBreaks": true,
"disallowOperatorBeforeLineBreak": ["."],
"disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~"],
"disallowSpaceBeforePostfixUnaryOperators": ["++", "--"],
"disallowSpacesInCallExpression": true,
"disallowSpacesInNamedFunctionExpression": {
"beforeOpeningRoundBrace": true},
"disallowTrailingComma": true,
"disallowTrailingWhitespace": true,
"maximumLineLength": 160,
"requireCommaBeforeLineBreak": true,
"requireCurlyBraces": [ "if",
"else",
"for",
"while",
"do",
"try",
"catch"],
"requireOperatorBeforeLineBreak": [ "?",
"=",
"+",
"-",
"/",
"*",
"==",
"===",
"!=",
"!==",
">",
">=",
"<",
"<="],
"requireSpaceAfterBinaryOperators": true
}

View File

@@ -1,11 +1,9 @@
{
"evil": true,
"laxbreak": true,
"latedef": true,
"node": true,
"undef": true,
"unused": "vars",
"trailing": true,
"noarg": true,
"eqnull": true,
"forin": true,

View File

@@ -1,3 +1,21 @@
# 2.3.0
2015-01-27
- add isruleset function
- add optional import option, causing less to not fail if file not found
- Fix browsers-side cache.
- Many fixes to import reference - support `@support` and keyframe
- Selectors now interpolate pseudo selectors (e.g. `:@{hover}`)
- Fix comments missed off if they were at the end of the file
- Fix !important used with parametric mixins
- Emits warnings for extends when the target is not found
- include-path now works on data-uri
- variables and function calls work for path in data-uri
- Fix absolute paths not working on imports sometimes.
- Unicode BOM removed again
- Misc. bug fixes
# 2.2.0
2015-01-04

View File

@@ -5,8 +5,8 @@ module.exports = function (grunt) {
// Report the elapsed execution time of tasks.
require('time-grunt')(grunt);
var COMPRESS_FOR_TESTS = true;
var COMPRESS_FOR_TESTS = true;
// Project configuration.
grunt.initConfig({
@@ -96,18 +96,18 @@ module.exports = function (grunt) {
options: {
banner: '<%= meta.banner %>',
mangle: true,
compress: {
pure_getters: true
}
compress: {
pure_getters: true
}
},
dist: {
src: ['<%= concat.dist.dest %>'],
dest: 'dist/less.min.js'
},
test: {
src: '<%= browserify.browser.dest %>',
dest: 'tmp/less.min.js'
}
test: {
src: '<%= browserify.browser.dest %>',
dest: 'tmp/less.min.js'
}
},
jshint: {
@@ -124,6 +124,13 @@ module.exports = function (grunt) {
}
},
jscs: {
src: ["test/**/*.js", "lib/less*/**/*.js", "bin/lessc"],
options: {
config: ".jscsrc"
}
},
connect: {
server: {
options: {
@@ -306,7 +313,7 @@ module.exports = function (grunt) {
// Clean the version of less built for the tests
clean: {
test: ['test/browser/less.js', 'tmp'],
test: ['test/browser/less.js', 'tmp', 'test/less-bom'],
"sourcemap-test": ['test/sourcemaps/*.css', 'test/sourcemaps/*.map'],
sauce_log: ["sc_*.log"]
}
@@ -347,7 +354,7 @@ module.exports = function (grunt) {
// Create the browser version of less.js
grunt.registerTask('browsertest-lessjs', [
'browserify:browser',
'uglify:test',
'uglify:test',
'concat:browsertest'
]);
@@ -365,19 +372,19 @@ module.exports = function (grunt) {
'connect::keepalive'
]);
var previous_force_state = grunt.option("force");
var previous_force_state = grunt.option("force");
grunt.registerTask("force",function(set){
if (set === "on") {
grunt.option("force",true);
}
else if (set === "off") {
grunt.option("force",false);
}
else if (set === "restore") {
grunt.option("force",previous_force_state);
}
});
grunt.registerTask("force",function(set){
if (set === "on") {
grunt.option("force",true);
}
else if (set === "off") {
grunt.option("force",false);
}
else if (set === "restore") {
grunt.option("force",previous_force_state);
}
});
grunt.registerTask('sauce', [
'browsertest-lessjs',
@@ -395,6 +402,7 @@ module.exports = function (grunt) {
var testTasks = [
'clean',
'jshint',
'jscs',
'shell:test',
'browsertest'
];

716
bin/lessc
View File

@@ -3,8 +3,15 @@
var path = require('path'),
fs = require('../lib/less-node/fs'),
os = require('os'),
errno,
mkdirp;
try {
errno = require('errno');
} catch (err) {
errno = null;
}
var less = require('../lib/less-node'),
pluginLoader = new less.PluginLoader(less),
plugin,
@@ -52,7 +59,7 @@ var checkArgFunc = function(arg, option) {
var checkBooleanArg = function(arg) {
var onOff = /^((on|t|true|y|yes)|(off|f|false|n|no))$/i.exec(arg);
if (!onOff) {
console.log(" unable to parse "+arg+" as a boolean. use one of on/t/true/y/yes/off/f/false/n/no");
console.log(" unable to parse " + arg + " as a boolean. use one of on/t/true/y/yes/off/f/false/n/no");
continueProcessing = false;
return false;
}
@@ -73,357 +80,406 @@ function printUsage() {
continueProcessing = false;
}
args = args.filter(function (arg) {
var match;
// self executing function so we can return
(function() {
args = args.filter(function (arg) {
var match;
match = arg.match(/^-I(.+)$/);
if (match) {
options.paths.push(match[1]);
return false;
}
match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=(.*))?$/i);
if (match) { arg = match[1]; }
else { return arg; }
switch (arg) {
case 'v':
case 'version':
console.log("lessc " + less.version.join('.') + " (Less Compiler) [JavaScript]");
continueProcessing = false;
break;
case 'verbose':
verbose = true;
break;
case 's':
case 'silent':
silent = true;
break;
case 'l':
case 'lint':
options.lint = true;
break;
case 'strict-imports':
options.strictImports = true;
break;
case 'h':
case 'help':
printUsage();
break;
case 'x':
case 'compress':
options.compress = true;
break;
case 'insecure':
options.insecure = true;
break;
case 'M':
case 'depends':
options.depends = true;
break;
case 'max-line-len':
if (checkArgFunc(arg, match[2])) {
options.maxLineLen = parseInt(match[2], 10);
if (options.maxLineLen <= 0) {
options.maxLineLen = -1;
}
}
break;
case 'no-color':
options.color = false;
break;
case 'no-ie-compat':
options.ieCompat = false;
break;
case 'no-js':
options.javascriptEnabled = false;
break;
case 'include-path':
if (checkArgFunc(arg, match[2])) {
options.paths = match[2].split(os.type().match(/Windows/) ? ';' : ':')
.map(function(p) {
if (p) {
return path.resolve(process.cwd(), p);
}
});
}
break;
case 'line-numbers':
if (checkArgFunc(arg, match[2])) {
options.dumpLineNumbers = match[2];
}
break;
case 'source-map':
options.sourceMap = true;
if (match[2]) {
sourceMapOptions.sourceMapFullFilename = match[2];
}
break;
case 'source-map-rootpath':
if (checkArgFunc(arg, match[2])) {
sourceMapOptions.sourceMapRootpath = match[2];
}
break;
case 'source-map-basepath':
if (checkArgFunc(arg, match[2])) {
sourceMapOptions.sourceMapBasepath = match[2];
}
break;
case 'source-map-map-inline':
sourceMapFileInline = true;
options.sourceMap = true;
break;
case 'source-map-less-inline':
sourceMapOptions.outputSourceFiles = true;
break;
case 'source-map-url':
if (checkArgFunc(arg, match[2])) {
sourceMapOptions.sourceMapURL = match[2];
}
break;
case 'rp':
case 'rootpath':
if (checkArgFunc(arg, match[2])) {
options.rootpath = match[2].replace(/\\/g, '/');
}
break;
case "ru":
case "relative-urls":
options.relativeUrls = true;
break;
case "sm":
case "strict-math":
if (checkArgFunc(arg, match[2])) {
options.strictMath = checkBooleanArg(match[2]);
}
break;
case "su":
case "strict-units":
if (checkArgFunc(arg, match[2])) {
options.strictUnits = checkBooleanArg(match[2]);
}
break;
case "global-var":
if (checkArgFunc(arg, match[2])) {
if (!options.globalVars) {
options.globalVars = {};
}
parseVariableOption(match[2], options.globalVars);
}
break;
case "modify-var":
if (checkArgFunc(arg, match[2])) {
if (!options.modifyVars) {
options.modifyVars = {};
}
parseVariableOption(match[2], options.modifyVars);
}
break;
case 'url-args':
if (checkArgFunc(arg, match[2])) {
options.urlArgs = match[2];
}
break;
case 'plugin':
var splitupArg = match[2].match(/^([^=]+)(=(.*))?/),
name = splitupArg[1],
pluginOptions = splitupArg[3];
plugin = pluginLoader.tryLoadPlugin(name, pluginOptions);
if (plugin) {
plugins.push(plugin);
} else {
console.log("Unable to load plugin " + name + " please make sure that it is installed under or at the same level as less");
console.log();
printUsage();
currentErrorcode = 1;
}
break;
default:
plugin = pluginLoader.tryLoadPlugin("less-plugin-" + arg, match[2]);
if (plugin) {
plugins.push(plugin);
} else {
console.log("Unable to interpret argument " + arg + " - if it is a plugin (less-plugin-" + arg + "), make sure that it is installed under or at the same level as less");
console.log();
printUsage();
currentErrorcode = 1;
}
break;
}
});
if (!continueProcessing) {
return;
}
var input = args[1];
if (input && input != '-') {
input = path.resolve(process.cwd(), input);
}
var output = args[2];
var outputbase = args[2];
if (output) {
output = path.resolve(process.cwd(), output);
if (warningMessages) {
console.log(warningMessages);
}
}
if (options.sourceMap) {
sourceMapOptions.sourceMapInputFilename = input;
if (!sourceMapOptions.sourceMapFullFilename) {
if (!output && !sourceMapFileInline) {
console.log("the sourcemap option only has an optional filename if the css filename is given");
console.log("consider adding --source-map-map-inline which embeds the sourcemap into the css");
return;
match = arg.match(/^-I(.+)$/);
if (match) {
options.paths.push(match[1]);
return false;
}
// its in the same directory, so always just the basename
sourceMapOptions.sourceMapOutputFilename = path.basename(output);
sourceMapOptions.sourceMapFullFilename = output + ".map";
// its in the same directory, so always just the basename
sourceMapOptions.sourceMapFilename = path.basename(sourceMapOptions.sourceMapFullFilename);
} else if (options.sourceMap && !sourceMapFileInline) {
var mapFilename = path.resolve(process.cwd(), sourceMapOptions.sourceMapFullFilename),
mapDir = path.dirname(mapFilename),
outputDir = path.dirname(output);
// find the path from the map to the output file
sourceMapOptions.sourceMapOutputFilename = path.join(
path.relative(mapDir, outputDir), path.basename(output));
// make the sourcemap filename point to the sourcemap relative to the css file output directory
sourceMapOptions.sourceMapFilename = path.join(
path.relative(outputDir, mapDir), path.basename(sourceMapOptions.sourceMapFullFilename));
}
}
match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=(.*))?$/i);
if (match) { arg = match[1]; }
else { return arg; }
if (sourceMapOptions.sourceMapBasepath === undefined) {
sourceMapOptions.sourceMapBasepath = input ? path.dirname(input) : process.cwd();
}
switch (arg) {
case 'v':
case 'version':
console.log("lessc " + less.version.join('.') + " (Less Compiler) [JavaScript]");
continueProcessing = false;
break;
case 'verbose':
verbose = true;
break;
case 's':
case 'silent':
silent = true;
break;
case 'l':
case 'lint':
options.lint = true;
break;
case 'strict-imports':
options.strictImports = true;
break;
case 'h':
case 'help':
printUsage();
break;
case 'x':
case 'compress':
options.compress = true;
break;
case 'insecure':
options.insecure = true;
break;
case 'M':
case 'depends':
options.depends = true;
break;
case 'max-line-len':
if (checkArgFunc(arg, match[2])) {
options.maxLineLen = parseInt(match[2], 10);
if (options.maxLineLen <= 0) {
options.maxLineLen = -1;
}
}
break;
case 'no-color':
options.color = false;
break;
case 'no-ie-compat':
options.ieCompat = false;
break;
case 'no-js':
options.javascriptEnabled = false;
break;
case 'include-path':
if (checkArgFunc(arg, match[2])) {
options.paths = match[2].split(os.type().match(/Windows/) ? ';' : ':')
.map(function(p) {
if (p) {
return path.resolve(process.cwd(), p);
}
});
}
break;
case 'line-numbers':
if (checkArgFunc(arg, match[2])) {
options.dumpLineNumbers = match[2];
}
break;
case 'source-map':
options.sourceMap = true;
if (match[2]) {
sourceMapOptions.sourceMapFullFilename = match[2];
}
break;
case 'source-map-rootpath':
if (checkArgFunc(arg, match[2])) {
sourceMapOptions.sourceMapRootpath = match[2];
}
break;
case 'source-map-basepath':
if (checkArgFunc(arg, match[2])) {
sourceMapOptions.sourceMapBasepath = match[2];
}
break;
case 'source-map-map-inline':
sourceMapFileInline = true;
options.sourceMap = true;
break;
case 'source-map-less-inline':
sourceMapOptions.outputSourceFiles = true;
break;
case 'source-map-url':
if (checkArgFunc(arg, match[2])) {
sourceMapOptions.sourceMapURL = match[2];
}
break;
case 'rp':
case 'rootpath':
if (checkArgFunc(arg, match[2])) {
options.rootpath = match[2].replace(/\\/g, '/');
}
break;
case "ru":
case "relative-urls":
options.relativeUrls = true;
break;
case "sm":
case "strict-math":
if (checkArgFunc(arg, match[2])) {
options.strictMath = checkBooleanArg(match[2]);
}
break;
case "su":
case "strict-units":
if (checkArgFunc(arg, match[2])) {
options.strictUnits = checkBooleanArg(match[2]);
}
break;
case "global-var":
if (checkArgFunc(arg, match[2])) {
if (!options.globalVars) {
options.globalVars = {};
}
parseVariableOption(match[2], options.globalVars);
}
break;
case "modify-var":
if (checkArgFunc(arg, match[2])) {
if (!options.modifyVars) {
options.modifyVars = {};
}
if (sourceMapOptions.sourceMapRootpath === undefined) {
var pathToMap = path.dirname(sourceMapFileInline ? output : sourceMapOptions.sourceMapFullFilename),
pathToInput = path.dirname(sourceMapOptions.sourceMapInputFilename);
sourceMapOptions.sourceMapRootpath = path.relative(pathToMap, pathToInput);
}
parseVariableOption(match[2], options.modifyVars);
}
break;
case 'url-args':
if (checkArgFunc(arg, match[2])) {
options.urlArgs = match[2];
}
break;
case 'plugin':
var splitupArg = match[2].match(/^([^=]+)(=(.*))?/),
name = splitupArg[1],
pluginOptions = splitupArg[3];
if (! input) {
console.log("lessc: no input files");
console.log("");
printUsage();
currentErrorcode = 1;
return;
}
var ensureDirectory = function (filepath) {
var dir = path.dirname(filepath),
cmd,
existsSync = fs.existsSync || path.existsSync;
if (!existsSync(dir)) {
if (mkdirp === undefined) {
try {mkdirp = require('mkdirp');}
catch(e) { mkdirp = null; }
plugin = pluginLoader.tryLoadPlugin(name, pluginOptions);
if (plugin) {
plugins.push(plugin);
} else {
console.log("Unable to load plugin " + name +
" please make sure that it is installed under or at the same level as less");
console.log();
printUsage();
currentErrorcode = 1;
}
break;
default:
plugin = pluginLoader.tryLoadPlugin("less-plugin-" + arg, match[2]);
if (plugin) {
plugins.push(plugin);
} else {
console.log("Unable to interpret argument " + arg +
" - if it is a plugin (less-plugin-" + arg + "), make sure that it is installed under or at" +
" the same level as less");
console.log();
printUsage();
currentErrorcode = 1;
}
break;
}
cmd = mkdirp && mkdirp.sync || fs.mkdirSync;
cmd(dir);
}
};
});
if (options.depends) {
if (!outputbase) {
console.log("option --depends requires an output path to be specified");
if (!continueProcessing) {
return;
}
process.stdout.write(outputbase + ": ");
}
if (!sourceMapFileInline) {
var writeSourceMap = function(output) {
var filename = sourceMapOptions.sourceMapFullFilename;
ensureDirectory(filename);
fs.writeFileSync(filename, output, 'utf8');
};
}
var input = args[1];
if (input && input != '-') {
input = path.resolve(process.cwd(), input);
}
var output = args[2];
var outputbase = args[2];
if (output) {
output = path.resolve(process.cwd(), output);
if (warningMessages) {
console.log(warningMessages);
}
}
var parseLessFile = function (e, data) {
if (e) {
console.log("lessc: " + e.message);
if (options.sourceMap) {
sourceMapOptions.sourceMapInputFilename = input;
if (!sourceMapOptions.sourceMapFullFilename) {
if (!output && !sourceMapFileInline) {
console.log("the sourcemap option only has an optional filename if the css filename is given");
console.log("consider adding --source-map-map-inline which embeds the sourcemap into the css");
return;
}
// its in the same directory, so always just the basename
sourceMapOptions.sourceMapOutputFilename = path.basename(output);
sourceMapOptions.sourceMapFullFilename = output + ".map";
// its in the same directory, so always just the basename
sourceMapOptions.sourceMapFilename = path.basename(sourceMapOptions.sourceMapFullFilename);
} else if (options.sourceMap && !sourceMapFileInline) {
var mapFilename = path.resolve(process.cwd(), sourceMapOptions.sourceMapFullFilename),
mapDir = path.dirname(mapFilename),
outputDir = path.dirname(output);
// find the path from the map to the output file
sourceMapOptions.sourceMapOutputFilename = path.join(
path.relative(mapDir, outputDir), path.basename(output));
// make the sourcemap filename point to the sourcemap relative to the css file output directory
sourceMapOptions.sourceMapFilename = path.join(
path.relative(outputDir, mapDir), path.basename(sourceMapOptions.sourceMapFullFilename));
}
}
if (sourceMapOptions.sourceMapBasepath === undefined) {
sourceMapOptions.sourceMapBasepath = input ? path.dirname(input) : process.cwd();
}
if (sourceMapOptions.sourceMapRootpath === undefined) {
var pathToMap = path.dirname(sourceMapFileInline ? output : sourceMapOptions.sourceMapFullFilename),
pathToInput = path.dirname(sourceMapOptions.sourceMapInputFilename);
sourceMapOptions.sourceMapRootpath = path.relative(pathToMap, pathToInput);
}
if (! input) {
console.log("lessc: no input files");
console.log("");
printUsage();
currentErrorcode = 1;
return;
}
options.paths = [path.dirname(input)].concat(options.paths);
options.filename = input;
var ensureDirectory = function (filepath) {
var dir = path.dirname(filepath),
cmd,
existsSync = fs.existsSync || path.existsSync;
if (!existsSync(dir)) {
if (mkdirp === undefined) {
try {mkdirp = require('mkdirp');}
catch(e) { mkdirp = null; }
}
cmd = mkdirp && mkdirp.sync || fs.mkdirSync;
cmd(dir);
}
};
if (options.lint) {
options.sourceMap = false;
}
sourceMapOptions.sourceMapFileInline = sourceMapFileInline;
if (options.sourceMap) {
options.sourceMap = sourceMapOptions;
if (options.depends) {
if (!outputbase) {
console.log("option --depends requires an output path to be specified");
return;
}
process.stdout.write(outputbase + ": ");
}
less.logger.addListener({
info: function(msg) {
if (verbose) {
if (!sourceMapFileInline) {
var writeSourceMap = function(output, onDone) {
var filename = sourceMapOptions.sourceMapFullFilename;
ensureDirectory(filename);
fs.writeFile(filename, output, 'utf8', function (err) {
if (err) {
var description = "Error: ";
if (errno && errno.errno[err.errno]) {
description += errno.errno[err.errno].description;
} else {
description += err.code + " " + err.message;
}
less.logger.error('lessc: failed to create file ' + filename);
less.logger.error(description);
} else {
less.logger.info('lessc: wrote ' + filename);
}
onDone();
});
};
}
var writeSourceMapIfNeeded = function(output, onDone) {
if (options.sourceMap && !sourceMapFileInline) {
writeSourceMap(output, onDone);
}
};
var writeOutput = function(output, result, onSuccess) {
if (output) {
ensureDirectory(output);
fs.writeFile(output, result.css, {encoding: 'utf8'}, function (err) {
if (err) {
var description = "Error: ";
if (errno && errno.errno[err.errno]) {
description += errno.errno[err.errno].description;
} else {
description += err.code + " " + err.message;
}
less.logger.error('lessc: failed to create file ' + output);
less.logger.error(description);
} else {
less.logger.info('lessc: wrote ' + output);
onSuccess();
}
});
} else if (!options.depends) {
process.stdout.write(result.css);
onSuccess();
}
};
var logDependencies = function(options, result) {
if (options.depends) {
var depends = "";
for(var i = 0; i < result.imports.length; i++) {
depends += result.imports[i] + " ";
}
console.log(depends);
}
};
var parseLessFile = function (e, data) {
if (e) {
console.log("lessc: " + e.message);
currentErrorcode = 1;
return;
}
data = data.replace(/^\uFEFF/, '');
options.paths = [path.dirname(input)].concat(options.paths);
options.filename = input;
if (options.lint) {
options.sourceMap = false;
}
sourceMapOptions.sourceMapFileInline = sourceMapFileInline;
if (options.sourceMap) {
options.sourceMap = sourceMapOptions;
}
less.logger.addListener({
info: function(msg) {
if (verbose) {
console.log(msg);
}
},
warn: function(msg) {
// do not show warning if outputting css to the console or the silent option is used
if (!silent && output) {
console.warn(msg);
}
},
error: function(msg) {
console.log(msg);
}
},
warn: function(msg) {
// do not show warning if outputting css to the console or the silent option is used
if (!silent && output) {
console.warn(msg);
}
},
error: function(msg) {
console.log(msg);
}
});
less.render(data, options)
.then(function(result) {
if(!options.lint) {
if (output) {
ensureDirectory(output);
fs.writeFileSync(output, result.css, 'utf8');
less.logger.info('lessc: wrote ' + output);
} else if (!options.depends) {
process.stdout.write(result.css);
}
if (options.sourceMap && !sourceMapFileInline) {
writeSourceMap(result.map);
}
if (options.depends) {
var depends = "";
for(var i = 0; i < result.imports.length; i++) {
depends += result.imports[i] + " ";
}
console.log(depends);
}
}
},
function(err) {
less.writeError(err, options);
currentErrorcode = 1;
});
};
if (input != '-') {
fs.readFile(input, 'utf8', parseLessFile);
} else {
process.stdin.resume();
process.stdin.setEncoding('utf8');
less.render(data, options)
.then(function(result) {
if(!options.lint) {
writeOutput(output, result, function() {
writeSourceMapIfNeeded(result.map, function() {
logDependencies(options, result);
});
});
}
},
function(err) {
less.writeError(err, options);
currentErrorcode = 1;
});
};
var buffer = '';
process.stdin.on('data', function(data) {
buffer += data;
});
if (input != '-') {
fs.readFile(input, 'utf8', parseLessFile);
} else {
process.stdin.resume();
process.stdin.setEncoding('utf8');
process.stdin.on('end', function() {
parseLessFile(false, buffer);
});
}
var buffer = '';
process.stdin.on('data', function(data) {
buffer += data;
});
process.stdin.on('end', function() {
parseLessFile(false, buffer);
});
}
})();

View File

@@ -1,6 +1,6 @@
{
"name": "less",
"version": "2.2.0",
"version": "2.3.0",
"main": "dist/less.js",
"ignore": [
"**/.*",

378
dist/less.js vendored
View File

@@ -1,5 +1,5 @@
/*!
* Less - Leaner CSS v2.2.0
* Less - Leaner CSS v2.3.0
* http://lesscss.org
*
* Copyright (c) 2009-2015, Alexis Sellier <self@cloudhead.net>
@@ -52,9 +52,9 @@ module.exports = function(window, options) {
options.useFileCache = true;
}
if (options.onReady === undefined) {
options.onReady = true;
}
if (options.onReady === undefined) {
options.onReady = true;
}
};
@@ -75,15 +75,15 @@ require("./add-default-options")(window, options);
var less = module.exports = require("./index")(window, options);
if (options.onReady) {
if (/!watch/.test(window.location.hash)) {
less.watch();
}
less.pageLoadFinished = less.registerStylesheets().then(
function () {
return less.refresh(less.env === 'development');
}
);
if (/!watch/.test(window.location.hash)) {
less.watch();
}
less.pageLoadFinished = less.registerStylesheets().then(
function () {
return less.refresh(less.env === 'development');
}
);
}
},{"./add-default-options":1,"./index":7,"promise/polyfill.js":undefined}],3:[function(require,module,exports){
var utils = require("./utils");
@@ -596,14 +596,14 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) {
if (webInfo) {
webInfo.remaining = remaining;
if (!instanceOptions.modifyVars) {
var css = cache.getCSS(path, webInfo);
if (!reload && css) {
webInfo.local = true;
callback(null, null, data, sheet, webInfo, path);
return;
}
}
if (!instanceOptions.modifyVars) {
var css = cache.getCSS(path, webInfo);
if (!reload && css) {
webInfo.local = true;
callback(null, css, data, sheet, webInfo, path);
return;
}
}
}
//TODO add tests around how this behaves when reloading
@@ -615,11 +615,11 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) {
e.href = path;
callback(e);
} else {
result.css = postProcessCSS(result.css);
if (!instanceOptions.modifyVars) {
cache.setCSS(sheet.href, webInfo.lastModified, result.css);
}
callback(null, result.css, data, sheet, webInfo, path);
result.css = postProcessCSS(result.css);
if (!instanceOptions.modifyVars) {
cache.setCSS(sheet.href, webInfo.lastModified, result.css);
}
callback(null, result.css, data, sheet, webInfo, path);
}
});
}
@@ -716,7 +716,7 @@ less.refresh = function (reload, modifyVars, clearFileCache) {
} else {
less.logger.info("rendered " + sheet.href + " successfully.");
}
browser.createCSS(window.document, css, sheet);
browser.createCSS(window.document, css, sheet);
less.logger.info("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms');
if (webInfo.remaining === 0) {
totalMilliseconds = new Date() - startTime;
@@ -854,6 +854,7 @@ contexts.Parse = function(options) {
};
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
@@ -921,7 +922,6 @@ contexts.Eval.prototype.normalizePath = function( path ) {
//todo - do the same for the toCSS ?
},{}],11:[function(require,module,exports){
module.exports = {
'aliceblue':'#f0f8ff',
@@ -1613,7 +1613,7 @@ colorFunctions = {
return new Color(c.value.slice(1));
}
if ((c instanceof Color) || (c = Color.fromKeyword(c.value))) {
c.keyword = undefined;
c.value = undefined;
return c;
}
throw {
@@ -1649,8 +1649,9 @@ module.exports = function(environment) {
var mimetype = mimetypeNode && mimetypeNode.value;
var filePath = filePathNode.value;
var currentDirectory = filePathNode.currentFileInfo.relativeUrls ?
filePathNode.currentFileInfo.currentDirectory : filePathNode.currentFileInfo.entryPath;
var currentFileInfo = this.currentFileInfo;
var currentDirectory = currentFileInfo.relativeUrls ?
currentFileInfo.currentDirectory : currentFileInfo.entryPath;
var fragmentStart = filePath.indexOf('#');
var fragment = '';
@@ -1672,7 +1673,7 @@ module.exports = function(environment) {
mimetype = environment.mimeLookup(filePath);
if (mimetype === "image/svg+xml") {
if (mimetype === "image/svg+xml") {
useBase64 = false;
} else {
// use base 64 unless it's an ASCII or UTF-8 format
@@ -1687,29 +1688,29 @@ module.exports = function(environment) {
var fileSync = fileManager.loadFileSync(filePath, currentDirectory, this.context, environment);
if (!fileSync.contents) {
logger.warn("Skipped data-uri embedding because file not found");
logger.warn("Skipped data-uri embedding of " + filePath + " because file not found");
return fallback(this, filePathNode || mimetypeNode);
}
var buf = fileSync.contents;
if (useBase64 && !environment.encodeBase64) {
return fallback(this, filePathNode);
}
if (useBase64 && !environment.encodeBase64) {
return fallback(this, filePathNode);
}
buf = useBase64 ? environment.encodeBase64(buf) : encodeURIComponent(buf);
var uri = "data:" + mimetype + ',' + buf + fragment;
// IE8 cannot handle a data-uri larger than 32,768 characters. If this is exceeded
// and the --ieCompat flag is enabled, return a normal url() instead.
var DATA_URI_MAX = 32768;
if (uri.length >= DATA_URI_MAX) {
// IE8 cannot handle a data-uri larger than 32,768 characters. If this is exceeded
// and the --ieCompat flag is enabled, return a normal url() instead.
var DATA_URI_MAX = 32768;
if (uri.length >= DATA_URI_MAX) {
if (this.context.ieCompat !== false) {
logger.warn("Skipped data-uri embedding of " + filePath + " because its size (" + uri.length + " characters) exceeds IE8-safe " + DATA_URI_MAX + " characters!");
if (this.context.ieCompat !== false) {
logger.warn("Skipped data-uri embedding of " + filePath + " because its size (" + uri.length + " characters) exceeds IE8-safe " + DATA_URI_MAX + " characters!");
return fallback(this, filePathNode || mimetypeNode);
}
}
return fallback(this, filePathNode || mimetypeNode);
}
}
return new URL(new Quoted('"' + uri + '"', uri, false, this.index, this.currentFileInfo), this.index, this.currentFileInfo);
});
@@ -1964,6 +1965,7 @@ functionRegistry.addMultiple({
module.exports = function(environment) {
var Dimension = require("../tree/dimension"),
Color = require("../tree/color"),
Expression = require("../tree/expression"),
Quoted = require("../tree/quoted"),
URL = require("../tree/url"),
functionRegistry = require("./function-registry");
@@ -2013,7 +2015,7 @@ module.exports = function(environment) {
'<' + gradientType + 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' + gradientDirectionSvg + '>';
for (i = 0; i < stops.length; i+= 1) {
if (stops[i].value) {
if (stops[i] instanceof Expression) {
color = stops[i].value[0];
position = stops[i].value[1];
} else {
@@ -2038,8 +2040,9 @@ module.exports = function(environment) {
});
};
},{"../tree/color":47,"../tree/dimension":53,"../tree/quoted":70,"../tree/url":77,"./function-registry":21}],27:[function(require,module,exports){
},{"../tree/color":47,"../tree/dimension":53,"../tree/expression":56,"../tree/quoted":70,"../tree/url":77,"./function-registry":21}],27:[function(require,module,exports){
var Keyword = require("../tree/keyword"),
DetachedRuleset = require("../tree/detached-ruleset"),
Dimension = require("../tree/dimension"),
Color = require("../tree/color"),
Quoted = require("../tree/quoted"),
@@ -2062,6 +2065,9 @@ var isa = function (n, Type) {
return (n instanceof Dimension) && n.unit.is(unit) ? Keyword.True : Keyword.False;
};
functionRegistry.addMultiple({
isruleset: function (n) {
return isa(n, DetachedRuleset);
},
iscolor: function (n) {
return isa(n, Color);
},
@@ -2118,7 +2124,7 @@ functionRegistry.addMultiple({
}
});
},{"../tree/anonymous":43,"../tree/color":47,"../tree/dimension":53,"../tree/keyword":62,"../tree/operation":68,"../tree/quoted":70,"../tree/url":77,"./function-registry":21}],28:[function(require,module,exports){
},{"../tree/anonymous":43,"../tree/color":47,"../tree/detached-ruleset":52,"../tree/dimension":53,"../tree/keyword":62,"../tree/operation":68,"../tree/quoted":70,"../tree/url":77,"./function-registry":21}],28:[function(require,module,exports){
var contexts = require("./contexts"),
Parser = require('./parser/parser');
@@ -2161,12 +2167,14 @@ module.exports = function(environment) {
importManager.queue.splice(importManager.queue.indexOf(path), 1); // Remove the path from the queue
var importedEqualsRoot = fullPath === importManager.rootFilename;
if (importOptions.optional && e) {
callback(null, {rules:[]}, false, null);
}
else {
importManager.files[fullPath] = root;
if (e && !importManager.error) { importManager.error = e; }
callback(e, root, importedEqualsRoot, fullPath);
}
};
var newFileInfo = {
@@ -2189,7 +2197,7 @@ module.exports = function(environment) {
var loadFileCallback = function(loadedFile) {
var resolvedFilename = loadedFile.filename,
contents = loadedFile.contents;
contents = loadedFile.contents.replace(/^\uFEFF/, '');
// Pass on an updated rootpath if path of imported file is relative and file
// is in a (sub|sup) directory
@@ -2246,7 +2254,7 @@ module.exports = function(environment, fileManagers) {
var SourceMapOutput, SourceMapBuilder, ParseTree, ImportManager, Environment;
var less = {
version: [2, 2, 0],
version: [2, 3, 0],
data: require('./data'),
tree: require('./tree'),
Environment: (Environment = require("./environment/environment")),
@@ -2355,7 +2363,7 @@ module.exports = {
},{}],32:[function(require,module,exports){
var LessError = require('./less-error'),
transformTree = require("./transform-tree"),
logger = require("./logger");
logger = require("./logger");
module.exports = function(SourceMapBuilder) {
var ParseTree = function(root, imports) {
@@ -2372,11 +2380,11 @@ ParseTree.prototype.toCSS = function(options) {
}
try {
var compress = Boolean(options.compress);
if (compress) {
logger.warn("The compress option has been deprecated. We recommend you use a dedicated css minifier, for instance see less-plugin-clean-css.");
}
var compress = Boolean(options.compress);
if (compress) {
logger.warn("The compress option has been deprecated. We recommend you use a dedicated css minifier, for instance see less-plugin-clean-css.");
}
var toCSSOptions = {
compress: compress,
dumpLineNumbers: options.dumpLineNumbers,
@@ -2463,6 +2471,10 @@ module.exports = function(environment, ParseTree, ImportManager) {
entryPath: entryPath,
rootFilename: filename
};
// add in a missing trailing slash
if (rootFileInfo.rootpath && rootFileInfo.rootpath.slice(-1) !== "/") {
rootFileInfo.rootpath += "/";
}
}
var imports = new ImportManager(context, rootFileInfo);
@@ -3085,13 +3097,17 @@ var Parser = function Parser(context, imports, fileInfo) {
primary: function () {
var mixin = this.mixin, root = [], node;
while (!parserInput.finished)
while (true)
{
while(true) {
node = this.comment();
if (!node) { break; }
root.push(node);
}
// always process comments before deciding if finished
if (parserInput.finished) {
break;
}
if (parserInput.peek('}')) {
break;
}
@@ -3712,7 +3728,7 @@ var Parser = function Parser(context, imports, fileInfo) {
c = this.combinator();
e = parserInput.$re(/^(?:\d+\.\d+|\d+)%/) || parserInput.$re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) ||
parserInput.$char('*') || parserInput.$char('&') || this.attribute() || parserInput.$re(/^\([^()@]+\)/) || parserInput.$re(/^[\.#](?=@)/) ||
parserInput.$char('*') || parserInput.$char('&') || this.attribute() || parserInput.$re(/^\([^()@]+\)/) || parserInput.$re(/^[\.#:](?=@)/) ||
this.entities.variableCurly();
if (! e) {
@@ -4023,7 +4039,7 @@ var Parser = function Parser(context, imports, fileInfo) {
},
importOption: function() {
var opt = parserInput.$re(/^(less|css|multiple|once|inline|reference)/);
var opt = parserInput.$re(/^(less|css|multiple|once|inline|reference|optional)/);
if (opt) {
return opt[1];
}
@@ -4237,19 +4253,19 @@ var Parser = function Parser(context, imports, fileInfo) {
sub: function () {
var a, e;
parserInput.save();
parserInput.save();
if (parserInput.$char('(')) {
a = this.addition();
if (a && parserInput.$char(')')) {
parserInput.forget();
e = new(tree.Expression)([a]);
e.parens = true;
parserInput.forget();
e = new(tree.Expression)([a]);
e.parens = true;
return e;
}
parserInput.restore("Expected ')'");
return;
parserInput.restore("Expected ')'");
return;
}
parserInput.restore();
parserInput.restore();
},
multiplication: function () {
var m, a, op, operation, isSpaced;
@@ -4963,7 +4979,6 @@ Assignment.prototype.genCSS = function (context, output) {
};
module.exports = Assignment;
},{"./node":67}],45:[function(require,module,exports){
var Node = require("./node");
@@ -5116,11 +5131,11 @@ Color.prototype.genCSS = function (context, output) {
Color.prototype.toCSS = function (context, doNotCompress) {
var compress = context && context.compress && !doNotCompress, color, alpha;
// `keyword` is set if this color was originally
// `value` is set if this color was originally
// converted from a named color string so we need
// to respect this and try to output named color too.
if (this.keyword) {
return this.keyword;
if (this.value) {
return this.value;
}
// If we have some transparency, the only way to represent it
@@ -5239,7 +5254,7 @@ Color.fromKeyword = function(keyword) {
}
if (c) {
c.keyword = keyword;
c.value = keyword;
return c;
}
};
@@ -5555,7 +5570,7 @@ module.exports = Dimension;
var Node = require("./node"),
Ruleset = require("./ruleset");
var Directive = function (name, value, rules, index, currentFileInfo, debugInfo) {
var Directive = function (name, value, rules, index, currentFileInfo, debugInfo, isReferenced) {
this.name = name;
this.value = value;
if (rules) {
@@ -5565,6 +5580,7 @@ var Directive = function (name, value, rules, index, currentFileInfo, debugInfo)
this.index = index;
this.currentFileInfo = currentFileInfo;
this.debugInfo = debugInfo;
this.isReferenced = isReferenced;
};
Directive.prototype = new Node();
@@ -5592,7 +5608,10 @@ Directive.prototype.genCSS = function (context, output) {
value.genCSS(context, output);
}
if (rules) {
this.outputRuleset(context, output, [rules]);
if (rules.type === "Ruleset") {
rules = [rules];
}
this.outputRuleset(context, output, rules);
} else {
output.add(';');
}
@@ -5607,7 +5626,7 @@ Directive.prototype.eval = function (context) {
rules.root = true;
}
return new Directive(this.name, value, rules,
this.index, this.currentFileInfo, this.debugInfo);
this.index, this.currentFileInfo, this.debugInfo, this.isReferenced);
};
Directive.prototype.variable = function (name) { if (this.rules) return Ruleset.prototype.variable.call(this.rules, name); };
Directive.prototype.find = function () { if (this.rules) return Ruleset.prototype.find.apply(this.rules, arguments); };
@@ -5624,6 +5643,9 @@ Directive.prototype.markReferenced = function () {
}
}
};
Directive.prototype.getIsReferenced = function () {
return !this.currentFileInfo || !this.currentFileInfo.reference || this.isReferenced;
};
Directive.prototype.outputRuleset = function (context, output, rules) {
var ruleCnt = rules.length, i;
context.tabLevel = (context.tabLevel | 0) + 1;
@@ -5720,7 +5742,7 @@ var Node = require("./node"),
var Expression = function (value) {
this.value = value;
if (!value) {
throw new Error("Expression requires a array parameter");
throw new Error("Expression requires an array parameter");
}
};
Expression.prototype = new Node();
@@ -5968,6 +5990,7 @@ module.exports = Import;
},{"./anonymous":43,"./media":63,"./node":67,"./quoted":70,"./ruleset":73,"./url":77}],59:[function(require,module,exports){
var tree = {};
tree.Node = require('./node');
tree.Alpha = require('./alpha');
tree.Color = require('./color');
tree.Directive = require('./directive');
@@ -6007,7 +6030,7 @@ tree.RulesetCall = require('./ruleset-call');
module.exports = tree;
},{"./alpha":42,"./anonymous":43,"./assignment":44,"./attribute":45,"./call":46,"./color":47,"./combinator":48,"./comment":49,"./condition":50,"./detached-ruleset":52,"./dimension":53,"./directive":54,"./element":55,"./expression":56,"./extend":57,"./import":58,"./javascript":60,"./keyword":62,"./media":63,"./mixin-call":64,"./mixin-definition":65,"./negative":66,"./operation":68,"./paren":69,"./quoted":70,"./rule":71,"./ruleset":73,"./ruleset-call":72,"./selector":74,"./unicode-descriptor":75,"./unit":76,"./url":77,"./value":78,"./variable":79}],60:[function(require,module,exports){
},{"./alpha":42,"./anonymous":43,"./assignment":44,"./attribute":45,"./call":46,"./color":47,"./combinator":48,"./comment":49,"./condition":50,"./detached-ruleset":52,"./dimension":53,"./directive":54,"./element":55,"./expression":56,"./extend":57,"./import":58,"./javascript":60,"./keyword":62,"./media":63,"./mixin-call":64,"./mixin-definition":65,"./negative":66,"./node":67,"./operation":68,"./paren":69,"./quoted":70,"./rule":71,"./ruleset":73,"./ruleset-call":72,"./selector":74,"./unicode-descriptor":75,"./unit":76,"./url":77,"./value":78,"./variable":79}],60:[function(require,module,exports){
var JsEvalNode = require("./js-eval-node"),
Dimension = require("./dimension"),
Quoted = require("./quoted"),
@@ -7137,15 +7160,15 @@ Ruleset.prototype.evalImports = function(context) {
}
};
Ruleset.prototype.makeImportant = function() {
return new Ruleset(this.selectors, this.rules.map(function (r) {
if (r.makeImportant) {
return r.makeImportant();
} else {
return r;
}
}), this.strictImports);
};
Ruleset.prototype.matchArgs = function (args) {
this.rules = this.rules.map(function (r) {
if (r.makeImportant) {
return r.makeImportant();
} else {
return r;
}
});
return this;
};Ruleset.prototype.matchArgs = function (args) {
return !args || args.length === 0;
};
// lets you call a css selector with a guard
@@ -7175,7 +7198,7 @@ Ruleset.prototype.variables = function () {
}
// when evaluating variables in an import statement, imports have not been eval'd
// so we need to go inside import statements.
// guard against root being a string (in the case of inlined less)
// guard against root being a string (in the case of inlined less)
if (r.type === "Import" && r.root && r.root.variables) {
var vars = r.root.variables();
for(var name in vars) {
@@ -7369,18 +7392,49 @@ Ruleset.prototype.genCSS = function (context, output) {
}
};
Ruleset.prototype.markReferenced = function () {
if (!this.selectors) {
return;
var s;
if (this.selectors) {
for (s = 0; s < this.selectors.length; s++) {
this.selectors[s].markReferenced();
}
}
for (var s = 0; s < this.selectors.length; s++) {
this.selectors[s].markReferenced();
if (this.rules) {
for (s = 0; s < this.rules.length; s++) {
if (this.rules[s].markReferenced)
this.rules[s].markReferenced();
}
}
};
Ruleset.prototype.getIsReferenced = function() {
var i, j, path, selector;
if (this.paths) {
for (i=0; i<this.paths.length; i++) {
path = this.paths[i];
for (j=0; j<path.length; j++) {
if (path[j].getIsReferenced && path[j].getIsReferenced())
return true;
}
}
}
if (this.selectors) {
for (i=0;i<this.selectors.length;i++) {
selector = this.selectors[i];
if (selector.getIsReferenced && selector.getIsReferenced())
return true;
}
}
return false;
};
Ruleset.prototype.joinSelectors = function (paths, context, selectors) {
for (var s = 0; s < selectors.length; s++) {
this.joinSelector(paths, context, selectors[s]);
}
};
Ruleset.prototype.joinSelector = function (paths, context, selector) {
var i, j, k,
@@ -7968,7 +8022,8 @@ module.exports = {
},{}],81:[function(require,module,exports){
var tree = require("../tree"),
Visitor = require("./visitor");
Visitor = require("./visitor"),
logger = require("../logger");
/*jshint loopfunc:true */
@@ -8063,11 +8118,30 @@ var ProcessExtendsVisitor = function() {
ProcessExtendsVisitor.prototype = {
run: function(root) {
var extendFinder = new ExtendFinderVisitor();
this.extendIndicies = {};
extendFinder.run(root);
if (!extendFinder.foundExtends) { return root; }
root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends));
this.allExtendsStack = [root.allExtends];
return this._visitor.visit(root);
var newRoot = this._visitor.visit(root);
this.checkExtendsForNonMatched(root.allExtends);
return newRoot;
},
checkExtendsForNonMatched: function(extendList) {
var indicies = this.extendIndicies;
extendList.filter(function(extend) {
return !extend.hasFoundMatches && extend.parent_ids.length == 1;
}).forEach(function(extend) {
var selector = "_unknown_";
try {
selector = extend.selector.toCSS({});
}catch(_){}
if(!indicies[extend.index + ' ' + selector]) {
indicies[extend.index + ' ' + selector] = true;
logger.warn("extend '"+selector+"' has no matches");
}
});
},
doExtendChaining: function (extendsList, extendsListTarget, iterationCount) {
//
@@ -8104,6 +8178,8 @@ ProcessExtendsVisitor.prototype = {
if (matches.length) {
extend.hasFoundMatches = true;
// we found a match, so for each self selector..
extend.selfSelectors.forEach(function(selfSelector) {
@@ -8187,6 +8263,7 @@ ProcessExtendsVisitor.prototype = {
matches = this.findMatch(allExtends[extendIndex], selectorPath);
if (matches.length) {
allExtends[extendIndex].hasFoundMatches = true;
allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) {
selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector));
@@ -8372,7 +8449,9 @@ ProcessExtendsVisitor.prototype = {
this.allExtendsStack.push(newAllExtends);
},
visitMediaOut: function (mediaNode) {
this.allExtendsStack.length = this.allExtendsStack.length - 1;
var lastIndex = this.allExtendsStack.length - 1;
this.checkExtendsForNonMatched(this.allExtendsStack[lastIndex]);
this.allExtendsStack.length = lastIndex;
},
visitDirective: function (directiveNode, visitArgs) {
var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);
@@ -8380,18 +8459,20 @@ ProcessExtendsVisitor.prototype = {
this.allExtendsStack.push(newAllExtends);
},
visitDirectiveOut: function (directiveNode) {
this.allExtendsStack.length = this.allExtendsStack.length - 1;
var lastIndex = this.allExtendsStack.length - 1;
this.checkExtendsForNonMatched(this.allExtendsStack[lastIndex]);
this.allExtendsStack.length = lastIndex;
}
};
module.exports = ProcessExtendsVisitor;
},{"../tree":59,"./visitor":87}],82:[function(require,module,exports){
},{"../logger":31,"../tree":59,"./visitor":87}],82:[function(require,module,exports){
function ImportSequencer(onSequencerEmpty) {
this.imports = [];
this.variableImports = [];
this._onSequencerEmpty = onSequencerEmpty;
this._currentDepth = 0;
this._currentDepth = 0;
}
ImportSequencer.prototype.addImport = function(callback) {
@@ -8414,27 +8495,27 @@ ImportSequencer.prototype.addVariableImport = function(callback) {
};
ImportSequencer.prototype.tryRun = function() {
this._currentDepth++;
try {
while(true) {
while(this.imports.length > 0) {
var importItem = this.imports[0];
if (!importItem.isReady) {
return;
}
this.imports = this.imports.slice(1);
importItem.callback.apply(null, importItem.args);
}
if (this.variableImports.length === 0) {
break;
}
var variableImport = this.variableImports[0];
this.variableImports = this.variableImports.slice(1);
variableImport();
}
} finally {
this._currentDepth--;
}
this._currentDepth++;
try {
while(true) {
while(this.imports.length > 0) {
var importItem = this.imports[0];
if (!importItem.isReady) {
return;
}
this.imports = this.imports.slice(1);
importItem.callback.apply(null, importItem.args);
}
if (this.variableImports.length === 0) {
break;
}
var variableImport = this.variableImports[0];
this.variableImports = this.variableImports.slice(1);
variableImport();
}
} finally {
this._currentDepth--;
}
if (this._currentDepth === 0 && this._onSequencerEmpty) {
this._onSequencerEmpty();
}
@@ -8473,12 +8554,12 @@ ImportVisitor.prototype = {
this.isFinished = true;
this._sequencer.tryRun();
},
_onSequencerEmpty: function() {
if (!this.isFinished) {
return;
}
this._finish(this.error);
},
_onSequencerEmpty: function() {
if (!this.isFinished) {
return;
}
this._finish(this.error);
},
visitImport: function (importNode, visitArgs) {
var inlineCSS = importNode.options.inline;
@@ -8532,9 +8613,9 @@ ImportVisitor.prototype = {
this._importer.push(evaldImportNode.getPath(), tryAppendLessExtension, evaldImportNode.currentFileInfo, evaldImportNode.options, sequencedOnImported);
} else {
this.importCount--;
if (this.isFinished) {
this._sequencer.tryRun();
}
if (this.isFinished) {
this._sequencer.tryRun();
}
}
},
onImported: function (importNode, context, e, root, importedAtRoot, fullPath) {
@@ -8584,7 +8665,7 @@ ImportVisitor.prototype = {
importVisitor.importCount--;
if (importVisitor.isFinished) {
importVisitor._sequencer.tryRun();
importVisitor._sequencer.tryRun();
}
},
visitRule: function (ruleNode, visitArgs) {
@@ -8724,10 +8805,10 @@ ToCSSVisitor.prototype = {
},
visitDirective: function(directiveNode, visitArgs) {
if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) {
return;
}
if (directiveNode.name === "@charset") {
if (!directiveNode.getIsReferenced()) {
return;
}
// Only output the debug info together with subsequent @charset definitions
// a comment (or @media statement) before the actual @charset directive would
// be considered illegal css as it has to be on the first line
@@ -8743,6 +8824,37 @@ ToCSSVisitor.prototype = {
}
if (directiveNode.rules && directiveNode.rules.rules) {
this._mergeRules(directiveNode.rules.rules);
//process childs
directiveNode.accept(this._visitor);
visitArgs.visitDeeper = false;
// the directive was directly referenced and therefore needs to be shown in the output
if (directiveNode.getIsReferenced()) {
return directiveNode;
}
if (!directiveNode.rules.rules) {
return ;
}
//the directive was not directly referenced
for (var r = 0; r<directiveNode.rules.rules.length; r++) {
var rule = directiveNode.rules.rules[r];
if (rule.getIsReferenced && rule.getIsReferenced()) {
//the directive contains something that was referenced (likely by extend)
//therefore it needs to be shown in output too
//marking as referenced in case the directive is stored inside another directive
directiveNode.markReferenced();
return directiveNode;
}
}
//The directive was not directly referenced and does not contain anything that
//was referenced. Therefore it must not be shown in output.
return ;
} else {
if (!directiveNode.getIsReferenced())
return;
}
return directiveNode;
},

12
dist/less.min.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -39,8 +39,8 @@ module.exports = function(window, options) {
options.useFileCache = true;
}
if (options.onReady === undefined) {
options.onReady = true;
}
if (options.onReady === undefined) {
options.onReady = true;
}
};

View File

@@ -14,13 +14,13 @@ require("./add-default-options")(window, options);
var less = module.exports = require("./index")(window, options);
if (options.onReady) {
if (/!watch/.test(window.location.hash)) {
less.watch();
}
less.pageLoadFinished = less.registerStylesheets().then(
function () {
return less.refresh(less.env === 'development');
}
);
if (/!watch/.test(window.location.hash)) {
less.watch();
}
less.pageLoadFinished = less.registerStylesheets().then(
function () {
return less.refresh(less.env === 'development');
}
);
}

View File

@@ -112,14 +112,14 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) {
if (webInfo) {
webInfo.remaining = remaining;
if (!instanceOptions.modifyVars) {
var css = cache.getCSS(path, webInfo);
if (!reload && css) {
webInfo.local = true;
callback(null, css, data, sheet, webInfo, path);
return;
}
}
if (!instanceOptions.modifyVars) {
var css = cache.getCSS(path, webInfo);
if (!reload && css) {
webInfo.local = true;
callback(null, css, data, sheet, webInfo, path);
return;
}
}
}
//TODO add tests around how this behaves when reloading
@@ -131,11 +131,11 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) {
e.href = path;
callback(e);
} else {
result.css = postProcessCSS(result.css);
if (!instanceOptions.modifyVars) {
cache.setCSS(sheet.href, webInfo.lastModified, result.css);
}
callback(null, result.css, data, sheet, webInfo, path);
result.css = postProcessCSS(result.css);
if (!instanceOptions.modifyVars) {
cache.setCSS(sheet.href, webInfo.lastModified, result.css);
}
callback(null, result.css, data, sheet, webInfo, path);
}
});
}
@@ -232,7 +232,7 @@ less.refresh = function (reload, modifyVars, clearFileCache) {
} else {
less.logger.info("rendered " + sheet.href + " successfully.");
}
browser.createCSS(window.document, css, sheet);
browser.createCSS(window.document, css, sheet);
less.logger.info("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms');
if (webInfo.remaining === 0) {
totalMilliseconds = new Date() - startTime;

View File

@@ -1,11 +1,11 @@
module.exports = {
extractId: function(href) {
return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain
.replace(/[\?\&]livereload=\w+/,'' ) // Remove LiveReload cachebuster
.replace(/^\//, '' ) // Remove root /
.replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension
.replace(/[^\.\w-]+/g, '-') // Replace illegal characters
.replace(/\./g, ':'); // Replace dots with colons(for valid id)
.replace(/[\?\&]livereload=\w+/, '' ) // Remove LiveReload cachebuster
.replace(/^\//, '' ) // Remove root /
.replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension
.replace(/[^\.\w-]+/g, '-') // Replace illegal characters
.replace(/\./g, ':'); // Replace dots with colons(for valid id)
},
addDataAttr: function(options, tag) {
for (var opt in tag.dataset) {

View File

@@ -30,7 +30,7 @@ FileManager.prototype.loadFile = function(filename, currentDirectory, options, e
}
var paths = isAbsoluteFilename ? [""] : [currentDirectory];
if (options.paths) paths.push.apply(paths, options.paths);
if (options.paths) { paths.push.apply(paths, options.paths); }
if (!isAbsoluteFilename && paths.indexOf('.') === -1) { paths.push('.'); }
// promise is guarenteed to be asyncronous
@@ -39,10 +39,10 @@ FileManager.prototype.loadFile = function(filename, currentDirectory, options, e
return new PromiseConstructor(function(fulfill, reject) {
(function tryPathIndex(i) {
if (i < paths.length) {
fullFilename = filename;
if (paths[i]) {
fullFilename = filename;
if (paths[i]) {
fullFilename = path.join(paths[i], fullFilename);
}
}
fs.stat(fullFilename, function (err) {
if (err) {
filenamesTried.push(fullFilename);
@@ -63,12 +63,16 @@ FileManager.prototype.loadFile = function(filename, currentDirectory, options, e
};
FileManager.prototype.loadFileSync = function(filename, currentDirectory, options, environment, encoding) {
var fullFilename, paths, filenamesTried=[], isAbsoluteFilename = this.isPathAbsolute(filename),data;
var fullFilename, paths, filenamesTried = [], isAbsoluteFilename = this.isPathAbsolute(filename) , data;
options = options || {};
paths = isAbsoluteFilename ? [""] : [currentDirectory];
if (options.paths) paths.push.apply(paths, options.paths);
if (!isAbsoluteFilename && paths.indexOf('.') === -1) { paths.push('.'); }
if (options.paths) {
paths.push.apply(paths, options.paths);
}
if (!isAbsoluteFilename && paths.indexOf('.') === -1) {
paths.push('.');
}
var err, result;
for (var i = 0; i < paths.length; i++) {

View File

@@ -1,34 +1,34 @@
var Dimension = require("../less/tree/dimension"),
Expression = require("../less/tree/expression"),
functionRegistry = require("./../less/functions/function-registry"),
path = require("path");
Expression = require("../less/tree/expression"),
functionRegistry = require("./../less/functions/function-registry"),
path = require("path");
function imageSize(filePathNode) {
var filePath = filePathNode.value;
var currentDirectory = filePathNode.currentFileInfo.relativeUrls ?
filePathNode.currentFileInfo.currentDirectory : filePathNode.currentFileInfo.entryPath;
var filePath = filePathNode.value;
var currentDirectory = filePathNode.currentFileInfo.relativeUrls ?
filePathNode.currentFileInfo.currentDirectory : filePathNode.currentFileInfo.entryPath;
var sizeOf = require('image-size');
filePath = path.join(currentDirectory, filePath);
return sizeOf(filePath);
var sizeOf = require('image-size');
filePath = path.join(currentDirectory, filePath);
return sizeOf(filePath);
}
var imageFunctions = {
"image-size": function(filePathNode) {
var size = imageSize(filePathNode);
return new Expression([
new Dimension(size.width, "px"),
new Dimension(size.height, "px")
]);
},
"image-width": function(filePathNode) {
var size = imageSize(filePathNode);
return new Dimension(size.width, "px");
},
"image-height": function(filePathNode) {
var size = imageSize(filePathNode);
return new Dimension(size.height, "px");
}
"image-size": function(filePathNode) {
var size = imageSize(filePathNode);
return new Expression([
new Dimension(size.width, "px"),
new Dimension(size.height, "px")
]);
},
"image-width": function(filePathNode) {
var size = imageSize(filePathNode);
return new Dimension(size.width, "px");
},
"image-height": function(filePathNode) {
var size = imageSize(filePathNode);
return new Dimension(size.height, "px");
}
};
functionRegistry.addMultiple(imageFunctions);

View File

@@ -41,7 +41,7 @@ var lessc_helper = {
console.log(" --source-map-rootpath=X Adds this path onto the sourcemap filename and less file paths.");
console.log(" --source-map-basepath=X Sets sourcemap base path, defaults to current working directory.");
console.log(" --source-map-less-inline Puts the less files into the map instead of referencing them.");
console.log(" --source-map-map-inline Puts the map (and any less files) into the output css file.");
console.log(" --source-map-map-inline Puts the map (and any less files) as a base64 data uri into the output css file.");
console.log(" --source-map-url=URL Sets a custom URL to map file, for sourceMappingURL comment");
console.log(" in generated CSS file.");
console.log(" -rp, --rootpath=URL Sets rootpath for url rewriting in relative imports and urls");
@@ -70,8 +70,8 @@ var lessc_helper = {
console.log(" media query which is compatible with the SASS");
console.log(" format, and 'all' which will do both.");
console.log(" --verbose Be verbose.");
console.log(" -x, --compress Compresses output by removing some whitespaces.");
console.log(" We recommend you use a dedicated minifer like less-plugin-clean-css");
console.log(" -x, --compress Compresses output by removing some whitespaces.");
console.log(" We recommend you use a dedicated minifer like less-plugin-clean-css");
console.log("");
console.log("Report bugs to: http://github.com/less/less.js/issues");
console.log("Home page: <http://lesscss.org/>");

View File

@@ -35,7 +35,7 @@ UrlFileManager.prototype.loadFile = function(filename, currentDirectory, options
request.get({uri: urlStr, strictSSL: !options.insecure }, function (error, res, body) {
if (error) {
reject({ type: 'File', message: "resource '" + urlStr + "' gave this Error:\n "+ error +"\n" });
reject({ type: 'File', message: "resource '" + urlStr + "' gave this Error:\n " + error + "\n" });
return;
}
if (res && res.statusCode === 404) {
@@ -43,7 +43,7 @@ UrlFileManager.prototype.loadFile = function(filename, currentDirectory, options
return;
}
if (!body) {
logger.warn('Warning: Empty body (HTTP '+ res.statusCode + ') returned by "' + urlStr +'"');
logger.warn('Warning: Empty body (HTTP '+ res.statusCode + ') returned by "' + urlStr + '"');
}
fulfill({ contents: body, filename: urlStr });
});

View File

@@ -114,7 +114,7 @@ less.Parser.fileLoader = function (file, currentFileInfo, callback, env) {
var j = file.lastIndexOf('/');
if(newFileInfo.relativeUrls && !/^(?:[a-z-]+:|\/)/.test(file) && j != -1) {
var relativeSubDirectory = file.slice(0, j+1);
var relativeSubDirectory = file.slice(0, j + 1);
newFileInfo.rootpath = newFileInfo.rootpath + relativeSubDirectory; // append (sub|sup) directory path of imported file
}
newFileInfo.currentDirectory = path;
@@ -135,7 +135,6 @@ less.Parser.fileLoader = function (file, currentFileInfo, callback, env) {
}
};
function writeFile(filename, content) {
var fstream = new java.io.FileWriter(filename);
var out = new java.io.BufferedWriter(fstream);
@@ -178,7 +177,7 @@ function writeFile(filename, content) {
var checkBooleanArg = function(arg) {
var onOff = /^((on|t|true|y|yes)|(off|f|false|n|no))$/i.exec(arg);
if (!onOff) {
print(" unable to parse "+arg+" as a boolean. use one of on/t/true/y/yes/off/f/false/n/no");
print(" unable to parse " + arg + " as a boolean. use one of on/t/true/y/yes/off/f/false/n/no");
continueProcessing = false;
return false;
}
@@ -371,7 +370,6 @@ function writeFile(filename, content) {
options.sourceMapOutputFilename = options.sourceMap;
}
if (!name) {
console.log("lessc: no inout files");
console.log("");

View File

@@ -108,4 +108,3 @@ contexts.Eval.prototype.normalizePath = function( path ) {
};
//todo - do the same for the toCSS ?

View File

@@ -13,9 +13,9 @@ module.exports = {
'ms': 0.001
},
angle: {
'rad': 1/(2*Math.PI),
'deg': 1/360,
'grad': 1/400,
'rad': 1 / (2 * Math.PI),
'deg': 1 / 360,
'grad': 1 / 400,
'turn': 1
}
};

View File

@@ -53,10 +53,10 @@ abstractFileManager.prototype.pathDiff = function pathDiff(url, baseUrl) {
}
baseUrlDirectories = baseUrlParts.directories.slice(i);
urlDirectories = urlParts.directories.slice(i);
for(i = 0; i < baseUrlDirectories.length-1; i++) {
for(i = 0; i < baseUrlDirectories.length - 1; i++) {
diff += "../";
}
for(i = 0; i < urlDirectories.length-1; i++) {
for(i = 0; i < urlDirectories.length - 1; i++) {
diff += urlDirectories[i] + "/";
}
return diff;
@@ -81,7 +81,7 @@ abstractFileManager.prototype.extractUrlParts = function extractUrlParts(url, ba
if (baseUrl && (!urlParts[1] || urlParts[2])) {
baseUrlParts = baseUrl.match(urlPartsRegex);
if (!baseUrlParts) {
throw new Error("Could not parse page url - '"+baseUrl+"'");
throw new Error("Could not parse page url - '" + baseUrl + "'");
}
urlParts[1] = urlParts[1] || baseUrlParts[1] || "";
if (!urlParts[2]) {
@@ -102,7 +102,7 @@ abstractFileManager.prototype.extractUrlParts = function extractUrlParts(url, ba
for(i = 0; i < directories.length; i++) {
if (directories[i] === ".." && i > 0) {
directories.splice(i-1, 2);
directories.splice(i - 1, 2);
i -= 2;
}
}

View File

@@ -33,9 +33,9 @@ var colorBlendModeFunctions = {
},
overlay: function(cb, cs) {
cb *= 2;
return (cb <= 1)
? colorBlendModeFunctions.multiply(cb, cs)
: colorBlendModeFunctions.screen(cb - 1, cs);
return (cb <= 1) ?
colorBlendModeFunctions.multiply(cb, cs) :
colorBlendModeFunctions.screen(cb - 1, cs);
},
softlight: function(cb, cs) {
var d = 1, e = cb;

View File

@@ -47,7 +47,7 @@ colorFunctions = {
h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);
if (h * 6 < 1) { return m1 + (m2 - m1) * h * 6; }
else if (h * 2 < 1) { return m2; }
else if (h * 3 < 2) { return m1 + (m2 - m1) * (2/3 - h) * 6; }
else if (h * 3 < 2) { return m1 + (m2 - m1) * (2 / 3 - h) * 6; }
else { return m1; }
}
@@ -57,9 +57,9 @@ colorFunctions = {
var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
var m1 = l * 2 - m2;
return colorFunctions.rgba(hue(h + 1/3) * 255,
return colorFunctions.rgba(hue(h + 1 / 3) * 255,
hue(h) * 255,
hue(h - 1/3) * 255,
hue(h - 1 / 3) * 255,
a);
},
@@ -127,9 +127,9 @@ colorFunctions = {
},
luminance: function (color) {
var luminance =
(0.2126 * color.rgb[0] / 255)
+ (0.7152 * color.rgb[1] / 255)
+ (0.0722 * color.rgb[2] / 255);
(0.2126 * color.rgb[0] / 255) +
(0.7152 * color.rgb[1] / 255) +
(0.0722 * color.rgb[2] / 255);
return new Dimension(luminance * color.alpha * 100, '%');
},
@@ -268,7 +268,7 @@ colorFunctions = {
};
},
tint: function(color, amount) {
return colorFunctions.mix(colorFunctions.rgb(255,255,255), color, amount);
return colorFunctions.mix(colorFunctions.rgb(255, 255, 255), color, amount);
},
shade: function(color, amount) {
return colorFunctions.mix(colorFunctions.rgb(0, 0, 0), color, amount);

View File

@@ -16,12 +16,13 @@ module.exports = function(environment) {
var mimetype = mimetypeNode && mimetypeNode.value;
var filePath = filePathNode.value;
var currentDirectory = filePathNode.currentFileInfo.relativeUrls ?
filePathNode.currentFileInfo.currentDirectory : filePathNode.currentFileInfo.entryPath;
var currentFileInfo = this.currentFileInfo;
var currentDirectory = currentFileInfo.relativeUrls ?
currentFileInfo.currentDirectory : currentFileInfo.entryPath;
var fragmentStart = filePath.indexOf('#');
var fragment = '';
if (fragmentStart!==-1) {
if (fragmentStart !== -1) {
fragment = filePath.slice(fragmentStart);
filePath = filePath.slice(0, fragmentStart);
}
@@ -39,7 +40,7 @@ module.exports = function(environment) {
mimetype = environment.mimeLookup(filePath);
if (mimetype === "image/svg+xml") {
if (mimetype === "image/svg+xml") {
useBase64 = false;
} else {
// use base 64 unless it's an ASCII or UTF-8 format
@@ -54,29 +55,30 @@ module.exports = function(environment) {
var fileSync = fileManager.loadFileSync(filePath, currentDirectory, this.context, environment);
if (!fileSync.contents) {
logger.warn("Skipped data-uri embedding because file not found");
logger.warn("Skipped data-uri embedding of " + filePath + " because file not found");
return fallback(this, filePathNode || mimetypeNode);
}
var buf = fileSync.contents;
if (useBase64 && !environment.encodeBase64) {
return fallback(this, filePathNode);
}
if (useBase64 && !environment.encodeBase64) {
return fallback(this, filePathNode);
}
buf = useBase64 ? environment.encodeBase64(buf) : encodeURIComponent(buf);
var uri = "data:" + mimetype + ',' + buf + fragment;
// IE8 cannot handle a data-uri larger than 32,768 characters. If this is exceeded
// and the --ieCompat flag is enabled, return a normal url() instead.
var DATA_URI_MAX = 32768;
if (uri.length >= DATA_URI_MAX) {
// IE8 cannot handle a data-uri larger than 32,768 characters. If this is exceeded
// and the --ieCompat flag is enabled, return a normal url() instead.
var DATA_URI_MAX = 32768;
if (uri.length >= DATA_URI_MAX) {
if (this.context.ieCompat !== false) {
logger.warn("Skipped data-uri embedding of " + filePath + " because its size (" + uri.length + " characters) exceeds IE8-safe " + DATA_URI_MAX + " characters!");
if (this.context.ieCompat !== false) {
logger.warn("Skipped data-uri embedding of " + filePath + " because its size (" + uri.length +
" characters) exceeds IE8-safe " + DATA_URI_MAX + " characters!");
return fallback(this, filePathNode || mimetypeNode);
}
}
return fallback(this, filePathNode || mimetypeNode);
}
}
return new URL(new Quoted('"' + uri + '"', uri, false, this.index, this.currentFileInfo), this.index, this.currentFileInfo);
});

View File

@@ -8,7 +8,9 @@ functionRegistry.addMultiple({
return new Anonymous(str instanceof JavaScript ? str.evaluated : str.value);
},
escape: function (str) {
return new Anonymous(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29"));
return new Anonymous(
encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B")
.replace(/\(/g, "%28").replace(/\)/g, "%29"));
},
replace: function (string, pattern, replacement, flags) {
var result = string.value;

View File

@@ -9,7 +9,9 @@ module.exports = function(environment) {
functionRegistry.add("svg-gradient", function(direction) {
function throwArgumentDescriptor() {
throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" };
throw { type: "Argument",
message: "svg-gradient expects direction, start_color [start_position], [color position,]...," +
" end_color [end_position]" };
}
if (arguments.length < 3) {
@@ -44,7 +46,8 @@ module.exports = function(environment) {
rectangleDimension = 'x="-50" y="-50" width="101" height="101"';
break;
default:
throw { type: "Argument", message: "svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'" };
throw { type: "Argument", message: "svg-gradient direction must be 'to bottom', 'to right'," +
" 'to bottom right', 'to top right' or 'ellipse at center'" };
}
returner = '<?xml version="1.0" ?>' +
'<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">' +
@@ -59,7 +62,7 @@ module.exports = function(environment) {
position = undefined;
}
if (!(color instanceof Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof Dimension))) {
if (!(color instanceof Color) || (!((i === 0 || i + 1 === stops.length) && position === undefined) && !(position instanceof Dimension))) {
throwArgumentDescriptor();
}
positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%";

View File

@@ -1,4 +1,5 @@
var Keyword = require("../tree/keyword"),
DetachedRuleset = require("../tree/detached-ruleset"),
Dimension = require("../tree/dimension"),
Color = require("../tree/color"),
Quoted = require("../tree/quoted"),
@@ -21,6 +22,9 @@ var isa = function (n, Type) {
return (n instanceof Dimension) && n.unit.is(unit) ? Keyword.True : Keyword.False;
};
functionRegistry.addMultiple({
isruleset: function (n) {
return isa(n, DetachedRuleset);
},
iscolor: function (n) {
return isa(n, Color);
},
@@ -48,7 +52,9 @@ functionRegistry.addMultiple({
isunit: isunit,
unit: function (val, unit) {
if(!(val instanceof Dimension)) {
throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof Operation ? ". Have you forgotten parenthesis?" : "") };
throw { type: "Argument",
message: "the first argument to unit must be a number" +
(val instanceof Operation ? ". Have you forgotten parenthesis?" : "") };
}
if (unit) {
if (unit instanceof Keyword) {

View File

@@ -40,12 +40,14 @@ module.exports = function(environment) {
importManager.queue.splice(importManager.queue.indexOf(path), 1); // Remove the path from the queue
var importedEqualsRoot = fullPath === importManager.rootFilename;
if (importOptions.optional && e) {
callback(null, {rules:[]}, false, null);
}
else {
importManager.files[fullPath] = root;
if (e && !importManager.error) { importManager.error = e; }
callback(e, root, importedEqualsRoot, fullPath);
}
};
var newFileInfo = {
@@ -68,7 +70,7 @@ module.exports = function(environment) {
var loadFileCallback = function(loadedFile) {
var resolvedFilename = loadedFile.filename,
contents = loadedFile.contents;
contents = loadedFile.contents.replace(/^\uFEFF/, '');
// Pass on an updated rootpath if path of imported file is relative and file
// is in a (sub|sup) directory
@@ -80,7 +82,10 @@ module.exports = function(environment) {
// then rootpath should become 'less/../'
newFileInfo.currentDirectory = fileManager.getPath(resolvedFilename);
if(newFileInfo.relativeUrls) {
newFileInfo.rootpath = fileManager.join((importManager.context.rootpath || ""), fileManager.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath));
newFileInfo.rootpath = fileManager.join(
(importManager.context.rootpath || ""),
fileManager.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath));
if (!fileManager.isPathAbsolute(newFileInfo.rootpath) && fileManager.alwaysMakePathsAbsolute()) {
newFileInfo.rootpath = fileManager.join(newFileInfo.entryPath, newFileInfo.rootpath);
}

View File

@@ -2,7 +2,7 @@ module.exports = function(environment, fileManagers) {
var SourceMapOutput, SourceMapBuilder, ParseTree, ImportManager, Environment;
var less = {
version: [2, 2, 0],
version: [2, 3, 0],
data: require('./data'),
tree: require('./tree'),
Environment: (Environment = require("./environment/environment")),

View File

@@ -1,6 +1,6 @@
var LessError = require('./less-error'),
transformTree = require("./transform-tree"),
logger = require("./logger");
logger = require("./logger");
module.exports = function(SourceMapBuilder) {
var ParseTree = function(root, imports) {
@@ -17,11 +17,11 @@ ParseTree.prototype.toCSS = function(options) {
}
try {
var compress = Boolean(options.compress);
if (compress) {
logger.warn("The compress option has been deprecated. We recommend you use a dedicated css minifier, for instance see less-plugin-clean-css.");
}
var compress = Boolean(options.compress);
if (compress) {
logger.warn("The compress option has been deprecated. We recommend you use a dedicated css minifier, for instance see less-plugin-clean-css.");
}
var toCSSOptions = {
compress: compress,
dumpLineNumbers: options.dumpLineNumbers,

View File

@@ -46,6 +46,10 @@ module.exports = function(environment, ParseTree, ImportManager) {
entryPath: entryPath,
rootFilename: filename
};
// add in a missing trailing slash
if (rootFileInfo.rootpath && rootFileInfo.rootpath.slice(-1) !== "/") {
rootFileInfo.rootpath += "/";
}
}
var imports = new ImportManager(context, rootFileInfo);

View File

@@ -204,7 +204,7 @@ var Parser = function Parser(context, imports, fileInfo) {
// Ruleset (Selector '.class', [
// Rule ("color", Value ([Expression [Color #fff]]))
// Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
// Rule ("width", Value ([Expression [Operation "+" [Variable "@w"][Dimension 4px]]]))
// Rule ("width", Value ([Expression [Operation " + " [Variable "@w"][Dimension 4px]]]))
// Ruleset (Selector [Element '>', '.child'], [...])
// ])
//
@@ -231,13 +231,17 @@ var Parser = function Parser(context, imports, fileInfo) {
primary: function () {
var mixin = this.mixin, root = [], node;
while (!parserInput.finished)
while (true)
{
while(true) {
node = this.comment();
if (!node) { break; }
root.push(node);
}
// always process comments before deciding if finished
if (parserInput.finished) {
break;
}
if (parserInput.peek('}')) {
break;
}
@@ -450,7 +454,9 @@ var Parser = function Parser(context, imports, fileInfo) {
var rgb;
if (parserInput.currentChar() === '#' && (rgb = parserInput.$re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) {
var colorCandidateString = rgb.input.match(/^#([\w]+).*/); // strip colons, brackets, whitespaces and other characters that should not definitely be part of color string
// strip colons, brackets, whitespaces and other characters that should not
// definitely be part of color string
var colorCandidateString = rgb.input.match(/^#([\w]+).*/);
colorCandidateString = colorCandidateString[1];
if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters
error("Invalid HEX color code");
@@ -546,8 +552,9 @@ var Parser = function Parser(context, imports, fileInfo) {
}
option = option && option[1];
if (!elements)
if (!elements) {
error("Missing target selector for :extend().");
}
extend = new(tree.Extend)(new(tree.Selector)(elements), option, index);
if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; }
@@ -857,11 +864,12 @@ var Parser = function Parser(context, imports, fileInfo) {
c = this.combinator();
e = parserInput.$re(/^(?:\d+\.\d+|\d+)%/) || parserInput.$re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) ||
parserInput.$char('*') || parserInput.$char('&') || this.attribute() || parserInput.$re(/^\([^&()@]+\)/) || parserInput.$re(/^[\.#](?=@)/) ||
e = parserInput.$re(/^(?:\d+\.\d+|\d+)%/) ||
parserInput.$re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) ||
parserInput.$char('*') || parserInput.$char('&') || this.attribute() ||
parserInput.$re(/^\([^&()@]+\)/) || parserInput.$re(/^[\.#:](?=@)/) ||
this.entities.variableCurly();
if (! e) {
parserInput.save();
if (parserInput.$char('(')) {
@@ -1170,7 +1178,7 @@ var Parser = function Parser(context, imports, fileInfo) {
},
importOption: function() {
var opt = parserInput.$re(/^(less|css|multiple|once|inline|reference)/);
var opt = parserInput.$re(/^(less|css|multiple|once|inline|reference|optional)/);
if (opt) {
return opt[1];
}
@@ -1384,19 +1392,19 @@ var Parser = function Parser(context, imports, fileInfo) {
sub: function () {
var a, e;
parserInput.save();
parserInput.save();
if (parserInput.$char('(')) {
a = this.addition();
if (a && parserInput.$char(')')) {
parserInput.forget();
e = new(tree.Expression)([a]);
e.parens = true;
parserInput.forget();
e = new(tree.Expression)([a]);
e.parens = true;
return e;
}
parserInput.restore("Expected ')'");
return;
parserInput.restore("Expected ')'");
return;
}
parserInput.restore();
parserInput.restore();
},
multiplication: function () {
var m, a, op, operation, isSpaced;
@@ -1603,8 +1611,8 @@ Parser.serializeVars = function(vars) {
for (var name in vars) {
if (Object.hasOwnProperty.call(vars, name)) {
var value = vars[name];
s += ((name[0] === '@') ? '' : '@') + name +': '+ value +
((('' + value).slice(-1) === ';') ? '' : ';');
s += ((name[0] === '@') ? '' : '@') + name + ': ' + value +
((String(value).slice(-1) === ';') ? '' : ';');
}
}

View File

@@ -15,7 +15,7 @@ module.exports = function (environment) {
}
if (options.sourceMapRootpath) {
this._sourceMapRootpath = options.sourceMapRootpath.replace(/\\/g, '/');
if (this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') {
if (this._sourceMapRootpath.charAt(this._sourceMapRootpath.length - 1) !== '/') {
this._sourceMapRootpath += '/';
}
} else {
@@ -66,11 +66,11 @@ module.exports = function (environment) {
}
inputSource = inputSource.substring(0, index);
sourceLines = inputSource.split("\n");
sourceColumns = sourceLines[sourceLines.length-1];
sourceColumns = sourceLines[sourceLines.length - 1];
}
lines = chunk.split("\n");
columns = lines[lines.length-1];
columns = lines[lines.length - 1];
if (fileInfo) {
if (!mapLines) {

View File

@@ -45,7 +45,7 @@ module.exports = function(root, options) {
if (options.pluginManager) {
var pluginVisitors = options.pluginManager.getVisitors();
for(i =0; i < pluginVisitors.length; i++) {
for(i = 0; i < pluginVisitors.length; i++) {
var pluginVisitor = pluginVisitors[i];
if (pluginVisitor.isPreEvalVisitor) {
preEvalVisitors.push(pluginVisitor);

View File

@@ -25,4 +25,3 @@ Assignment.prototype.genCSS = function (context, output) {
}
};
module.exports = Assignment;

View File

@@ -26,7 +26,7 @@ Condition.prototype.eval = function (context) {
default: return false;
}
}
}) (this.op, this.lvalue.eval(context), this.rvalue.eval(context));
})(this.op, this.lvalue.eval(context), this.rvalue.eval(context));
return this.negate ? !result : result;
};

View File

@@ -1,5 +1,5 @@
var debugInfo = function(context, ctx, lineSeparator) {
var result="";
var result = "";
if (context.dumpLineNumbers && !context.compress) {
switch(context.dumpLineNumbers) {
case 'comments':
@@ -21,8 +21,12 @@ debugInfo.asComment = function(ctx) {
};
debugInfo.asMediaQuery = function(ctx) {
var filenameWithProtocol = ctx.debugInfo.fileName;
if (!/^[a-z]+:\/\//i.test(filenameWithProtocol)) {
filenameWithProtocol = 'file://' + filenameWithProtocol;
}
return '@media -sass-debug-info{filename{font-family:' +
('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) {
filenameWithProtocol.replace(/([.:\/\\])/g, function (a) {
if (a == '\\') {
a = '\/';
}

View File

@@ -25,7 +25,7 @@ Dimension.prototype.toColor = function () {
};
Dimension.prototype.genCSS = function (context, output) {
if ((context && context.strictUnits) && !this.unit.isSingular()) {
throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());
throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: " + this.unit.toString());
}
var value = this.fround(context, this.value),

View File

@@ -1,7 +1,7 @@
var Node = require("./node"),
Ruleset = require("./ruleset");
var Directive = function (name, value, rules, index, currentFileInfo, debugInfo) {
var Directive = function (name, value, rules, index, currentFileInfo, debugInfo, isReferenced) {
this.name = name;
this.value = value;
if (rules) {
@@ -11,6 +11,7 @@ var Directive = function (name, value, rules, index, currentFileInfo, debugInfo)
this.index = index;
this.currentFileInfo = currentFileInfo;
this.debugInfo = debugInfo;
this.isReferenced = isReferenced;
};
Directive.prototype = new Node();
@@ -38,9 +39,9 @@ Directive.prototype.genCSS = function (context, output) {
value.genCSS(context, output);
}
if (rules) {
if (rules.type === "Ruleset") {
rules = [rules];
}
if (rules.type === "Ruleset") {
rules = [rules];
}
this.outputRuleset(context, output, rules);
} else {
output.add(';');
@@ -56,11 +57,23 @@ Directive.prototype.eval = function (context) {
rules.root = true;
}
return new Directive(this.name, value, rules,
this.index, this.currentFileInfo, this.debugInfo);
this.index, this.currentFileInfo, this.debugInfo, this.isReferenced);
};
Directive.prototype.variable = function (name) {
if (this.rules) {
return Ruleset.prototype.variable.call(this.rules, name);
}
};
Directive.prototype.find = function () {
if (this.rules) {
return Ruleset.prototype.find.apply(this.rules, arguments);
}
};
Directive.prototype.rulesets = function () {
if (this.rules) {
return Ruleset.prototype.rulesets.apply(this.rules);
}
};
Directive.prototype.variable = function (name) { if (this.rules) return Ruleset.prototype.variable.call(this.rules, name); };
Directive.prototype.find = function () { if (this.rules) return Ruleset.prototype.find.apply(this.rules, arguments); };
Directive.prototype.rulesets = function () { if (this.rules) return Ruleset.prototype.rulesets.apply(this.rules); };
Directive.prototype.markReferenced = function () {
var i, rules;
this.isReferenced = true;
@@ -73,6 +86,9 @@ Directive.prototype.markReferenced = function () {
}
}
};
Directive.prototype.getIsReferenced = function () {
return !this.currentFileInfo || !this.currentFileInfo.reference || this.isReferenced;
};
Directive.prototype.outputRuleset = function (context, output, rules) {
var ruleCnt = rules.length, i;
context.tabLevel = (context.tabLevel | 0) + 1;

View File

@@ -100,7 +100,7 @@ Import.prototype.evalPath = function (context) {
var pathValue = path.value;
// Add the base path if the import is relative
if (pathValue && context.isPathRelative(pathValue)) {
path.value = rootpath +pathValue;
path.value = rootpath + pathValue;
}
}
path.value = context.normalizePath(path.value);

View File

@@ -1,5 +1,6 @@
var tree = {};
tree.Node = require('./node');
tree.Alpha = require('./alpha');
tree.Color = require('./color');
tree.Directive = require('./directive');

View File

@@ -154,8 +154,9 @@ Media.prototype.permute = function (arr) {
}
};
Media.prototype.bubbleSelectors = function (selectors) {
if (!selectors)
return;
if (!selectors) {
return;
}
this.rules = [new Ruleset(selectors.slice(0), [this.rules[0]])];
};
module.exports = Media;

View File

@@ -22,7 +22,7 @@ MixinCall.prototype.accept = function (visitor) {
};
MixinCall.prototype.eval = function (context) {
var mixins, mixin, mixinPath, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule,
candidates = [], candidate, conditionResult = [], defaultResult, defFalseEitherCase=-1,
candidates = [], candidate, conditionResult = [], defaultResult, defFalseEitherCase = -1,
defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset, noArgumentsFilter;
function calcDefGroup(mixin, mixinPath) {
@@ -84,7 +84,7 @@ MixinCall.prototype.eval = function (context) {
if (mixin.matchArgs(args, context)) {
candidate = {mixin: mixin, group: calcDefGroup(mixin, mixinPath)};
if (candidate.group!==defFalseEitherCase) {
if (candidate.group !== defFalseEitherCase) {
candidates.push(candidate);
}
@@ -105,8 +105,7 @@ MixinCall.prototype.eval = function (context) {
defaultResult = defTrue;
if ((count[defTrue] + count[defFalse]) > 1) {
throw { type: 'Runtime',
message: 'Ambiguous use of `default()` found when matching for `'
+ this.format(args) + '`',
message: 'Ambiguous use of `default()` found when matching for `' + this.format(args) + '`',
index: this.index, filename: this.currentFileInfo.filename };
}
}

View File

@@ -132,9 +132,10 @@ Definition.prototype.evalCall = function (context, args, important) {
Definition.prototype.matchCondition = function (args, context) {
if (this.condition && !this.condition.eval(
new contexts.Eval(context,
[this.evalParams(context, new contexts.Eval(context, this.frames ? this.frames.concat(context.frames) : context.frames), args, [])] // the parameter variables
.concat(this.frames) // the parent namespace/mixin frames
.concat(context.frames)))) { // the current environment frames
[this.evalParams(context, /* the parameter variables*/
new contexts.Eval(context, this.frames ? this.frames.concat(context.frames) : context.frames), args, [])]
.concat(this.frames) // the parent namespace/mixin frames
.concat(context.frames)))) { // the current environment frames
return false;
}
return true;

View File

@@ -37,7 +37,7 @@ Quoted.prototype.eval = function (context) {
do {
value = evaluatedValue;
evaluatedValue = value.replace(regexp, replacementFnc);
} while (value!==evaluatedValue);
} while (value !== evaluatedValue);
return evaluatedValue;
}
value = iterativeReplace(value, /`([^`]+)`/g, javascriptReplacement);

View File

@@ -42,9 +42,8 @@ Rule.prototype.eval = function (context) {
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);
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) {

View File

@@ -108,7 +108,7 @@ Ruleset.prototype.eval = function (context) {
});
rsRules.splice.apply(rsRules, [i, 1].concat(rules));
rsRuleCnt += rules.length - 1;
i += rules.length-1;
i += rules.length - 1;
ruleset.resetCache();
} else if (rsRules[i].type === "RulesetCall") {
/*jshint loopfunc:true */
@@ -121,7 +121,7 @@ Ruleset.prototype.eval = function (context) {
});
rsRules.splice.apply(rsRules, [i, 1].concat(rules));
rsRuleCnt += rules.length - 1;
i += rules.length-1;
i += rules.length - 1;
ruleset.resetCache();
}
}
@@ -174,7 +174,7 @@ Ruleset.prototype.evalImports = function(context) {
importRules = rules[i].eval(context);
if (importRules && importRules.length) {
rules.splice.apply(rules, [i, 1].concat(importRules));
i+= importRules.length-1;
i+= importRules.length - 1;
} else {
rules.splice(i, 1, importRules);
}
@@ -183,20 +183,20 @@ Ruleset.prototype.evalImports = function(context) {
}
};
Ruleset.prototype.makeImportant = function() {
return new Ruleset(this.selectors, this.rules.map(function (r) {
if (r.makeImportant) {
return r.makeImportant();
} else {
return r;
}
}), this.strictImports);
};
Ruleset.prototype.matchArgs = function (args) {
this.rules = this.rules.map(function (r) {
if (r.makeImportant) {
return r.makeImportant();
} else {
return r;
}
});
return this;
};Ruleset.prototype.matchArgs = function (args) {
return !args || args.length === 0;
};
// lets you call a css selector with a guard
Ruleset.prototype.matchCondition = function (args, context) {
var lastSelector = this.selectors[this.selectors.length-1];
var lastSelector = this.selectors[this.selectors.length - 1];
if (!lastSelector.evaldCondition) {
return false;
}
@@ -221,7 +221,7 @@ Ruleset.prototype.variables = function () {
}
// when evaluating variables in an import statement, imports have not been eval'd
// so we need to go inside import statements.
// guard against root being a string (in the case of inlined less)
// guard against root being a string (in the case of inlined less)
if (r.type === "Import" && r.root && r.root.variables) {
var vars = r.root.variables();
for(var name in vars) {
@@ -415,13 +415,46 @@ Ruleset.prototype.genCSS = function (context, output) {
}
};
Ruleset.prototype.markReferenced = function () {
if (!this.selectors) {
return;
var s;
if (this.selectors) {
for (s = 0; s < this.selectors.length; s++) {
this.selectors[s].markReferenced();
}
}
for (var s = 0; s < this.selectors.length; s++) {
this.selectors[s].markReferenced();
if (this.rules) {
for (s = 0; s < this.rules.length; s++) {
if (this.rules[s].markReferenced) {
this.rules[s].markReferenced();
}
}
}
};
Ruleset.prototype.getIsReferenced = function() {
var i, j, path, selector;
if (this.paths) {
for (i = 0; i < this.paths.length; i++) {
path = this.paths[i];
for (j = 0; j < path.length; j++) {
if (path[j].getIsReferenced && path[j].getIsReferenced()) {
return true;
}
}
}
}
if (this.selectors) {
for (i = 0; i < this.selectors.length; i++) {
selector = this.selectors[i];
if (selector.getIsReferenced && selector.getIsReferenced()) {
return true;
}
}
}
return false;
};
Ruleset.prototype.joinSelectors = function (paths, context, selectors) {
for (var s = 0; s < selectors.length; s++) {
this.joinSelector(paths, context, selectors[s]);
@@ -468,12 +501,14 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
var i, j, k, currentElements, newSelectors, selectorsMultiplied, sel, el, hadParentSelector = false;
function findNestedSelector(element) {
var maybeSelector;
if (element.value.type !== 'Paren')
if (element.value.type !== 'Paren') {
return null;
}
maybeSelector = element.value.value;
if (maybeSelector.type !== 'Selector')
if (maybeSelector.type !== 'Selector') {
return null;
}
return maybeSelector;
}
@@ -669,4 +704,4 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
}
};
module.exports = Ruleset;
module.exports = Ruleset;

View File

@@ -51,8 +51,9 @@ Selector.prototype.match = function (other) {
return olen; // return number of matched elements
};
Selector.prototype.CacheElements = function() {
if (this._elements)
if (this._elements) {
return;
}
var elements = this.elements.map( function(v) {
return v.combinator.value + (v.value.value || v.value);

View File

@@ -28,7 +28,7 @@ URL.prototype.eval = function (context) {
context.isPathRelative(val.value)) {
if (!val.quote) {
rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; });
rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\" + match; });
}
val.value = rootpath + val.value;
}

View File

@@ -26,7 +26,7 @@ Value.prototype.genCSS = function (context, output) {
var i;
for(i = 0; i < this.value.length; i++) {
this.value[i].genCSS(context, output);
if (i+1 < this.value.length) {
if (i + 1 < this.value.length) {
output.add((context && context.compress) ? ',' : ', ');
}
}

View File

@@ -27,7 +27,7 @@ Variable.prototype.eval = function (context) {
var v = frame.variable(name);
if (v) {
if (v.important) {
var importantScope = context.importantScope[context.importantScope.length-1];
var importantScope = context.importantScope[context.importantScope.length - 1];
importantScope.important = v.important;
}
return v.value.eval(context);

View File

@@ -61,7 +61,7 @@ ExtendFinderVisitor.prototype = {
extend.findSelfSelectors(selectorPath);
extend.ruleset = rulesetNode;
if (j === 0) { extend.firstExtendOnThisSelectorPath = true; }
this.allExtendsStack[this.allExtendsStack.length-1].push(extend);
this.allExtendsStack[this.allExtendsStack.length - 1].push(extend);
}
}
@@ -116,21 +116,22 @@ ProcessExtendsVisitor.prototype = {
if(!indicies[extend.index + ' ' + selector]) {
indicies[extend.index + ' ' + selector] = true;
logger.warn("extend '"+selector+"' has no matches");
logger.warn("extend '" + selector + "' has no matches");
}
});
},
doExtendChaining: function (extendsList, extendsListTarget, iterationCount) {
//
// chaining is different from normal extension.. if we extend an extend then we are not just copying, altering and pasting
// the selector we would do normally, but we are also adding an extend with the same target selector
// chaining is different from normal extension.. if we extend an extend then we are not just copying, altering
// and pasting the selector we would do normally, but we are also adding an extend with the same target selector
// this means this new extend can then go and alter other extends
//
// this method deals with all the chaining work - without it, extend is flat and doesn't work on other extend selectors
// this is also the most expensive.. and a match on one selector can cause an extension of a selector we had already processed if
// we look at each selector at a time, as is done in visitRuleset
// this is also the most expensive.. and a match on one selector can cause an extension of a selector we had already
// processed if we look at each selector at a time, as is done in visitRuleset
var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend;
var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath,
extend, targetExtend, newExtend;
iterationCount = iterationCount || 0;
@@ -168,7 +169,7 @@ ProcessExtendsVisitor.prototype = {
newExtend.selfSelectors = newSelector;
// add the extend onto the list of extends for that selector
newSelector[newSelector.length-1].extendList = [newExtend];
newSelector[newSelector.length - 1].extendList = [newExtend];
// record that we need to add it.
extendsToAdd.push(newExtend);
@@ -202,11 +203,13 @@ ProcessExtendsVisitor.prototype = {
selectorTwo = extendsToAdd[0].selector.toCSS();
}
catch(e) {}
throw {message: "extend circular reference detected. One of the circular extends is currently:"+selectorOne+":extend(" + selectorTwo+")"};
throw { message: "extend circular reference detected. One of the circular extends is currently:" +
selectorOne + ":extend(" + selectorTwo + ")"};
}
// now process the new extends on the existing rules so that we can handle a extending b extending c ectending d extending e...
return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1));
// now process the new extends on the existing rules so that we can handle a extending b extending c extending
// d extending e...
return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount + 1));
} else {
return extendsToAdd;
}
@@ -224,7 +227,8 @@ ProcessExtendsVisitor.prototype = {
if (rulesetNode.root) {
return;
}
var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath;
var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length - 1],
selectorsToAdd = [], extendVisitor = this, selectorPath;
// look at each selector path in the ruleset, find any extend matches and then copy, find and replace
@@ -234,7 +238,7 @@ ProcessExtendsVisitor.prototype = {
// extending extends happens initially, before the main pass
if (rulesetNode.extendOnEveryPath) { continue; }
var extendList = selectorPath[selectorPath.length-1].extendList;
var extendList = selectorPath[selectorPath.length - 1].extendList;
if (extendList && extendList.length) { continue; }
matches = this.findMatch(allExtends[extendIndex], selectorPath);
@@ -271,15 +275,16 @@ ProcessExtendsVisitor.prototype = {
// if we allow elements before our match we can add a potential match every time. otherwise only at the first element.
if (extend.allowBefore || (haystackSelectorIndex === 0 && hackstackElementIndex === 0)) {
potentialMatches.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator});
potentialMatches.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0,
initialCombinator: haystackElement.combinator});
}
for(i = 0; i < potentialMatches.length; i++) {
potentialMatch = potentialMatches[i];
// selectors add " " onto the first element. When we use & it joins the selectors together, but if we don't
// then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to work out
// what the resulting combinator will be
// then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to
// work out what the resulting combinator will be
targetCombinator = haystackElement.combinator.value;
if (targetCombinator === '' && hackstackElementIndex === 0) {
targetCombinator = ' ';
@@ -297,7 +302,8 @@ ProcessExtendsVisitor.prototype = {
if (potentialMatch) {
potentialMatch.finished = potentialMatch.matched === needleElements.length;
if (potentialMatch.finished &&
(!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) {
(!extend.allowAfter &&
(hackstackElementIndex + 1 < hackstackSelector.elements.length || haystackSelectorIndex + 1 < haystackSelectorPath.length))) {
potentialMatch = null;
}
}
@@ -343,7 +349,7 @@ ProcessExtendsVisitor.prototype = {
if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) {
return false;
}
for(var i = 0; i <elementValue1.elements.length; i++) {
for(var i = 0; i < elementValue1.elements.length; i++) {
if (elementValue1.elements[i].combinator.value !== elementValue2.elements[i].combinator.value) {
if (i !== 0 || (elementValue1.elements[i].combinator.value || ' ') !== (elementValue2.elements[i].combinator.value || ' ')) {
return false;
@@ -381,7 +387,8 @@ ProcessExtendsVisitor.prototype = {
);
if (match.pathIndex > currentSelectorPathIndex && currentSelectorPathElementIndex > 0) {
path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
path[path.length - 1].elements = path[path.length - 1]
.elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
currentSelectorPathElementIndex = 0;
currentSelectorPathIndex++;
}
@@ -410,7 +417,8 @@ ProcessExtendsVisitor.prototype = {
}
if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) {
path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
path[path.length - 1].elements = path[path.length - 1]
.elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
currentSelectorPathIndex++;
}
@@ -421,7 +429,7 @@ ProcessExtendsVisitor.prototype = {
visitRulesetOut: function (rulesetNode) {
},
visitMedia: function (mediaNode, visitArgs) {
var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);
var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length - 1]);
newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends));
this.allExtendsStack.push(newAllExtends);
},
@@ -431,7 +439,7 @@ ProcessExtendsVisitor.prototype = {
this.allExtendsStack.length = lastIndex;
},
visitDirective: function (directiveNode, visitArgs) {
var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);
var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length - 1]);
newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends));
this.allExtendsStack.push(newAllExtends);
},

View File

@@ -2,7 +2,7 @@ function ImportSequencer(onSequencerEmpty) {
this.imports = [];
this.variableImports = [];
this._onSequencerEmpty = onSequencerEmpty;
this._currentDepth = 0;
this._currentDepth = 0;
}
ImportSequencer.prototype.addImport = function(callback) {
@@ -25,27 +25,27 @@ ImportSequencer.prototype.addVariableImport = function(callback) {
};
ImportSequencer.prototype.tryRun = function() {
this._currentDepth++;
try {
while(true) {
while(this.imports.length > 0) {
var importItem = this.imports[0];
if (!importItem.isReady) {
return;
}
this.imports = this.imports.slice(1);
importItem.callback.apply(null, importItem.args);
}
if (this.variableImports.length === 0) {
break;
}
var variableImport = this.variableImports[0];
this.variableImports = this.variableImports.slice(1);
variableImport();
}
} finally {
this._currentDepth--;
}
this._currentDepth++;
try {
while(true) {
while(this.imports.length > 0) {
var importItem = this.imports[0];
if (!importItem.isReady) {
return;
}
this.imports = this.imports.slice(1);
importItem.callback.apply(null, importItem.args);
}
if (this.variableImports.length === 0) {
break;
}
var variableImport = this.variableImports[0];
this.variableImports = this.variableImports.slice(1);
variableImport();
}
} finally {
this._currentDepth--;
}
if (this._currentDepth === 0 && this._onSequencerEmpty) {
this._onSequencerEmpty();
}

View File

@@ -28,12 +28,12 @@ ImportVisitor.prototype = {
this.isFinished = true;
this._sequencer.tryRun();
},
_onSequencerEmpty: function() {
if (!this.isFinished) {
return;
}
this._finish(this.error);
},
_onSequencerEmpty: function() {
if (!this.isFinished) {
return;
}
this._finish(this.error);
},
visitImport: function (importNode, visitArgs) {
var inlineCSS = importNode.options.inline;
@@ -84,12 +84,13 @@ ImportVisitor.prototype = {
var onImported = this.onImported.bind(this, evaldImportNode, context),
sequencedOnImported = this._sequencer.addImport(onImported);
this._importer.push(evaldImportNode.getPath(), tryAppendLessExtension, evaldImportNode.currentFileInfo, evaldImportNode.options, sequencedOnImported);
this._importer.push(evaldImportNode.getPath(), tryAppendLessExtension, evaldImportNode.currentFileInfo,
evaldImportNode.options, sequencedOnImported);
} else {
this.importCount--;
if (this.isFinished) {
this._sequencer.tryRun();
}
if (this.isFinished) {
this._sequencer.tryRun();
}
}
},
onImported: function (importNode, context, e, root, importedAtRoot, fullPath) {
@@ -139,7 +140,7 @@ ImportVisitor.prototype = {
importVisitor.importCount--;
if (importVisitor.isFinished) {
importVisitor._sequencer.tryRun();
importVisitor._sequencer.tryRun();
}
},
visitRule: function (ruleNode, visitArgs) {

View File

@@ -46,16 +46,16 @@ ToCSSVisitor.prototype = {
},
visitDirective: function(directiveNode, visitArgs) {
if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) {
return;
}
if (directiveNode.name === "@charset") {
if (!directiveNode.getIsReferenced()) {
return;
}
// Only output the debug info together with subsequent @charset definitions
// a comment (or @media statement) before the actual @charset directive would
// be considered illegal css as it has to be on the first line
if (this.charset) {
if (directiveNode.debugInfo) {
var comment = new tree.Comment("/* " + directiveNode.toCSS(this._context).replace(/\n/g, "")+" */\n");
var comment = new tree.Comment("/* " + directiveNode.toCSS(this._context).replace(/\n/g, "") + " */\n");
comment.debugInfo = directiveNode.debugInfo;
return this._visitor.visit(comment);
}
@@ -65,6 +65,38 @@ ToCSSVisitor.prototype = {
}
if (directiveNode.rules && directiveNode.rules.rules) {
this._mergeRules(directiveNode.rules.rules);
//process childs
directiveNode.accept(this._visitor);
visitArgs.visitDeeper = false;
// the directive was directly referenced and therefore needs to be shown in the output
if (directiveNode.getIsReferenced()) {
return directiveNode;
}
if (!directiveNode.rules.rules) {
return ;
}
//the directive was not directly referenced
for (var r = 0; r < directiveNode.rules.rules.length; r++) {
var rule = directiveNode.rules.rules[r];
if (rule.getIsReferenced && rule.getIsReferenced()) {
//the directive contains something that was referenced (likely by extend)
//therefore it needs to be shown in output too
//marking as referenced in case the directive is stored inside another directive
directiveNode.markReferenced();
return directiveNode;
}
}
//The directive was not directly referenced and does not contain anything that
//was referenced. Therefore it must not be shown in output.
return ;
} else {
if (!directiveNode.getIsReferenced()) {
return;
}
}
return directiveNode;
},
@@ -225,7 +257,7 @@ ToCSSVisitor.prototype = {
var spacedGroups = [];
var lastSpacedGroup = [];
parts.map(function (p) {
if (p.merge==="+") {
if (p.merge === "+") {
if (lastSpacedGroup.length > 0) {
spacedGroups.push(toExpression(lastSpacedGroup));
}

View File

@@ -1,6 +1,6 @@
{
"name": "less",
"version": "2.2.0",
"version": "2.3.0",
"description": "Leaner CSS",
"homepage": "http://lesscss.org",
"author": {
@@ -40,13 +40,14 @@
"test": "grunt test"
},
"optionalDependencies": {
"errno": "^0.1.1",
"graceful-fs": "^3.0.5",
"image-size": "~0.3.5",
"mime": "^1.2.11",
"request": "^2.51.0",
"mkdirp": "^0.5.0",
"source-map": "^0.1.x",
"promise": "^6.0.1",
"image-size": "~0.3.5"
"request": "^2.51.0",
"source-map": "^0.1.x"
},
"devDependencies": {
"diff": "^1.0",
@@ -57,6 +58,7 @@
"grunt-contrib-jasmine": "^0.8.1",
"grunt-contrib-jshint": "^0.10.0",
"grunt-contrib-uglify": "^0.7.0",
"grunt-jscs": "^1.2.0",
"grunt-shell": "^1.1.1",
"grunt-browserify": "^3.2.0",
"matchdep": "^0.3.0",

View File

@@ -130,10 +130,10 @@ var testErrorSheet = function (sheet) {
.replace(/<h3>|<\/?p>|<a href="[^"]*">|<\/a>|<ul>|<\/?pre( class="?[^">]*"?)?>|<\/li>|<\/?label>/ig, "")
.replace(/<\/h3>/ig, " ")
.replace(/<li>|<\/ul>|<br>/ig, "\n"))
.replace(/&amp;/ig,"&")
.replace(/&amp;/ig, "&")
// for IE8
.replace(/\r\n/g,"\n")
.replace(/\. \nin/,". in");
.replace(/\r\n/g, "\n")
.replace(/\. \nin/, ". in");
actualErrorMsg = innerText
.replace(/\n\d+/g, function (lineNo) {
return lineNo + " ";
@@ -149,7 +149,7 @@ var testErrorSheet = function (sheet) {
.replace(/\{pathrel\}/g, "")
.replace(/\{pathhref\}/g, "http://localhost:8081/test/less/errors/")
.replace(/\{404status\}/g, " (404)")
.replace(/\{node\}.*\{\/node\}/g, "")
.replace(/\{node\}.*\{\/node\}/g, "")
.replace(/\n$/, "");
expect(actualErrorMsg).toEqual(errorTxt);
if (errorTxt == actualErrorMsg) {

View File

@@ -180,7 +180,6 @@
// export public
jasmine.JSReporter = JSReporter;
// ------------------------------------------------------------------------
// Jasmine JSReporter for Jasmine 2.0
// ------------------------------------------------------------------------
@@ -240,7 +239,7 @@
suite.specs = [];
suite.suites = [];
suite.passed = true;
suite.parentId = this.suiteStack.slice(this.suiteStack.length -1)[0];
suite.parentId = this.suiteStack.slice(this.suiteStack.length - 1)[0];
if (suite.parentId) {
this.suites[suite.parentId].suites.push(suite);
} else {
@@ -273,7 +272,7 @@
spec = this._cacheSpec(spec);
spec.timer = new Timer().start();
// build up suites->spec tree as we go
spec.suiteId = this.suiteStack.slice(this.suiteStack.length -1)[0];
spec.suiteId = this.suiteStack.slice(this.suiteStack.length - 1)[0];
this.suites[spec.suiteId].specs.push(spec);
};

View File

@@ -2,4 +2,3 @@ var less = {
strictUnits: true,
strictMath: true,
logLevel: 4 };

View File

@@ -1,4 +1,3 @@
describe("less.js error tests", function() {
testLessErrorsInDocument();
});

View File

@@ -3,4 +3,3 @@ var less = {
errorReporting: "console",
strictMath: false,
strictUnits: false };

View File

@@ -1,4 +1,3 @@
describe("less.js javascript disabled error tests", function() {
testLessErrorsInDocument();
});

View File

@@ -1,4 +1,3 @@
var less = {logLevel: 4,
errorReporting: "console"};
less.env = "production";

View File

@@ -1,4 +1,3 @@
var less = {logLevel: 4,
errorReporting: "console"};
less.relativeUrls = true;

View File

@@ -1,4 +1,3 @@
var less = {logLevel: 4,
errorReporting: "console"};
less.rootpath = "https://localhost/";

View File

@@ -2,4 +2,3 @@ var less = {logLevel: 4,
errorReporting: "console"};
less.rootpath = "https://www.github.com/cloudhead/less.js/";
less.relativeUrls = true;

View File

@@ -3,4 +3,3 @@ var less = {
errorReporting: "console",
strictMath: true,
strictUnits: true };

72
test/copy-bom.js Normal file
View File

@@ -0,0 +1,72 @@
/*jshint latedef: nofunc */
// This is used to copy a folder (the test/less/* files & sub-folders), adding a BOM to the start of each LESS and CSS file.
// This is a based on the copySync method from fs-extra (https://github.com/jprichardson/node-fs-extra/).
module.exports = function() {
var path = require('path'),
fs = require('fs');
var BUF_LENGTH = 64 * 1024;
var _buff = new Buffer(BUF_LENGTH);
function copyFolderWithBom(src, dest) {
var stats = fs.lstatSync(src);
var destFolder = path.dirname(dest);
var destFolderExists = fs.existsSync(destFolder);
var performCopy = false;
if (stats.isFile()) {
if (!destFolderExists) {
fs.mkdirSync(destFolder);
}
if (src.match(/\.(css|less)$/)) {
copyFileAddingBomSync(src, dest);
} else {
copyFileSync(src, dest);
}
}
else if (stats.isDirectory()) {
if (!fs.existsSync(destFolder)) {
fs.mkdirSync(destFolder);
}
if (!fs.existsSync(dest)) {
fs.mkdirSync(dest);
}
fs.readdirSync(src).forEach(function(d) {
if (d !== 'bom') {
copyFolderWithBom(path.join(src, d), path.join(dest, d));
}
});
}
}
function copyFileAddingBomSync(srcFile, destFile) {
var contents = fs.readFileSync(srcFile, { encoding: 'utf8' });
if (!contents.length || contents.charCodeAt(0) !== 0xFEFF) {
contents = '\ufeff' + contents;
}
fs.writeFileSync(destFile, contents, { encoding: 'utf8' });
}
function copyFileSync(srcFile, destFile) {
var fdr = fs.openSync(srcFile, 'r')
var stat = fs.fstatSync(fdr)
var fdw = fs.openSync(destFile, 'w', stat.mode)
var bytesRead = 1
var pos = 0
while (bytesRead > 0) {
bytesRead = fs.readSync(fdr, _buff, 0, BUF_LENGTH, pos)
fs.writeSync(fdw, _buff, 0, bytesRead)
pos += bytesRead
}
fs.closeSync(fdr)
fs.closeSync(fdw)
}
return {
copyFolderWithBom: copyFolderWithBom
};
};

View File

@@ -80,3 +80,4 @@
#output-block {
comment: /* // Not commented out // */;
}
/*comment on last line*/

View File

@@ -127,6 +127,7 @@
mixt: rgba(255, 0, 0, 0.5);
}
#built-in .is-a {
ruleset: true;
color: true;
color1: true;
color2: true;

View File

@@ -34,6 +34,14 @@ div#id.class[a=1][b=2].class:not(1) {
.visible + .visible .sub {
color: green;
}
@supports (something: else) {
.class {
something: else;
}
.nestedToo .class {
something: else;
}
}
.b {
color: red;
color: green;
@@ -66,3 +74,20 @@ div#id.class[a=1][b=2].class:not(1) {
color: red;
}
}
.test {
color: red;
}
.test:first-child {
color: blue;
}
@keyframes some-name {
property: value;
}
@supports (animation-name: test) {
@keyframes some-name {
property: value;
}
.selector {
color: red;
}
}

View File

@@ -43,3 +43,9 @@
.class .inner {
test: 9;
}
.when-calling-nested-issue-2394 {
width: auto !important;
}
.when-calling-nested-with-param-issue-2394 {
width: 10px !important;
}

View File

@@ -119,7 +119,7 @@ p a span {
.bloodred {
color: green;
}
#blood.blood.red.black {
#blood.blood.red.black:blood {
color: black;
}
:nth-child(3) {

View File

@@ -57,6 +57,7 @@
}
#data-uri {
uri: url("data:image/jpeg;base64,bm90IGFjdHVhbGx5IGEganBlZyBmaWxlCg==");
background-image: url("data:image/jpeg;base64,bm90IGFjdHVhbGx5IGEganBlZyBmaWxlCg=="), url("data:image/jpeg;base64,bm90IGFjdHVhbGx5IGEganBlZyBmaWxlCg==");
uri-fragment: url("data:image/jpeg;base64,bm90IGFjdHVhbGx5IGEganBlZyBmaWxlCg==#fragment");
}
#data-uri-guess {

View File

@@ -4,12 +4,11 @@ var lessTest = require("./less-test"),
stylize = require('../lib/less-node/lessc-helper').stylize;
function getErrorPathReplacementFunction(dir) {
return function(input) {
return input.replace(
/\{path\}/g, path.join(process.cwd(), "/test/less/" + dir + "/"))
.replace(/\{node\}/g, "")
.replace(/\{\/node\}/g, "")
.replace(/\{pathrel\}/g, path.join("test", "less", dir + "/"))
return function(input, baseDir) {
return input.replace(/\{path\}/g, path.join(process.cwd(), baseDir, dir + "/"))
.replace(/\{node\}/g, "")
.replace(/\{\/node\}/g, "")
.replace(/\{pathrel\}/g, path.join(baseDir, dir + "/"))
.replace(/\{pathhref\}/g, "")
.replace(/\{404status\}/g, "")
.replace(/\r\n/g, '\n');
@@ -17,6 +16,7 @@ function getErrorPathReplacementFunction(dir) {
}
console.log("\n" + stylize("Less", 'underline') + "\n");
lessTester.prepBomTest();
lessTester.runTestSet({strictMath: true, relativeUrls: true, silent: true});
lessTester.runTestSet({strictMath: true, strictUnits: true}, "errors/",
lessTester.testErrors, null, getErrorPathReplacementFunction("errors"));
@@ -34,18 +34,18 @@ lessTester.runTestSet({strictMath: true, strictUnits: true}, "strict-units/");
lessTester.runTestSet({}, "legacy/");
lessTester.runTestSet({strictMath: true, strictUnits: true, sourceMap: true, globalVars: true }, "sourcemaps/",
lessTester.testSourcemap, null, null,
function(filename, type) {
function(filename, type, baseFolder) {
if (type === "vars") {
return path.join('test/less/', filename) + '.json';
return path.join(baseFolder, filename) + '.json';
}
return path.join('test/sourcemaps', filename) + '.json';
});
lessTester.runTestSet({globalVars: true, banner: "/**\n * Test\n */\n"}, "globalVars/",
null, null, null, function(name) { return path.join('test/less/', name) + '.json'; });
null, null, null, function(name, type, baseFolder) { return path.join(baseFolder, name) + '.json'; });
lessTester.runTestSet({modifyVars: true}, "modifyVars/",
null, null, null, function(name) { return path.join('test/less/', name) + '.json'; });
null, null, null, function(name, type, baseFolder) { return path.join(baseFolder, name) + '.json'; });
lessTester.runTestSet({urlArgs: '424242'}, "url-args/");
lessTester.runTestSet({paths: ['test/data/','test/less/import/']}, "include-path/");
lessTester.runTestSet({paths: ['test/data/', 'test/less/import/']}, "include-path/");
lessTester.testSyncronous({syncImport: true}, "import");
lessTester.testSyncronous({syncImport: true}, "css");
lessTester.testNoOptions();

View File

@@ -3,6 +3,7 @@
module.exports = function() {
var path = require('path'),
fs = require('fs');
copyBom = require('./copy-bom')();
var less = require('../lib/less-node');
var stylize = require('../lib/less-node/lessc-helper').stylize;
@@ -10,10 +11,13 @@ module.exports = function() {
var globals = Object.keys(global);
var oneTestOnly = process.argv[2],
isFinished = false;
isFinished = false;
var isVerbose = process.env.npm_config_loglevel === 'verbose';
var normalFolder = 'test/less';
var bomFolder = 'test/less-bom';
less.logger.addListener({
info: function(msg) {
if (isVerbose) {
@@ -21,10 +25,10 @@ module.exports = function() {
}
},
warn: function(msg) {
process.stdout.write(msg + "\n");
process.stdout.write(msg + "\n");
},
error: function(msg) {
process.stdout.write(msg + "\n");
process.stdout.write(msg + "\n");
}
});
@@ -32,21 +36,21 @@ module.exports = function() {
queueRunning = false;
function queue(func) {
if (queueRunning) {
//console.log("adding to queue");
//console.log("adding to queue");
queueList.push(func);
} else {
//console.log("first in queue - starting");
//console.log("first in queue - starting");
queueRunning = true;
func();
}
}
function release() {
if (queueList.length) {
//console.log("running next in queue");
//console.log("running next in queue");
var func = queueList.shift();
setTimeout(func, 0);
setTimeout(func, 0);
} else {
//console.log("stopping queue");
//console.log("stopping queue");
queueRunning = false;
}
}
@@ -67,9 +71,9 @@ module.exports = function() {
}
});
function testSourcemap(name, err, compiledLess, doReplacements, sourcemap) {
function testSourcemap(name, err, compiledLess, doReplacements, sourcemap, baseFolder) {
fs.readFile(path.join('test/', name) + '.json', 'utf8', function (e, expectedSourcemap) {
process.stdout.write("- " + name + ": ");
process.stdout.write("- " + path.join(baseFolder, name) + ": ");
if (sourcemap === expectedSourcemap) {
ok('OK');
} else if (err) {
@@ -84,10 +88,10 @@ module.exports = function() {
});
}
function testErrors(name, err, compiledLess, doReplacements) {
fs.readFile(path.join('test/less/', name) + '.txt', 'utf8', function (e, expectedErr) {
process.stdout.write("- " + name + ": ");
expectedErr = doReplacements(expectedErr, 'test/less/errors/');
function testErrors(name, err, compiledLess, doReplacements, sourcemap, baseFolder) {
fs.readFile(path.join(baseFolder, name) + '.txt', 'utf8', function (e, expectedErr) {
process.stdout.write("- " + path.join(baseFolder, name) + ": ");
expectedErr = doReplacements(expectedErr, baseFolder);
if (!err) {
if (compiledLess) {
fail("No Error", 'red');
@@ -108,8 +112,8 @@ module.exports = function() {
function globalReplacements(input, directory) {
var p = path.join(process.cwd(), directory),
pathimport = path.join(process.cwd(), directory + "import/"),
pathesc = p.replace(/[.:/\\]/g, function(a) { return '\\' + (a=='\\' ? '\/' : a); }),
pathimportesc = pathimport.replace(/[.:/\\]/g, function(a) { return '\\' + (a=='\\' ? '\/' : a); });
pathesc = p.replace(/[.:/\\]/g, function(a) { return '\\' + (a == '\\' ? '\/' : a); }),
pathimportesc = pathimport.replace(/[.:/\\]/g, function(a) { return '\\' + (a == '\\' ? '\/' : a); });
return input.replace(/\{path\}/g, p)
.replace(/\{pathesc\}/g, pathesc)
@@ -125,13 +129,13 @@ module.exports = function() {
}
function testSyncronous(options, filenameNoExtension) {
if (oneTestOnly && ("Test Sync " + filenameNoExtension) !== oneTestOnly) {
return;
}
totalTests++;
if (oneTestOnly && ("Test Sync " + filenameNoExtension) !== oneTestOnly) {
return;
}
totalTests++;
queue(function() {
var isSync = true;
toCSS(options, path.join('test/less/', filenameNoExtension + ".less"), function (err, result) {
toCSS(options, path.join(normalFolder, filenameNoExtension + ".less"), function (err, result) {
process.stdout.write("- Test Sync " + filenameNoExtension + ": ");
if (isSync) {
@@ -139,13 +143,27 @@ module.exports = function() {
} else {
fail("Not Sync");
}
release();
release();
});
isSync = false;
});
}
function prepBomTest() {
copyBom.copyFolderWithBom(normalFolder, bomFolder);
}
function runTestSet(options, foldername, verifyFunction, nameModifier, doReplacements, getFilename) {
var options2 = options ? JSON.parse(JSON.stringify(options)) : {};
runTestSetInternal(normalFolder, options, foldername, verifyFunction, nameModifier, doReplacements, getFilename);
runTestSetInternal(bomFolder, options2, foldername, verifyFunction, nameModifier, doReplacements, getFilename);
}
function runTestSetNormalOnly(options, foldername, verifyFunction, nameModifier, doReplacements, getFilename) {
runTestSetInternal(normalFolder, options, foldername, verifyFunction, nameModifier, doReplacements, getFilename);
}
function runTestSetInternal(baseFolder, options, foldername, verifyFunction, nameModifier, doReplacements, getFilename) {
foldername = foldername || "";
if(!doReplacements) {
@@ -156,7 +174,7 @@ module.exports = function() {
return foldername + path.basename(file, '.less');
}
fs.readdirSync(path.join('test/less/', foldername)).forEach(function (file) {
fs.readdirSync(path.join(baseFolder, foldername)).forEach(function (file) {
if (! /\.less/.test(file)) { return; }
var name = getBasename(file);
@@ -169,32 +187,32 @@ module.exports = function() {
if (options.sourceMap) {
options.sourceMapOutputFilename = name + ".css";
options.sourceMapBasepath = path.join(process.cwd(), "test/less");
options.sourceMapBasepath = path.join(process.cwd(), baseFolder);
options.sourceMapRootpath = "testweb/";
// TODO separate options?
options.sourceMap = options;
}
options.getVars = function(file) {
return JSON.parse(fs.readFileSync(getFilename(getBasename(file), 'vars'), 'utf8'));
return JSON.parse(fs.readFileSync(getFilename(getBasename(file), 'vars', baseFolder), 'utf8'));
};
var doubleCallCheck = false;
var doubleCallCheck = false;
queue(function() {
toCSS(options, path.join('test/less/', foldername + file), function (err, result) {
if (doubleCallCheck) {
totalTests++;
fail("less is calling back twice");
process.stdout.write(doubleCallCheck + "\n");
process.stdout.write((new Error()).stack + "\n");
return;
}
doubleCallCheck = (new Error()).stack;
toCSS(options, path.join(baseFolder, foldername + file), function (err, result) {
if (doubleCallCheck) {
totalTests++;
fail("less is calling back twice");
process.stdout.write(doubleCallCheck + "\n");
process.stdout.write((new Error()).stack + "\n");
return;
}
doubleCallCheck = (new Error()).stack;
if (verifyFunction) {
var verificationResult = verifyFunction(name, err, result && result.css, doReplacements, result && result.map);
release();
return verificationResult;
var verificationResult = verifyFunction(name, err, result && result.css, doReplacements, result && result.map, baseFolder);
release();
return verificationResult;
}
if (err) {
fail("ERROR: " + (err && err.message));
@@ -208,9 +226,9 @@ module.exports = function() {
var css_name = name;
if(nameModifier) { css_name = nameModifier(name); }
fs.readFile(path.join('test/css', css_name) + '.css', 'utf8', function (e, css) {
process.stdout.write("- " + css_name + ": ");
process.stdout.write("- " + path.join(baseFolder, css_name) + ": ");
css = css && doReplacements(css, 'test/less/' + foldername);
css = css && doReplacements(css, path.join(baseFolder, foldername));
if (result.css === css) { ok('OK'); }
else {
difference("FAIL", css, result.css);
@@ -225,10 +243,10 @@ module.exports = function() {
function diff(left, right) {
require('diff').diffLines(left, right).forEach(function(item) {
if(item.added || item.removed) {
var text = item.value.replace("\n", String.fromCharCode(182) + "\n");
var text = item.value.replace("\n", String.fromCharCode(182) + "\n").replace('\ufeff', '[[BOM]]');
process.stdout.write(stylize(text, item.added ? 'green' : 'red'));
} else {
process.stdout.write(item.value);
process.stdout.write(item.value.replace('\ufeff', '[[BOM]]'));
}
});
process.stdout.write("\n");
@@ -253,16 +271,16 @@ module.exports = function() {
passedTests++;
endTest();
}
function finished() {
isFinished = true;
endTest();
}
function finished() {
isFinished = true;
endTest();
}
function endTest() {
if (isFinished && ((failedTests + passedTests) >= totalTests)) {
var leaked = checkGlobalLeaks();
var leaked = checkGlobalLeaks();
process.stdout.write("\n");
if (failedTests > 0) {
process.stdout.write(failedTests + stylize(" Failed", "red") + ", " + passedTests + " passed\n");
@@ -289,8 +307,7 @@ module.exports = function() {
return false;
}
function toCSS(options, path, callback) {
function toCSS(options, path, callback) {
options = options || {};
var str = fs.readFileSync(path, 'utf8'), addPath = require('path').dirname(path);
@@ -311,9 +328,9 @@ module.exports = function() {
}
function testNoOptions() {
if (oneTestOnly && "Integration" !== oneTestOnly) {
return;
}
if (oneTestOnly && "Integration" !== oneTestOnly) {
return;
}
totalTests++;
try {
process.stdout.write("- Integration - creating parser without options: ");
@@ -327,10 +344,12 @@ module.exports = function() {
return {
runTestSet: runTestSet,
runTestSetNormalOnly: runTestSetNormalOnly,
testSyncronous: testSyncronous,
testErrors: testErrors,
testSourcemap: testSourcemap,
testNoOptions: testNoOptions,
finished: finished
prepBomTest: prepBomTest,
finished: finished
};
};

View File

@@ -99,3 +99,4 @@
/*by block */
@string_w_comment: ~"/* // Not commented out // */";
#output-block { comment: @string_w_comment; }
/*comment on last line*/

View File

@@ -137,6 +137,10 @@
mixt: mix(#ff0000, transparent);
.is-a {
@rules: {
color: red;
};
ruleset: isruleset(@rules);
color: iscolor(#ddd);
color1: iscolor(red);
color2: iscolor(rgb(0, 0, 0));

View File

@@ -18,4 +18,6 @@
}
.class:extend(.class all) {
}
}
.mixin-with-nested-selectors();
.mixin-with-directives(some-name);

View File

@@ -2,6 +2,8 @@
@import url(/absolute/something.css) screen and (color) and (max-width: 600px);
@import (optional) "file-does-not-exist.does-not-exist";
@var: 100px;
@import url("//ha.com/file.css") (min-width:@var);

View File

@@ -48,4 +48,42 @@
@media (max-size: @max-size) {
color: red;
}
}
//https://github.com/less/less.js/issues/2359
@supports (something: else) {
.class {
something: else;
}
.nestedToo {
.class {
something: else;
}
}
.invisible {
something: else;
}
}
//https://github.com/less/less.js/issues/1979
.mixin-with-nested-selectors() {
.test {
color: red;
&:first-child {
color: blue;
}
}
}
.mixin-with-directives(@keyframeName) {
@keyframes @keyframeName {
@rules1();
}
@supports (animation-name: test) {
@keyframes @keyframeName {
@rules2();
}
.selector {
color: red;
}
}
@rules1: {property: value;};
@rules2: {property: value;};
}

View File

@@ -23,3 +23,15 @@
.mixin !important;
.mixin(9);
}
.size(@aaa: auto) {
.set-width(@aaa) {
width: @aaa;
}
.set-width(@aaa);
}
.when-calling-nested-issue-2394 {
.size() !important;
}
.when-calling-nested-with-param-issue-2394 {
.size(10px) !important;
}

View File

@@ -117,7 +117,7 @@ a {
color: green;
}
.red {
#@{theme}.@{theme}&.black {
#@{theme}.@{theme}&.black:@{theme} {
color:black;
}
}

View File

@@ -45,6 +45,9 @@
#data-uri {
uri: data-uri('image/jpeg;base64', '../data/image.jpg');
@var: replace('../data/replace.jpg', "replace", "image");
background-image: data-uri(@var), data-uri(replace('../data/image.filext', "filext", "jpg"));
uri-fragment: data-uri('image/jpeg;base64', '../data/image.jpg#fragment');
}

View File

@@ -8,10 +8,12 @@ var options = {
}
less.render(input, options, function (err, result) {
if (err) console.log(err);
if (result.css === expectedCss) {
console.log("PASS")
} else {
console.log("FAIL")
if (err) {
console.log(err);
}
})
if (result.css === expectedCss) {
console.log("PASS");
} else {
console.log("FAIL");
}
});