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

fix(issue:3766) add support for container queries #3811

Merged
merged 4 commits into from
Aug 5, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
14,062 changes: 7,098 additions & 6,964 deletions dist/less.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions 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.

14,062 changes: 7,098 additions & 6,964 deletions packages/less/dist/less.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions 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.

7 changes: 7 additions & 0 deletions packages/less/src/less/parser/parser-input.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,13 @@ export default () => {
return tok;
};

parserInput.$peekChar = tok => {
if (input.charAt(parserInput.i) !== tok) {
return null;
}
return tok;
};

parserInput.$str = tok => {
const tokLength = tok.length;

Expand Down
77 changes: 47 additions & 30 deletions packages/less/src/less/parser/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +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';

//
// less.js - parser
Expand Down Expand Up @@ -1698,7 +1699,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
const options = (dir ? this.importOptions() : null) || {};

if ((path = this.entities.quoted() || this.entities.url())) {
features = this.mediaFeatures();
features = this.mediaFeatures({});

if (!parserInput.$char(';')) {
parserInput.i = index;
Expand Down Expand Up @@ -1752,7 +1753,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
}
},

mediaFeature: function () {
mediaFeature: function (syntaxOptions) {
const entities = this.entities;
const nodes = [];
let e;
Expand All @@ -1764,10 +1765,20 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
nodes.push(e);
} else if (parserInput.$char('(')) {
p = this.property();
e = this.value();
parserInput.save();
if (!p && syntaxOptions.queryInParens && parserInput.$re(/^[a-z-]*\s*([<>]=|<=|>=|[<>]|=)/)) {
parserInput.restore();
p = this.condition();
} else {
parserInput.restore();
e = this.value();
}
if (parserInput.$char(')')) {
if (p && e) {
nodes.push(new(tree.Paren)(new(tree.Declaration)(p, e, null, null, parserInput.i + currentIndex, fileInfo, true)));
if (p && !e) {
nodes.push(new (tree.Paren)(new (tree.QueryInParens)(p.op, p.lvalue, p.rvalue, p._index)));
e = p;
} else if (p && e) {
nodes.push(new (tree.Paren)(new (tree.Declaration)(p, e, null, null, parserInput.i + currentIndex, fileInfo, true)));
} else if (e) {
nodes.push(new(tree.Paren)(e));
} else {
Expand All @@ -1785,12 +1796,12 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
}
},

mediaFeatures: function () {
mediaFeatures: function (syntaxOptions) {
const entities = this.entities;
const features = [];
let e;
do {
e = this.mediaFeature();
e = this.mediaFeature(syntaxOptions);
if (e) {
features.push(e);
if (!parserInput.$char(',')) { break; }
Expand All @@ -1806,38 +1817,44 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
return features.length > 0 ? features : null;
},

media: function () {
let features;
let rules;
let media;
prepareAndGetNestableAtRule: function (treeType, index, debugInfo, syntaxOptions) {
const features = this.mediaFeatures(syntaxOptions);

const rules = this.block();

if (!rules) {
error('media definitions require block statements after any features');
}

parserInput.forget();

const atRule = new (treeType)(rules, features, index + currentIndex, fileInfo);
if (context.dumpLineNumbers) {
atRule.debugInfo = debugInfo;
}

return atRule;
},

nestableAtRule: function () {
let debugInfo;
const index = parserInput.i;

if (context.dumpLineNumbers) {
debugInfo = getDebugInfo(index);
}

parserInput.save();

if (parserInput.$str('@media')) {
features = this.mediaFeatures();

rules = this.block();

if (!rules) {
error('media definitions require block statements after any features');
if (parserInput.$peekChar('@')) {
if (parserInput.$str('@media')) {
return this.prepareAndGetNestableAtRule(tree.Media, index, debugInfo, MediaSyntaxOptions);
}

parserInput.forget();

media = new(tree.Media)(rules, features, index + currentIndex, fileInfo);
if (context.dumpLineNumbers) {
media.debugInfo = debugInfo;

if (parserInput.$str('@container')) {
return this.prepareAndGetNestableAtRule(tree.Container, index, debugInfo, ContainerSyntaxOptions);
}

return media;
}

parserInput.restore();
},

Expand Down Expand Up @@ -1919,7 +1936,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {

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

value = this['import']() || this.plugin() || this.media();
value = this['import']() || this.plugin() || this.nestableAtRule();
if (value) {
return value;
}
Expand Down Expand Up @@ -2422,4 +2439,4 @@ Parser.serializeVars = vars => {
return s;
};

export default Parser;
export default Parser;
7 changes: 7 additions & 0 deletions packages/less/src/less/tree/atrule-syntax.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const MediaSyntaxOptions = {
queryInParens: false
};

export const ContainerSyntaxOptions = {
queryInParens: true
};
63 changes: 63 additions & 0 deletions packages/less/src/less/tree/container.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import Ruleset from './ruleset';
import Value from './value';
import Selector from './selector';
import AtRule from './atrule';
import NestableAtRulePrototype from './nested-at-rule';

const Container = 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);
};

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

...NestableAtRulePrototype,

genCSS(context, output) {
output.add('@container ', this._fileInfo, this._index);
this.features.genCSS(context, output);
this.outputRuleset(context, output, this.rules);
},

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

const media = new Container(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);
}
});

export default Container;
7 changes: 5 additions & 2 deletions packages/less/src/less/tree/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ import Value from './value';
import JavaScript from './javascript';
import Assignment from './assignment';
import Condition from './condition';
import QueryInParens from './query-in-parens';
import Paren from './paren';
import Media from './media';
import Container from './container';
import UnicodeDescriptor from './unicode-descriptor';
import Negative from './negative';
import Extend from './extend';
Expand All @@ -43,8 +45,9 @@ export default {
Ruleset, Element, Attribute, Combinator, Selector,
Quoted, Expression, Declaration, Call, URL, Import,
Comment, Anonymous, Value, JavaScript, Assignment,
Condition, Paren, Media, UnicodeDescriptor, Negative,
Extend, VariableCall, NamespaceValue,
Condition, Paren, Media, Container, QueryInParens,
UnicodeDescriptor, Negative, Extend, VariableCall,
NamespaceValue,
mixin: {
Call: MixinCall,
Definition: MixinDefinition
Expand Down
94 changes: 2 additions & 92 deletions packages/less/src/less/tree/media.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import Ruleset from './ruleset';
import Value from './value';
import Selector from './selector';
import Anonymous from './anonymous';
import Expression from './expression';
import AtRule from './atrule';
import * as utils from '../utils';
import NestableAtRulePrototype from './nested-at-rule';

const Media = function(value, features, index, currentFileInfo, visibilityInfo) {
this._index = index;
Expand All @@ -25,18 +23,7 @@ const Media = function(value, features, index, currentFileInfo, visibilityInfo)
Media.prototype = Object.assign(new AtRule(), {
type: 'Media',

isRulesetLike() {
return true;
},

accept(visitor) {
if (this.features) {
this.features = visitor.visit(this.features);
}
if (this.rules) {
this.rules = visitor.visitArray(this.rules);
}
},
...NestableAtRulePrototype,

genCSS(context, output) {
output.add('@media ', this._fileInfo, this._index);
Expand Down Expand Up @@ -70,83 +57,6 @@ Media.prototype = Object.assign(new AtRule(), {

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

evalTop(context) {
let result = this;

// Render all dependent Media blocks.
if (context.mediaBlocks.length > 1) {
const selectors = (new Selector([], null, null, this.getIndex(), this.fileInfo())).createEmptySelectors();
result = new Ruleset(selectors, context.mediaBlocks);
result.multiMedia = true;
result.copyVisibilityInfo(this.visibilityInfo());
this.setParent(result, this);
}

delete context.mediaBlocks;
delete context.mediaPath;

return result;
},

evalNested(context) {
let i;
let value;
const 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];
}

// Trace all permutations to generate the resulting media-query.
//
// (a, b and c) with nested (d, e) ->
// a and d
// a and e
// b and c and d
// b and c and e
this.features = new Value(this.permute(path).map(path => {
path = path.map(fragment => fragment.toCSS ? fragment : new Anonymous(fragment));

for (i = path.length - 1; i > 0; i--) {
path.splice(i, 0, new Anonymous('and'));
}

return new Expression(path);
}));
this.setParent(this.features, this);

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

permute(arr) {
if (arr.length === 0) {
return [];
} else if (arr.length === 1) {
return arr[0];
} else {
const result = [];
const rest = this.permute(arr.slice(1));
for (let i = 0; i < rest.length; i++) {
for (let j = 0; j < arr[0].length; j++) {
result.push([arr[0][j]].concat(rest[i]));
}
}
return result;
}
},

bubbleSelectors(selectors) {
if (!selectors) {
return;
}
this.rules = [new Ruleset(utils.copyArray(selectors), [this.rules[0]])];
this.setParent(this.rules, this);
}
});

Expand Down
Loading