Files
less.js/lib/less-node/environment.js

221 lines
7.9 KiB
JavaScript

var path = require('path'),
url = require('url'),
request,
fs = require('./fs'),
isUrlRe = /^(?:https?:)?\/\//i;
module.exports = {
warn: function(msg) {
console.warn(msg);
},
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;
},
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));
}
}
}
};