Skip to content

Commit

Permalink
fix(issue:3766) add support for container queries (#3811)
Browse files Browse the repository at this point in the history
* fix(issue:3766) add support for container queries

* Add support for CSS Container Queries
* Add tests for CSS Container Queries

* feat(media-queries-level-4) update media query

* Add support for Media Queries Level 4.
* Add tests for Media Queries Level 4.

* fix(mq-4 regex) fix regex for media queries

* Fix regex used for Media Queries Level 4 syntax parsing.

* fix(media-query-syntax) fix parsing of invalid CSS

* Fix parsing of invalid CSS for media queries to be consistent with
  Less.js version 4.1.3 handling.
  • Loading branch information
puckowski authored Aug 5, 2023
1 parent 8b5aef9 commit 012d549
Show file tree
Hide file tree
Showing 18 changed files with 14,977 additions and 14,066 deletions.
14,089 changes: 7,123 additions & 6,966 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,089 changes: 7,123 additions & 6,966 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
95 changes: 62 additions & 33 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,22 +1753,39 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
}
},

mediaFeature: function () {
mediaFeature: function (syntaxOptions) {
const entities = this.entities;
const nodes = [];
let e;
let p;
let rangeP;
parserInput.save();
do {
e = entities.keyword() || entities.variable() || entities.mixinLookup();
if (e) {
nodes.push(e);
} else if (parserInput.$char('(')) {
p = this.property();
e = this.value();
parserInput.save();
if (!p && syntaxOptions.queryInParens && parserInput.$re(/^[0-9a-z-]*\s*([<>]=|<=|>=|[<>]|=)/)) {
parserInput.restore();
p = this.condition();

parserInput.save();
rangeP = this.atomicCondition(null, p.rvalue);
if (!rangeP) {
parserInput.restore();
}
} 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, rangeP ? rangeP.op : null, rangeP ? rangeP.rvalue : null, 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 +1803,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 +1824,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 +1943,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 @@ -2231,7 +2255,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
parserInput.forget();
return body;
},
atomicCondition: function () {
atomicCondition: function (needsParens, preparsedCond) {
const entities = this.entities;
const index = parserInput.i;
let a;
Expand All @@ -2243,7 +2267,12 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
return this.addition() || entities.keyword() || entities.quoted() || entities.mixinLookup();
}).bind(this)

a = cond();
if (preparsedCond) {
a = preparsedCond;
} else {
a = cond();
}

if (a) {
if (parserInput.$char('>')) {
if (parserInput.$char('=')) {
Expand Down Expand Up @@ -2275,7 +2304,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
} else {
error('expected expression');
}
} else {
} else if (!preparsedCond) {
c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index + currentIndex, false);
}
return c;
Expand Down Expand Up @@ -2422,4 +2451,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: true
};

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
Loading

0 comments on commit 012d549

Please sign in to comment.