mirror of
https://github.com/less/less.js.git
synced 2026-04-09 03:00:20 -04:00
import interpolation finished. refactored the import phase to occur only within the import visitor - so that the import eval env can be passed between imports.
This commit is contained in:
@@ -249,26 +249,32 @@ function loadStyleSheet(sheet, callback, reload, remaining) {
|
||||
var timestamp = cache && cache.getItem(href + ':timestamp');
|
||||
var styles = { css: css, timestamp: timestamp };
|
||||
var env;
|
||||
var newFileInfo = {
|
||||
relativeUrls: less.relativeUrls,
|
||||
currentDirectory: hrefParts.path,
|
||||
filename: href
|
||||
};
|
||||
|
||||
if (sheet instanceof less.tree.parseEnv) {
|
||||
env = new less.tree.parseEnv(sheet);
|
||||
newFileInfo.entryPath = env.currentFileInfo.entryPath;
|
||||
newFileInfo.rootpath = env.currentFileInfo.rootpath;
|
||||
newFileInfo.rootFilename = env.currentFileInfo.rootFilename;
|
||||
} else {
|
||||
env = new less.tree.parseEnv(less);
|
||||
env.entryPath = hrefParts.path;
|
||||
env.mime = sheet.type;
|
||||
newFileInfo.entryPath = hrefParts.path;
|
||||
newFileInfo.rootpath = less.rootpath || hrefParts.path;
|
||||
newFileInfo.rootFilename = href;
|
||||
}
|
||||
|
||||
if (env.relativeUrls) {
|
||||
//todo - this relies on option being set on less object rather than being passed in as an option
|
||||
// - need an originalRootpath
|
||||
if (less.rootpath) {
|
||||
env.rootpath = extractUrlParts(less.rootpath + pathDiff(hrefParts.path, env.entryPath)).path;
|
||||
newFileInfo.rootpath = extractUrlParts(less.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path;
|
||||
} else {
|
||||
env.rootpath = hrefParts.path;
|
||||
}
|
||||
} else {
|
||||
if (!less.rootpath) {
|
||||
env.rootpath = env.entryPath;
|
||||
newFileInfo.rootpath = hrefParts.path;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,8 +293,8 @@ function loadStyleSheet(sheet, callback, reload, remaining) {
|
||||
try {
|
||||
env.contents[href] = data; // Updating content cache
|
||||
env.paths = [hrefParts.path];
|
||||
env.filename = href;
|
||||
env.rootFilename = env.rootFilename || href;
|
||||
env.currentFileInfo = newFileInfo;
|
||||
|
||||
new(less.Parser)(env).parse(data, function (e, root) {
|
||||
if (e) { return callback(e, null, null, sheet); }
|
||||
try {
|
||||
@@ -296,7 +302,7 @@ function loadStyleSheet(sheet, callback, reload, remaining) {
|
||||
//TODO - there must be a better way? A generic less-to-css function that can both call error
|
||||
//and removeNode where appropriate
|
||||
//should also add tests
|
||||
if (env.rootFilename === href) {
|
||||
if (env.currentFileInfo.rootFilename === href) {
|
||||
removeNode(document.getElementById('less-error-message:' + extractId(href)));
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,28 +1,46 @@
|
||||
(function (tree) {
|
||||
|
||||
var parseCopyProperties = [
|
||||
'paths', // paths to search for imports on
|
||||
'paths', // option - unmodified - paths to search for imports on
|
||||
'optimization', // option - optimization level (for the chunker)
|
||||
'filename', // current filename, used for error reporting
|
||||
'files', // list of files that have been imported, used for import-once
|
||||
'contents', // browser-only, contents of all the files
|
||||
'rootpath', // current rootpath to append to all url's
|
||||
'relativeUrls', // option - whether to adjust URL's to be relative
|
||||
'strictImports', // option -
|
||||
'dumpLineNumbers', // option - whether to dump line numbers
|
||||
'compress', // option - whether to compress
|
||||
'processImports', // option - whether to process imports. if false then imports will not be imported
|
||||
'mime', // browser only - mime type for sheet import
|
||||
'entryPath', // browser only - path of entry less file
|
||||
'rootFilename', // browser only - href of the entry less file
|
||||
'currentDirectory' // node only - the current directory
|
||||
'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc.
|
||||
];
|
||||
|
||||
//currentFileInfo = {
|
||||
// 'relativeUrls' - option - whether to adjust URL's to be relative
|
||||
// 'filename' - full resolved filename of current file
|
||||
// 'rootpath' - path to append to normal URLs for this node
|
||||
// 'currentDirectory' - path to the current file, absolute
|
||||
// 'rootFilename' - filename of the base file
|
||||
// 'entryPath' = absolute path to the entry file
|
||||
|
||||
tree.parseEnv = function(options) {
|
||||
copyFromOriginal(options, this, parseCopyProperties);
|
||||
|
||||
if (!this.contents) { this.contents = {}; }
|
||||
if (!this.rootpath) { this.rootpath = ''; }
|
||||
if (!this.files) { this.files = {}; }
|
||||
|
||||
if (!this.currentFileInfo) {
|
||||
var filename = options.filename || "input";
|
||||
options.filename = null;
|
||||
var entryPath = filename.replace(/[^\/\\]*$/, "");
|
||||
this.currentFileInfo = {
|
||||
filename: filename,
|
||||
relativeUrls: this.relativeUrls,
|
||||
rootpath: options.rootpath || "",
|
||||
currentDirectory: entryPath,
|
||||
entryPath: entryPath,
|
||||
rootFilename: filename
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
tree.parseEnv.prototype.toSheet = function (path) {
|
||||
|
||||
@@ -376,7 +376,7 @@ tree.functions = {
|
||||
"data-uri": function(mimetypeNode, filePathNode) {
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
return new tree.URL(filePathNode || mimetypeNode, this.rootpath).eval(this.env);
|
||||
return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env);
|
||||
}
|
||||
|
||||
var mimetype = mimetypeNode.value;
|
||||
@@ -390,8 +390,12 @@ tree.functions = {
|
||||
filePath = mimetype;
|
||||
}
|
||||
|
||||
if (this.currentDirectory && this.env.isPathRelative(filePath)) {
|
||||
filePath = path.join(this.currentDirectory, filePath);
|
||||
if (this.env.isPathRelative(filePath)) {
|
||||
if (this.currentFileInfo.relativeUrls) {
|
||||
filePath = path.join(this.currentFileInfo.currentDirectory, filePath);
|
||||
} else {
|
||||
filePath = path.join(this.currentFileInfo.entryPath, filePath);
|
||||
}
|
||||
}
|
||||
|
||||
// detect the mimetype if not given
|
||||
@@ -421,15 +425,13 @@ tree.functions = {
|
||||
var DATA_URI_MAX_KB = 32,
|
||||
fileSizeInKB = parseInt((buf.length / 1024), 10);
|
||||
if (fileSizeInKB >= DATA_URI_MAX_KB) {
|
||||
// the url() must be relative, not an absolute file path
|
||||
filePath = path.relative(this.currentDirectory, filePath);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return new tree.URL(filePathNode || mimetypeNode, this.rootpath).eval(this.env);
|
||||
return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env);
|
||||
} else if (!this.env.silent) {
|
||||
// if explicitly disabled (via --no-ie-compat on CLI, or env.ieCompat === false), merely warn
|
||||
console.warn("WARNING: Embedding %s (%dKB) exceeds IE8's data-uri size limit of %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB);
|
||||
@@ -517,10 +519,9 @@ function clamp(val) {
|
||||
return Math.min(1, Math.max(0, val));
|
||||
}
|
||||
|
||||
tree.functionCall = function(env, rootpath, currentDirectory) {
|
||||
tree.functionCall = function(env, currentFileInfo) {
|
||||
this.env = env;
|
||||
this.rootpath = rootpath;
|
||||
this.currentDirectory = currentDirectory;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
};
|
||||
|
||||
tree.functionCall.prototype = tree.functions;
|
||||
|
||||
@@ -1,22 +1,62 @@
|
||||
(function (tree) {
|
||||
tree.importVisitor = function(root, importer) {
|
||||
tree.importVisitor = function(root, importer, finish, evalEnv) {
|
||||
this._visitor = new tree.visitor(this);
|
||||
this._importer = importer;
|
||||
this.env = new tree.evalEnv();
|
||||
this._finish = finish;
|
||||
this.env = evalEnv || new tree.evalEnv();
|
||||
this.importCount = 0;
|
||||
|
||||
// process the contents
|
||||
this._visitor.visit(root);
|
||||
|
||||
this.isFinished = true;
|
||||
|
||||
if (this.importCount === 0) {
|
||||
this._finish();
|
||||
}
|
||||
};
|
||||
|
||||
tree.importVisitor.prototype = {
|
||||
visitImport: function (importNode, visitArgs) {
|
||||
var importVisitor = this,
|
||||
evaldImportNode;
|
||||
|
||||
if (!importNode.css) {
|
||||
importNode = importNode.evalForImport(this.env);
|
||||
this._importer.push(importNode.getPath(), function (e, root, imported) {
|
||||
if (e) { e.index = importNode.index; }
|
||||
if (imported && importNode.once) { importNode.skip = imported; }
|
||||
importNode.root = root || new(tree.Ruleset)([], []);
|
||||
});
|
||||
|
||||
try {
|
||||
evaldImportNode = importNode.evalForImport(this.env);
|
||||
} catch(e){
|
||||
if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; }
|
||||
// attempt to eval properly and treat as css
|
||||
importNode.css = true;
|
||||
// if that fails, this error will be thrown
|
||||
importNode.error = e;
|
||||
}
|
||||
|
||||
if (evaldImportNode && !evaldImportNode.css) {
|
||||
importNode = evaldImportNode;
|
||||
this.importCount++;
|
||||
var env = new tree.evalEnv(this.env, this.env.frames.slice(0));
|
||||
this._importer.push(importNode.getPath(), importNode.currentFileInfo, function (e, root, imported) {
|
||||
if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; }
|
||||
if (imported && importNode.once) { importNode.skip = imported; }
|
||||
|
||||
var subFinish = function() {
|
||||
importVisitor.importCount--;
|
||||
|
||||
if (importVisitor.importCount === 0 && importVisitor.isFinished) {
|
||||
importVisitor._finish();
|
||||
}
|
||||
};
|
||||
|
||||
if (root) {
|
||||
importNode.root = root;
|
||||
new(tree.importVisitor)(root, importVisitor._importer, subFinish, env);
|
||||
} else {
|
||||
subFinish();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
visitArgs.visitDeeper = false;
|
||||
return importNode;
|
||||
|
||||
@@ -103,13 +103,20 @@ var less = {
|
||||
|
||||
var isUrlRe = /^(?:https?:)?\/\//i;
|
||||
|
||||
less.Parser.importer = function (file, paths, callback, env) {
|
||||
var pathname, dirname, data;
|
||||
less.Parser.importer = function (file, currentFileInfo, callback, env) {
|
||||
var pathname, dirname, data,
|
||||
newFileInfo = {
|
||||
relativeUrls: env.relativeUrls,
|
||||
entryPath: currentFileInfo.entryPath,
|
||||
rootpath: currentFileInfo.rootpath,
|
||||
rootFilename: currentFileInfo.rootFilename
|
||||
};
|
||||
|
||||
function parseFile(e, data) {
|
||||
if (e) { return callback(e); }
|
||||
|
||||
env = new less.tree.parseEnv(env);
|
||||
env.processImports = false;
|
||||
|
||||
var j = file.lastIndexOf('/');
|
||||
|
||||
@@ -121,23 +128,22 @@ less.Parser.importer = function (file, paths, callback, env) {
|
||||
// then rootpath should become 'less/module/nav/'
|
||||
// - If path of imported file is '../mixins.less' and rootpath is 'less/',
|
||||
// then rootpath should become 'less/../'
|
||||
if(env.relativeUrls && !/^(?:[a-z-]+:|\/)/.test(file) && j != -1) {
|
||||
env.rootpath = env.rootpath + file.slice(0, j+1); // append (sub|sup) directory path of imported file
|
||||
}
|
||||
if (env.relativeUrls) {
|
||||
env.currentDirectory = pathname.replace(/[^\\\/]*$/, "");
|
||||
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 = pathname.replace(/[^\\\/]*$/, "");
|
||||
newFileInfo.filename = pathname;
|
||||
|
||||
env.contents[pathname] = data; // Updating top importing parser content cache.
|
||||
env.paths = [dirname].concat(paths);
|
||||
env.filename = pathname;
|
||||
env.currentFileInfo = newFileInfo;
|
||||
new(less.Parser)(env).parse(data, function (e, root) {
|
||||
callback(e, root, pathname);
|
||||
});
|
||||
};
|
||||
|
||||
var isUrl = isUrlRe.test( file );
|
||||
if (isUrl || isUrlRe.test(paths[0])) {
|
||||
if (isUrl || isUrlRe.test(currentFileInfo.currentDirectory)) {
|
||||
if (request === undefined) {
|
||||
try { request = require('request'); }
|
||||
catch(e) { request = null; }
|
||||
@@ -147,7 +153,7 @@ less.Parser.importer = function (file, paths, callback, env) {
|
||||
return;
|
||||
}
|
||||
|
||||
var urlStr = isUrl ? file : url.resolve(paths[0], file),
|
||||
var urlStr = isUrl ? file : url.resolve(currentFileInfo.currentDirectory, file),
|
||||
urlObj = url.parse(urlStr),
|
||||
req = {
|
||||
host: urlObj.hostname,
|
||||
@@ -172,9 +178,7 @@ less.Parser.importer = function (file, paths, callback, env) {
|
||||
});
|
||||
} else {
|
||||
|
||||
// TODO: Undo this at some point,
|
||||
// or use different approach.
|
||||
var paths = [].concat(paths);
|
||||
var paths = [currentFileInfo.currentDirectory].concat(env.paths);
|
||||
paths.push('.');
|
||||
|
||||
for (var i = 0; i < paths.length; i++) {
|
||||
@@ -187,8 +191,6 @@ less.Parser.importer = function (file, paths, callback, env) {
|
||||
}
|
||||
}
|
||||
|
||||
paths = paths.slice(0, paths.length - 1);
|
||||
|
||||
if (!pathname) {
|
||||
|
||||
callback({ type: 'File', message: "'" + file + "' wasn't found" });
|
||||
|
||||
@@ -71,15 +71,6 @@ less.Parser = function Parser(env) {
|
||||
env = new tree.parseEnv(env);
|
||||
}
|
||||
|
||||
if (!env.currentDirectory && env.filename) {
|
||||
// only works for node, only used for node
|
||||
env.currentDirectory = env.filename.replace(/[^\/\\]*$/, "");
|
||||
}
|
||||
|
||||
// This function is called after all files
|
||||
// have been imported through `@import`.
|
||||
var finish = function () {};
|
||||
|
||||
var imports = this.imports = {
|
||||
paths: env.paths || [], // Search paths, when importing
|
||||
queue: [], // Files which haven't been imported yet
|
||||
@@ -87,25 +78,23 @@ less.Parser = function Parser(env) {
|
||||
contents: env.contents, // Holds the imported file contents
|
||||
mime: env.mime, // MIME type of .less files
|
||||
error: null, // Error in parsing/evaluating an import
|
||||
push: function (path, callback) {
|
||||
var that = this;
|
||||
push: function (path, currentFileInfo, callback) {
|
||||
var parserImporter = this;
|
||||
this.queue.push(path);
|
||||
|
||||
//
|
||||
// Import a file asynchronously
|
||||
//
|
||||
less.Parser.importer(path, this.paths, function (e, root, fullPath) {
|
||||
that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue
|
||||
less.Parser.importer(path, currentFileInfo, function (e, root, fullPath) {
|
||||
parserImporter.queue.splice(parserImporter.queue.indexOf(path), 1); // Remove the path from the queue
|
||||
|
||||
var imported = fullPath in that.files;
|
||||
var imported = fullPath in parserImporter.files;
|
||||
|
||||
that.files[fullPath] = root; // Store the root
|
||||
parserImporter.files[fullPath] = root; // Store the root
|
||||
|
||||
if (e && !that.error) { that.error = e; }
|
||||
if (e && !parserImporter.error) { parserImporter.error = e; }
|
||||
|
||||
callback(e, root, imported);
|
||||
|
||||
if (that.queue.length === 0) { finish(that.error); } // Call `finish` if we're done importing
|
||||
}, env);
|
||||
}
|
||||
};
|
||||
@@ -220,7 +209,7 @@ less.Parser = function Parser(env) {
|
||||
}
|
||||
|
||||
function getInput(e, env) {
|
||||
if (e.filename && env.filename && (e.filename !== env.filename)) {
|
||||
if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) {
|
||||
return parser.imports.contents[e.filename];
|
||||
} else {
|
||||
return input;
|
||||
@@ -236,17 +225,15 @@ less.Parser = function Parser(env) {
|
||||
column: column };
|
||||
}
|
||||
|
||||
function getFileName(e) {
|
||||
if(less.mode === 'browser' || less.mode === 'rhino')
|
||||
return e.filename;
|
||||
else
|
||||
return require('path').resolve(e.filename);
|
||||
}
|
||||
function getDebugInfo(index, inputStream, env) {
|
||||
var filename = env.currentFileInfo.filename;
|
||||
if(less.mode !== 'browser' && less.mode !== 'rhino') {
|
||||
filename = require('path').resolve(filename);
|
||||
}
|
||||
|
||||
function getDebugInfo(index, inputStream, e) {
|
||||
return {
|
||||
lineNumber: getLocation(index, inputStream).line + 1,
|
||||
fileName: getFileName(e)
|
||||
fileName: filename
|
||||
};
|
||||
}
|
||||
|
||||
@@ -259,7 +246,7 @@ less.Parser = function Parser(env) {
|
||||
|
||||
this.type = e.type || 'Syntax';
|
||||
this.message = e.message;
|
||||
this.filename = e.filename || env.filename;
|
||||
this.filename = e.filename || env.currentFileInfo.filename;
|
||||
this.index = e.index;
|
||||
this.line = typeof(line) === 'number' ? line + 1 : null;
|
||||
this.callLine = e.call && (getLocation(e.call, input).line + 1);
|
||||
@@ -281,8 +268,6 @@ less.Parser = function Parser(env) {
|
||||
// the individual nodes in the tree.
|
||||
this.optimization = ('optimization' in this.env) ? this.env.optimization : 1;
|
||||
|
||||
this.env.filename = this.env.filename || null;
|
||||
|
||||
//
|
||||
// The Parser
|
||||
//
|
||||
@@ -360,7 +345,7 @@ less.Parser = function Parser(env) {
|
||||
index: i-1,
|
||||
type: 'Parse',
|
||||
message: (level > 0) ? "missing closing `}`" : "missing opening `{`",
|
||||
filename: env.filename
|
||||
filename: env.currentFileInfo.filename
|
||||
}, env);
|
||||
}
|
||||
|
||||
@@ -457,7 +442,7 @@ less.Parser = function Parser(env) {
|
||||
type: "Parse",
|
||||
message: "Unrecognised input",
|
||||
index: i,
|
||||
filename: env.filename,
|
||||
filename: env.currentFileInfo.filename,
|
||||
line: line,
|
||||
column: column,
|
||||
extract: [
|
||||
@@ -468,16 +453,9 @@ less.Parser = function Parser(env) {
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
new tree.importVisitor(root, parser.imports);
|
||||
}
|
||||
catch(e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
finish = function (e) {
|
||||
var finish = function (e) {
|
||||
e = error || e || parser.imports.error;
|
||||
|
||||
|
||||
if (e) {
|
||||
if (!(e instanceof LessError)) {
|
||||
e = new(LessError)(e, env);
|
||||
@@ -490,7 +468,14 @@ less.Parser = function Parser(env) {
|
||||
}
|
||||
};
|
||||
|
||||
if (this.imports.queue.length === 0) {
|
||||
if (env.processImports !== false) {
|
||||
try {
|
||||
new tree.importVisitor(root, this.imports, finish);
|
||||
}
|
||||
catch(e) {
|
||||
error = e;
|
||||
}
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
},
|
||||
@@ -576,7 +561,7 @@ less.Parser = function Parser(env) {
|
||||
// "milky way" 'he\'s the one!'
|
||||
//
|
||||
quoted: function () {
|
||||
var str, j = i, e;
|
||||
var str, j = i, e, index = i;
|
||||
|
||||
if (input.charAt(j) === '~') { j++, e = true } // Escaped strings
|
||||
if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return;
|
||||
@@ -584,7 +569,7 @@ less.Parser = function Parser(env) {
|
||||
e && $('~');
|
||||
|
||||
if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) {
|
||||
return new(tree.Quoted)(str[0], str[1] || str[2], e);
|
||||
return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -642,7 +627,7 @@ less.Parser = function Parser(env) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (name) { return new(tree.Call)(name, args, index, env.filename, env.rootpath, env.currentDirectory); }
|
||||
if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); }
|
||||
},
|
||||
arguments: function () {
|
||||
var args = [], arg;
|
||||
@@ -690,7 +675,7 @@ less.Parser = function Parser(env) {
|
||||
expect(')');
|
||||
|
||||
return new(tree.URL)((value.value != null || value instanceof tree.Variable)
|
||||
? value : new(tree.Anonymous)(value), env.rootpath);
|
||||
? value : new(tree.Anonymous)(value), env.currentFileInfo);
|
||||
},
|
||||
|
||||
//
|
||||
@@ -705,7 +690,7 @@ less.Parser = function Parser(env) {
|
||||
var name, index = i;
|
||||
|
||||
if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) {
|
||||
return new(tree.Variable)(name, index, env.filename);
|
||||
return new(tree.Variable)(name, index, env.currentFileInfo);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -714,7 +699,7 @@ less.Parser = function Parser(env) {
|
||||
var name, curly, index = i;
|
||||
|
||||
if (input.charAt(i) === '@' && (curly = $(/^@\{([\w-]+)\}/))) {
|
||||
return new(tree.Variable)("@" + curly[1], index, env.filename);
|
||||
return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -916,7 +901,7 @@ less.Parser = function Parser(env) {
|
||||
}
|
||||
|
||||
if (elements.length > 0 && ($(';') || peek('}'))) {
|
||||
return new(tree.mixin.Call)(elements, args, index, env.filename, important);
|
||||
return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important);
|
||||
}
|
||||
|
||||
restore();
|
||||
@@ -1220,7 +1205,7 @@ less.Parser = function Parser(env) {
|
||||
if ($(';')) {
|
||||
features = features && new(tree.Value)(features);
|
||||
var importOnce = dir[1] !== 'multiple';
|
||||
return new(tree.Import)(path, features, importOnce, index, env.rootpath);
|
||||
return new(tree.Import)(path, features, importOnce, index, env.currentFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1524,14 +1509,18 @@ if (less.mode === 'browser' || less.mode === 'rhino') {
|
||||
//
|
||||
// Used by `@import` directives
|
||||
//
|
||||
less.Parser.importer = function (path, paths, callback, env) {
|
||||
if (!/^([a-z-]+:)?\//.test(path) && paths.length > 0) {
|
||||
path = paths[0] + path;
|
||||
less.Parser.importer = function (path, currentFileInfo, callback, env) {
|
||||
if (!/^([a-z-]+:)?\//.test(path) && currentFileInfo.currentDirectory) {
|
||||
path = currentFileInfo.currentDirectory + path;
|
||||
}
|
||||
var sheetEnv = env.toSheet(path);
|
||||
sheetEnv.processImports = false;
|
||||
sheetEnv.currentFileInfo = currentFileInfo;
|
||||
|
||||
// We pass `true` as 3rd argument, to force the reload of the import.
|
||||
// This is so we can get the syntax tree as opposed to just the CSS output,
|
||||
// as we need this to evaluate the current stylesheet.
|
||||
loadStyleSheet(env.toSheet(path),
|
||||
loadStyleSheet(sheetEnv,
|
||||
function (e, root, data, sheet, _, path) {
|
||||
callback.call(null, e, root, path);
|
||||
}, true);
|
||||
|
||||
@@ -3,13 +3,11 @@
|
||||
//
|
||||
// A function call node.
|
||||
//
|
||||
tree.Call = function (name, args, index, filename, rootpath, currentDirectory) {
|
||||
tree.Call = function (name, args, index, currentFileInfo) {
|
||||
this.name = name;
|
||||
this.args = args;
|
||||
this.index = index;
|
||||
this.filename = filename;
|
||||
this.rootpath = rootpath;
|
||||
this.currentDirectory = currentDirectory;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
};
|
||||
tree.Call.prototype = {
|
||||
type: "Call",
|
||||
@@ -36,7 +34,7 @@ tree.Call.prototype = {
|
||||
|
||||
if (nameLC in tree.functions) { // 1.
|
||||
try {
|
||||
func = new tree.functionCall(env, this.rootpath, this.currentDirectory);
|
||||
func = new tree.functionCall(env, this.currentFileInfo);
|
||||
result = func[nameLC].apply(func, args);
|
||||
if (result != null) {
|
||||
return result;
|
||||
@@ -45,7 +43,7 @@ tree.Call.prototype = {
|
||||
throw { type: e.type || "Runtime",
|
||||
message: "error evaluating function `" + this.name + "`" +
|
||||
(e.message ? ': ' + e.message : ''),
|
||||
index: this.index, filename: this.filename };
|
||||
index: this.index, filename: this.currentFileInfo.filename };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
// `import,push`, we also pass it a callback, which it'll call once
|
||||
// the file has been fetched, and parsed.
|
||||
//
|
||||
tree.Import = function (path, features, once, index, rootpath) {
|
||||
tree.Import = function (path, features, once, index, currentFileInfo) {
|
||||
var that = this;
|
||||
|
||||
this.once = once;
|
||||
this.index = index;
|
||||
this.path = path;
|
||||
this.features = features;
|
||||
this.rootpath = rootpath;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
|
||||
var pathValue = this.getPath();
|
||||
if (pathValue) {
|
||||
@@ -61,15 +61,16 @@ tree.Import.prototype = {
|
||||
return null;
|
||||
},
|
||||
evalForImport: function (env) {
|
||||
return new(tree.Import)(this.path.eval(env), this.features, this.once, this.index);
|
||||
return new(tree.Import)(this.path.eval(env), this.features, this.once, this.index, this.currentFileInfo);
|
||||
},
|
||||
evalPath: function (env) {
|
||||
var path = this.path.eval(env);
|
||||
if (this.rootpath && !(path instanceof tree.URL)) {
|
||||
var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath;
|
||||
if (rootpath && !(path instanceof tree.URL)) {
|
||||
var pathValue = path.value;
|
||||
// Add the base path if the import is relative
|
||||
if (pathValue && env.isPathRelative(pathValue)) {
|
||||
path.value = this.rootpath + pathValue;
|
||||
path.value = rootpath + pathValue;
|
||||
}
|
||||
}
|
||||
return path;
|
||||
@@ -80,7 +81,11 @@ tree.Import.prototype = {
|
||||
if (this.skip) { return []; }
|
||||
|
||||
if (this.css) {
|
||||
return new(tree.Import)(this.evalPath(env), features, this.once, this.index);
|
||||
var newImport = new(tree.Import)(this.evalPath(env), features, this.once, this.index);
|
||||
if (!newImport.css && this.error) {
|
||||
throw this.error;
|
||||
}
|
||||
return newImport;
|
||||
} else {
|
||||
ruleset = new(tree.Ruleset)([], this.root.rules.slice(0));
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
(function (tree) {
|
||||
|
||||
tree.mixin = {};
|
||||
tree.mixin.Call = function (elements, args, index, filename, important) {
|
||||
tree.mixin.Call = function (elements, args, index, currentFileInfo, important) {
|
||||
this.selector = new(tree.Selector)(elements);
|
||||
this.arguments = args;
|
||||
this.index = index;
|
||||
this.filename = filename;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
this.important = important;
|
||||
};
|
||||
tree.mixin.Call.prototype = {
|
||||
@@ -42,7 +42,7 @@ tree.mixin.Call.prototype = {
|
||||
Array.prototype.push.apply(
|
||||
rules, mixin.eval(env, args, this.important).rules);
|
||||
} catch (e) {
|
||||
throw { message: e.message, index: this.index, filename: this.filename, stack: e.stack };
|
||||
throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack };
|
||||
}
|
||||
}
|
||||
match = true;
|
||||
@@ -69,11 +69,11 @@ tree.mixin.Call.prototype = {
|
||||
}
|
||||
return argValue;
|
||||
}).join(', ') : "") + ")`",
|
||||
index: this.index, filename: this.filename };
|
||||
index: this.index, filename: this.currentFileInfo.filename };
|
||||
} else {
|
||||
throw { type: 'Name',
|
||||
message: this.selector.toCSS().trim() + " is undefined",
|
||||
index: this.index, filename: this.filename };
|
||||
index: this.index, filename: this.currentFileInfo.filename };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
(function (tree) {
|
||||
|
||||
tree.Quoted = function (str, content, escaped, i) {
|
||||
tree.Quoted = function (str, content, escaped, index, currentFileInfo) {
|
||||
this.escaped = escaped;
|
||||
this.value = content || '';
|
||||
this.quote = str.charAt(0);
|
||||
this.index = i;
|
||||
this.index = index;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
};
|
||||
tree.Quoted.prototype = {
|
||||
type: "Quoted",
|
||||
@@ -20,7 +21,7 @@ tree.Quoted.prototype = {
|
||||
var value = this.value.replace(/`([^`]+)`/g, function (_, exp) {
|
||||
return new(tree.JavaScript)(exp, that.index, true).eval(env).value;
|
||||
}).replace(/@\{([\w-]+)\}/g, function (_, name) {
|
||||
var v = new(tree.Variable)('@' + name, that.index).eval(env, true);
|
||||
var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true);
|
||||
return (v instanceof tree.Quoted) ? v.value : v.toCSS();
|
||||
});
|
||||
return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
(function (tree) {
|
||||
|
||||
tree.URL = function (val, rootpath) {
|
||||
tree.URL = function (val, currentFileInfo) {
|
||||
this.value = val;
|
||||
this.rootpath = rootpath;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
};
|
||||
tree.URL.prototype = {
|
||||
type: "Url",
|
||||
@@ -16,8 +16,8 @@ tree.URL.prototype = {
|
||||
var val = this.value.eval(ctx), rootpath;
|
||||
|
||||
// Add the base path if the URL is relative
|
||||
if (this.rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) {
|
||||
rootpath = this.rootpath;
|
||||
rootpath = this.currentFileInfo && this.currentFileInfo.rootpath;
|
||||
if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) {
|
||||
if (!val.quote) {
|
||||
rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; });
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(function (tree) {
|
||||
|
||||
tree.Variable = function (name, index, file) { this.name = name, this.index = index, this.file = file };
|
||||
tree.Variable = function (name, index, currentFileInfo) { this.name = name, this.index = index, this.currentFileInfo = currentFileInfo };
|
||||
tree.Variable.prototype = {
|
||||
type: "Variable",
|
||||
eval: function (env) {
|
||||
@@ -13,7 +13,7 @@ tree.Variable.prototype = {
|
||||
if (this.evaluating) {
|
||||
throw { type: 'Name',
|
||||
message: "Recursive variable definition for " + name,
|
||||
filename: this.file,
|
||||
filename: this.currentFileInfo.file,
|
||||
index: this.index };
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ tree.Variable.prototype = {
|
||||
else {
|
||||
throw { type: 'Name',
|
||||
message: "variable " + name + " is undefined",
|
||||
filename: this.file,
|
||||
filename: this.currentFileInfo.filename,
|
||||
index: this.index };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
body {
|
||||
width: 100%;
|
||||
}
|
||||
.a {
|
||||
var: test;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
@import "import/../css/background.css";
|
||||
|
||||
@import "import/import-test-d.css";
|
||||
|
||||
@import "file.css";
|
||||
@font-face {
|
||||
src: url("/fonts/garamond-pro.ttf");
|
||||
src: local(Futura-Medium), url(fonts.svg#MyGeometricModern) format("svg");
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
NameError: .mixin-not-defined is undefined in {pathrel}mixin-not-defined.less on line 11, column 1:
|
||||
NameError: .mixin-not-defined is undefined in {path}mixin-not-defined.less on line 11, column 1:
|
||||
10
|
||||
11 .mixin-not-defined();
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
ParseError: missing opening `{` in {pathrel}parse-error-curly-bracket.less on line 1, column 1:
|
||||
ParseError: missing opening `{` in {path}parse-error-curly-bracket.less on line 1, column 2:
|
||||
1 }}
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
@my_theme: "test";
|
||||
|
||||
@import "import/import-@{my_theme}-e.less";
|
||||
@import "import/import-@{my_theme}-e.less";
|
||||
|
||||
@import "import/import-@{in}@{terpolation}.less";
|
||||
|
||||
@in: "in";
|
||||
@terpolation: "terpolation";
|
||||
2
test/less/import.less
vendored
2
test/less/import.less
vendored
@@ -12,4 +12,4 @@
|
||||
}
|
||||
@import "import/import-test-e" screen and (max-width: 600px);
|
||||
|
||||
@import url("import/import-test-a.less");
|
||||
@import url("import/import-test-a.less");
|
||||
1
test/less/import/import-interpolation.less
Normal file
1
test/less/import/import-interpolation.less
Normal file
@@ -0,0 +1 @@
|
||||
@import "import-@{in}@{terpolation}2.less";
|
||||
5
test/less/import/import-interpolation2.less
Normal file
5
test/less/import/import-interpolation2.less
Normal file
@@ -0,0 +1,5 @@
|
||||
.a {
|
||||
var: test;
|
||||
}
|
||||
|
||||
@in: "redefined-does-nothing";
|
||||
@@ -50,3 +50,8 @@
|
||||
#data-uri-toobig {
|
||||
uri: data-uri('../data/data-uri-fail.png');
|
||||
}
|
||||
.add_an_import(@file_to_import) {
|
||||
@import "@{file_to_import}";
|
||||
}
|
||||
|
||||
.add_an_import("file.css");
|
||||
|
||||
Reference in New Issue
Block a user