Skip to content

Commit 5d54af2

Browse files
committed
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.
1 parent 14cee5d commit 5d54af2

21 files changed

+216
-135
lines changed

lib/less/browser.js

+16-10
Original file line numberDiff line numberDiff line change
@@ -249,26 +249,32 @@ function loadStyleSheet(sheet, callback, reload, remaining) {
249249
var timestamp = cache && cache.getItem(href + ':timestamp');
250250
var styles = { css: css, timestamp: timestamp };
251251
var env;
252+
var newFileInfo = {
253+
relativeUrls: less.relativeUrls,
254+
currentDirectory: hrefParts.path,
255+
filename: href
256+
};
252257

253258
if (sheet instanceof less.tree.parseEnv) {
254259
env = new less.tree.parseEnv(sheet);
260+
newFileInfo.entryPath = env.currentFileInfo.entryPath;
261+
newFileInfo.rootpath = env.currentFileInfo.rootpath;
262+
newFileInfo.rootFilename = env.currentFileInfo.rootFilename;
255263
} else {
256264
env = new less.tree.parseEnv(less);
257-
env.entryPath = hrefParts.path;
258265
env.mime = sheet.type;
266+
newFileInfo.entryPath = hrefParts.path;
267+
newFileInfo.rootpath = less.rootpath || hrefParts.path;
268+
newFileInfo.rootFilename = href;
259269
}
260270

261271
if (env.relativeUrls) {
262272
//todo - this relies on option being set on less object rather than being passed in as an option
263273
// - need an originalRootpath
264274
if (less.rootpath) {
265-
env.rootpath = extractUrlParts(less.rootpath + pathDiff(hrefParts.path, env.entryPath)).path;
275+
newFileInfo.rootpath = extractUrlParts(less.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path;
266276
} else {
267-
env.rootpath = hrefParts.path;
268-
}
269-
} else {
270-
if (!less.rootpath) {
271-
env.rootpath = env.entryPath;
277+
newFileInfo.rootpath = hrefParts.path;
272278
}
273279
}
274280

@@ -287,16 +293,16 @@ function loadStyleSheet(sheet, callback, reload, remaining) {
287293
try {
288294
env.contents[href] = data; // Updating content cache
289295
env.paths = [hrefParts.path];
290-
env.filename = href;
291-
env.rootFilename = env.rootFilename || href;
296+
env.currentFileInfo = newFileInfo;
297+
292298
new(less.Parser)(env).parse(data, function (e, root) {
293299
if (e) { return callback(e, null, null, sheet); }
294300
try {
295301
callback(e, root, data, sheet, { local: false, lastModified: lastModified, remaining: remaining }, href);
296302
//TODO - there must be a better way? A generic less-to-css function that can both call error
297303
//and removeNode where appropriate
298304
//should also add tests
299-
if (env.rootFilename === href) {
305+
if (env.currentFileInfo.rootFilename === href) {
300306
removeNode(document.getElementById('less-error-message:' + extractId(href)));
301307
}
302308
} catch (e) {

lib/less/env.js

+25-7
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,46 @@
11
(function (tree) {
22

33
var parseCopyProperties = [
4-
'paths', // paths to search for imports on
4+
'paths', // option - unmodified - paths to search for imports on
55
'optimization', // option - optimization level (for the chunker)
6-
'filename', // current filename, used for error reporting
76
'files', // list of files that have been imported, used for import-once
87
'contents', // browser-only, contents of all the files
9-
'rootpath', // current rootpath to append to all url's
108
'relativeUrls', // option - whether to adjust URL's to be relative
119
'strictImports', // option -
1210
'dumpLineNumbers', // option - whether to dump line numbers
1311
'compress', // option - whether to compress
12+
'processImports', // option - whether to process imports. if false then imports will not be imported
1413
'mime', // browser only - mime type for sheet import
15-
'entryPath', // browser only - path of entry less file
16-
'rootFilename', // browser only - href of the entry less file
17-
'currentDirectory' // node only - the current directory
14+
'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc.
1815
];
1916

17+
//currentFileInfo = {
18+
// 'relativeUrls' - option - whether to adjust URL's to be relative
19+
// 'filename' - full resolved filename of current file
20+
// 'rootpath' - path to append to normal URLs for this node
21+
// 'currentDirectory' - path to the current file, absolute
22+
// 'rootFilename' - filename of the base file
23+
// 'entryPath' = absolute path to the entry file
24+
2025
tree.parseEnv = function(options) {
2126
copyFromOriginal(options, this, parseCopyProperties);
2227

2328
if (!this.contents) { this.contents = {}; }
24-
if (!this.rootpath) { this.rootpath = ''; }
2529
if (!this.files) { this.files = {}; }
30+
31+
if (!this.currentFileInfo) {
32+
var filename = options.filename || "input";
33+
options.filename = null;
34+
var entryPath = filename.replace(/[^\/\\]*$/, "");
35+
this.currentFileInfo = {
36+
filename: filename,
37+
relativeUrls: this.relativeUrls,
38+
rootpath: options.rootpath || "",
39+
currentDirectory: entryPath,
40+
entryPath: entryPath,
41+
rootFilename: filename
42+
};
43+
}
2644
};
2745

2846
tree.parseEnv.prototype.toSheet = function (path) {

lib/less/functions.js

+10-9
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ tree.functions = {
376376
"data-uri": function(mimetypeNode, filePathNode) {
377377

378378
if (typeof window !== 'undefined') {
379-
return new tree.URL(filePathNode || mimetypeNode, this.rootpath).eval(this.env);
379+
return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env);
380380
}
381381

382382
var mimetype = mimetypeNode.value;
@@ -390,8 +390,12 @@ tree.functions = {
390390
filePath = mimetype;
391391
}
392392

393-
if (this.currentDirectory && this.env.isPathRelative(filePath)) {
394-
filePath = path.join(this.currentDirectory, filePath);
393+
if (this.env.isPathRelative(filePath)) {
394+
if (this.currentFileInfo.relativeUrls) {
395+
filePath = path.join(this.currentFileInfo.currentDirectory, filePath);
396+
} else {
397+
filePath = path.join(this.currentFileInfo.entryPath, filePath);
398+
}
395399
}
396400

397401
// detect the mimetype if not given
@@ -421,15 +425,13 @@ tree.functions = {
421425
var DATA_URI_MAX_KB = 32,
422426
fileSizeInKB = parseInt((buf.length / 1024), 10);
423427
if (fileSizeInKB >= DATA_URI_MAX_KB) {
424-
// the url() must be relative, not an absolute file path
425-
filePath = path.relative(this.currentDirectory, filePath);
426428

427429
if (this.env.ieCompat !== false) {
428430
if (!this.env.silent) {
429431
console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB);
430432
}
431433

432-
return new tree.URL(filePathNode || mimetypeNode, this.rootpath).eval(this.env);
434+
return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env);
433435
} else if (!this.env.silent) {
434436
// if explicitly disabled (via --no-ie-compat on CLI, or env.ieCompat === false), merely warn
435437
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) {
517519
return Math.min(1, Math.max(0, val));
518520
}
519521

520-
tree.functionCall = function(env, rootpath, currentDirectory) {
522+
tree.functionCall = function(env, currentFileInfo) {
521523
this.env = env;
522-
this.rootpath = rootpath;
523-
this.currentDirectory = currentDirectory;
524+
this.currentFileInfo = currentFileInfo;
524525
};
525526

526527
tree.functionCall.prototype = tree.functions;

lib/less/import-visitor.js

+48-8
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,62 @@
11
(function (tree) {
2-
tree.importVisitor = function(root, importer) {
2+
tree.importVisitor = function(root, importer, finish, evalEnv) {
33
this._visitor = new tree.visitor(this);
44
this._importer = importer;
5-
this.env = new tree.evalEnv();
5+
this._finish = finish;
6+
this.env = evalEnv || new tree.evalEnv();
7+
this.importCount = 0;
68

79
// process the contents
810
this._visitor.visit(root);
11+
12+
this.isFinished = true;
13+
14+
if (this.importCount === 0) {
15+
this._finish();
16+
}
917
};
1018

1119
tree.importVisitor.prototype = {
1220
visitImport: function (importNode, visitArgs) {
21+
var importVisitor = this,
22+
evaldImportNode;
23+
1324
if (!importNode.css) {
14-
importNode = importNode.evalForImport(this.env);
15-
this._importer.push(importNode.getPath(), function (e, root, imported) {
16-
if (e) { e.index = importNode.index; }
17-
if (imported && importNode.once) { importNode.skip = imported; }
18-
importNode.root = root || new(tree.Ruleset)([], []);
19-
});
25+
26+
try {
27+
evaldImportNode = importNode.evalForImport(this.env);
28+
} catch(e){
29+
if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; }
30+
// attempt to eval properly and treat as css
31+
importNode.css = true;
32+
// if that fails, this error will be thrown
33+
importNode.error = e;
34+
}
35+
36+
if (evaldImportNode && !evaldImportNode.css) {
37+
importNode = evaldImportNode;
38+
this.importCount++;
39+
var env = new tree.evalEnv(this.env, this.env.frames.slice(0));
40+
this._importer.push(importNode.getPath(), importNode.currentFileInfo, function (e, root, imported) {
41+
if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; }
42+
if (imported && importNode.once) { importNode.skip = imported; }
43+
44+
var subFinish = function() {
45+
importVisitor.importCount--;
46+
47+
if (importVisitor.importCount === 0 && importVisitor.isFinished) {
48+
importVisitor._finish();
49+
}
50+
};
51+
52+
if (root) {
53+
importNode.root = root;
54+
new(tree.importVisitor)(root, importVisitor._importer, subFinish, env);
55+
} else {
56+
subFinish();
57+
}
58+
});
59+
}
2060
}
2161
visitArgs.visitDeeper = false;
2262
return importNode;

lib/less/index.js

+18-16
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,20 @@ var less = {
103103

104104
var isUrlRe = /^(?:https?:)?\/\//i;
105105

106-
less.Parser.importer = function (file, paths, callback, env) {
107-
var pathname, dirname, data;
106+
less.Parser.importer = function (file, currentFileInfo, callback, env) {
107+
var pathname, dirname, data,
108+
newFileInfo = {
109+
relativeUrls: env.relativeUrls,
110+
entryPath: currentFileInfo.entryPath,
111+
rootpath: currentFileInfo.rootpath,
112+
rootFilename: currentFileInfo.rootFilename
113+
};
108114

109115
function parseFile(e, data) {
110116
if (e) { return callback(e); }
111117

112118
env = new less.tree.parseEnv(env);
119+
env.processImports = false;
113120

114121
var j = file.lastIndexOf('/');
115122

@@ -121,23 +128,22 @@ less.Parser.importer = function (file, paths, callback, env) {
121128
// then rootpath should become 'less/module/nav/'
122129
// - If path of imported file is '../mixins.less' and rootpath is 'less/',
123130
// then rootpath should become 'less/../'
124-
if(env.relativeUrls && !/^(?:[a-z-]+:|\/)/.test(file) && j != -1) {
125-
env.rootpath = env.rootpath + file.slice(0, j+1); // append (sub|sup) directory path of imported file
126-
}
127-
if (env.relativeUrls) {
128-
env.currentDirectory = pathname.replace(/[^\\\/]*$/, "");
131+
if(newFileInfo.relativeUrls && !/^(?:[a-z-]+:|\/)/.test(file) && j != -1) {
132+
var relativeSubDirectory = file.slice(0, j+1);
133+
newFileInfo.rootpath = newFileInfo.rootpath + relativeSubDirectory; // append (sub|sup) directory path of imported file
129134
}
135+
newFileInfo.currentDirectory = pathname.replace(/[^\\\/]*$/, "");
136+
newFileInfo.filename = pathname;
130137

131138
env.contents[pathname] = data; // Updating top importing parser content cache.
132-
env.paths = [dirname].concat(paths);
133-
env.filename = pathname;
139+
env.currentFileInfo = newFileInfo;
134140
new(less.Parser)(env).parse(data, function (e, root) {
135141
callback(e, root, pathname);
136142
});
137143
};
138144

139145
var isUrl = isUrlRe.test( file );
140-
if (isUrl || isUrlRe.test(paths[0])) {
146+
if (isUrl || isUrlRe.test(currentFileInfo.currentDirectory)) {
141147
if (request === undefined) {
142148
try { request = require('request'); }
143149
catch(e) { request = null; }
@@ -147,7 +153,7 @@ less.Parser.importer = function (file, paths, callback, env) {
147153
return;
148154
}
149155

150-
var urlStr = isUrl ? file : url.resolve(paths[0], file),
156+
var urlStr = isUrl ? file : url.resolve(currentFileInfo.currentDirectory, file),
151157
urlObj = url.parse(urlStr),
152158
req = {
153159
host: urlObj.hostname,
@@ -172,9 +178,7 @@ less.Parser.importer = function (file, paths, callback, env) {
172178
});
173179
} else {
174180

175-
// TODO: Undo this at some point,
176-
// or use different approach.
177-
var paths = [].concat(paths);
181+
var paths = [currentFileInfo.currentDirectory].concat(env.paths);
178182
paths.push('.');
179183

180184
for (var i = 0; i < paths.length; i++) {
@@ -187,8 +191,6 @@ less.Parser.importer = function (file, paths, callback, env) {
187191
}
188192
}
189193

190-
paths = paths.slice(0, paths.length - 1);
191-
192194
if (!pathname) {
193195

194196
callback({ type: 'File', message: "'" + file + "' wasn't found" });

0 commit comments

Comments
 (0)