From 1b423ba5af141b05932639b792c1c463a6675acf Mon Sep 17 00:00:00 2001 From: Daniel Puckowski Date: Mon, 21 Jul 2025 19:13:43 -0400 Subject: [PATCH] fix(issue#4348): parse layer nesting syntax * Add fixes for layer at-rule nesting syntax parsing and associated tests. --- packages/less/src/less/parser/parser.js | 99 +++++++++++++++--------- packages/test-data/css/_main/layer.css | 22 ++++++ packages/test-data/less/_main/layer.less | 25 ++++++ 3 files changed, 108 insertions(+), 38 deletions(-) diff --git a/packages/less/src/less/parser/parser.js b/packages/less/src/less/parser/parser.js index 3fe7a37f5..f1bc2d32a 100644 --- a/packages/less/src/less/parser/parser.js +++ b/packages/less/src/less/parser/parser.js @@ -2048,7 +2048,49 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) { return null; } }, - + atruleUnknown: function (value, name, hasBlock) { + value = this.permissiveValue(/^[{;]/); + hasBlock = (parserInput.currentChar() === '{'); + if (!value) { + if (!hasBlock && parserInput.currentChar() !== ';') { + error(''.concat(name, ' rule is missing block or ending semi-colon')); + } + } + else if (!value.value) { + value = null; + } + return [value, hasBlock]; + }, + atruleBlock: function (rules, value, isRooted, isKeywordList) { + rules = this.blockRuleset(); + parserInput.save(); + if (!rules && !isRooted) { + value = this.entity(); + rules = this.blockRuleset(); + } + if (!rules && !isRooted) { + parserInput.restore(); + var 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(); + } + + return [rules, value, isKeywordList]; + }, // // A CSS AtRule // @@ -2127,48 +2169,29 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) { error(`expected ${name} expression`); } } else if (hasUnknown) { - value = this.permissiveValue(/^[{;]/); - hasBlock = (parserInput.currentChar() === '{'); - if (!value) { - if (!hasBlock && parserInput.currentChar() !== ';') { - error(`${name} rule is missing block or ending semi-colon`); - } - } - else if (!value.value) { - value = null; - } + const unknownPackage = this.atruleUnknown(value, name, hasBlock); + value = unknownPackage[0]; + hasBlock = unknownPackage[1]; } - + if (hasBlock) { - rules = this.blockRuleset(); - - parserInput.save(); - - if (!rules && !isRooted) { - value = this.entity(); - rules = this.blockRuleset(); - } + let blockPackage = this.atruleBlock(rules, value, isRooted, isKeywordList); + rules = blockPackage[0]; + value = blockPackage[1]; + isKeywordList = blockPackage[2]; - if (!rules && !isRooted) { + if (!rules && !hasUnknown) { parserInput.restore(); - - let e = []; - value = this.entity(); - - while (parserInput.$char(',')) { - e.push(value); - value = this.entity(); + name = parserInput.$re(/^@[a-z-]+/); + const unknownPackage = this.atruleUnknown(value, name, hasBlock); + value = unknownPackage[0]; + hasBlock = unknownPackage[1]; + if (hasBlock) { + blockPackage = this.atruleBlock(rules, value, isRooted, isKeywordList); + rules = blockPackage[0]; + value = blockPackage[1]; + isKeywordList = blockPackage[2]; } - - if (value && e.length > 0) { - e.push(value); - value = e; - isKeywordList = true; - } else { - rules = this.blockRuleset(); - } - } else { - parserInput.forget(); } } diff --git a/packages/test-data/css/_main/layer.css b/packages/test-data/css/_main/layer.css index 43218ad0a..7196325af 100644 --- a/packages/test-data/css/_main/layer.css +++ b/packages/test-data/css/_main/layer.css @@ -69,3 +69,25 @@ .parent:hover { background: lightgray; } +@layer foo.baz { + .bar { + font-weight: bold; + } +} +@layer framework { + @layer layout { + .container { + display: grid; + gap: 2rem; + } + } +} +@layer framework.layout { + main { + padding: 2rem; + } + p { + margin-block: 1rem; + color: #555; + } +} diff --git a/packages/test-data/less/_main/layer.less b/packages/test-data/less/_main/layer.less index 8e93ec6b9..9340f83af 100644 --- a/packages/test-data/less/_main/layer.less +++ b/packages/test-data/less/_main/layer.less @@ -85,3 +85,28 @@ background: lightgray; } } + +@layer foo.baz { + .bar { + font-weight: bold; + } +} + +@layer framework { + @layer layout { + .container { + display: grid; + gap: 2rem; + } + } +} + +@layer framework.layout { + main { + padding: 2rem; + } + p { + margin-block: 1rem; + color: #555; + } +}