Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Namespaced values #3242

Merged
merged 25 commits into from
Jun 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3642ca9
Added target tests for namespacing
matthew-dean Feb 4, 2018
f3c7f70
calc() fix - fixes #974
matthew-dean Feb 10, 2018
e2242b9
Add support for nested functions in calc()
matthew-dean Feb 10, 2018
327b5d5
Merge branch '3.x' into edge
matthew-dean Feb 10, 2018
c185461
Merge branch 'feature/calc-fix' into edge
matthew-dean Feb 10, 2018
192ab14
Merge branch 'master' into edge
matthew-dean Feb 16, 2018
3657e5a
Merge branch 'master' into edge
matthew-dean Jun 26, 2018
737d700
Parses and retrieves a namespaced value!!
matthew-dean Jun 26, 2018
a56aed0
Parsing and eval-ing most values, just have to fix some lookups for DRs
matthew-dean Jun 26, 2018
1b07021
Correctly parses and does lookups, but breaks other tests
matthew-dean Jun 27, 2018
2349595
Merge branch 'master' into edge
matthew-dean Jun 27, 2018
b07af62
Finished tests and feature implementation
matthew-dean Jun 28, 2018
82a2e75
Merge branch 'master' into feature/namespace-value
matthew-dean Jun 28, 2018
8e9d75a
Commit dist files
matthew-dean Jun 28, 2018
6e3c124
Added one more namespaced map test
matthew-dean Jun 28, 2018
2188fb1
Experimenting with more namespacing stuff
matthew-dean Jun 29, 2018
c56b28e
Added amazingly cool aliasing example
matthew-dean Jun 29, 2018
b94f041
Added more CSS Grid tests
matthew-dean Jun 29, 2018
d3a9c05
Added the ability to alias mixin calls
matthew-dean Jun 30, 2018
f968e61
Added tests for passing mixins into mixins, since it's just another v…
matthew-dean Jun 30, 2018
ca944ed
Release v3.5.0-beta.3
matthew-dean Jun 30, 2018
fdf55d2
Merge branch 'master' into release/v3.5.0-beta.3
matthew-dean Jun 30, 2018
b9b4eb3
Merge branch 'release/v3.5.0-beta.3' into feature/namespace-value
matthew-dean Jun 30, 2018
3a2f805
Merge branch 'master' of https://github.com/less/less.js
matthew-dean Jun 30, 2018
03f8130
Added dist files for this PR
matthew-dean Jun 30, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Less test",
"program": "${workspaceFolder}/test/index.js",
"cwd": "${workspaceFolder}",
"console": "integratedTerminal"
}
]
}
461 changes: 356 additions & 105 deletions dist/less.js

Large diffs are not rendered by default.

13 changes: 7 additions & 6 deletions dist/less.min.js

Large diffs are not rendered by default.

186 changes: 154 additions & 32 deletions lib/less/parser/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -573,11 +573,26 @@ var Parser = function Parser(context, imports, fileInfo) {
// see `parsers.variable`.
//
variable: function () {
var name, index = parserInput.i;
var ch, name, index = parserInput.i;

parserInput.save();
if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^@@?[\w-]+/))) {
ch = parserInput.currentChar();
if (ch === '(' || ch === '[') {
// this may be a VariableCall lookup
var result = parsers.variableCall(name);
if (!result) {
return parserInput.restore();
}
else {
parserInput.forget();
return result;
}
}
parserInput.forget();
return new(tree.Variable)(name, index, fileInfo);
}
parserInput.restore();
},

// A variable entity using the protective {} e.g. @{var}
Expand Down Expand Up @@ -718,18 +733,49 @@ var Parser = function Parser(context, imports, fileInfo) {
},

