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:
Luke Page
2013-03-01 13:06:34 +00:00
parent 14cee5d04f
commit 5d54af2039
21 changed files with 216 additions and 135 deletions

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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" });

View File

@@ -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);

View File

@@ -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 };
}
}

View File

@@ -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));

View File

@@ -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 };
}
}
};

View File

@@ -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);

View File

@@ -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; });
}

View File

@@ -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 };
}
}

View File

@@ -1,3 +1,6 @@
body {
width: 100%;
}
.a {
var: test;
}

View File

@@ -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");

View File

@@ -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();

View File

@@ -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 }}

View File

@@ -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";

View File

@@ -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");

View File

@@ -0,0 +1 @@
@import "import-@{in}@{terpolation}2.less";

View File

@@ -0,0 +1,5 @@
.a {
var: test;
}
@in: "redefined-does-nothing";

View File

@@ -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");