fix(issue:4267) support starting-style at-rule (#4333)

* fix(issue:4267) support starting-style at-rule

* Add support for the starting-style at-rule.

* fix(issue:4267) improve comment and call handling

* Improve at-rule comment and call handling and add more starting-style
  at-rule tests.

* refactor(issue:4267) refactor starting-style code

* Refactor new code introduced to broaded at-rule support for
  starting-syle at-rule and other at-rules.

* chore(issue:4267) cleanup at-rule enhancement

* Cleanup at-rule enhancement code for starting-style at-rule support.

* fix: pin Playwright to exact version

* Pin Playwright to exact version to try to resolve CI issues.
This commit is contained in:
Daniel Puckowski
2025-03-31 14:48:29 -04:00
committed by GitHub
parent ddef3ebd2d
commit 5d3f6ea2a4
8 changed files with 269 additions and 19 deletions

View File

@@ -84,7 +84,7 @@
"less-plugin-clean-css": "^1.6.0",
"minimist": "^1.2.0",
"mocha": "^6.2.1",
"playwright": "~1.50.1",
"playwright": "1.50.1",
"mocha-teamcity-reporter": "^3.0.0",
"nock": "^11.8.2",
"npm-run-all": "^4.1.5",

View File

@@ -2102,6 +2102,9 @@ const Parser = function Parser(context, imports, fileInfo, currentIndex) {
hasUnknown = true;
isRooted = false;
break;
case '@starting-style':
isRooted = false;
break;
default:
hasUnknown = true;
break;

View File

@@ -2,6 +2,7 @@ import Node from './node';
import Selector from './selector';
import Ruleset from './ruleset';
import Anonymous from './anonymous';
import NestableAtRulePrototype from './nested-at-rule';
const AtRule = function(
name,
@@ -14,19 +15,45 @@ const AtRule = function(
visibilityInfo
) {
let i;
var selectors = (new Selector([], null, null, this._index, this._fileInfo)).createEmptySelectors();
this.name = name;
this.value = (value instanceof Node) ? value : (value ? new Anonymous(value) : value);
if (rules) {
if (Array.isArray(rules)) {
this.rules = rules;
const allDeclarations = this.declarationsBlock(rules);
let allRulesetDeclarations = true;
rules.forEach(rule => {
if (rule.type === 'Ruleset' && rule.rules) allRulesetDeclarations = allRulesetDeclarations && this.declarationsBlock(rule.rules, true);
});
if (allDeclarations && !isRooted) {
this.simpleBlock = true;
this.declarations = rules;
} else if (allRulesetDeclarations && rules.length === 1 && !isRooted && !value) {
this.simpleBlock = true;
this.declarations = rules[0].rules ? rules[0].rules : rules;
} else {
this.rules = rules;
}
} else {
this.rules = [rules];
this.rules[0].selectors = (new Selector([], null, null, index, currentFileInfo)).createEmptySelectors();
const allDeclarations = this.declarationsBlock(rules.rules);
if (allDeclarations && !isRooted && !value) {
this.simpleBlock = true;
this.declarations = rules.rules;
} else {
this.rules = [rules];
this.rules[0].selectors = (new Selector([], null, null, index, currentFileInfo)).createEmptySelectors();
}
}
for (i = 0; i < this.rules.length; i++) {
this.rules[i].allowImports = true;
if (!this.simpleBlock) {
for (i = 0; i < this.rules.length; i++) {
this.rules[i].allowImports = true;
}
}
this.setParent(selectors, this);
this.setParent(this.rules, this);
}
this._index = index;
@@ -39,10 +66,24 @@ const AtRule = function(
AtRule.prototype = Object.assign(new Node(), {
type: 'AtRule',
...NestableAtRulePrototype,
declarationsBlock(rules, mergeable = false) {
if (!mergeable) {
return rules.filter(function (node) { return (node.type === 'Declaration' || node.type === 'Comment') && !node.merge}).length === rules.length;
} else {
return rules.filter(function (node) { return (node.type === 'Declaration' || node.type === 'Comment'); }).length === rules.length;
}
},
accept(visitor) {
const value = this.value, rules = this.rules;
const value = this.value, rules = this.rules, declarations = this.declarations;
if (rules) {
this.rules = visitor.visitArray(rules);
} else if (declarations) {
this.declarations = visitor.visitArray(declarations);
}
if (value) {
this.value = visitor.visit(value);
@@ -58,13 +99,15 @@ AtRule.prototype = Object.assign(new Node(), {
},
genCSS(context, output) {
const value = this.value, rules = this.rules;
const value = this.value, rules = this.rules || this.declarations;
output.add(this.name, this.fileInfo(), this.getIndex());
if (value) {
output.add(' ');
value.genCSS(context, output);
}
if (rules) {
if (this.simpleBlock) {
this.outputRuleset(context, output, this.declarations);
} else if (rules) {
this.outputRuleset(context, output, rules);
} else {
output.add(';');
@@ -72,8 +115,8 @@ AtRule.prototype = Object.assign(new Node(), {
},
eval(context) {
let mediaPathBackup, mediaBlocksBackup, value = this.value, rules = this.rules;
let mediaPathBackup, mediaBlocksBackup, value = this.value, rules = this.rules || this.declarations;
// media stored inside other atrule should not bubble over it
// backpup media bubbling information
mediaPathBackup = context.mediaPath;
@@ -85,17 +128,78 @@ AtRule.prototype = Object.assign(new Node(), {
if (value) {
value = value.eval(context);
}
if (rules) {
// assuming that there is only one rule at this point - that is how parser constructs the rule
rules = [rules[0].eval(context)];
rules[0].root = true;
rules = this.evalRoot(context, rules);
}
if (Array.isArray(rules) && rules[0].rules && Array.isArray(rules[0].rules) && rules[0].rules.length) {
const allMergeableDeclarations = this.declarationsBlock(rules[0].rules, true);
if (allMergeableDeclarations && !this.isRooted && !value) {
var mergeRules = context.pluginManager.less.visitors.ToCSSVisitor.prototype._mergeRules;
mergeRules(rules[0].rules);
rules = rules[0].rules;
rules.forEach(rule => rule.merge = false);
}
}
if (this.simpleBlock && rules) {
rules[0].functionRegistry = context.frames[0].functionRegistry.inherit();
rules= rules.map(function (rule) { return rule.eval(context); });
}
// restore media bubbling information
context.mediaPath = mediaPathBackup;
context.mediaBlocks = mediaBlocksBackup;
return new AtRule(this.name, value, rules, this.getIndex(), this.fileInfo(), this.debugInfo, this.isRooted, this.visibilityInfo());
},
return new AtRule(this.name, value, rules,
this.getIndex(), this.fileInfo(), this.debugInfo, this.isRooted, this.visibilityInfo());
evalRoot(context, rules) {
let ampersandCount = 0;
let noAmpersandCount = 0;
let noAmpersands = true;
let allAmpersands = false;
if (!this.simpleBlock) {
rules = [rules[0].eval(context)];
}
let precedingSelectors = [];
if (context.frames.length > 0) {
for (let index = 0; index < context.frames.length; index++) {
const frame = context.frames[index];
if (
frame.type === 'Ruleset' &&
frame.rules &&
frame.rules.length > 0
) {
if (frame && !frame.root && frame.selectors && frame.selectors.length > 0) {
precedingSelectors = precedingSelectors.concat(frame.selectors);
}
}
if (precedingSelectors.length > 0) {
let value = '';
const output = { add: function (s) { value += s; } };
for (let i = 0; i < precedingSelectors.length; i++) {
precedingSelectors[i].genCSS(context, output);
}
if (/^&+$/.test(value.replace(/\s+/g, ''))) {
noAmpersands = false;
noAmpersandCount++;
} else {
allAmpersands = false;
ampersandCount++;
}
}
}
}
const mixedAmpersands = ampersandCount > 0 && noAmpersandCount > 0 && !allAmpersands && !noAmpersands;
if (
(this.isRooted && ampersandCount > 0 && noAmpersandCount === 0 && !allAmpersands && noAmpersands)
|| !mixedAmpersands
) {
rules[0].root = true;
}
return rules;
},
variable(name) {

View File

@@ -166,7 +166,17 @@ ImportVisitor.prototype = {
}
},
visitAtRule: function (atRuleNode, visitArgs) {
this.context.frames.unshift(atRuleNode);
if (atRuleNode.value) {
this.context.frames.unshift(atRuleNode);
} else if (atRuleNode.declarations && atRuleNode.declarations.length) {
if (atRuleNode.isRooted) {
this.context.frames.unshift(atRuleNode);
} else {
this.context.frames.unshift(atRuleNode.declarations[0]);
}
} else if (atRuleNode.rules && atRuleNode.rules.length) {
this.context.frames.unshift(atRuleNode);
}
},
visitAtRuleOut: function (atRuleNode) {
this.context.frames.shift();

View File

@@ -52,7 +52,11 @@ class JoinSelectorVisitor {
visitAtRule(atRuleNode, visitArgs) {
const context = this.contexts[this.contexts.length - 1];
if (atRuleNode.rules && atRuleNode.rules.length) {
if (atRuleNode.declarations && atRuleNode.declarations.length) {
atRuleNode.declarations[0].root = (context.length === 0 || context[0].multiMedia);
}
else if (atRuleNode.rules && atRuleNode.rules.length) {
atRuleNode.rules[0].root = (atRuleNode.isRooted || context.length === 0 || null);
}
}

View File

@@ -0,0 +1,55 @@
#nav {
transition: background-color 3.5s;
background-color: gray;
}
[popover]:popover-open {
opacity: 1;
transform: scaleX(1);
@starting-style {
opacity: 0;
transform: scaleX(0);
}
}
#target {
transition: background-color 1.5s;
background-color: green;
}
@starting-style {
#target {
background-color: transparent;
}
}
#source {
transition: background-color 2.5s;
background-color: red;
}
source:first {
opacity: 1;
transform: scaleX(1);
@starting-style {
opacity: 0;
transform: scaleX(0);
}
}
#footer {
color: yellow;
}
@starting-style {
#footer {
background-color: transparent;
}
}
nav > [popover]:popover-open {
opacity: 1;
transform: scaleX(1);
@starting-style {
padding: 10px 8px 6px 4px;
}
}
aside > [popover]:popover-open {
opacity: 1;
transform: scaleX(1);
@starting-style {
padding: 10px 20px 30px 40px;
}
}

View File

@@ -0,0 +1,74 @@
#nav {
transition: background-color 3.5s;
background-color: gray;
}
[popover]:popover-open {
opacity: 1;
transform: scaleX(1);
@starting-style {
opacity: 0;
transform: scaleX(0);
}
}
#target {
transition: background-color 1.5s;
background-color: green;
}
@starting-style {
#target {
background-color: transparent;
}
}
#source {
transition: background-color 2.5s;
background-color: red;
}
source:first {
opacity: 1;
transform: scaleX(1);
@starting-style {
opacity: 0;
transform: scaleX(0);
}
}
#footer {
color: yellow;
}
@starting-style {
#footer {
background-color: transparent;
}
}
nav > [popover]:popover-open {
opacity: 1;
transform: scaleX(1);
@starting-style {
padding+_: 10px;
padding+_: 8px;
padding+_: 6px;
padding+_: 4px;
}
}
aside > [popover]:popover-open {
opacity: 1;
transform: scaleX(1);
@starting-style {
// vector math
each(1 2 3 4, {
padding+_: (@value * 10px);
});
}
}

2
pnpm-lock.yaml generated
View File

@@ -136,7 +136,7 @@ importers:
specifier: ^2.2.3
version: 2.9.3
playwright:
specifier: ~1.50.1
specifier: 1.50.1
version: 1.50.1
promise:
specifier: ^7.1.1