[Pkg-javascript-commits] [less.js] 142/285: Pull out the file manager aspect of the environment so new file managers can be added dynamically
Jonas Smedegaard
dr at jones.dk
Mon Oct 26 23:23:48 UTC 2015
This is an automated email from the git hooks/post-receive script.
js pushed a commit to annotated tag v2.0.0
in repository less.js.
commit 7a6b1e278b5a81ae6ca984e2d04c6146464162b0
Author: Luke Page <luke.a.page at gmail.com>
Date: Sat Oct 4 17:46:30 2014 +0100
Pull out the file manager aspect of the environment so new file managers can be added dynamically
---
bin/lessc | 1 -
lib/less-browser/browser-import.js | 137 +++++++++++++++++
lib/less-browser/environment.js | 203 +------------------------
lib/less-browser/index.js | 44 +++---
lib/less-node/environment.js | 204 --------------------------
lib/less-node/file-import.js | 79 ++++++++++
lib/less-node/index.js | 7 +-
lib/less-node/url-import.js | 52 +++++++
lib/less/contexts.js | 3 +-
lib/less/environment/abstract-file-manager.js | 111 ++++++++++++++
lib/less/environment/api.js | 23 +++
lib/less/environment/environment.js | 36 +++++
lib/less/functions/data-uri.js | 37 ++---
lib/less/imports.js | 92 ++++++------
lib/less/index.js | 12 +-
lib/less/parser/parser.js | 1 -
lib/less/source-map-builder.js | 4 +-
lib/less/tree/import.js | 1 -
18 files changed, 551 insertions(+), 496 deletions(-)
diff --git a/bin/lessc b/bin/lessc
index c170d00..afb75c3 100755
--- a/bin/lessc
+++ b/bin/lessc
@@ -281,7 +281,6 @@ if (options.sourceMap) {
// 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));
- console.log(sourceMapOptions.sourceMapFile);
}
}
diff --git a/lib/less-browser/browser-import.js b/lib/less-browser/browser-import.js
new file mode 100644
index 0000000..5a5ec3f
--- /dev/null
+++ b/lib/less-browser/browser-import.js
@@ -0,0 +1,137 @@
+/*global window, XMLHttpRequest */
+
+module.exports = function(options, isFileProtocol, log, logLevel) {
+
+var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise,
+ AbstractFileManager = require("../less/environment/abstract-file-manager.js");
+
+var fileCache = {};
+
+//TODOS - move log somewhere. pathDiff and doing something similar in node. use pathDiff in the other browser file for the initial load
+// isFileProtocol is global
+
+function getXMLHttpRequest() {
+ if (window.XMLHttpRequest && (window.location.protocol !== "file:" || !("ActiveXObject" in window))) {
+ return new XMLHttpRequest();
+ } else {
+ try {
+ /*global ActiveXObject */
+ return new ActiveXObject("Microsoft.XMLHTTP");
+ } catch (e) {
+ log("browser doesn't support AJAX.", logLevel.errors);
+ return null;
+ }
+ }
+};
+
+var BrowserImport = function() {
+};
+
+BrowserImport.prototype = new AbstractFileManager();
+
+BrowserImport.prototype.alwaysMakePathsAbsolute = function alwaysMakePathsAbsolute() {
+ return true;
+};
+BrowserImport.prototype.join = function (url, baseUrl) {
+ // diff between two paths to create a relative path
+
+ var urlParts = this.extractUrlParts(url),
+ baseUrlParts = this.extractUrlParts(baseUrl),
+ i, max, urlDirectories, baseUrlDirectories, diff = "";
+ if (urlParts.hostPart !== baseUrlParts.hostPart) {
+ return "";
+ }
+ max = Math.max(baseUrlParts.directories.length, urlParts.directories.length);
+ for(i = 0; i < max; i++) {
+ if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; }
+ }
+ baseUrlDirectories = baseUrlParts.directories.slice(i);
+ urlDirectories = urlParts.directories.slice(i);
+ for(i = 0; i < baseUrlDirectories.length-1; i++) {
+ diff += "../";
+ }
+ for(i = 0; i < urlDirectories.length-1; i++) {
+ diff += urlDirectories[i] + "/";
+ }
+ return diff;
+};
+BrowserImport.prototype.doXHR = function doXHR(url, type, callback, errback) {
+
+ var xhr = getXMLHttpRequest();
+ var async = isFileProtocol ? options.fileAsync : options.async;
+
+ if (typeof(xhr.overrideMimeType) === 'function') {
+ xhr.overrideMimeType('text/css');
+ }
+ log("XHR: Getting '" + url + "'", logLevel.debug);
+ xhr.open('GET', url, async);
+ xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5');
+ xhr.send(null);
+
+ function handleResponse(xhr, callback, errback) {
+ if (xhr.status >= 200 && xhr.status < 300) {
+ callback(xhr.responseText,
+ xhr.getResponseHeader("Last-Modified"));
+ } else if (typeof(errback) === 'function') {
+ errback(xhr.status, url);
+ }
+ }
+
+ if (isFileProtocol && !options.fileAsync) {
+ if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) {
+ callback(xhr.responseText);
+ } else {
+ errback(xhr.status, url);
+ }
+ } else if (async) {
+ xhr.onreadystatechange = function () {
+ if (xhr.readyState == 4) {
+ handleResponse(xhr, callback, errback);
+ }
+ };
+ } else {
+ handleResponse(xhr, callback, errback);
+ }
+};
+BrowserImport.prototype.supports = function(filename, currentDirectory, options, environment) {
+ return true;
+};
+
+BrowserImport.prototype.loadFile = function loadFile(filename, currentDirectory, options, environment) {
+ return new PromiseConstructor(function(fullfill, reject) {
+ if (currentDirectory && !this.isPathAbsolute(filename)) {
+ filename = currentDirectory + filename;
+ }
+
+ options = options || {};
+
+ // sheet may be set to the stylesheet for the initial load or a collection of properties including
+ // some env variables for imports
+ var hrefParts = this.extractUrlParts(filename, window.location.href);
+ var href = hrefParts.url;
+
+ if (options.useFileCache && fileCache[href]) {
+ try {
+ var lessText = fileCache[href];
+ fullfill({ content: lessText, filename: href, webInfo: { lastModified: new Date() }});
+ } catch (e) {
+ reject({filename: href, message: "Error loading file " + href + " error was " + e.message});
+ }
+ return;
+ }
+
+ this.doXHR(href, options.mime, function doXHRCallback(data, lastModified) {
+ // per file cache
+ fileCache[href] = data;
+
+ // Use remote copy (re-parse)
+ fullfill({ content: data, filename: href, webInfo: { lastModified: lastModified }});
+ }, function doXHRError(status, url) {
+ reject({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")", filename: href });
+ });
+ }.bind(this));
+};
+
+
+return new BrowserImport();
+};
diff --git a/lib/less-browser/environment.js b/lib/less-browser/environment.js
index 5f65bd4..6adc7d6 100644
--- a/lib/less-browser/environment.js
+++ b/lib/less-browser/environment.js
@@ -1,206 +1,5 @@
-/*global window, XMLHttpRequest */
-
-module.exports = function(options, isFileProtocol, log, logLevel) {
-
-var fileCache = {};
-
-//TODOS - move log somewhere. pathDiff and doing something similar in node. use pathDiff in the other browser file for the initial load
-// isFileProtocol is global
-
-function getXMLHttpRequest() {
- if (window.XMLHttpRequest && (window.location.protocol !== "file:" || !("ActiveXObject" in window))) {
- return new XMLHttpRequest();
- } else {
- try {
- /*global ActiveXObject */
- return new ActiveXObject("Microsoft.XMLHTTP");
- } catch (e) {
- log("browser doesn't support AJAX.", logLevel.errors);
- return null;
- }
- }
-}
-
-return {
- // make generic but overriddable
+module.exports = {
warn: function warn(msg) {
console.warn(msg);
- },
- // make generic but overriddable
- getPath: function getPath(filename) {
- var j = filename.lastIndexOf('/');
- if (j < 0) {
- j = filename.lastIndexOf('\\');
- }
- if (j < 0) {
- return "";
- }
- return filename.slice(0, j + 1);
- },
- // make generic but overriddable
- isPathAbsolute: function isPathAbsolute(filename) {
- return /^(?:[a-z-]+:|\/|\\)/i.test(filename);
- },
- alwaysMakePathsAbsolute: function alwaysMakePathsAbsolute() {
- return true;
- },
- supportsDataURI: function() {
- return false;
- },
- pathDiff: function pathDiff(url, baseUrl) {
- // diff between two paths to create a relative path
-
- var urlParts = this.extractUrlParts(url),
- baseUrlParts = this.extractUrlParts(baseUrl),
- i, max, urlDirectories, baseUrlDirectories, diff = "";
- if (urlParts.hostPart !== baseUrlParts.hostPart) {
- return "";
- }
- max = Math.max(baseUrlParts.directories.length, urlParts.directories.length);
- for(i = 0; i < max; i++) {
- if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; }
- }
- baseUrlDirectories = baseUrlParts.directories.slice(i);
- urlDirectories = urlParts.directories.slice(i);
- for(i = 0; i < baseUrlDirectories.length-1; i++) {
- diff += "../";
- }
- for(i = 0; i < urlDirectories.length-1; i++) {
- diff += urlDirectories[i] + "/";
- }
- return diff;
- },
- join: function join(basePath, laterPath) {
- if (!basePath) {
- return laterPath;
- }
- return this.extractUrlParts(laterPath, basePath).path;
- },
- // helper function, not part of API
- extractUrlParts: function extractUrlParts(url, baseUrl) {
- // urlParts[1] = protocol&hostname || /
- // urlParts[2] = / if path relative to host base
- // urlParts[3] = directories
- // urlParts[4] = filename
- // urlParts[5] = parameters
-
- var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i,
- urlParts = url.match(urlPartsRegex),
- returner = {}, directories = [], i, baseUrlParts;
-
- if (!urlParts) {
- throw new Error("Could not parse sheet href - '"+url+"'");
- }
-
- // Stylesheets in IE don't always return the full path
- if (!urlParts[1] || urlParts[2]) {
- baseUrlParts = baseUrl.match(urlPartsRegex);
- if (!baseUrlParts) {
- throw new Error("Could not parse page url - '"+baseUrl+"'");
- }
- urlParts[1] = urlParts[1] || baseUrlParts[1] || "";
- if (!urlParts[2]) {
- urlParts[3] = baseUrlParts[3] + urlParts[3];
- }
- }
-
- if (urlParts[3]) {
- directories = urlParts[3].replace(/\\/g, "/").split("/");
-
- // extract out . before .. so .. doesn't absorb a non-directory
- for(i = 0; i < directories.length; i++) {
- if (directories[i] === ".") {
- directories.splice(i, 1);
- i -= 1;
- }
- }
-
- for(i = 0; i < directories.length; i++) {
- if (directories[i] === ".." && i > 0) {
- directories.splice(i-1, 2);
- i -= 2;
- }
- }
- }
-
- returner.hostPart = urlParts[1];
- returner.directories = directories;
- returner.path = urlParts[1] + directories.join("/");
- returner.fileUrl = returner.path + (urlParts[4] || "");
- returner.url = returner.fileUrl + (urlParts[5] || "");
- return returner;
- },
- doXHR: function doXHR(url, type, callback, errback) {
-
- var xhr = getXMLHttpRequest();
- var async = isFileProtocol ? options.fileAsync : options.async;
-
- if (typeof(xhr.overrideMimeType) === 'function') {
- xhr.overrideMimeType('text/css');
- }
- log("XHR: Getting '" + url + "'", logLevel.debug);
- xhr.open('GET', url, async);
- xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5');
- xhr.send(null);
-
- function handleResponse(xhr, callback, errback) {
- if (xhr.status >= 200 && xhr.status < 300) {
- callback(xhr.responseText,
- xhr.getResponseHeader("Last-Modified"));
- } else if (typeof(errback) === 'function') {
- errback(xhr.status, url);
- }
- }
-
- if (isFileProtocol && !options.fileAsync) {
- if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) {
- callback(xhr.responseText);
- } else {
- errback(xhr.status, url);
- }
- } else if (async) {
- xhr.onreadystatechange = function () {
- if (xhr.readyState == 4) {
- handleResponse(xhr, callback, errback);
- }
- };
- } else {
- handleResponse(xhr, callback, errback);
- }
- },
- loadFile: function loadFile(filename, currentDirectory, options, callback) {
- if (currentDirectory && !this.isPathAbsolute(filename)) {
- filename = currentDirectory + filename;
- }
-
- options = options || {};
-
- // sheet may be set to the stylesheet for the initial load or a collection of properties including
- // some env variables for imports
- var hrefParts = this.extractUrlParts(filename, window.location.href);
- var href = hrefParts.url;
-
- if (options.useFileCache && fileCache[href]) {
- try {
- var lessText = fileCache[href];
- callback(null, lessText, href, { lastModified: new Date() });
- } catch (e) {
- callback(e, null, href);
- }
- return;
- }
-
- this.doXHR(href, options.mime, function doXHRCallback(data, lastModified) {
- // per file cache
- fileCache[href] = data;
-
- // Use remote copy (re-parse)
- callback(null, data, href, { lastModified: lastModified });
- }, function doXHRError(status, url) {
- callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, href);
- });
-
}
};
-
-};
diff --git a/lib/less-browser/index.js b/lib/less-browser/index.js
index 59d68bb..8736884 100644
--- a/lib/less-browser/index.js
+++ b/lib/less-browser/index.js
@@ -29,9 +29,10 @@ function log(str, level) {
var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol),
options = window.less || {},
- environment = require("./environment")(options, isFileProtocol, log, logLevel);
+ environment = require("./environment"),
+ browserImport = require("./browser-import")(options, isFileProtocol, log, logLevel);
-window.less = less = require('../less')(environment);
+window.less = less = require('../less')(environment, [browserImport]);
less.env = options.env || (location.hostname == '127.0.0.1' ||
location.hostname == '0.0.0.0' ||
@@ -41,6 +42,10 @@ less.env = options.env || (location.hostname == '127.0.0.1' ||
isFileProtocol ? 'development'
: 'production');
+if (!options.hasOwnProperty('disableDataURIs')) {
+ options.disableDataURIs = true;
+}
+
// The amount of logging in the javascript console.
// 3 - Debug, information and errors
// 2 - Information and errors
@@ -377,7 +382,12 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) {
instanceOptions.useFileCache = true;
}
- less.environment.loadFile(sheet.href, null, instanceOptions, function loadInitialFileCallback(e, data, path, webInfo) {
+ browserImport.loadFile(sheet.href, null, instanceOptions, environment)
+ .then(function loadInitialFileCallback(loadedFile) {
+
+ var data = loadedFile.contents,
+ path = loadedFile.filename,
+ webInfo = loadedFile.webInfo;
var newFileInfo = {
currentDirectory: less.environment.getPath(path),
@@ -408,18 +418,18 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) {
//TODO add tests around how this behaves when reloading
removeError(path);
- if (data) {
- instanceOptions.rootFileInfo = newFileInfo;
- less.render(data, instanceOptions)
- .then(function(result) {
- callback(null, result.css, data, sheet, webInfo, path);
- },
- function(e) {
- callback(e, null, null, sheet);
- });
- } else {
- callback(e, null, null, sheet, webInfo, path);
- }
+ instanceOptions.rootFileInfo = newFileInfo;
+ less.render(data, instanceOptions)
+ .then(function(result) {
+ callback(null, result.css, data, sheet, webInfo, path);
+ },
+ function(e) {
+ e.href = path;
+ callback(e);
+ });
+ },
+ function(e) {
+ callback(e);
});
}
@@ -435,7 +445,7 @@ function initRunningMode(){
if (less.watchMode) {
loadStyleSheets(function (e, css, _, sheet, env) {
if (e) {
- error(e, sheet.href);
+ error(e, e.href || sheet.href);
} else if (css) {
css = postProcessCSS(css);
createCSS(css, sheet, env.lastModified);
@@ -498,7 +508,7 @@ less.refresh = function (reload, modifyVars) {
loadStyleSheets(function (e, css, _, sheet, webInfo) {
if (e) {
- return error(e, sheet.href);
+ return error(e, e.href || sheet.href);
}
if (webInfo.local) {
log("loading " + sheet.href + " from cache.", logLevel.info);
diff --git a/lib/less-node/environment.js b/lib/less-node/environment.js
index f4abb06..63b564b 100644
--- a/lib/less-node/environment.js
+++ b/lib/less-node/environment.js
@@ -1,9 +1,3 @@
-var path = require('path'),
- url = require('url'),
- request,
- fs = require('./fs'),
- isUrlRe = /^(?:https?:)?\/\//i;
-
module.exports = {
warn: function(msg) {
console.warn(msg);
@@ -11,211 +5,13 @@ module.exports = {
encodeBase64: function encodeBase64(str) {
return new Buffer(str).toString('base64');
},
- supportsDataURI: function() {
- return true;
- },
mimeLookup: function (filename) {
return require('mime').lookup(filename);
},
charsetLookup: function (mime) {
return require('mime').charsets.lookup(mime);
},
- readFileSync: function (filename) {
- return require("fs").readFileSync(filename);
- },
- getPath: function (filename) {
- var j = filename.lastIndexOf('/');
- if (j < 0) {
- j = filename.lastIndexOf('\\');
- }
- if (j < 0) {
- return "";
- }
- return filename.slice(0, j + 1);
- },
- isPathAbsolute: function(filename) {
- return (/^(?:[a-z-]+:|\/|\\)/i).test(filename);
- },
- getAbsolutePath: function getAbsolutePath(filename) {
- return require('path').resolve(filename);
- },
getSourceMapGenerator: function getSourceMapGenerator() {
return require("source-map").SourceMapGenerator;
- },
- alwaysMakePathsAbsolute: function alwaysMakePathsAbsolute() {
- return false;
- },
- join: function join(basePath, laterPath) {
- if (!basePath) {
- return laterPath;
- }
- return basePath + laterPath;
- },
- pathDiff: function pathDiff(url, baseUrl) {
- // diff between two paths to create a relative path
-
- var urlParts = this.extractUrlParts(url),
- baseUrlParts = this.extractUrlParts(baseUrl),
- i, max, urlDirectories, baseUrlDirectories, diff = "";
- if (urlParts.hostPart !== baseUrlParts.hostPart) {
- return "";
- }
- max = Math.max(baseUrlParts.directories.length, urlParts.directories.length);
- for(i = 0; i < max; i++) {
- if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; }
- }
- baseUrlDirectories = baseUrlParts.directories.slice(i);
- urlDirectories = urlParts.directories.slice(i);
- for(i = 0; i < baseUrlDirectories.length-1; i++) {
- diff += "../";
- }
- for(i = 0; i < urlDirectories.length-1; i++) {
- diff += urlDirectories[i] + "/";
- }
- return diff;
- },
- // helper function, not part of API
- extractUrlParts: function extractUrlParts(url, baseUrl) {
- // urlParts[1] = protocol&hostname || /
- // urlParts[2] = / if path relative to host base
- // urlParts[3] = directories
- // urlParts[4] = filename
- // urlParts[5] = parameters
-
- var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i,
- urlParts = url.match(urlPartsRegex),
- returner = {}, directories = [], i, baseUrlParts;
-
- if (!urlParts) {
- throw new Error("Could not parse sheet href - '"+url+"'");
- }
-
- // Stylesheets in IE don't always return the full path
- if (baseUrl && (!urlParts[1] || urlParts[2])) {
- baseUrlParts = baseUrl.match(urlPartsRegex);
- if (!baseUrlParts) {
- throw new Error("Could not parse page url - '"+baseUrl+"'");
- }
- urlParts[1] = urlParts[1] || baseUrlParts[1] || "";
- if (!urlParts[2]) {
- urlParts[3] = baseUrlParts[3] + urlParts[3];
- }
- }
-
- if (urlParts[3]) {
- directories = urlParts[3].replace(/\\/g, "/").split("/");
-
- // extract out . before .. so .. doesn't absorb a non-directory
- for(i = 0; i < directories.length; i++) {
- if (directories[i] === ".") {
- directories.splice(i, 1);
- i -= 1;
- }
- }
-
- for(i = 0; i < directories.length; i++) {
- if (directories[i] === ".." && i > 0) {
- directories.splice(i-1, 2);
- i -= 2;
- }
- }
- }
-
- returner.hostPart = urlParts[1];
- returner.directories = directories;
- returner.path = urlParts[1] + directories.join("/");
- returner.fileUrl = returner.path + (urlParts[4] || "");
- returner.url = returner.fileUrl + (urlParts[5] || "");
- return returner;
- },
- // consider if we really want this in the environment or whether all or some of this should be moved to (for node) built in import manager plugins?
- loadFile: function(filename, currentDirectory, options, callback) {
- var fullFilename,
- data,
- isUrl = isUrlRe.test( filename );
-
- options = options || {};
-
- if (isUrl || isUrlRe.test(currentDirectory)) {
- if (request === undefined) {
- try { request = require('request'); }
- catch(e) { request = null; }
- }
- if (!request) {
- callback({ type: 'File', message: "optional dependency 'request' required to import over http(s)\n" });
- return;
- }
-
- var urlStr = isUrl ? filename : url.resolve(currentDirectory, filename),
- urlObj = url.parse(urlStr);
-
- if (!urlObj.protocol) {
- urlObj.protocol = "http";
- urlStr = urlObj.format();
- }
-
- request.get({uri: urlStr, strictSSL: !options.insecure }, function (error, res, body) {
- if (error) {
- callback({ type: 'File', message: "resource '" + urlStr + "' gave this Error:\n "+ error +"\n" });
- }
- if (res && res.statusCode === 404) {
- callback({ type: 'File', message: "resource '" + urlStr + "' was not found\n" });
- return;
- }
- if (!body) {
- this.warn('Warning: Empty body (HTTP '+ res.statusCode + ') returned by "' + urlStr +'"');
- }
- fullFilename = urlStr;
- callback(null, body, fullFilename);
- });
- } else {
-
- var paths = [currentDirectory];
- if (options.paths) paths.push.apply(paths, options.paths);
- if (paths.indexOf('.') === -1) paths.push('.');
-
- if (options.syncImport) {
- for (var i = 0; i < paths.length; i++) {
- try {
- fullFilename = path.join(paths[i], filename);
- fs.statSync(fullFilename);
- break;
- } catch (e) {
- fullFilename = null;
- }
- }
-
- if (!fullFilename) {
- callback({ type: 'File', message: "'" + filename + "' wasn't found" });
- return;
- }
-
- data = fs.readFileSync(fullFilename, 'utf-8');
- callback(null, data, fullFilename);
- } else {
- (function tryPathIndex(i) {
- if (i < paths.length) {
- fullFilename = path.join(paths[i], filename);
- fs.stat(fullFilename, function (err) {
- if (err) {
- tryPathIndex(i + 1);
- } else {
- fs.readFile(fullFilename, 'utf-8', function(e, data) {
- if (e) { callback(e); }
-
- // do processing in the next tick to allow
- // file handling to dispose
- process.nextTick(function() {
- callback(null, data, fullFilename);
- });
- });
- }
- });
- } else {
- callback({ type: 'File', message: "'" + filename + "' wasn't found" });
- }
- }(0));
- }
- }
}
};
diff --git a/lib/less-node/file-import.js b/lib/less-node/file-import.js
new file mode 100644
index 0000000..84734b7
--- /dev/null
+++ b/lib/less-node/file-import.js
@@ -0,0 +1,79 @@
+var path = require('path'),
+ fs = require('./fs'),
+ PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise,
+ AbstractFileManager = require("../less/environment/abstract-file-manager.js");
+
+var FileImport = function() {
+};
+
+FileImport.prototype = new AbstractFileManager();
+
+FileImport.prototype.supports = function(filename, currentDirectory, options, environment) {
+ return true;
+};
+FileImport.prototype.supportsSync = function(filename, currentDirectory, options, environment) {
+ return true;
+};
+
+FileImport.prototype.loadFile = function(filename, currentDirectory, options, environment) {
+ return new PromiseConstructor(function(fullfill, reject) {
+ var fullFilename,
+ data;
+
+ options = options || {};
+
+ var paths = [currentDirectory];
+ if (options.paths) paths.push.apply(paths, options.paths);
+ if (paths.indexOf('.') === -1) paths.push('.');
+
+ if (options.syncImport) {
+ for (var i = 0; i < paths.length; i++) {
+ try {
+ fullFilename = path.join(paths[i], filename);
+ fs.statSync(fullFilename);
+ break;
+ } catch (e) {
+ fullFilename = null;
+ }
+ }
+
+ if (!fullFilename) {
+ reject({ type: 'File', message: "'" + filename + "' wasn't found" });
+ return;
+ }
+
+ data = fs.readFileSync(fullFilename, 'utf-8');
+ fullfill({ contents: data, filename: fullFilename});
+ } else {
+ (function tryPathIndex(i) {
+ if (i < paths.length) {
+ fullFilename = path.join(paths[i], filename);
+ fs.stat(fullFilename, function (err) {
+ if (err) {
+ tryPathIndex(i + 1);
+ } else {
+ fs.readFile(fullFilename, 'utf-8', function(e, data) {
+ if (e) { reject(e); return; }
+
+ // do processing in the next tick to allow
+ // file handling to dispose
+ process.nextTick(function() {
+ fullfill({ contents: data, filename: fullFilename});
+ });
+ });
+ }
+ });
+ } else {
+ reject({ type: 'File', message: "'" + filename + "' wasn't found" });
+ }
+ }(0));
+ }
+ });
+};
+
+FileImport.prototype.loadFileSync = function(filename, currentDirectory, options, environment) {
+ filename = path.join(currentDirectory, filename);
+ return { contents: fs.readFileSync(filename), filename: filename };
+};
+
+module.exports = new FileImport();
diff --git a/lib/less-node/index.js b/lib/less-node/index.js
index 63f2ded..8392690 100644
--- a/lib/less-node/index.js
+++ b/lib/less-node/index.js
@@ -1,6 +1,8 @@
var environment = require("./environment"),
+ fileImport = require("./file-import.js"),
+ urlImport = require("./url-import.js"),
createFromEnvironment = require("../less"),
- less = createFromEnvironment(environment),
+ less = createFromEnvironment(environment, [fileImport, urlImport]),
lesscHelper = require('./lessc-helper');
// allow people to create less with their own environment
@@ -8,7 +10,8 @@ less.createFromEnvironment = createFromEnvironment;
less.lesscHelper = lesscHelper;
less.PluginManager = require("./node-plugin-manager");
less.fs = require("./fs");
-less.environment = environment;
+less.fileImport = require("./file-import.js");
+less.urlImport = require("./url-import.js");
less.formatError = function(ctx, options) {
options = options || {};
diff --git a/lib/less-node/url-import.js b/lib/less-node/url-import.js
new file mode 100644
index 0000000..7d5aa46
--- /dev/null
+++ b/lib/less-node/url-import.js
@@ -0,0 +1,52 @@
+var isUrlRe = /^(?:https?:)?\/\//i,
+ url = require('url'),
+ request,
+ PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise,
+ AbstractFileManager = require("../less/environment/abstract-file-manager.js");
+
+var UrlImport = function() {
+};
+
+UrlImport.prototype = new AbstractFileManager();
+
+UrlImport.prototype.supports = function(filename, currentDirectory, options, environment) {
+ return isUrlRe.test( filename ) || isUrlRe.test(currentDirectory);
+};
+
+UrlImport.prototype.loadFile = function(filename, currentDirectory, options, environment) {
+ return new PromiseConstructor(function(fullfill, reject) {
+ if (request === undefined) {
+ try { request = require('request'); }
+ catch(e) { request = null; }
+ }
+ if (!request) {
+ reject({ type: 'File', message: "optional dependency 'request' required to import over http(s)\n" });
+ return;
+ }
+
+ var urlStr = isUrlRe.test( filename ) ? filename : url.resolve(currentDirectory, filename),
+ urlObj = url.parse(urlStr);
+
+ if (!urlObj.protocol) {
+ urlObj.protocol = "http";
+ urlStr = urlObj.format();
+ }
+
+ 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" });
+ return;
+ }
+ if (res && res.statusCode === 404) {
+ reject({ type: 'File', message: "resource '" + urlStr + "' was not found\n" });
+ return;
+ }
+ if (!body) {
+ environment.warn('Warning: Empty body (HTTP '+ res.statusCode + ') returned by "' + urlStr +'"');
+ }
+ fullfill({ contents: body, filename: urlStr });
+ });
+ });
+};
+
+module.exports = new UrlImport();
diff --git a/lib/less/contexts.js b/lib/less/contexts.js
index 467cfe6..b486535 100644
--- a/lib/less/contexts.js
+++ b/lib/less/contexts.js
@@ -49,7 +49,8 @@ var evalCopyProperties = [
'sourceMap', // whether to output a source map
'importMultiple', // whether we are currently importing multiple copies
'urlArgs', // whether to add args into url tokens
- 'javascriptEnabled'// option - whether JavaScript is enabled. if undefined, defaults to true
+ 'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true
+ 'disableDataURIs' // option - disable data URIs (they will be converted to URL())
];
contexts.evalEnv = function(options, frames) {
diff --git a/lib/less/environment/abstract-file-manager.js b/lib/less/environment/abstract-file-manager.js
new file mode 100644
index 0000000..1ca31b2
--- /dev/null
+++ b/lib/less/environment/abstract-file-manager.js
@@ -0,0 +1,111 @@
+var abstractFileManager = function() {
+};
+
+abstractFileManager.prototype.getPath = function (filename) {
+ var j = filename.lastIndexOf('/');
+ if (j < 0) {
+ j = filename.lastIndexOf('\\');
+ }
+ if (j < 0) {
+ return "";
+ }
+ return filename.slice(0, j + 1);
+};
+
+abstractFileManager.prototype.supportsSync = function() {
+ return false;
+};
+
+abstractFileManager.prototype.alwaysMakePathsAbsolute = function() {
+ return false;
+};
+
+abstractFileManager.prototype.isPathAbsolute = function(filename) {
+ return (/^(?:[a-z-]+:|\/|\\)/i).test(filename);
+};
+
+abstractFileManager.prototype.join = function(basePath, laterPath) {
+ if (!basePath) {
+ return laterPath;
+ }
+ return basePath + laterPath;
+};
+abstractFileManager.prototype.pathDiff = function pathDiff(url, baseUrl) {
+ // diff between two paths to create a relative path
+
+ var urlParts = this.extractUrlParts(url),
+ baseUrlParts = this.extractUrlParts(baseUrl),
+ i, max, urlDirectories, baseUrlDirectories, diff = "";
+ if (urlParts.hostPart !== baseUrlParts.hostPart) {
+ return "";
+ }
+ max = Math.max(baseUrlParts.directories.length, urlParts.directories.length);
+ for(i = 0; i < max; i++) {
+ if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; }
+ }
+ baseUrlDirectories = baseUrlParts.directories.slice(i);
+ urlDirectories = urlParts.directories.slice(i);
+ for(i = 0; i < baseUrlDirectories.length-1; i++) {
+ diff += "../";
+ }
+ for(i = 0; i < urlDirectories.length-1; i++) {
+ diff += urlDirectories[i] + "/";
+ }
+ return diff;
+};
+// helper function, not part of API
+abstractFileManager.prototype.extractUrlParts = function extractUrlParts(url, baseUrl) {
+ // urlParts[1] = protocol&hostname || /
+ // urlParts[2] = / if path relative to host base
+ // urlParts[3] = directories
+ // urlParts[4] = filename
+ // urlParts[5] = parameters
+
+ var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i,
+ urlParts = url.match(urlPartsRegex),
+ returner = {}, directories = [], i, baseUrlParts;
+
+ if (!urlParts) {
+ throw new Error("Could not parse sheet href - '" + url + "'");
+ }
+
+ // Stylesheets in IE don't always return the full path
+ if (!urlParts[1] || urlParts[2]) {
+ baseUrlParts = baseUrl.match(urlPartsRegex);
+ if (!baseUrlParts) {
+ throw new Error("Could not parse page url - '"+baseUrl+"'");
+ }
+ urlParts[1] = urlParts[1] || baseUrlParts[1] || "";
+ if (!urlParts[2]) {
+ urlParts[3] = baseUrlParts[3] + urlParts[3];
+ }
+ }
+
+ if (urlParts[3]) {
+ directories = urlParts[3].replace(/\\/g, "/").split("/");
+
+ // extract out . before .. so .. doesn't absorb a non-directory
+ for(i = 0; i < directories.length; i++) {
+ if (directories[i] === ".") {
+ directories.splice(i, 1);
+ i -= 1;
+ }
+ }
+
+ for(i = 0; i < directories.length; i++) {
+ if (directories[i] === ".." && i > 0) {
+ directories.splice(i-1, 2);
+ i -= 2;
+ }
+ }
+ }
+
+ returner.hostPart = urlParts[1];
+ returner.directories = directories;
+ returner.path = urlParts[1] + directories.join("/");
+ returner.fileUrl = returner.path + (urlParts[4] || "");
+ returner.url = returner.fileUrl + (urlParts[5] || "");
+ return returner;
+};
+
+module.exports = abstractFileManager;
diff --git a/lib/less/environment/api.js b/lib/less/environment/api.js
index 90a71fc..bd72a50 100644
--- a/lib/less/environment/api.js
+++ b/lib/less/environment/api.js
@@ -6,6 +6,29 @@ module.exports = {
warn: function(msg) {
},
/**
+ * Converts a string to a base 64 string
+ * @param str
+ */
+ encodeBase64: function(str) {
+ },
+ /**
+ * Lookup the mime-type of a filename
+ * @param filename
+ */
+ mimeLookup: function (filename) {
+ },
+ /**
+ * Look up the charset of a mime type
+ * @param mime
+ */
+ charsetLookup: function (mime) {
+ },
+ /**
+ *
+ */
+ getSourceMapGenerator: function getSourceMapGenerator() {
+ },
+ /**
* gets the path from the filename, e.g. "http://wwe.files.com/ha/ha.less" would return
* "http://wwe.files.com/ha/"
* If the filename is a file e.g. "file.less" it should return the empty string ""
diff --git a/lib/less/environment/environment.js b/lib/less/environment/environment.js
new file mode 100644
index 0000000..454bcaa
--- /dev/null
+++ b/lib/less/environment/environment.js
@@ -0,0 +1,36 @@
+var environment = function(externalEnvironment, fileManagers) {
+ this.fileManagers = fileManagers || [];
+ var functions = ["warn", "encodeBase64", "mimeLookup", "charsetLookup", "getSourceMapGenerator"],
+ emptyFunc = function() {};
+
+ for(var i = 0; i < functions.length; i++) {
+ var propName = functions[i],
+ environmentFunc = externalEnvironment[propName];
+ if (environmentFunc) {
+ this[propName] = environmentFunc.bind(externalEnvironment);
+ } else {
+ this[propName] = emptyFunc;
+ this.warn("missing function in environment - " + propName);
+ }
+ }
+};
+
+environment.prototype.getFileManager = function (filename, currentDirectory, options, environment, isSync) {
+ for(var i = this.fileManagers.length - 1; i >= 0 ; i--) {
+ var fileManager = this.fileManagers[i];
+ if (fileManager[isSync ? "supports" : "supportsSync"](filename, currentDirectory, options, environment)) {
+ return fileManager;
+ }
+ }
+ return null;
+};
+
+environment.prototype.addFileManager = function (fileManager) {
+ this.fileManagers.push(fileManager);
+};
+
+environment.prototype.clearFileManagers = function () {
+ this.fileManagers = [];
+};
+
+module.exports = environment;
diff --git a/lib/less/functions/data-uri.js b/lib/less/functions/data-uri.js
index 8a4ee5e..76d44c1 100644
--- a/lib/less/functions/data-uri.js
+++ b/lib/less/functions/data-uri.js
@@ -1,17 +1,22 @@
module.exports = function(environment) {
var Anonymous = require("../tree/anonymous"),
URL = require("../tree/url"),
- functionRegistry = require("./function-registry");
+ functionRegistry = require("./function-registry"),
+ fallback = function(functionContext, node) {
+ return new URL(node, functionContext.index, functionContext.currentFileInfo).eval(functionContext.env);
+ };
functionRegistry.add("data-uri", function(mimetypeNode, filePathNode) {
- if (!environment.supportsDataURI()) {
- return new URL(filePathNode || mimetypeNode, this.index, this.currentFileInfo).eval(this.env);
- }
-
var mimetype = mimetypeNode.value;
var filePath = (filePathNode && filePathNode.value);
+ var fileManager = environment.getFileManager(filePath, this.env.currentFileInfo, this.env, environment, true);
+
+ if (!fileManager) {
+ return fallback(this, filePathNode || mimetypeNode);
+ }
+
var useBase64 = false;
if (arguments.length < 2) {
@@ -25,13 +30,8 @@ module.exports = function(environment) {
filePath = filePath.slice(0, fragmentStart);
}
- if (this.env.isPathRelative(filePath)) {
- if (this.currentFileInfo.relativeUrls) {
- filePath = environment.join(this.currentFileInfo.currentDirectory, filePath);
- } else {
- filePath = environment.join(this.currentFileInfo.entryPath, filePath);
- }
- }
+ var currentDirectory = this.currentFileInfo.relativeUrls ?
+ this.currentFileInfo.currentDirectory : this.currentFileInfo.entryPath;
// detect the mimetype if not given
if (arguments.length < 2) {
@@ -47,7 +47,12 @@ module.exports = function(environment) {
useBase64 = /;base64$/.test(mimetype);
}
- var buf = environment.readFileSync(filePath);
+ var fileSync = fileManager.loadFileSync(filePath, currentDirectory, this.env, environment);
+ if (!fileSync.contents) {
+ environment.warn("Skipped data-uri embedding because file not found");
+ return fallback(this, filePathNode || mimetypeNode);
+ }
+ var buf = fileSync.contents;
// IE8 cannot handle a data-uri larger than 32KB. If this is exceeded
// and the --ieCompat flag is enabled, return a normal url() instead.
@@ -56,11 +61,9 @@ module.exports = function(environment) {
if (fileSizeInKB >= DATA_URI_MAX_KB) {
if (this.env.ieCompat !== false) {
- if (!this.env.silent) {
- console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB);
- }
+ environment.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB);
- return new URL(filePathNode || mimetypeNode, this.index, this.currentFileInfo).eval(this.env);
+ return fallback(this, filePathNode || mimetypeNode);
}
}
diff --git a/lib/less/imports.js b/lib/less/imports.js
index e426c6d..a8299c5 100644
--- a/lib/less/imports.js
+++ b/lib/less/imports.js
@@ -24,11 +24,6 @@ module.exports = function(environment) {
this.queue = []; // Files which haven't been imported yet
this.files = []; // Holds the imported parse trees.
};
- ImportManager.prototype.getAbsolutePath = function(filename) {
- // proxy needed for "DebugInfo"
- // I hope one day we can remove this function
- return environment.getAbsolutePath(filename);
- };
ImportManager.prototype.push = function (path, currentFileInfo, importOptions, callback) {
var parserImports = this;
this.queue.push(path);
@@ -52,46 +47,55 @@ module.exports = function(environment) {
rootFilename: currentFileInfo.rootFilename
};
- environment.loadFile(path, currentFileInfo.currentDirectory, this.env, function loadFileCallback(e, contents, resolvedFilename) {
- if (e) {
- fileParsedFunc(e);
- return;
- }
-
- // Pass on an updated rootpath if path of imported file is relative and file
- // is in a (sub|sup) directory
- //
- // Examples:
- // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/',
- // then rootpath should become 'less/module/nav/'
- // - If path of imported file is '../mixins.less' and rootpath is 'less/',
- // then rootpath should become 'less/../'
- newFileInfo.currentDirectory = environment.getPath(resolvedFilename);
- if(newFileInfo.relativeUrls) {
- newFileInfo.rootpath = environment.join((parserImports.env.rootpath || ""), environment.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath));
- if (!environment.isPathAbsolute(newFileInfo.rootpath) && environment.alwaysMakePathsAbsolute()) {
- newFileInfo.rootpath = environment.join(newFileInfo.entryPath, newFileInfo.rootpath);
+ var fileManager = environment.getFileManager(path, currentFileInfo.currentDirectory, this.env, environment);
+
+ if (!fileManager) {
+ fileParsedFunc({ message: "Could not find a file-manager for " + path });
+ return;
+ }
+
+ fileManager.loadFile(path, currentFileInfo.currentDirectory, this.env, environment)
+ .then(function loadFileCallback(loadedFile) {
+ var resolvedFilename = loadedFile.filename,
+ contents = loadedFile.contents;
+
+ // Pass on an updated rootpath if path of imported file is relative and file
+ // is in a (sub|sup) directory
+ //
+ // Examples:
+ // - If path of imported file is 'module/nav/nav.less' and rootpath is 'less/',
+ // then rootpath should become 'less/module/nav/'
+ // - If path of imported file is '../mixins.less' and rootpath is 'less/',
+ // then rootpath should become 'less/../'
+ newFileInfo.currentDirectory = fileManager.getPath(resolvedFilename);
+ if(newFileInfo.relativeUrls) {
+ newFileInfo.rootpath = fileManager.join((parserImports.env.rootpath || ""), environment.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath));
+ if (!fileManager.isPathAbsolute(newFileInfo.rootpath) && fileManager.alwaysMakePathsAbsolute()) {
+ newFileInfo.rootpath = fileManager.join(newFileInfo.entryPath, newFileInfo.rootpath);
+ }
+ }
+ newFileInfo.filename = resolvedFilename;
+
+ var newEnv = new contexts.parseEnv(parserImports.env);
+
+ newEnv.processImports = false;
+ parserImports.contents[resolvedFilename] = contents;
+
+ if (currentFileInfo.reference || importOptions.reference) {
+ newFileInfo.reference = true;
+ }
+
+ if (importOptions.inline) {
+ fileParsedFunc(null, contents, resolvedFilename);
+ } else {
+ new Parser(newEnv, parserImports, newFileInfo).parse(contents, function (e, root) {
+ fileParsedFunc(e, root, resolvedFilename);
+ });
}
- }
- newFileInfo.filename = resolvedFilename;
-
- var newEnv = new contexts.parseEnv(parserImports.env);
-
- newEnv.processImports = false;
- parserImports.contents[resolvedFilename] = contents;
-
- if (currentFileInfo.reference || importOptions.reference) {
- newFileInfo.reference = true;
- }
-
- if (importOptions.inline) {
- fileParsedFunc(null, contents, resolvedFilename);
- } else {
- new Parser(newEnv, parserImports, newFileInfo).parse(contents, function (e, root) {
- fileParsedFunc(e, root, resolvedFilename);
- });
- }
- });
+ },
+ function(error) {
+ fileParsedFunc(error);
+ });
};
return ImportManager;
};
diff --git a/lib/less/index.js b/lib/less/index.js
index 5b1f5ce..e520b2c 100644
--- a/lib/less/index.js
+++ b/lib/less/index.js
@@ -1,17 +1,19 @@
-module.exports = function(environment) {
- var SourceMapOutput, SourceMapBuilder, ParseTree, ImportManager;
+module.exports = function(environment, fileManagers) {
+ var SourceMapOutput, SourceMapBuilder, ParseTree, ImportManager, Environment;
var less = {
version: [2, 0, 0],
data: require('./data'),
tree: require('./tree'),
+ Environment: (Environment = require("./environment/environment")),
+ AbstractFileManager: require("./environment/abstract-file-manager"),
+ environment: (environment = new Environment(environment, fileManagers)),
visitors: require('./visitors'),
Parser: require('./parser/parser'),
functions: require('./functions')(environment),
contexts: require("./contexts"),
- environment: environment,
- SourceMapOutput: (SourceMapOutput = require('./source-map-output.js')(environment)),
- SourceMapBuilder: (SourceMapBuilder = require('./source-map-builder.js')(SourceMapOutput)),
+ SourceMapOutput: (SourceMapOutput = require('./source-map-output')(environment)),
+ SourceMapBuilder: (SourceMapBuilder = require('./source-map-builder')(SourceMapOutput)),
ParseTree: (ParseTree = require('./parse-tree')(SourceMapBuilder)),
ImportManager: (ImportManager = require('./imports')(environment)),
render: require("./render")(environment, ParseTree, ImportManager),
diff --git a/lib/less/parser/parser.js b/lib/less/parser/parser.js
index 85792d3..43a6ba1 100644
--- a/lib/less/parser/parser.js
+++ b/lib/less/parser/parser.js
@@ -73,7 +73,6 @@ var Parser = function Parser(env, imports, fileInfo) {
function getDebugInfo(index) {
var filename = fileInfo.filename;
- filename = imports.getAbsolutePath(filename);
return {
lineNumber: utils.getLocation(index, parserInput.getInput()).line + 1,
diff --git a/lib/less/source-map-builder.js b/lib/less/source-map-builder.js
index 3cd382f..f212d11 100644
--- a/lib/less/source-map-builder.js
+++ b/lib/less/source-map-builder.js
@@ -23,7 +23,9 @@ module.exports = function (SourceMapOutput) {
var css = sourceMapOutput.toCSS(options);
this.sourceMap = sourceMapOutput.sourceMap;
this.sourceMapURL = sourceMapOutput.sourceMapURL;
- this.sourceMapInputFilename = sourceMapOutput.normalizeFilename(this.options.sourceMapInputFilename);
+ if (this.options.sourceMapInputFilename) {
+ this.sourceMapInputFilename = sourceMapOutput.normalizeFilename(this.options.sourceMapInputFilename);
+ }
return css;
};
diff --git a/lib/less/tree/import.js b/lib/less/tree/import.js
index e0ac6d8..9171990 100644
--- a/lib/less/tree/import.js
+++ b/lib/less/tree/import.js
@@ -111,7 +111,6 @@ Import.prototype.eval = function (env) {
}
if (this.options.inline) {
- //todo needs to reference css file not import
var contents = new(Anonymous)(this.root, 0, {filename: this.importedFilename}, true, true);
return this.features ? new(Media)([contents], this.features.value) : [contents];
} else if (this.css) {
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/less.js.git
More information about the Pkg-javascript-commits
mailing list