mirror of
https://github.com/less/less.js.git
synced 2026-04-09 03:00:20 -04:00
Relative URLs in LESS files should be relative to the file that defines them.
It is up to the parser and compiler to rewrite them when those files are imported by another LESS file. - Modified and added test cases for import and import-once rules - Fixed difference between client side and server side handling of relative urls - Added a -rootpath option to lessc to specify another base path for the url rewriting. By default, rootpath=''
This commit is contained in:
committed by
Luke Page
parent
22a4a0c874
commit
e59a93b5fd
@@ -14,7 +14,8 @@ var options = {
|
||||
silent: false,
|
||||
paths: [],
|
||||
color: true,
|
||||
strictImports: false
|
||||
strictImports: false,
|
||||
rootpath: ''
|
||||
};
|
||||
var continueProcessing = true,
|
||||
currentErrorcode;
|
||||
@@ -77,6 +78,10 @@ args = args.filter(function (arg) {
|
||||
case 'line-numbers':
|
||||
options.dumpLineNumbers = match[2];
|
||||
break;
|
||||
case 'rp':
|
||||
case 'rootpath':
|
||||
options.rootpath = path.normalize(match[2] + '/');
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -120,6 +125,7 @@ var parseLessFile = function (e, data) {
|
||||
paths: [path.dirname(input)].concat(options.paths),
|
||||
optimization: options.optimization,
|
||||
filename: input,
|
||||
rootpath: options.rootpath,
|
||||
strictImports: options.strictImports,
|
||||
dumpLineNumbers: options.dumpLineNumbers
|
||||
}).parse(data, function (err, tree) {
|
||||
|
||||
@@ -211,6 +211,7 @@ function loadStyleSheet(sheet, callback, reload, remaining) {
|
||||
var css = cache && cache.getItem(href);
|
||||
var timestamp = cache && cache.getItem(href + ':timestamp');
|
||||
var styles = { css: css, timestamp: timestamp };
|
||||
var rootpath = sheet.rootpath || hrefParts.path;
|
||||
|
||||
xhr(href, sheet.type, function (data, lastModified) {
|
||||
// Store data this session
|
||||
@@ -231,6 +232,7 @@ function loadStyleSheet(sheet, callback, reload, remaining) {
|
||||
paths: [hrefParts.path],
|
||||
mime: sheet.type,
|
||||
filename: href,
|
||||
rootpath: rootpath,
|
||||
contents: contents, // Passing top importing parser content cache ref down.
|
||||
files: files,
|
||||
dumpLineNumbers: less.dumpLineNumbers
|
||||
|
||||
@@ -104,6 +104,21 @@ less.Parser.importer = function (file, paths, callback, env) {
|
||||
|
||||
function parseFile(e, data) {
|
||||
if (e) return callback(e);
|
||||
|
||||
var rootpath = env.rootpath,
|
||||
j = file.lastIndexOf('/');
|
||||
|
||||
// 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/../'
|
||||
if(!/^(?:[a-z-]+:|\/)/.test(file) && j != -1) {
|
||||
rootpath = rootpath + file.slice(0, j+1); // append (sub|sup) directory path of imported file
|
||||
}
|
||||
|
||||
env.contents[pathname] = data; // Updating top importing parser content cache.
|
||||
new(less.Parser)({
|
||||
@@ -112,6 +127,7 @@ less.Parser.importer = function (file, paths, callback, env) {
|
||||
contents: env.contents,
|
||||
files: env.files,
|
||||
syncImport: env.syncImport,
|
||||
rootpath: rootpath,
|
||||
dumpLineNumbers: env.dumpLineNumbers
|
||||
}).parse(data, function (e, root) {
|
||||
callback(e, root, pathname);
|
||||
@@ -169,6 +185,7 @@ less.Parser.importer = function (file, paths, callback, env) {
|
||||
paths = paths.slice(0, paths.length - 1);
|
||||
|
||||
if (!pathname) {
|
||||
|
||||
if (typeof(env.errback) === "function") {
|
||||
env.errback(file, paths, callback);
|
||||
} else {
|
||||
|
||||
@@ -70,6 +70,7 @@ less.Parser = function Parser(env) {
|
||||
var env = env || { };
|
||||
// env.contents and files must be passed arround with top env
|
||||
if (!env.contents) { env.contents = {}; }
|
||||
env.rootpath = env.rootpath || ''; // env.rootpath must be initialized to '' if not provided
|
||||
if (!env.files) { env.files = {}; }
|
||||
|
||||
// This function is called after all files
|
||||
@@ -670,7 +671,7 @@ less.Parser = function Parser(env) {
|
||||
expect(')');
|
||||
|
||||
return new(tree.URL)((value.value != null || value instanceof tree.Variable)
|
||||
? value : new(tree.Anonymous)(value), imports.paths);
|
||||
? value : new(tree.Anonymous)(value), env.rootpath);
|
||||
},
|
||||
|
||||
//
|
||||
@@ -1198,7 +1199,7 @@ less.Parser = function Parser(env) {
|
||||
if (dir && (path = $(this.entities.quoted) || $(this.entities.url))) {
|
||||
features = $(this.mediaFeatures);
|
||||
if ($(';')) {
|
||||
return new(tree.Import)(path, imports, features, (dir[1] === 'once'), index);
|
||||
return new(tree.Import)(path, imports, features, (dir[1] === 'once'), index, env.rootpath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1500,7 +1501,7 @@ if (less.mode === 'browser' || less.mode === 'rhino') {
|
||||
// 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.
|
||||
// __ Now using the hack of passing a ref to top parser's content cache in the 1st arg. __
|
||||
loadStyleSheet({ href: path, title: path, type: env.mime, contents: env.contents, files: env.files }, function (e, root, data, sheet, _, path) {
|
||||
loadStyleSheet({ href: path, title: path, type: env.mime, contents: env.contents, files: env.files, rootpath: env.rootpath }, function (e, root, data, sheet, _, path) {
|
||||
if (e && typeof(env.errback) === "function") {
|
||||
env.errback.call(null, path, paths, callback, env);
|
||||
} else {
|
||||
|
||||
@@ -11,14 +11,15 @@
|
||||
// `import,push`, we also pass it a callback, which it'll call once
|
||||
// the file has been fetched, and parsed.
|
||||
//
|
||||
tree.Import = function (path, imports, features, once, index) {
|
||||
tree.Import = function (path, imports, features, once, index, rootpath) {
|
||||
var that = this;
|
||||
|
||||
this.once = once;
|
||||
this.index = index;
|
||||
this._path = path;
|
||||
this.features = features && new(tree.Value)(features);
|
||||
|
||||
this.rootpath = rootpath;
|
||||
|
||||
// The '.less' extension is optional
|
||||
if (path instanceof tree.Quoted) {
|
||||
this.path = /\.(le?|c)ss(\?.*)?$/.test(path.value) ? path.value : path.value + '.less';
|
||||
@@ -52,6 +53,10 @@ tree.Import.prototype = {
|
||||
var features = this.features ? ' ' + this.features.toCSS(env) : '';
|
||||
|
||||
if (this.css) {
|
||||
// Add the base path if the import is relative
|
||||
if (typeof this._path.value === "string" && !/^(?:[a-z-]+:|\/)/.test(this._path.value)) {
|
||||
this._path.value = this.rootpath + this._path.value;
|
||||
}
|
||||
return "@import " + this._path.toCSS() + features + ';\n';
|
||||
} else {
|
||||
return "";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
(function (tree) {
|
||||
|
||||
tree.URL = function (val, paths) {
|
||||
tree.URL = function (val, rootpath) {
|
||||
this.value = val;
|
||||
this.paths = paths;
|
||||
this.rootpath = rootpath;
|
||||
};
|
||||
tree.URL.prototype = {
|
||||
toCSS: function () {
|
||||
@@ -11,12 +11,12 @@ tree.URL.prototype = {
|
||||
eval: function (ctx) {
|
||||
var val = this.value.eval(ctx);
|
||||
|
||||
// Add the base path if the URL is relative and we are in the browser
|
||||
if (typeof window !== 'undefined' && typeof val.value === "string" && !/^(?:[a-z-]+:|\/)/.test(val.value) && this.paths.length > 0) {
|
||||
val.value = this.paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value);
|
||||
// Add the base path if the URL is relative
|
||||
if (typeof val.value === "string" && !/^(?:[a-z-]+:|\/)/.test(val.value)) {
|
||||
val.value = this.rootpath + val.value;
|
||||
}
|
||||
|
||||
return new(tree.URL)(val, this.paths);
|
||||
return new(tree.URL)(val, this.rootpath);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@import "import-test-d.css";
|
||||
@import "import/import-test-d.css";
|
||||
|
||||
@import "../import-test-d.css";
|
||||
@import "import/deeper/../import-test-d.css";
|
||||
#import {
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
16
test/css/import.css
vendored
16
test/css/import.css
vendored
@@ -1,8 +1,10 @@
|
||||
@import "import-test-d.css";
|
||||
@import "import/import-test-d.css";
|
||||
|
||||
@import url(http://fonts.googleapis.com/css?family=Open+Sans);
|
||||
|
||||
@import url(something.css) screen and (color) and (max-width: 600px);
|
||||
|
||||
@import "import/../css/background.css";
|
||||
#import {
|
||||
color: #ff0000;
|
||||
}
|
||||
@@ -21,3 +23,15 @@
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
#logo {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: url('import/imports/../assets/logo.png');
|
||||
}
|
||||
@font-face {
|
||||
font-family: xecret;
|
||||
src: url('import/imports/../assets/xecret.ttf');
|
||||
}
|
||||
#secret {
|
||||
font-family: xecret, sans-serif;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@import "import/import-once-test-c";
|
||||
@import-once "import/import-once-test-c";
|
||||
@import-once "import/import-once-test-c.less";
|
||||
@import-once "import/deeper/import-once-test-a";
|
||||
2
test/less/import.less
vendored
2
test/less/import.less
vendored
@@ -9,3 +9,5 @@
|
||||
height: @a + 10%;
|
||||
}
|
||||
@import "import/import-test-e" screen and (max-width: 600px);
|
||||
|
||||
@import "import/import-and-relative-paths-test";
|
||||
|
||||
5
test/less/import/import-and-relative-paths-test.less
Normal file
5
test/less/import/import-and-relative-paths-test.less
Normal file
@@ -0,0 +1,5 @@
|
||||
@import "../css/background.css";
|
||||
|
||||
@import "imports/logo";
|
||||
@import "imports/font";
|
||||
|
||||
8
test/less/import/imports/font.less
Normal file
8
test/less/import/imports/font.less
Normal file
@@ -0,0 +1,8 @@
|
||||
@font-face {
|
||||
font-family: xecret;
|
||||
src: url('../assets/xecret.ttf');
|
||||
}
|
||||
|
||||
#secret {
|
||||
font-family: xecret, sans-serif;
|
||||
}
|
||||
5
test/less/import/imports/logo.less
Normal file
5
test/less/import/imports/logo.less
Normal file
@@ -0,0 +1,5 @@
|
||||
#logo {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: url('../assets/logo.png');
|
||||
}
|
||||
Reference in New Issue
Block a user