mirror of
https://github.com/less/less.js.git
synced 2026-05-01 03:00:22 -04:00
@@ -16,8 +16,12 @@ abstractFileManager.prototype.getPath = function (filename) {
|
||||
return filename.slice(0, j + 1);
|
||||
};
|
||||
|
||||
abstractFileManager.prototype.tryAppendExtension = function(path, ext) {
|
||||
return /(\.[a-z]*$)|([\?;].*)$/.test(path) ? path : path + ext;
|
||||
};
|
||||
|
||||
abstractFileManager.prototype.tryAppendLessExtension = function(path) {
|
||||
return /(\.[a-z]*$)|([\?;].*)$/.test(path) ? path : path + '.less';
|
||||
return this.tryAppendExtension(path, '.less');
|
||||
};
|
||||
|
||||
abstractFileManager.prototype.supportsSync = function() {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
var functionRegistry = require("./function-registry"),
|
||||
Expression = require("../tree/expression");
|
||||
var Expression = require("../tree/expression");
|
||||
|
||||
var functionCaller = function(name, context, index, currentFileInfo) {
|
||||
this.name = name.toLowerCase();
|
||||
this.func = functionRegistry.get(this.name);
|
||||
this.index = index;
|
||||
this.context = context;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
|
||||
this.func = context.frames[0].functionRegistry.get(this.name);
|
||||
};
|
||||
functionCaller.prototype.isValid = function() {
|
||||
return Boolean(this.func);
|
||||
|
||||
@@ -1,18 +1,29 @@
|
||||
module.exports = {
|
||||
_data: {},
|
||||
add: function(name, func) {
|
||||
if (this._data.hasOwnProperty(name)) {
|
||||
//TODO warn
|
||||
function makeRegistry( base ) {
|
||||
return {
|
||||
_data: {},
|
||||
add: function(name, func) {
|
||||
// precautionary case conversion, as later querying of
|
||||
// the registry by function-caller uses lower case as well.
|
||||
name = name.toLowerCase();
|
||||
|
||||
if (this._data.hasOwnProperty(name)) {
|
||||
//TODO warn
|
||||
}
|
||||
this._data[name] = func;
|
||||
},
|
||||
addMultiple: function(functions) {
|
||||
Object.keys(functions).forEach(
|
||||
function(name) {
|
||||
this.add(name, functions[name]);
|
||||
}.bind(this));
|
||||
},
|
||||
get: function(name) {
|
||||
return this._data[name] || ( base && base.get( name ));
|
||||
},
|
||||
inherit : function() {
|
||||
return makeRegistry( this );
|
||||
}
|
||||
this._data[name] = func;
|
||||
},
|
||||
addMultiple: function(functions) {
|
||||
Object.keys(functions).forEach(
|
||||
function(name) {
|
||||
this.add(name, functions[name]);
|
||||
}.bind(this));
|
||||
},
|
||||
get: function(name) {
|
||||
return this._data[name];
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = makeRegistry( null );
|
||||
@@ -1,5 +1,6 @@
|
||||
var contexts = require("./contexts"),
|
||||
Parser = require('./parser/parser');
|
||||
Parser = require('./parser/parser'),
|
||||
FunctionImporter = require('./plugins/function-importer');
|
||||
|
||||
module.exports = function(environment) {
|
||||
|
||||
@@ -65,7 +66,7 @@ module.exports = function(environment) {
|
||||
}
|
||||
|
||||
if (tryAppendLessExtension) {
|
||||
path = fileManager.tryAppendLessExtension(path);
|
||||
path = fileManager.tryAppendExtension(path, importOptions.plugin ? ".js" : ".less");
|
||||
}
|
||||
|
||||
var loadFileCallback = function(loadedFile) {
|
||||
@@ -101,7 +102,11 @@ module.exports = function(environment) {
|
||||
newFileInfo.reference = true;
|
||||
}
|
||||
|
||||
if (importOptions.inline) {
|
||||
if (importOptions.plugin) {
|
||||
new FunctionImporter(newEnv, newFileInfo).eval(contents, function (e, root) {
|
||||
fileParsedFunc(e, root, resolvedFilename);
|
||||
});
|
||||
} else if (importOptions.inline) {
|
||||
fileParsedFunc(null, contents, resolvedFilename);
|
||||
} else {
|
||||
new Parser(newEnv, importManager, newFileInfo).parse(contents, function (e, root) {
|
||||
|
||||
@@ -1304,6 +1304,41 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
}
|
||||
},
|
||||
|
||||
//
|
||||
// A @plugin directive, used to import compiler extensions dynamically.
|
||||
//
|
||||
// @plugin "lib";
|
||||
//
|
||||
// Depending on our environment, importing is done differently:
|
||||
// In the browser, it's an XHR request, in Node, it would be a
|
||||
// file-system operation. The function used for importing is
|
||||
// stored in `import`, which we pass to the Import constructor.
|
||||
//
|
||||
plugin: function () {
|
||||
var path,
|
||||
index = parserInput.i,
|
||||
dir = parserInput.$re(/^@plugin?\s+/);
|
||||
|
||||
if (dir) {
|
||||
var options = { plugin : true };
|
||||
|
||||
if ((path = this.entities.quoted() || this.entities.url())) {
|
||||
|
||||
if (!parserInput.$(';')) {
|
||||
parserInput.i = index;
|
||||
error("missing semi-colon on plugin");
|
||||
}
|
||||
|
||||
return new(tree.Import)(path, null, options, index, fileInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
parserInput.i = index;
|
||||
error("malformed plugin statement");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
//
|
||||
// A CSS Directive
|
||||
//
|
||||
@@ -1315,7 +1350,7 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
|
||||
if (parserInput.currentChar() !== '@') { return; }
|
||||
|
||||
value = this['import']() || this.media();
|
||||
value = this['import']() || this.plugin() || this.media();
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
35
lib/less/plugins/function-importer.js
Normal file
35
lib/less/plugins/function-importer.js
Normal file
@@ -0,0 +1,35 @@
|
||||
var LessError = require('../less-error'),
|
||||
tree = require("../tree");
|
||||
|
||||
var FunctionImporter = module.exports = function FunctionImporter(context, fileInfo) {
|
||||
this.fileInfo = fileInfo;
|
||||
};
|
||||
|
||||
FunctionImporter.prototype.eval = function(contents, callback) {
|
||||
var loaded = {},
|
||||
loader,
|
||||
registry;
|
||||
|
||||
registry = {
|
||||
add: function(name, func) {
|
||||
loaded[name] = func;
|
||||
},
|
||||
addMultiple: function(functions) {
|
||||
Object.keys(functions).forEach(function(name) {
|
||||
loaded[name] = functions[name];
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
loader = new Function("functions", "tree", "fileInfo", contents);
|
||||
loader(registry, tree, this.fileInfo);
|
||||
} catch(e) {
|
||||
callback(new LessError({
|
||||
message: "Plugin evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" ,
|
||||
filename: this.fileInfo.filename
|
||||
}), null );
|
||||
}
|
||||
|
||||
callback(null, { functions: loaded });
|
||||
};
|
||||
@@ -50,7 +50,7 @@ Import.prototype.accept = function (visitor) {
|
||||
this.features = visitor.visit(this.features);
|
||||
}
|
||||
this.path = visitor.visit(this.path);
|
||||
if (!this.options.inline && this.root) {
|
||||
if (!this.options.plugin && !this.options.inline && this.root) {
|
||||
this.root = visitor.visit(this.root);
|
||||
}
|
||||
};
|
||||
@@ -109,7 +109,8 @@ Import.prototype.evalPath = function (context) {
|
||||
return path;
|
||||
};
|
||||
Import.prototype.eval = function (context) {
|
||||
var ruleset, features = this.features && this.features.eval(context);
|
||||
var ruleset, registry,
|
||||
features = this.features && this.features.eval(context);
|
||||
|
||||
if (this.skip) {
|
||||
if (typeof this.skip === "function") {
|
||||
@@ -120,7 +121,13 @@ Import.prototype.eval = function (context) {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.inline) {
|
||||
if (this.options.plugin) {
|
||||
registry = context.frames[0] && context.frames[0].functionRegistry;
|
||||
if ( registry && this.root.functions ) {
|
||||
registry.addMultiple( this.root.functions );
|
||||
}
|
||||
return [];
|
||||
} else if (this.options.inline) {
|
||||
var contents = new Anonymous(this.root, 0, {filename: this.importedFilename}, true, true);
|
||||
return this.features ? new Media([contents], this.features.value) : [contents];
|
||||
} else if (this.css) {
|
||||
|
||||
@@ -44,6 +44,7 @@ Definition.prototype.evalParams = function (context, mixinEnv, args, evaldArgume
|
||||
i, j, val, name, isNamedFound, argIndex, argsLength = 0;
|
||||
|
||||
mixinEnv = new contexts.Eval(mixinEnv, [frame].concat(mixinEnv.frames));
|
||||
frame.functionRegistry = context.frames[0].functionRegistry.inherit();
|
||||
|
||||
if (args) {
|
||||
args = args.slice(0);
|
||||
|
||||
@@ -4,6 +4,7 @@ var Node = require("./node"),
|
||||
Element = require("./element"),
|
||||
Paren = require("./paren"),
|
||||
contexts = require("../contexts"),
|
||||
globalFunctionRegistry = require("../functions/function-registry"),
|
||||
defaultFunc = require("../functions/default"),
|
||||
getDebugInfo = require("./debug-info");
|
||||
|
||||
@@ -66,6 +67,10 @@ Ruleset.prototype.eval = function (context) {
|
||||
rules.length = 0;
|
||||
}
|
||||
|
||||
// inherit a function registry from the frames stack when possible;
|
||||
// otherwise from the global registry
|
||||
ruleset.functionRegistry = ((context.frames[0] && context.frames[0].functionRegistry) || globalFunctionRegistry).inherit();
|
||||
|
||||
// push the current ruleset to the frames stack
|
||||
var ctxFrames = context.frames;
|
||||
ctxFrames.unshift(ruleset);
|
||||
|
||||
6
test/css/import-plugin-scoped.css
Normal file
6
test/css/import-plugin-scoped.css
Normal file
@@ -0,0 +1,6 @@
|
||||
.in-scope {
|
||||
result: PASS;
|
||||
}
|
||||
.out-of-scope {
|
||||
result: test();
|
||||
}
|
||||
6
test/css/import-plugin-tiered.css
Normal file
6
test/css/import-plugin-tiered.css
Normal file
@@ -0,0 +1,6 @@
|
||||
.test {
|
||||
result: PASS;
|
||||
}
|
||||
.test-again {
|
||||
result: PASS;
|
||||
}
|
||||
3
test/css/import-plugin.css
Normal file
3
test/css/import-plugin.css
Normal file
@@ -0,0 +1,3 @@
|
||||
.test {
|
||||
result: PASS;
|
||||
}
|
||||
9
test/less/import-plugin-scoped.less
Normal file
9
test/less/import-plugin-scoped.less
Normal file
@@ -0,0 +1,9 @@
|
||||
.in-scope {
|
||||
@plugin "./plugins/test";
|
||||
result : test();
|
||||
}
|
||||
|
||||
.out-of-scope {
|
||||
result : test();
|
||||
}
|
||||
|
||||
5
test/less/import-plugin-tiered.less
Normal file
5
test/less/import-plugin-tiered.less
Normal file
@@ -0,0 +1,5 @@
|
||||
@import "import-plugin";
|
||||
|
||||
.test-again {
|
||||
result : test();
|
||||
}
|
||||
5
test/less/import-plugin.less
Normal file
5
test/less/import-plugin.less
Normal file
@@ -0,0 +1,5 @@
|
||||
@plugin "./plugins/test";
|
||||
|
||||
.test {
|
||||
result : test();
|
||||
}
|
||||
4
test/less/plugins/test.js
Normal file
4
test/less/plugins/test.js
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
functions.add("test", function() {
|
||||
return new tree.Anonymous( "PASS" );
|
||||
});
|
||||
Reference in New Issue
Block a user