Skip to content

Master starting style rule #8

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

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
319 changes: 306 additions & 13 deletions dist/less.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/less.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/less.min.js.map

Large diffs are not rendered by default.

319 changes: 306 additions & 13 deletions packages/less/dist/less.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/less/dist/less.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/less/dist/less.min.js.map

Large diffs are not rendered by default.

38 changes: 34 additions & 4 deletions packages/less/src/less/parser/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import visitors from '../visitors';
import getParserInput from './parser-input';
import * as utils from '../utils';
import functionRegistry from '../functions/function-registry';
import { ContainerSyntaxOptions, MediaSyntaxOptions } from '../tree/atrule-syntax';
import { ContainerSyntaxOptions, MediaSyntaxOptions, ScopeSyntaxOptions } from '../tree/atrule-syntax';

//
// less.js - parser
Expand Down Expand Up @@ -1820,7 +1820,14 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
parserInput.restore();
e = this.value();
}
if (parserInput.$char(')')) {

if (syntaxOptions.scopeAtRule && !p && syntaxOptions.queryInParens) {
parserInput.restore();
p = this.selector();
if (p) {
nodes.push(p);
}
} else if (parserInput.$char(')')) {
if (p && !e) {
nodes.push(new (tree.Paren)(new (tree.QueryInParens)(p.op, p.lvalue, p.rvalue, rangeP ? rangeP.op : null, rangeP ? rangeP.rvalue : null, p._index)));
e = p;
Expand Down Expand Up @@ -1896,10 +1903,33 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
if (parserInput.$str('@media')) {
return this.prepareAndGetNestableAtRule(tree.Media, index, debugInfo, MediaSyntaxOptions);
}

if (parserInput.$str('@container')) {
else if (parserInput.$str('@container')) {
return this.prepareAndGetNestableAtRule(tree.Container, index, debugInfo, ContainerSyntaxOptions);
}
else if (parserInput.$str('@scope')) {
return this.prepareAndGetNestableAtRule(tree.Scope, index, debugInfo, ScopeSyntaxOptions);
}
else if (parserInput.$str('@starting-style')) {
var rules = [];
if (parserInput.$re(/.*{/)) {
let e;
while (e = this.declaration()) {
rules.push(e);
}
}
if (rules.length === 0) {
parserInput.restore();
return this.prepareAndGetNestableAtRule(tree.StartingStyle, index, debugInfo, MediaSyntaxOptions);
} else if (parserInput.$char('}')) {
var atRule = new (tree.StartingStyle)(rules, [], index + currentIndex, fileInfo);
if (context.dumpLineNumbers) {
atRule.debugInfo = debugInfo;
}
return atRule;
} else {
error('starting-style definitions require declarations or rulesets after declaration');
}
}
}

parserInput.restore();
Expand Down
5 changes: 5 additions & 0 deletions packages/less/src/less/tree/atrule-syntax.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,8 @@ export const MediaSyntaxOptions = {
export const ContainerSyntaxOptions = {
queryInParens: true
};

export const ScopeSyntaxOptions = {
queryInParens: true,
scopeAtRule: true
};
4 changes: 4 additions & 0 deletions packages/less/src/less/tree/combinator.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ Combinator.prototype = Object.assign(new Node(), {

genCSS(context, output) {
const spaceOrEmpty = (context.compress || _noSpaceCombinators[this.value]) ? '' : ' ';
if (context.scopeAtRule && context.scopeRuleOffset ===0 && this.value === ' ') {
this.value = '';
}
context.scopeRuleOffset++;
output.add(spaceOrEmpty + this.value + spaceOrEmpty);
}
});
Expand Down
4 changes: 3 additions & 1 deletion packages/less/src/less/tree/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import QueryInParens from './query-in-parens';
import Paren from './paren';
import Media from './media';
import Container from './container';
import Scope from './scope';
import StartingStyle from './starting-style';
import UnicodeDescriptor from './unicode-descriptor';
import Negative from './negative';
import Extend from './extend';
Expand All @@ -47,7 +49,7 @@ export default {
Comment, Anonymous, Value, JavaScript, Assignment,
Condition, Paren, Media, Container, QueryInParens,
UnicodeDescriptor, Negative, Extend, VariableCall,
NamespaceValue,
NamespaceValue, Scope, StartingStyle,
mixin: {
Call: MixinCall,
Definition: MixinDefinition
Expand Down
1 change: 1 addition & 0 deletions packages/less/src/less/tree/media.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Media.prototype = Object.assign(new AtRule(), {

genCSS(context, output) {
output.add('@media ', this._fileInfo, this._index);
context.firstSelector = true;
this.features.genCSS(context, output);
this.outputRuleset(context, output, this.rules);
},
Expand Down
27 changes: 21 additions & 6 deletions packages/less/src/less/tree/ruleset.js
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ Ruleset.prototype = Object.assign(new Node(), {

const paths = this.paths;
const pathCnt = paths.length;
let pathSubCnt;
let pathSubCnt, appendedAmp;

sep = context.compress ? ',' : (`,\n${tabSetStr}`);

Expand All @@ -516,11 +516,22 @@ Ruleset.prototype = Object.assign(new Node(), {
if (i > 0) { output.add(sep); }

context.firstSelector = true;
path[0].genCSS(context, output);

if (!(pathSubCnt > 1 && path[0].elements.length === 1 && path[0].elements[0].value === '&')) {
path[0].genCSS(context, output);
}
appendedAmp = true;

context.firstSelector = false;
for (j = 1; j < pathSubCnt; j++) {
if (j === 0 && pathSubCnt > 1 && path[j].elements.length === 1 && path[j].elements[0].value === '&') {
continue;
} else if (path[j].elements[0].value === '&' && appendedAmp) {
continue;
}

path[j].genCSS(context, output);
appendedAmp = true;
}
}

Expand Down Expand Up @@ -559,18 +570,20 @@ Ruleset.prototype = Object.assign(new Node(), {
context.tabLevel--;
}

context.scopeRuleOffset = 0;

if (!output.isEmpty() && !context.compress && this.firstRoot) {
output.add('\n');
}
},

joinSelectors(paths, context, selectors) {
joinSelectors(paths, context, selectors, visitArgs) {
for (let s = 0; s < selectors.length; s++) {
this.joinSelector(paths, context, selectors[s]);
this.joinSelector(paths, context, selectors[s], visitArgs);
}
},

joinSelector(paths, context, selector) {
joinSelector(paths, context, selector, visitArgs) {

function createParenthesis(elementsToPak, originalElement) {
let replacementParen, j;
Expand Down Expand Up @@ -754,6 +767,8 @@ Ruleset.prototype = Object.assign(new Node(), {
currentElements.push(el);
}

} else if (el.value === '&' && el.combinator.value === '' && visitArgs && visitArgs.preserve) {
currentElements.push(new Element(el.value));
} else {
hadParentSelector = true;
// the new list of selectors to add
Expand Down Expand Up @@ -820,7 +835,7 @@ Ruleset.prototype = Object.assign(new Node(), {
let i, newPaths, hadParentSelector;

newPaths = [];
hadParentSelector = replaceParentSelector(newPaths, context, selector);
hadParentSelector = replaceParentSelector(newPaths, context, selector, visitArgs);

if (!hadParentSelector) {
if (context.length > 0) {
Expand Down
156 changes: 156 additions & 0 deletions packages/less/src/less/tree/scope.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import Ruleset from './ruleset';
import Value from './value';
import Selector from './selector';
import AtRule from './atrule';
import NestableAtRulePrototype from './nested-at-rule';
import Anonymous from './anonymous';
import Expression from './expression';

const Scope = function (value, features, index, currentFileInfo, visibilityInfo) {
this._index = index;
this._fileInfo = currentFileInfo;

const selectors = (new Selector([], null, null, this._index, this._fileInfo)).createEmptySelectors();

this.features = new Value(features);
this.rules = [new Ruleset(selectors, value)];
this.rules[0].allowImports = true;
this.copyVisibilityInfo(visibilityInfo);
this.allowRoot = true;
this.setParent(selectors, this);
this.setParent(this.features, this);
this.setParent(this.rules, this);
};

Scope.prototype = Object.assign(new AtRule(), {
type: 'Scope',

...NestableAtRulePrototype,

accept: function (visitor) {
if (this.features) {
this.features = visitor.visit(this.features, { preserve: true });
}
if (this.rules) {
this.rules = visitor.visitArray(this.rules, undefined, { preserve: true });
}
},

genCSS(context, output) {
if (this.rules && (Array.isArray(this.rules) && this.rules.length > 0) || (Array.isArray(this.rules[0]) && this.rules[0].length > 0)) {
context.scopeRuleOffset = 0;
context.scopeAtRule = true;
output.add('@scope ', this._fileInfo, this._index);
context.firstSelector = true;
this.features.genCSS(context, output);
context.scopeRuleOffset = 0;
this.outputRuleset(context, output, this.rules);
}
},

eval(context) {
if (!context.mediaBlocks) {
context.mediaBlocks = [];
context.mediaPath = [];
}

const media = new Scope(null, [], this._index, this._fileInfo, this.visibilityInfo());
if (this.debugInfo) {
this.rules[0].debugInfo = this.debugInfo;
media.debugInfo = this.debugInfo;
}

media.features = this.features.eval(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();

context.mediaPath.pop();

return context.mediaPath.length === 0 ? media.evalTop(context) :
media.evalNested(context);
},

getNestedElementValue(pathNode) {
let tmp = pathNode.value.trim();

if (tmp.startsWith('(')) {
tmp = tmp.substring(1);
}
if (tmp.endsWith(')')) {
tmp = tmp.substring(0, tmp.length - 1);
}
if (tmp.startsWith(':scope')) {
tmp = tmp.substring(6).trim();
}

return tmp;
},

evalNested(context) {
let i, n;
let value;
let path = context.mediaPath.concat([this]);

// Extract the media-query conditions separated with `,` (OR).
for (i = 0; i < path.length; i++) {
value = path[i].features instanceof Value ?
path[i].features.value : path[i].features;
path[i] = Array.isArray(value) ? value : [value];
}

let fromCss = '', toCss = '', tmp;

for (i = 0; i < path.length; ++i) {
let buildTo = true;

for (n = 0; n < path[i].length; ++n) {
for (let e = 0; e < path[i][n].elements.length; ++e) {
if (path[i][n].elements[e].value === 'to') {
buildTo = false;
} else if (buildTo) {
tmp = this.getNestedElementValue(path[i][n].elements[e]);

if (fromCss.length > 0 && !tmp.startsWith('>')) {
fromCss += ' > ';
}else {
fromCss += fromCss !== '' ? ' ' : '';
}

fromCss += tmp;
} else {
tmp = this.getNestedElementValue(path[i][n].elements[e]);

if (toCss.length > 0 && !tmp.startsWith('>')) {
toCss += ' > ';
}

toCss += tmp;
}
}
}
}

path = new Value(
new Expression([
new Selector(' (' + fromCss + ')'),
new Anonymous('to'),
new Selector('(' + fromCss + ' > ' + toCss + ')')
])
);

this.features = path;
this.setParent(this.features, this);

// Fake a tree-node that doesn't output anything.
return new Ruleset([], []);
},
});

export default Scope;
Loading