diff --git a/lib/less/tree/import.js b/lib/less/tree/import.js index a6c980e75..18e439285 100644 --- a/lib/less/tree/import.js +++ b/lib/less/tree/import.js @@ -82,9 +82,11 @@ Import.prototype.isVariableImport = function () { }; Import.prototype.evalForImport = function (context) { var path = this.path; + if (path instanceof URL) { path = path.value; } + return new Import(path.eval(context), this.features, this.options, this.index, this.currentFileInfo); }; Import.prototype.evalPath = function (context) { @@ -108,6 +110,14 @@ Import.prototype.eval = function (context) { var ruleset, registry, features = this.features && this.features.eval(context); + if (this.options.plugin) { + registry = context.frames[0] && context.frames[0].functionRegistry; + if ( registry && this.root && this.root.functions ) { + registry.addMultiple( this.root.functions ); + } + return []; + } + if (this.skip) { if (typeof this.skip === "function") { this.skip = this.skip(); @@ -117,13 +127,7 @@ Import.prototype.eval = function (context) { } } - 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) { + 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) { diff --git a/lib/less/tree/media.js b/lib/less/tree/media.js index 1514f0b53..054d7715e 100644 --- a/lib/less/tree/media.js +++ b/lib/less/tree/media.js @@ -59,6 +59,7 @@ Media.prototype.eval = function (context) { context.mediaPath.push(media); context.mediaBlocks.push(media); + this.rules[0].functionRegistry = context.frames[0].functionRegistry.inherit(); context.frames.unshift(this.rules[0]); media.rules = [this.rules[0].eval(context)]; context.frames.shift(); diff --git a/lib/less/tree/mixin-definition.js b/lib/less/tree/mixin-definition.js index 9a92ed01b..2f2b9dc1b 100644 --- a/lib/less/tree/mixin-definition.js +++ b/lib/less/tree/mixin-definition.js @@ -43,8 +43,10 @@ Definition.prototype.evalParams = function (context, mixinEnv, args, evaldArgume params = this.params.slice(0), i, j, val, name, isNamedFound, argIndex, argsLength = 0; + if (mixinEnv.frames && mixinEnv.frames[0] && mixinEnv.frames[0].functionRegistry) { + frame.functionRegistry = mixinEnv.frames[0].functionRegistry.inherit(); + } mixinEnv = new contexts.Eval(mixinEnv, [frame].concat(mixinEnv.frames)); - frame.functionRegistry = context.frames[0].functionRegistry.inherit(); if (args) { args = args.slice(0); diff --git a/lib/less/tree/ruleset.js b/lib/less/tree/ruleset.js index ea2948f5b..e8d0f7ced 100644 --- a/lib/less/tree/ruleset.js +++ b/lib/less/tree/ruleset.js @@ -69,7 +69,16 @@ Ruleset.prototype.eval = function (context) { // 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(); + ruleset.functionRegistry = (function (frames) { + var i = 0, + n = frames.length, + found; + for ( ; i !== n ; ++i ) { + found = frames[ i ].functionRegistry; + if ( found ) { return found; } + } + return globalFunctionRegistry; + }(context.frames)).inherit(); // push the current ruleset to the frames stack var ctxFrames = context.frames; diff --git a/lib/less/visitors/import-visitor.js b/lib/less/visitors/import-visitor.js index 8ddddb6b3..e643117c2 100644 --- a/lib/less/visitors/import-visitor.js +++ b/lib/less/visitors/import-visitor.js @@ -103,6 +103,7 @@ ImportVisitor.prototype = { var importVisitor = this, inlineCSS = importNode.options.inline, + isPlugin = importNode.options.plugin, duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector; if (!context.importMultiple) { @@ -123,7 +124,7 @@ ImportVisitor.prototype = { importNode.root = root; importNode.importedFilename = fullPath; - if (!inlineCSS && (context.importMultiple || !duplicateImport)) { + if (!inlineCSS && !isPlugin && (context.importMultiple || !duplicateImport)) { importVisitor.recursionDetector[fullPath] = true; var oldContext = this.context; @@ -144,7 +145,16 @@ ImportVisitor.prototype = { } }, visitRule: function (ruleNode, visitArgs) { - visitArgs.visitDeeper = false; + if (ruleNode.value.type === "DetachedRuleset") { + this.context.frames.unshift(ruleNode); + } else { + visitArgs.visitDeeper = false; + } + }, + visitRuleOut : function(ruleNode) { + if (ruleNode.value.type === "DetachedRuleset") { + this.context.frames.shift(); + } }, visitDirective: function (directiveNode, visitArgs) { this.context.frames.unshift(directiveNode); diff --git a/test/css/import-plugin-scoped.css b/test/css/import-plugin-scoped.css deleted file mode 100644 index e42ec6478..000000000 --- a/test/css/import-plugin-scoped.css +++ /dev/null @@ -1,6 +0,0 @@ -.in-scope { - result: PASS; -} -.out-of-scope { - result: test(); -} diff --git a/test/css/import-plugin-tiered.css b/test/css/import-plugin-tiered.css deleted file mode 100644 index 7960b9f3f..000000000 --- a/test/css/import-plugin-tiered.css +++ /dev/null @@ -1,6 +0,0 @@ -.test { - result: PASS; -} -.test-again { - result: PASS; -} diff --git a/test/css/import-plugin.css b/test/css/import-plugin.css deleted file mode 100644 index 1c337756a..000000000 --- a/test/css/import-plugin.css +++ /dev/null @@ -1,3 +0,0 @@ -.test { - result: PASS; -} diff --git a/test/css/plugin.css b/test/css/plugin.css new file mode 100644 index 000000000..b837dc63e --- /dev/null +++ b/test/css/plugin.css @@ -0,0 +1,44 @@ +.other { + trans: transitive; +} +.class { + trans: transitive; + global: global; + local: test-local(); + shadow: global; +} +.class .local { + global: global; + local: local; + shadow: local; +} +.class { + ns-mixin-global: global; + ns-mixin-local: local; + ns-mixin-shadow: local; + mixin-local: local; + mixin-global: global; + mixin-shadow: local; + ruleset-local: local; + ruleset-global: global; + ruleset-shadow: local; + class-local: test-local(); +} +@media screen { + .test { + result: global; + } +} +@font-face { + result: global; +} +@media screen and (min-width: 100px) and (max-width: 400px) { + .test { + result: global; + } +} +@media screen { + .test { + result: local; + } +} diff --git a/test/less/import-plugin-scoped.less b/test/less/import-plugin-scoped.less deleted file mode 100644 index 7c0fa7825..000000000 --- a/test/less/import-plugin-scoped.less +++ /dev/null @@ -1,9 +0,0 @@ -.in-scope { - @plugin "./plugins/test"; - result : test(); -} - -.out-of-scope { - result : test(); -} - diff --git a/test/less/import-plugin-tiered.less b/test/less/import-plugin-tiered.less deleted file mode 100644 index eb487738b..000000000 --- a/test/less/import-plugin-tiered.less +++ /dev/null @@ -1,5 +0,0 @@ -@import "import-plugin"; - -.test-again { - result : test(); -} \ No newline at end of file diff --git a/test/less/import-plugin.less b/test/less/import-plugin.less deleted file mode 100644 index 7ee43e30a..000000000 --- a/test/less/import-plugin.less +++ /dev/null @@ -1,5 +0,0 @@ -@plugin "./plugins/test"; - -.test { - result : test(); -} \ No newline at end of file diff --git a/test/less/plugin.less b/test/less/plugin.less new file mode 100644 index 000000000..17524a400 --- /dev/null +++ b/test/less/plugin.less @@ -0,0 +1,85 @@ +// importing plugin globally +@plugin "./plugin/plugin-global"; + +// transitively include plugins from importing another sheet +@import "./plugin/plugin-transitive"; + + +// `test-global` function should be reachable +// `test-local` function should not be reachable +// `test-shadow` function should return global version +.class { + trans : test-transitive(); + global : test-global(); + local : test-local(); + shadow : test-shadow(); + + // `test-global` function should propagate and be reachable + // `test-local` function should be reachable + // `test-shadow` function should return local version, shadowing global version + .local { + @plugin "./plugin/plugin-local"; + global : test-global(); + local : test-local(); + shadow : test-shadow(); + } +} + +// calling a mixin or detached ruleset should not bubble local plugins +// imported inside either into the parent scope. +.mixin() { + @plugin "./plugin/plugin-local"; + mixin-local : test-local(); + mixin-global : test-global(); + mixin-shadow : test-shadow(); +} +@ruleset : { + @plugin "./plugin/plugin-local"; + ruleset-local : test-local(); + ruleset-global : test-global(); + ruleset-shadow : test-shadow(); +}; +#ns { + @plugin "./plugin/plugin-local"; + .mixin() { + ns-mixin-global : test-global(); + ns-mixin-local : test-local(); + ns-mixin-shadow : test-shadow(); + } +} +.class { + #ns > .mixin(); + .mixin(); + @ruleset(); + class-local : test-local(); +} + + +// `test-global` function should propagate into directive scope +@media screen { + .test { + result : test-global(); + } +} +@font-face { + result : test-global(); +} + +// `test-global` function should propagate into nested directive scopes +@media screen and (min-width:100px) { + @media (max-width:400px) { + .test { + result : test-global(); + } + } +} + +.test { + @media screen { + @plugin "./plugin/plugin-local"; + result : test-local(); + } +} + + + diff --git a/test/less/plugin/plugin-global.js b/test/less/plugin/plugin-global.js new file mode 100644 index 000000000..adc828c49 --- /dev/null +++ b/test/less/plugin/plugin-global.js @@ -0,0 +1,9 @@ + +functions.addMultiple({ + "test-shadow" : function() { + return new tree.Anonymous( "global" ); + }, + "test-global" : function() { + return new tree.Anonymous( "global" ); + } +}); diff --git a/test/less/plugin/plugin-local.js b/test/less/plugin/plugin-local.js new file mode 100644 index 000000000..b54ca97a8 --- /dev/null +++ b/test/less/plugin/plugin-local.js @@ -0,0 +1,8 @@ +functions.addMultiple({ + "test-shadow" : function() { + return new tree.Anonymous( "local" ); + }, + "test-local" : function() { + return new tree.Anonymous( "local" ); + } +}); diff --git a/test/less/plugin/plugin-transitive.js b/test/less/plugin/plugin-transitive.js new file mode 100644 index 000000000..1da1864c4 --- /dev/null +++ b/test/less/plugin/plugin-transitive.js @@ -0,0 +1,5 @@ +functions.addMultiple({ + "test-transitive" : function() { + return new tree.Anonymous( "transitive" ); + } +}); diff --git a/test/less/plugin/plugin-transitive.less b/test/less/plugin/plugin-transitive.less new file mode 100644 index 000000000..8e4ca00bb --- /dev/null +++ b/test/less/plugin/plugin-transitive.less @@ -0,0 +1,5 @@ +@plugin "plugin-transitive"; + +.other { + trans : test-transitive(); +} \ No newline at end of file diff --git a/test/less/plugins/test.js b/test/less/plugins/test.js deleted file mode 100644 index f54ee82b1..000000000 --- a/test/less/plugins/test.js +++ /dev/null @@ -1,4 +0,0 @@ - -functions.add("test", function() { - return new tree.Anonymous( "PASS" ); -});