diff --git a/packages/less/src/less/parser/parser.js b/packages/less/src/less/parser/parser.js index 6af533ff33..53f8a58324 100644 --- a/packages/less/src/less/parser/parser.js +++ b/packages/less/src/less/parser/parser.js @@ -2065,6 +2065,7 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) { let hasUnknown; let hasBlock = true; let isRooted = true; + let isKeywordList = false; if (parserInput.currentChar() !== '@') { return; } @@ -2105,6 +2106,9 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) { case '@starting-style': isRooted = false; break; + case '@layer': + isRooted = false; + break; default: hasUnknown = true; break; @@ -2137,9 +2141,38 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) { if (hasBlock) { rules = this.blockRuleset(); + + parserInput.save(); + + if (!rules && !isRooted) { + value = this.entity(); + rules = this.blockRuleset(); + } + + if (!rules && !isRooted) { + parserInput.restore(); + + let e = []; + value = this.entity(); + + while (parserInput.$char(',')) { + e.push(value); + value = this.entity(); + } + + if (value && e.length > 0) { + e.push(value); + value = e; + isKeywordList = true; + } else { + rules = this.blockRuleset(); + } + } else { + parserInput.forget(); + } } - if (rules || (!hasBlock && value && parserInput.$char(';'))) { + if (rules || isKeywordList || (!hasBlock && value && parserInput.$char(';'))) { parserInput.forget(); return new(tree.AtRule)(name, value, rules, index + currentIndex, fileInfo, context.dumpLineNumbers ? getDebugInfo(index) : null, diff --git a/packages/less/src/less/tree/atrule.js b/packages/less/src/less/tree/atrule.js index 5cdac997d0..df36a5d876 100644 --- a/packages/less/src/less/tree/atrule.js +++ b/packages/less/src/less/tree/atrule.js @@ -77,6 +77,14 @@ AtRule.prototype = Object.assign(new Node(), { } }, + keywordList(rules) { + if (!Array.isArray(rules)) { + return false; + } else { + return rules.filter(function (node) { return (node.type === 'Keyword' || node.type === 'Comment'); }).length === rules.length; + } + }, + accept(visitor) { const value = this.value, rules = this.rules, declarations = this.declarations; @@ -127,6 +135,9 @@ AtRule.prototype = Object.assign(new Node(), { if (value) { value = value.eval(context); + if (value.value && this.keywordList(value.value)) { + value = new Anonymous(value.value.map(keyword => keyword.value).join(', '), this.getIndex(), this.fileInfo()); + } } if (rules) { @@ -143,7 +154,7 @@ AtRule.prototype = Object.assign(new Node(), { } if (this.simpleBlock && rules) { rules[0].functionRegistry = context.frames[0].functionRegistry.inherit(); - rules= rules.map(function (rule) { return rule.eval(context); }); + rules = rules.map(function (rule) { return rule.eval(context); }); } // restore media bubbling information diff --git a/packages/test-data/css/_main/layer.css b/packages/test-data/css/_main/layer.css new file mode 100644 index 0000000000..97235d7500 --- /dev/null +++ b/packages/test-data/css/_main/layer.css @@ -0,0 +1,66 @@ +@layer { + .main::before { + color: #f00; + } +} +@layer legacy { + .sub-rule ul { + color: white; + } +} +@layer primevue { + .test { + foo: bar; + } +} +@layer reset, base, components, utilities; +@layer reset { + *, + *::before, + *::after { + box-sizing: border-box; + } +} +@layer base { + body { + margin: 0; + font-family: system-ui, sans-serif; + } + body header { + background-color: #f0f0f0; + padding: 1rem; + } +} +@layer components { + .button { + display: inline-block; + padding: 0.5rem 1rem; + background-color: blue; + color: white; + } + .button:hover { + background-color: darkblue; + } +} +@layer utilities { + .text-center { + text-align: center; + } + .responsive { + width: 100%; + } + @media (min-width: 768px) { + .responsive { + width: 50%; + } + } +} +.parent { + color: black; +} +.parent .child { + color: red; +} +.parent:hover { + background: lightgray; +} diff --git a/packages/test-data/less/_main/import/layer-import.less b/packages/test-data/less/_main/import/layer-import.less new file mode 100644 index 0000000000..f30c9561f7 --- /dev/null +++ b/packages/test-data/less/_main/import/layer-import.less @@ -0,0 +1,5 @@ +.sub-rule { + ul { + color: white; + } +} diff --git a/packages/test-data/less/_main/layer.less b/packages/test-data/less/_main/layer.less new file mode 100644 index 0000000000..00d0aae118 --- /dev/null +++ b/packages/test-data/less/_main/layer.less @@ -0,0 +1,81 @@ +.main { + @layer { + &::before { + color: #f00; + } + } +} + +@layer legacy { + @import "./import/layer-import.less"; +} + +@layer-name: primevue; + +@layer @layer-name { + .test { + foo: bar; + } +} + +@layer reset, base, components, utilities; + +@layer reset { + + *, + *::before, + *::after { + box-sizing: border-box; + } +} + +@layer base { + body { + margin: 0; + font-family: system-ui, sans-serif; + + header { + background-color: #f0f0f0; + padding: 1rem; + } + } +} + +@layer components { + .button { + display: inline-block; + padding: 0.5rem 1rem; + background-color: blue; + color: white; + + &:hover { + background-color: darkblue; + } + } +} + +@layer utilities { + .text-center { + text-align: center; + } + + .responsive { + width: 100%; + + @media (min-width: 768px) { + width: 50%; + } + } +} + +.parent { + color: black; + + .child { + color: red; + } + + &:hover { + background: lightgray; + } +}