//
// Call a variable value
// Call a variable value to retrieve a detached ruleset
// or a value from a detached ruleset's rules.
//
// @fink()
// @fink();
// @fink;
// color: @fink[@color];
//
variableCall: function () {
var name;
variableCall: function (parsedName) {
var lookups, important, i = parserInput.i,
inValue = !!parsedName, name = parsedName;

parserInput.save();

if (name || (parserInput.currentChar() === '@'
&& (name = parserInput.$re(/^(@[\w-]+)(\(\s*\))?/)))) {

lookups = this.mixin.ruleLookups();

if (parserInput.currentChar() === '@'
&& (name = parserInput.$re(/^(@[\w-]+)\(\s*\)/))
&& parsers.end()) {
return new tree.VariableCall(name[1]);
if (!lookups && name[2] !== '()') {
parserInput.restore('Missing \'[...]\' lookup in variable call');
return;
}

if (!inValue) {
name = name[1];
}

if (lookups && parsers.important()) {
important = true;
}

var call = new tree.VariableCall(name, i, fileInfo);
if (!inValue && parsers.end()) {
parserInput.forget();
return call;
}
else {
parserInput.forget();
return new tree.NamespaceValue(call, lookups, important, i, fileInfo);
}
}

parserInput.restore();
},

//
Expand Down Expand Up @@ -793,54 +839,85 @@ var Parser = function Parser(context, imports, fileInfo) {
// A Mixin call, with an optional argument list
//
// #mixins > .square(#fff);
// #mixins.square(#fff);
// .rounded(4px, black);
// .button;
//
// We can lookup / return a value using the lookup syntax:
//
// color: #mixin.square(#fff)[@color];
//
// The `while` loop is there because mixins can be
// namespaced, but we only support the child and descendant
// selector for now.
//
call: function () {
var s = parserInput.currentChar(), important = false, index = parserInput.i, elemIndex,
elements, elem, e, c, args;
call: function (inValue) {
var s = parserInput.currentChar(), important = false,
index = parserInput.i, elements, args, hasParens;

if (s !== '.' && s !== '#') { return; }

parserInput.save(); // stop us absorbing part of an invalid selector

while (true) {
elemIndex = parserInput.i;
e = parserInput.$re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/);
if (!e) {
break;
}
elem = new(tree.Element)(c, e, false, elemIndex, fileInfo);
if (elements) {
elements.push(elem);
} else {
elements = [ elem ];
}
c = parserInput.$char('>');
}
elements = this.elements();

if (elements) {
if (parserInput.$char('(')) {
args = this.args(true).args;
expectChar(')');
hasParens = true;
}

var lookups = this.ruleLookups();

if (inValue && !lookups && !hasParens) {
// This isn't a valid in-value mixin call
parserInput.restore();
return;
}

if (parsers.important()) {
if (!inValue && parsers.important()) {
important = true;
}

if (parsers.end()) {
if (inValue || parsers.end()) {
parserInput.forget();
return new(tree.mixin.Call)(elements, args, index, fileInfo, important);
var mixin = new(tree.mixin.Call)(elements, args, index, fileInfo, !lookups && important);
if (lookups) {
return new tree.NamespaceValue(mixin, lookups, important);
}
else {
return mixin;
}
}
}

parserInput.restore();
},
/**
* Matching elements for mixins
* (Start with . or # and can have > )
*/
elements: function() {
var elements, e, c, elem, elemIndex,
re = /^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/;
while (true) {
elemIndex = parserInput.i;
e = parserInput.$re(re);

if (!e) {
break;
}
elem = new(tree.Element)(c, e, false, elemIndex, fileInfo);
if (elements) {
elements.push(elem);
} else {
elements = [ elem ];
}
c = parserInput.$char('>');
}
return elements;
},
args: function (isCall) {
var entities = parsers.entities,
returner = { args:null, variadic: false },
Expand Down Expand Up @@ -1022,9 +1099,54 @@ var Parser = function Parser(context, imports, fileInfo) {
} else {
parserInput.forget();
}
},

ruleLookups: function() {
var rule, args, lookups = [];

if (parserInput.currentChar() !== '[') {
return;
}

while (true) {
parserInput.save();
args = null;
rule = this.lookupValue();
if (!rule) {
parserInput.restore();
break;
}
lookups.push(rule);
parserInput.forget();
}
if (lookups.length > 0) {
return lookups;
}
},

lookupValue: function() {
parserInput.save();

if (!parserInput.$char('[')) {
parserInput.restore();
return;
}

var name = parserInput.$re(/^(?:@{0,2}|\$)[_a-zA-Z0-9-]+/);

if (!parserInput.$char(']')) {
parserInput.restore();
return;
}

if (name) {
parserInput.forget();
return name;
}

parserInput.restore();
}
},

//
// Entities are the smallest recognized token,
// and can be found inside a rule's value.
Expand All @@ -1033,7 +1155,7 @@ var Parser = function Parser(context, imports, fileInfo) {
var entities = this.entities;

return this.comment() || entities.literal() || entities.variable() || entities.url() ||
entities.property() || entities.call() || entities.keyword() || entities.javascript();
entities.property() || entities.call() || entities.keyword() || this.mixin.call(true) || entities.javascript();
},

//
Expand Down Expand Up @@ -1339,7 +1461,7 @@ var Parser = function Parser(context, imports, fileInfo) {
},
anonymousValue: function () {
var index = parserInput.i;
var match = parserInput.$re(/^([^@\$+\/'"*`(;{}-]*);/);
var match = parserInput.$re(/^([^.#@\$+\/'"*`(;{}-]*);/);
if (match) {
return new(tree.Anonymous)(match[1], index);
}
Expand Down
1 change: 1 addition & 0 deletions lib/less/tree/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,6 @@ tree.UnicodeDescriptor = require('./unicode-descriptor');
tree.Negative = require('./negative');
tree.Extend = require('./extend');
tree.VariableCall = require('./variable-call');
tree.NamespaceValue = require('./namespace-value');

module.exports = tree;
2 changes: 2 additions & 0 deletions lib/less/tree/mixin-call.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ MixinCall.prototype.eval = function (context) {
candidates = [], candidate, conditionResult = [], defaultResult, defFalseEitherCase = -1,
defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset, noArgumentsFilter;

this.selector = this.selector.eval(context);

function calcDefGroup(mixin, mixinPath) {
var f, p, namespace;

Expand Down
9 changes: 8 additions & 1 deletion lib/less/tree/mixin-definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ var Selector = require('./selector'),
Element = require('./element'),
Ruleset = require('./ruleset'),
Declaration = require('./declaration'),
DetachedRuleset = require('./detached-ruleset'),
Expression = require('./expression'),
contexts = require('../contexts'),
utils = require('../utils');
Expand Down Expand Up @@ -97,7 +98,13 @@ Definition.prototype.evalParams = function (context, mixinEnv, args, evaldArgume
} else {
val = arg && arg.value;
if (val) {
val = val.eval(context);
// This was a mixin call, pass in a detached ruleset of it's eval'd rules
if (Array.isArray(val)) {
val = new DetachedRuleset(new Ruleset('', val));
}
else {
val = val.eval(context);
}
} else if (params[i].value) {
val = params[i].value.eval(mixinEnv);
frame.resetCache();
Expand Down
Loading