451 lines
14 KiB
JavaScript
451 lines
14 KiB
JavaScript
/* jshint rhino:true, unused: false */
|
|
/* jscs:disable validateIndentation */
|
|
/*global name:true, less, loadStyleSheet, os */
|
|
|
|
function formatError(ctx, options) {
|
|
options = options || {};
|
|
|
|
var message = "";
|
|
var extract = ctx.extract;
|
|
var error = [];
|
|
|
|
// var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; };
|
|
var stylize = function (str) { return str; };
|
|
|
|
// only output a stack if it isn't a less error
|
|
if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); }
|
|
|
|
if (!ctx.hasOwnProperty('index') || !extract) {
|
|
return ctx.stack || ctx.message;
|
|
}
|
|
|
|
if (typeof extract[0] === 'string') {
|
|
error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey'));
|
|
}
|
|
|
|
if (typeof extract[1] === 'string') {
|
|
var errorTxt = ctx.line + ' ';
|
|
if (extract[1]) {
|
|
errorTxt += extract[1].slice(0, ctx.column) +
|
|
stylize(stylize(stylize(extract[1][ctx.column], 'bold') +
|
|
extract[1].slice(ctx.column + 1), 'red'), 'inverse');
|
|
}
|
|
error.push(errorTxt);
|
|
}
|
|
|
|
if (typeof extract[2] === 'string') {
|
|
error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey'));
|
|
}
|
|
error = error.join('\n') + stylize('', 'reset') + '\n';
|
|
|
|
message += stylize(ctx.type + 'Error: ' + ctx.message, 'red');
|
|
if (ctx.filename) {
|
|
message += stylize(' in ', 'red') + ctx.filename +
|
|
stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey');
|
|
}
|
|
|
|
message += '\n' + error;
|
|
|
|
if (ctx.callLine) {
|
|
message += stylize('from ', 'red') + (ctx.filename || '') + '/n';
|
|
message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n';
|
|
}
|
|
|
|
return message;
|
|
}
|
|
|
|
function writeError(ctx, options) {
|
|
options = options || {};
|
|
if (options.silent) { return; }
|
|
var message = formatError(ctx, options);
|
|
throw new Error(message);
|
|
}
|
|
|
|
function loadStyleSheet(sheet, callback, reload, remaining) {
|
|
var endOfPath = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')),
|
|
sheetName = name.slice(0, endOfPath + 1) + sheet.href,
|
|
contents = sheet.contents || {},
|
|
input = readFile(sheetName);
|
|
|
|
input = input.replace(/^\xEF\xBB\xBF/, '');
|
|
|
|
contents[sheetName] = input;
|
|
|
|
var parser = new less.Parser({
|
|
paths: [sheet.href.replace(/[\w\.-]+$/, '')],
|
|
contents: contents
|
|
});
|
|
parser.parse(input, function (e, root) {
|
|
if (e) {
|
|
return writeError(e);
|
|
}
|
|
try {
|
|
callback(e, root, input, sheet, { local: false, lastModified: 0, remaining: remaining }, sheetName);
|
|
} catch(e) {
|
|
writeError(e);
|
|
}
|
|
});
|
|
}
|
|
|
|
less.Parser.fileLoader = function (file, currentFileInfo, callback, env) {
|
|
|
|
var href = file;
|
|
if (currentFileInfo && currentFileInfo.currentDirectory && !/^\//.test(file)) {
|
|
href = less.modules.path.join(currentFileInfo.currentDirectory, file);
|
|
}
|
|
|
|
var path = less.modules.path.dirname(href);
|
|
|
|
var newFileInfo = {
|
|
currentDirectory: path + '/',
|
|
filename: href
|
|
};
|
|
|
|
if (currentFileInfo) {
|
|
newFileInfo.entryPath = currentFileInfo.entryPath;
|
|
newFileInfo.rootpath = currentFileInfo.rootpath;
|
|
newFileInfo.rootFilename = currentFileInfo.rootFilename;
|
|
newFileInfo.relativeUrls = currentFileInfo.relativeUrls;
|
|
} else {
|
|
newFileInfo.entryPath = path;
|
|
newFileInfo.rootpath = less.rootpath || path;
|
|
newFileInfo.rootFilename = href;
|
|
newFileInfo.relativeUrls = env.relativeUrls;
|
|
}
|
|
|
|
var j = file.lastIndexOf('/');
|
|
if (newFileInfo.relativeUrls && !/^(?:[a-z-]+:|\/)/.test(file) && 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;
|
|
newFileInfo.filename = href;
|
|
|
|
var data = null;
|
|
try {
|
|
data = readFile(href);
|
|
} catch (e) {
|
|
callback({ type: 'File', message: "'" + less.modules.path.basename(href) + "' wasn't found" });
|
|
return;
|
|
}
|
|
|
|
try {
|
|
callback(null, data, href, newFileInfo, { lastModified: 0 });
|
|
} catch (e) {
|
|
callback(e, null, href);
|
|
}
|
|
};
|
|
|
|
function writeFile(filename, content) {
|
|
var fstream = new java.io.FileWriter(filename);
|
|
var out = new java.io.BufferedWriter(fstream);
|
|
out.write(content);
|
|
out.close();
|
|
}
|
|
|
|
// Command line integration via Rhino
|
|
(function (args) {
|
|
|
|
var options = {
|
|
depends: false,
|
|
compress: false,
|
|
cleancss: false,
|
|
max_line_len: -1,
|
|
silent: false,
|
|
verbose: false,
|
|
lint: false,
|
|
paths: [],
|
|
color: true,
|
|
strictImports: false,
|
|
rootpath: '',
|
|
relativeUrls: false,
|
|
ieCompat: true,
|
|
strictMath: false,
|
|
strictUnits: false
|
|
};
|
|
var continueProcessing = true,
|
|
currentErrorcode;
|
|
|
|
var checkArgFunc = function(arg, option) {
|
|
if (!option) {
|
|
print(arg + " option requires a parameter");
|
|
continueProcessing = false;
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
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");
|
|
continueProcessing = false;
|
|
return false;
|
|
}
|
|
return Boolean(onOff[2]);
|
|
};
|
|
|
|
var warningMessages = "";
|
|
var sourceMapFileInline = false;
|
|
|
|
args = args.filter(function (arg) {
|
|
var 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':
|
|
options.verbose = true;
|
|
break;
|
|
case 's':
|
|
case 'silent':
|
|
options.silent = true;
|
|
break;
|
|
case 'l':
|
|
case 'lint':
|
|
options.lint = true;
|
|
break;
|
|
case 'strict-imports':
|
|
options.strictImports = true;
|
|
break;
|
|
case 'h':
|
|
case 'help':
|
|
//TODO
|
|
// require('../lib/less/lessc_helper').printUsage();
|
|
continueProcessing = false;
|
|
break;
|
|
case 'x':
|
|
case 'compress':
|
|
options.compress = true;
|
|
break;
|
|
case 'M':
|
|
case 'depends':
|
|
options.depends = true;
|
|
break;
|
|
case 'yui-compress':
|
|
warningMessages += "yui-compress option has been removed. assuming clean-css.";
|
|
options.cleancss = true;
|
|
break;
|
|
case 'clean-css':
|
|
options.cleancss = 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])) {
|
|
// support for both ; and : path separators
|
|
// even on windows when using absolute paths with drive letters (eg C:\path:D:\path)
|
|
options.paths = match[2]
|
|
.split(os.type().match(/Windows/) ? /:(?!\\)|;/ : ':')
|
|
.map(function(p) {
|
|
if (p) {
|
|
// return path.resolve(process.cwd(), p);
|
|
return p;
|
|
}
|
|
});
|
|
}
|
|
break;
|
|
case 'line-numbers':
|
|
if (checkArgFunc(arg, match[2])) {
|
|
options.dumpLineNumbers = match[2];
|
|
}
|
|
break;
|
|
case 'source-map':
|
|
if (!match[2]) {
|
|
options.sourceMap = true;
|
|
} else {
|
|
options.sourceMap = match[2];
|
|
}
|
|
break;
|
|
case 'source-map-rootpath':
|
|
if (checkArgFunc(arg, match[2])) {
|
|
options.sourceMapRootpath = match[2];
|
|
}
|
|
break;
|
|
case 'source-map-basepath':
|
|
if (checkArgFunc(arg, match[2])) {
|
|
options.sourceMapBasepath = match[2];
|
|
}
|
|
break;
|
|
case 'source-map-map-inline':
|
|
sourceMapFileInline = true;
|
|
options.sourceMap = true;
|
|
break;
|
|
case 'source-map-less-inline':
|
|
options.outputSourceFiles = true;
|
|
break;
|
|
case 'source-map-url':
|
|
if (checkArgFunc(arg, match[2])) {
|
|
options.sourceMapURL = match[2];
|
|
}
|
|
break;
|
|
case 'source-map-output-map-file':
|
|
if (checkArgFunc(arg, match[2])) {
|
|
options.writeSourceMap = function(sourceMapContent) {
|
|
writeFile(match[2], sourceMapContent);
|
|
};
|
|
}
|
|
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;
|
|
default:
|
|
console.log('invalid option ' + arg);
|
|
continueProcessing = false;
|
|
}
|
|
});
|
|
|
|
if (!continueProcessing) {
|
|
return;
|
|
}
|
|
|
|
var name = args[0];
|
|
if (name && name != '-') {
|
|
// name = path.resolve(process.cwd(), name);
|
|
}
|
|
var output = args[1];
|
|
var outputbase = args[1];
|
|
if (output) {
|
|
options.sourceMapOutputFilename = output;
|
|
// output = path.resolve(process.cwd(), output);
|
|
if (warningMessages) {
|
|
console.log(warningMessages);
|
|
}
|
|
}
|
|
|
|
// options.sourceMapBasepath = process.cwd();
|
|
// options.sourceMapBasepath = '';
|
|
|
|
if (options.sourceMap === true) {
|
|
console.log("output: " + output);
|
|
if (!output && !sourceMapFileInline) {
|
|
console.log("the sourcemap option only has an optional filename if the css filename is given");
|
|
return;
|
|
}
|
|
options.sourceMapFullFilename = options.sourceMapOutputFilename + ".map";
|
|
options.sourceMap = less.modules.path.basename(options.sourceMapFullFilename);
|
|
} else if (options.sourceMap) {
|
|
options.sourceMapOutputFilename = options.sourceMap;
|
|
}
|
|
|
|
if (!name) {
|
|
console.log("lessc: no inout files");
|
|
console.log("");
|
|
// TODO
|
|
// require('../lib/less/lessc_helper').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; }
|
|
// }
|
|
// cmd = mkdirp && mkdirp.sync || fs.mkdirSync;
|
|
// cmd(dir);
|
|
// }
|
|
// };
|
|
|
|
if (options.depends) {
|
|
if (!outputbase) {
|
|
console.log("option --depends requires an output path to be specified");
|
|
return;
|
|
}
|
|
console.log(outputbase + ": ");
|
|
}
|
|
|
|
if (!name) {
|
|
console.log('No files present in the fileset');
|
|
quit(1);
|
|
}
|
|
|
|
var input = null;
|
|
try {
|
|
input = readFile(name, 'utf-8');
|
|
|
|
} catch (e) {
|
|
console.log('lesscss: couldn\'t open file ' + name);
|
|
quit(1);
|
|
}
|
|
|
|
options.filename = name;
|
|
var result;
|
|
try {
|
|
var parser = new less.Parser(options);
|
|
parser.parse(input, function (e, root) {
|
|
if (e) {
|
|
writeError(e, options);
|
|
quit(1);
|
|
} else {
|
|
result = root.toCSS(options);
|
|
if (output) {
|
|
writeFile(output, result);
|
|
console.log("Written to " + output);
|
|
} else {
|
|
print(result);
|
|
}
|
|
quit(0);
|
|
}
|
|
});
|
|
}
|
|
catch(e) {
|
|
writeError(e, options);
|
|
quit(1);
|
|
}
|
|
}(arguments));
|