index.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. /* jshint rhino:true, unused: false */
  2. /* global name:true, less, loadStyleSheet, os */
  3. function formatError(ctx, options) {
  4. options = options || {};
  5. var message = "";
  6. var extract = ctx.extract;
  7. var error = [];
  8. // var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; };
  9. var stylize = function (str) { return str; };
  10. // only output a stack if it isn't a less error
  11. if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); }
  12. if (!ctx.hasOwnProperty('index') || !extract) {
  13. return ctx.stack || ctx.message;
  14. }
  15. if (typeof extract[0] === 'string') {
  16. error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey'));
  17. }
  18. if (typeof extract[1] === 'string') {
  19. var errorTxt = ctx.line + ' ';
  20. if (extract[1]) {
  21. errorTxt += extract[1].slice(0, ctx.column) +
  22. stylize(stylize(stylize(extract[1][ctx.column], 'bold') +
  23. extract[1].slice(ctx.column + 1), 'red'), 'inverse');
  24. }
  25. error.push(errorTxt);
  26. }
  27. if (typeof extract[2] === 'string') {
  28. error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey'));
  29. }
  30. error = error.join('\n') + stylize('', 'reset') + '\n';
  31. message += stylize(ctx.type + 'Error: ' + ctx.message, 'red');
  32. if (ctx.filename) {
  33. message += stylize(' in ', 'red') + ctx.filename +
  34. stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey');
  35. }
  36. message += '\n' + error;
  37. if (ctx.callLine) {
  38. message += stylize('from ', 'red') + (ctx.filename || '') + '/n';
  39. message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n';
  40. }
  41. return message;
  42. }
  43. function writeError(ctx, options) {
  44. options = options || {};
  45. if (options.silent) { return; }
  46. var message = formatError(ctx, options);
  47. throw new Error(message);
  48. }
  49. function loadStyleSheet(sheet, callback, reload, remaining) {
  50. var endOfPath = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')),
  51. sheetName = name.slice(0, endOfPath + 1) + sheet.href,
  52. contents = sheet.contents || {},
  53. input = readFile(sheetName);
  54. input = input.replace(/^\xEF\xBB\xBF/, '');
  55. contents[sheetName] = input;
  56. var parser = new less.Parser({
  57. paths: [sheet.href.replace(/[\w\.-]+$/, '')],
  58. contents: contents
  59. });
  60. parser.parse(input, function (e, root) {
  61. if (e) {
  62. return writeError(e);
  63. }
  64. try {
  65. callback(e, root, input, sheet, { local: false, lastModified: 0, remaining: remaining }, sheetName);
  66. } catch (e) {
  67. writeError(e);
  68. }
  69. });
  70. }
  71. less.Parser.fileLoader = function (file, currentFileInfo, callback, env) {
  72. var href = file;
  73. if (currentFileInfo && currentFileInfo.currentDirectory && !/^\//.test(file)) {
  74. href = less.modules.path.join(currentFileInfo.currentDirectory, file);
  75. }
  76. var path = less.modules.path.dirname(href);
  77. var newFileInfo = {
  78. currentDirectory: path + '/',
  79. filename: href
  80. };
  81. if (currentFileInfo) {
  82. newFileInfo.entryPath = currentFileInfo.entryPath;
  83. newFileInfo.rootpath = currentFileInfo.rootpath;
  84. newFileInfo.rootFilename = currentFileInfo.rootFilename;
  85. newFileInfo.relativeUrls = currentFileInfo.relativeUrls;
  86. } else {
  87. newFileInfo.entryPath = path;
  88. newFileInfo.rootpath = less.rootpath || path;
  89. newFileInfo.rootFilename = href;
  90. newFileInfo.relativeUrls = env.relativeUrls;
  91. }
  92. var j = file.lastIndexOf('/');
  93. if (newFileInfo.relativeUrls && !/^(?:[a-z-]+:|\/)/.test(file) && j != -1) {
  94. var relativeSubDirectory = file.slice(0, j + 1);
  95. newFileInfo.rootpath = newFileInfo.rootpath + relativeSubDirectory; // append (sub|sup) directory path of imported file
  96. }
  97. newFileInfo.currentDirectory = path;
  98. newFileInfo.filename = href;
  99. var data = null;
  100. try {
  101. data = readFile(href);
  102. } catch (e) {
  103. callback({ type: 'File', message: "'" + less.modules.path.basename(href) + "' wasn't found" });
  104. return;
  105. }
  106. try {
  107. callback(null, data, href, newFileInfo, { lastModified: 0 });
  108. } catch (e) {
  109. callback(e, null, href);
  110. }
  111. };
  112. function writeFile(filename, content) {
  113. var fstream = new java.io.FileWriter(filename);
  114. var out = new java.io.BufferedWriter(fstream);
  115. out.write(content);
  116. out.close();
  117. }
  118. // Command line integration via Rhino
  119. (function (args) {
  120. var options = require('../default-options');
  121. var continueProcessing = true,
  122. currentErrorcode;
  123. var checkArgFunc = function(arg, option) {
  124. if (!option) {
  125. print(arg + " option requires a parameter");
  126. continueProcessing = false;
  127. return false;
  128. }
  129. return true;
  130. };
  131. var checkBooleanArg = function(arg) {
  132. var onOff = /^((on|t|true|y|yes)|(off|f|false|n|no))$/i.exec(arg);
  133. if (!onOff) {
  134. print(" unable to parse " + arg + " as a boolean. use one of on/t/true/y/yes/off/f/false/n/no");
  135. continueProcessing = false;
  136. return false;
  137. }
  138. return Boolean(onOff[2]);
  139. };
  140. var warningMessages = "";
  141. var sourceMapFileInline = false;
  142. args = args.filter(function (arg) {
  143. var match = arg.match(/^-I(.+)$/);
  144. if (match) {
  145. options.paths.push(match[1]);
  146. return false;
  147. }
  148. match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=(.*))?$/i);
  149. if (match) {
  150. arg = match[1];
  151. }
  152. else {
  153. return arg;
  154. }
  155. switch (arg) {
  156. case 'v':
  157. case 'version':
  158. console.log("lessc " + less.version.join('.') + " (Less Compiler) [JavaScript]");
  159. continueProcessing = false;
  160. break;
  161. case 'verbose':
  162. options.verbose = true;
  163. break;
  164. case 's':
  165. case 'silent':
  166. options.silent = true;
  167. break;
  168. case 'l':
  169. case 'lint':
  170. options.lint = true;
  171. break;
  172. case 'strict-imports':
  173. options.strictImports = true;
  174. break;
  175. case 'h':
  176. case 'help':
  177. // TODO
  178. // require('../lib/less/lessc_helper').printUsage();
  179. continueProcessing = false;
  180. break;
  181. case 'x':
  182. case 'compress':
  183. options.compress = true;
  184. break;
  185. case 'M':
  186. case 'depends':
  187. options.depends = true;
  188. break;
  189. case 'yui-compress':
  190. warningMessages += "yui-compress option has been removed. assuming clean-css.";
  191. options.cleancss = true;
  192. break;
  193. case 'clean-css':
  194. options.cleancss = true;
  195. break;
  196. case 'max-line-len':
  197. if (checkArgFunc(arg, match[2])) {
  198. options.maxLineLen = parseInt(match[2], 10);
  199. if (options.maxLineLen <= 0) {
  200. options.maxLineLen = -1;
  201. }
  202. }
  203. break;
  204. case 'no-color':
  205. options.color = false;
  206. break;
  207. case 'no-ie-compat':
  208. options.ieCompat = false;
  209. break;
  210. case 'no-js':
  211. options.javascriptEnabled = false;
  212. break;
  213. case 'include-path':
  214. if (checkArgFunc(arg, match[2])) {
  215. // support for both ; and : path separators
  216. // even on windows when using absolute paths with drive letters (eg C:\path:D:\path)
  217. options.paths = match[2]
  218. .split(os.type().match(/Windows/) ? /:(?!\\)|;/ : ':')
  219. .map(function(p) {
  220. if (p) {
  221. // return path.resolve(process.cwd(), p);
  222. return p;
  223. }
  224. });
  225. }
  226. break;
  227. case 'line-numbers':
  228. if (checkArgFunc(arg, match[2])) {
  229. options.dumpLineNumbers = match[2];
  230. }
  231. break;
  232. case 'source-map':
  233. if (!match[2]) {
  234. options.sourceMap = true;
  235. } else {
  236. options.sourceMap = match[2];
  237. }
  238. break;
  239. case 'source-map-rootpath':
  240. if (checkArgFunc(arg, match[2])) {
  241. options.sourceMapRootpath = match[2];
  242. }
  243. break;
  244. case 'source-map-basepath':
  245. if (checkArgFunc(arg, match[2])) {
  246. options.sourceMapBasepath = match[2];
  247. }
  248. break;
  249. case 'source-map-map-inline':
  250. sourceMapFileInline = true;
  251. options.sourceMap = true;
  252. break;
  253. case 'source-map-less-inline':
  254. options.outputSourceFiles = true;
  255. break;
  256. case 'source-map-url':
  257. if (checkArgFunc(arg, match[2])) {
  258. options.sourceMapURL = match[2];
  259. }
  260. break;
  261. case 'source-map-output-map-file':
  262. if (checkArgFunc(arg, match[2])) {
  263. options.writeSourceMap = function(sourceMapContent) {
  264. writeFile(match[2], sourceMapContent);
  265. };
  266. }
  267. break;
  268. case 'rp':
  269. case 'rootpath':
  270. if (checkArgFunc(arg, match[2])) {
  271. options.rootpath = match[2].replace(/\\/g, '/');
  272. }
  273. break;
  274. case "ru":
  275. case "relative-urls":
  276. options.relativeUrls = true;
  277. break;
  278. case "sm":
  279. case "strict-math":
  280. if (checkArgFunc(arg, match[2])) {
  281. options.strictMath = checkBooleanArg(match[2]);
  282. }
  283. break;
  284. case "su":
  285. case "strict-units":
  286. if (checkArgFunc(arg, match[2])) {
  287. options.strictUnits = checkBooleanArg(match[2]);
  288. }
  289. break;
  290. default:
  291. console.log('invalid option ' + arg);
  292. continueProcessing = false;
  293. }
  294. });
  295. if (!continueProcessing) {
  296. return;
  297. }
  298. var name = args[0];
  299. if (name && name != '-') {
  300. // name = path.resolve(process.cwd(), name);
  301. }
  302. var output = args[1];
  303. var outputbase = args[1];
  304. if (output) {
  305. options.sourceMapOutputFilename = output;
  306. // output = path.resolve(process.cwd(), output);
  307. if (warningMessages) {
  308. console.log(warningMessages);
  309. }
  310. }
  311. // options.sourceMapBasepath = process.cwd();
  312. // options.sourceMapBasepath = '';
  313. if (options.sourceMap === true) {
  314. console.log("output: " + output);
  315. if (!output && !sourceMapFileInline) {
  316. console.log("the sourcemap option only has an optional filename if the css filename is given");
  317. return;
  318. }
  319. options.sourceMapFullFilename = options.sourceMapOutputFilename + ".map";
  320. options.sourceMap = less.modules.path.basename(options.sourceMapFullFilename);
  321. } else if (options.sourceMap) {
  322. options.sourceMapOutputFilename = options.sourceMap;
  323. }
  324. if (!name) {
  325. console.log("lessc: no inout files");
  326. console.log("");
  327. // TODO
  328. // require('../lib/less/lessc_helper').printUsage();
  329. currentErrorcode = 1;
  330. return;
  331. }
  332. /*
  333. var ensureDirectory = function (filepath) {
  334. var dir = path.dirname(filepath),
  335. cmd,
  336. existsSync = fs.existsSync || path.existsSync;
  337. if (!existsSync(dir)) {
  338. if (mkdirp === undefined) {
  339. try {mkdirp = require('mkdirp');}
  340. catch(e) { mkdirp = null; }
  341. }
  342. cmd = mkdirp && mkdirp.sync || fs.mkdirSync;
  343. cmd(dir);
  344. }
  345. }; */
  346. if (options.depends) {
  347. if (!outputbase) {
  348. console.log("option --depends requires an output path to be specified");
  349. return;
  350. }
  351. console.log(outputbase + ": ");
  352. }
  353. if (!name) {
  354. console.log('No files present in the fileset');
  355. quit(1);
  356. }
  357. var input = null;
  358. try {
  359. input = readFile(name, 'utf-8');
  360. } catch (e) {
  361. console.log('lesscss: couldn\'t open file ' + name);
  362. quit(1);
  363. }
  364. options.filename = name;
  365. var result;
  366. try {
  367. var parser = new less.Parser(options);
  368. parser.parse(input, function (e, root) {
  369. if (e) {
  370. writeError(e, options);
  371. quit(1);
  372. } else {
  373. result = root.toCSS(options);
  374. if (output) {
  375. writeFile(output, result);
  376. console.log("Written to " + output);
  377. } else {
  378. print(result);
  379. }
  380. quit(0);
  381. }
  382. });
  383. }
  384. catch (e) {
  385. writeError(e, options);
  386. quit(1);
  387. }
  388. }(arguments));