-
Notifications
You must be signed in to change notification settings - Fork 356
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Since 1.77.5 @extend called inside @mixin from loop (1400 icons) causes crash or unacceptable compile times #2504
Comments
Faced the same issue with significant performance downgrade so locked up to version 1.77.4 until the solution is found |
Can either of you provide an example stylesheet that demonstrates the performance regression? |
In our case it is somehow connected with On On the latest version of So the difference is 10x slower for this specific code. On actual code (not stripped down here for your convenience) the difference is much more higher, like 20x+. The example to check is below. It relies on @import "bootstrap-sass/assets/stylesheets/bootstrap/variables";
@import "bootstrap-sass/assets/stylesheets/bootstrap/mixins";
@import "bootstrap-sass/assets/stylesheets/bootstrap/buttons";
@import "bootstrap-sass/assets/stylesheets/bootstrap/button-groups"; // !!! When commenting this line, everything speeds up
.transparent-border{
border-color: transparent !important;
}
@mixin btn-light{
&, &:hover, &:focus, &:active, &:hover:active, &:focus:active, &:focus-visible{
color: inherit;
outline: initial;
// Dark mode
[data-theme="dark"] &{
color: white;
}
}
}
@mixin btn-dark{
&, &:hover, &:focus, &:active, &:hover:active, &:focus:active, &:focus-visible{
color: white;
outline: initial;
// Dark mode
[data-theme="dark"] &{
color: white;
}
}
}
.btn-success{
@extend .btn;
@include btn-dark;
border-color: transparent;
&:hover, &:focus, &:active, &:hover:active, &:focus:active{
border-color: transparent;
}
}
.btn-primary{
@extend .btn;
@include btn-dark;
@extend .transparent-border;
}
.btn-secondary{
@extend .btn;
@include btn-light;
// Dark mode
[data-theme="dark"] &{
background-color: transparent;
}
}
.btn-danger{
@extend .btn;
@include btn-dark;
}
.btn-default{
@extend .btn;
@include btn-light;
color: #6C6C6C !important;
// @include transparent-border;
// Dark mode
[data-theme="dark"] &{
background-color: transparent;
}
}
.btn-link-back{
@extend .btn;
display: inline-block;
padding-left: 0;
font-weight: 500;
margin-bottom: 10px;
&:hover, &:focus, &:active, &:hover:active, &:focus:active{
text-decoration: none;
box-shadow: none;
}
// Hide this link on mobile phones
@media(max-width: $screen-sm-max){
display: none;
}
}
.btn-get-prize{
@extend .btn;
@include btn-dark;
width: 100%;
margin-top: 10px;
}
.btn-toggle-favourites{
@extend .btn;
@extend .btn-primary;
width: 100%;
margin-top: 10px;
}
.btn-points{
@extend .btn;
@include btn-dark;
}
.btn-collect, .btn-collect-nopoints{
@extend .btn;
@include btn-dark;
background-color: #939;
}
.btn-accept{
@extend .btn;
@include btn-dark;
margin-left: 5px;
background-color: #c06;
}
.btn-challenge-close{
@extend .btn;
@include btn-dark;
width: 100%;
margin-top: 10px;
}
.btn-challenge-accept{
@extend .btn;
@include btn-dark;
margin-left: 5px;
background-color: #c06;
float: right;
@media(max-width: $screen-sm-max){
width: 100%;
float: none;
margin-left: 0;
}
}
.btn-challenge-answer{
@extend .btn;
@include btn-dark;
background-color: #063;
text-align: center;
margin-top: 10px;
white-space: normal;
/* Don't let the button go too wide */
white-space: normal;
}
.btn-points-transfer{
@extend .btn-primary;
}
.btn-login-customer{
@extend .btn;
@include btn-dark;
}
.btn-login-phone{
@extend .btn;
@include btn-dark;
}
.btn-login-facebook{
@extend .btn;
@include btn-dark;
background-color: #1877F2 !important;
}
.btn-login-linkedin{
@extend .btn;
@include btn-dark;
background-color: #0077B5 !important;
}
.btn-login-google{
@extend .btn;
@include btn-dark;
background-color: #4285F4 !important;
}
.btn-login-apple{
@extend .btn;
@include btn-dark;
background-color: #777 !important;
}
.btn-next{
@extend .btn;
@include btn-dark;
background-color: #38c0ea !important;
}
.btn-back{
@extend .btn-default;
}
.btn-ask-name-popup{
@extend .btn-primary;
}
.btn-deactivate-account{
@extend .btn-danger;
@include btn-dark;
}
.btn-reward-get-by-sms,
.btn-reward-get-by-email,
.btn-reward-print{
@extend .btn;
@include btn-dark;
@extend .btn-block;
}
.btn-add-to-google-wallet, .btn-add-to-apple-wallet{
@extend .btn;
@include btn-dark;
@extend .btn-block;
background-color: #1f1f1f;
border: 1px solid #747775;
}
.btn-reward-mark-as-used{
@extend .btn-primary;
@include btn-dark;
@extend .btn-block;
}
.btn-fb-share{
@extend .btn;
@include btn-dark;
background-color: #3b5998;
@media(max-width: $screen-sm-max){
background-color: transparent;
font-size: 18px;
padding: 0;
margin-top: 5px;
}
}
.btn-share-button{
@extend .btn;
@include btn-dark;
margin-top: 5px;
@media(max-width: $screen-sm-max){
width: auto;
background-color: transparent;
color: white;
}
}
.btn-news-mark-all-read, .btn-news-mark-as-unread{
@extend .btn-default;
@extend .btn-sm;
@include btn-light;
} |
Also, @tomastan is right about commenting out // @import "node_modules/bootstrap-sass/assets/stylesheets/bootstrap/button-groups"; This is how it looks with |
@nex3 I'm able to reproduce the issue with no dependencies at all. The summary is that the cost of the same The production is written in ruby, but you can easily rewrite it in JS or Dart if needed. # test.rb
require 'benchmark'
require 'sass-embedded'
puts Sass.info
(1..100).each do |i|
i = i*10 # use an increment of 10 to speed it up
r = Benchmark.measure do
Sass.compile('test.scss', logger: Sass::Logger.silent, functions: { 'count()' => ->(_) { Sass::Value::Number.new(i) } })
end
puts "#{i}:\t#{r}"
end // test.scss
.btn {
color: #000;
}
$count: count();
@for $i from 1 through $count {
ul:nth-child(#{$i}) {
@extend .btn;
}
} |
Or even simpler, this take ~18 seconds on my laptop with Dart AOT snapshot: // test.scss
.btn {
color: #000;
}
@for $i from 1 through 1000 {
ul:nth-child(#{$i}) {
@extend .btn;
}
} On dart-sass 1.77.4, the same code only took 0.005s. |
That aligns with what I've seen. Thank you for investigating. I've seen it taking 20+ minutes with our codebase. Actually at some points I'm not even sure if it is still working or crashed. |
FlameGraph and profiling data from Dart devtools: dart_devtools_2025-02-07_06_22_31.421.json |
The regression is for sure caused by #2255. In fact, the comment in diff --git a/lib/src/extend/extension_store.dart b/lib/src/extend/extension_store.dart
index 3637b5aac..2bbc2a9cb 100644
--- a/lib/src/extend/extension_store.dart
+++ b/lib/src/extend/extension_store.dart
@@ -901,13 +901,6 @@ class ExtensionStore {
// document, and thus should never be trimmed.
List<ComplexSelector> _trim(List<ComplexSelector> selectors,
bool isOriginal(ComplexSelector complex)) {
- // Avoid truly horrific quadratic behavior.
- //
- // TODO(nweiz): I think there may be a way to get perfect trimming without
- // going quadratic by building some sort of trie-like data structure that
- // can be used to look up superselectors.
- if (selectors.length > 100) return selectors;
-
// This is n² on the sequences, but only comparing between separate
// sequences should limit the quadratic behavior. We iterate from last to
// first and reverse the result so that, if two selectors are identical, we For my minimal reproduction, just adding back |
Background
We have a SASS repo that serves as our CSS framework for our WordPress Theme Framework here at Boston University. To be fair at the start it is an old version with old code that we are working to replace. However in the meantime we've run into an issue that I've isolated to a change in 1.77.5 which made some change to
@extend
.In our SASS that is compiled we have an @mixin that includes an @extend. The @mixin is an icon mixin and the @extend is pretty simple, it is just adding the shared styles for all of the icons (font family, font-style, line-height, text-transform, etc).
The @mixin however is called a massive amount of times from an @each that loops over a map of 1400+ icons. So this mixin and @extend is called a lot on every build. It's been this way for many years and compiled quickly on node-sass previously. We are moving now to webpack and dart-sass as part of these updates.
1.77.1
Earlier last year I was able to compile this code as-is with all 1,400+ icons quickly with dart-sass 1.77.1 at the time. In recent weeks a colleague started working on the repo and installed the deps which resulted in her getting a newer version of dart-sass (1.83). For her the compile was never finishing or at least taking so long (5-10+ minutes) that it appears to crash/hang and we quite the build. I was able to recreate this on my machine by installing newer versions of dart-sass.
1.77.5
To try to isolate the problem I've tested each version of dart sass and it seems to be that the changes in 1.77.5 are causing the crash/hang during compile. 1.77.4 compiles a build in a painful 32 seconds, but 1.77.5 does not finish after even 15 minutes. If I remove the @extend from the @mixin the 1,400 icons compile with 1.77.5 in 12 seconds.
Some code samples:
@each:
@mixin: (
@extend %icon-content-#{$position};
line is the problem)If I reduce the map of icons from 1,400+ to an essential list of only 115 icons in the map. That results in dart-sass 1.77.5 successfully compiling in 14 seconds with the @extend enabled. Somewhere between 115 icons and 1,400+ icons makes 1.77.5 crash/hang when the @extend is enabled.
We are going to change things so all 1,400 icons are no longer processed on every build/watch as we don't really need that many but it still does seem to be that something in 1.77.5 changed that breaks/crashes/hangs the build that had been working for many years on both node-sass and last year on earlier versions of dart-sass.
The text was updated successfully, but these errors were encountered: