diff --git a/CHANGELOG.md b/CHANGELOG.md index 9994c8e86..e7ab81f9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ native CSS `filter` functions. This is in addition to number values which were already allowed. +* Fix a cosmetic bug where an outer rule could be duplicated after nesting was + resolved, instead of re-using a shared rule. + ## 1.61.0 * **Potentially breaking change:** Drop support for End-of-Life Node.js 12. diff --git a/lib/src/ast/css/modifiable/at_rule.dart b/lib/src/ast/css/modifiable/at_rule.dart index e616de5c2..1abe95285 100644 --- a/lib/src/ast/css/modifiable/at_rule.dart +++ b/lib/src/ast/css/modifiable/at_rule.dart @@ -22,6 +22,12 @@ class ModifiableCssAtRule extends ModifiableCssParentNode implements CssAtRule { T accept(ModifiableCssVisitor visitor) => visitor.visitCssAtRule(this); + bool equalsIgnoringChildren(ModifiableCssNode other) => + other is ModifiableCssAtRule && + name == other.name && + value == other.value && + isChildless == other.isChildless; + ModifiableCssAtRule copyWithoutChildren() => ModifiableCssAtRule(name, span, childless: isChildless, value: value); diff --git a/lib/src/ast/css/modifiable/keyframe_block.dart b/lib/src/ast/css/modifiable/keyframe_block.dart index 69233f355..fd99536e2 100644 --- a/lib/src/ast/css/modifiable/keyframe_block.dart +++ b/lib/src/ast/css/modifiable/keyframe_block.dart @@ -4,6 +4,7 @@ import 'package:source_span/source_span.dart'; +import '../../../utils.dart'; import '../../../visitor/interface/modifiable_css.dart'; import '../keyframe_block.dart'; import '../value.dart'; @@ -20,6 +21,10 @@ class ModifiableCssKeyframeBlock extends ModifiableCssParentNode T accept(ModifiableCssVisitor visitor) => visitor.visitCssKeyframeBlock(this); + bool equalsIgnoringChildren(ModifiableCssNode other) => + other is ModifiableCssKeyframeBlock && + listEquals(selector.value, other.selector.value); + ModifiableCssKeyframeBlock copyWithoutChildren() => ModifiableCssKeyframeBlock(selector, span); } diff --git a/lib/src/ast/css/modifiable/media_rule.dart b/lib/src/ast/css/modifiable/media_rule.dart index f1dcf25e4..a38fce7ec 100644 --- a/lib/src/ast/css/modifiable/media_rule.dart +++ b/lib/src/ast/css/modifiable/media_rule.dart @@ -4,6 +4,7 @@ import 'package:source_span/source_span.dart'; +import '../../../utils.dart'; import '../../../visitor/interface/modifiable_css.dart'; import '../media_query.dart'; import '../media_rule.dart'; @@ -25,6 +26,9 @@ class ModifiableCssMediaRule extends ModifiableCssParentNode T accept(ModifiableCssVisitor visitor) => visitor.visitCssMediaRule(this); + bool equalsIgnoringChildren(ModifiableCssNode other) => + other is ModifiableCssMediaRule && listEquals(queries, other.queries); + ModifiableCssMediaRule copyWithoutChildren() => ModifiableCssMediaRule(queries, span); } diff --git a/lib/src/ast/css/modifiable/node.dart b/lib/src/ast/css/modifiable/node.dart index 0f4495f32..057040975 100644 --- a/lib/src/ast/css/modifiable/node.dart +++ b/lib/src/ast/css/modifiable/node.dart @@ -66,6 +66,9 @@ abstract class ModifiableCssParentNode extends ModifiableCssNode : _children = children, children = UnmodifiableListView(children); + /// Returns whether [this] is equal to [other], ignoring their child nodes. + bool equalsIgnoringChildren(ModifiableCssNode other); + /// Returns a copy of [this] with an empty [children] list. /// /// This is *not* a deep copy. If other parts of this node are modifiable, diff --git a/lib/src/ast/css/modifiable/style_rule.dart b/lib/src/ast/css/modifiable/style_rule.dart index d182e917f..9cf017a7c 100644 --- a/lib/src/ast/css/modifiable/style_rule.dart +++ b/lib/src/ast/css/modifiable/style_rule.dart @@ -32,6 +32,9 @@ class ModifiableCssStyleRule extends ModifiableCssParentNode T accept(ModifiableCssVisitor visitor) => visitor.visitCssStyleRule(this); + bool equalsIgnoringChildren(ModifiableCssNode other) => + other is ModifiableCssStyleRule && other.selector == selector; + ModifiableCssStyleRule copyWithoutChildren() => ModifiableCssStyleRule(_selector, span, originalSelector: originalSelector); diff --git a/lib/src/ast/css/modifiable/stylesheet.dart b/lib/src/ast/css/modifiable/stylesheet.dart index 46610a948..dfb78f616 100644 --- a/lib/src/ast/css/modifiable/stylesheet.dart +++ b/lib/src/ast/css/modifiable/stylesheet.dart @@ -18,6 +18,9 @@ class ModifiableCssStylesheet extends ModifiableCssParentNode T accept(ModifiableCssVisitor visitor) => visitor.visitCssStylesheet(this); + bool equalsIgnoringChildren(ModifiableCssNode other) => + other is ModifiableCssStylesheet; + ModifiableCssStylesheet copyWithoutChildren() => ModifiableCssStylesheet(span); } diff --git a/lib/src/ast/css/modifiable/supports_rule.dart b/lib/src/ast/css/modifiable/supports_rule.dart index ef921b511..ad5101cd3 100644 --- a/lib/src/ast/css/modifiable/supports_rule.dart +++ b/lib/src/ast/css/modifiable/supports_rule.dart @@ -20,6 +20,9 @@ class ModifiableCssSupportsRule extends ModifiableCssParentNode T accept(ModifiableCssVisitor visitor) => visitor.visitCssSupportsRule(this); + bool equalsIgnoringChildren(ModifiableCssNode other) => + other is ModifiableCssSupportsRule && condition == other.condition; + ModifiableCssSupportsRule copyWithoutChildren() => ModifiableCssSupportsRule(condition, span); } diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index 5f44d7a6b..30c72ff49 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -3343,8 +3343,14 @@ class _EvaluateVisitor if (parent.hasFollowingSibling) { // A node with siblings must have a parent var grandparent = parent.parent!; - parent = parent.copyWithoutChildren(); - grandparent.addChild(parent); + if (parent.equalsIgnoringChildren(grandparent.children.last)) { + // If we've already made a copy of [parent] and nothing else has been + // added after it, re-use it. + parent = grandparent.children.last as ModifiableCssParentNode; + } else { + parent = parent.copyWithoutChildren(); + grandparent.addChild(parent); + } } } diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index a8a064879..75c876f1b 100644 --- a/lib/src/visitor/evaluate.dart +++ b/lib/src/visitor/evaluate.dart @@ -5,7 +5,7 @@ // DO NOT EDIT. This file was generated from async_evaluate.dart. // See tool/grind/synchronize.dart for details. // -// Checksum: 06d1dd221c149650242b3e09b3f507125606bf0f +// Checksum: 17862153344c8577d780b3e039a1ce5ebb774c17 // // ignore_for_file: unused_import @@ -3314,8 +3314,14 @@ class _EvaluateVisitor if (parent.hasFollowingSibling) { // A node with siblings must have a parent var grandparent = parent.parent!; - parent = parent.copyWithoutChildren(); - grandparent.addChild(parent); + if (parent.equalsIgnoringChildren(grandparent.children.last)) { + // If we've already made a copy of [parent] and nothing else has been + // added after it, re-use it. + parent = grandparent.children.last as ModifiableCssParentNode; + } else { + parent = parent.copyWithoutChildren(); + grandparent.addChild(parent); + } } } diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index b6082240d..a2877c77b 100644 --- a/pkg/sass_api/pubspec.yaml +++ b/pkg/sass_api/pubspec.yaml @@ -2,7 +2,7 @@ name: sass_api # Note: Every time we add a new Sass AST node, we need to bump the *major* # version because it's a breaking change for anyone who's implementing the # visitor interface(s). -version: 6.3.0-dev +version: 6.3.0 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass diff --git a/pubspec.yaml b/pubspec.yaml index 6e7f0d71e..c9fae8723 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.62.0-dev +version: 1.62.0 description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass