From 55c1189d196631e784f050508b702280226e34e0 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 31 Jul 2023 16:54:22 -0700 Subject: [PATCH 1/3] Deprecate explicitly passing null as an alpha value This allows us to reserve null to indicate a missing alpha channel instead in future versions. --- CHANGELOG.md | 23 +++++++++++++++ lib/src/deprecation.dart | 5 ++++ lib/src/functions/color.dart | 9 ++++-- lib/src/io/interface.dart | 2 +- lib/src/io/js.dart | 2 +- lib/src/io/vm.dart | 2 +- lib/src/js/value/color.dart | 17 +++++++---- lib/src/parse/stylesheet.dart | 2 +- lib/src/value/color.dart | 55 ++++++++++++++++++++++++++--------- pkg/sass_api/CHANGELOG.md | 4 +++ pkg/sass_api/pubspec.yaml | 4 +-- pubspec.yaml | 2 +- 12 files changed, 99 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c81a93cd..238060c6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,26 @@ +## 1.64.3 + +### Dart API + +* Deprecate explicitly passing `null` as the alpha channel for + `SassColor.rgb()`, `SassColor.hsl()`, and `SassColor.hwb()`. Omitting the + `alpha` channel is still allowed. In future releases, `null` will be used to + indicate a [missing component]. This deprecation is named `null-alpha`. + + [missing component]: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#missing_color_components + +### JS API + +* Deprecate explicitly passing `null` as the alpha channel for `new + SassColor()`. Omitting the `alpha` channel or passing `undefined` for it is + still allowed. In future releases, `null` will be used to indicate a [missing + component]. This deprecation is named `null-alpha`. + + [missing component]: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#missing_color_components + + (Note that this was already prohibited by the TypeScript types, but in + practice prior to this `null` was treated as `1`.) + ## 1.64.2 ### Dart API diff --git a/lib/src/deprecation.dart b/lib/src/deprecation.dart index 0d913424e..36ffff04f 100644 --- a/lib/src/deprecation.dart +++ b/lib/src/deprecation.dart @@ -5,6 +5,7 @@ import 'package:collection/collection.dart'; import 'package:pub_semver/pub_semver.dart'; +import 'io.dart'; import 'util/nullable.dart'; /// A deprecated feature in the language. @@ -59,6 +60,10 @@ enum Deprecation { description: 'Using !default or !global multiple times for one variable.'), + nullAlpha('null-alpha', + deprecatedIn: '1.62.3', + description: 'Passing null as alpha in the ${isJS ? 'JS' : 'Dart'} API.'), + /// Deprecation for `@import` rules. import.future('import', description: '@import rules.'), diff --git a/lib/src/functions/color.dart b/lib/src/functions/color.dart index 3b9e0b294..b663b1380 100644 --- a/lib/src/functions/color.dart +++ b/lib/src/functions/color.dart @@ -600,7 +600,8 @@ Value _rgb(String name, List arguments) { fuzzyRound(_percentageOrUnitless(green, 255, "green")), fuzzyRound(_percentageOrUnitless(blue, 255, "blue")), alpha.andThen((alpha) => - _percentageOrUnitless(alpha.assertNumber("alpha"), 1, "alpha")), + _percentageOrUnitless(alpha.assertNumber("alpha"), 1, "alpha")) ?? + 1, ColorFormat.rgbFunction); } @@ -653,7 +654,8 @@ Value _hsl(String name, List arguments) { saturation.value.clamp(0, 100), lightness.value.clamp(0, 100), alpha.andThen((alpha) => - _percentageOrUnitless(alpha.assertNumber("alpha"), 1, "alpha")), + _percentageOrUnitless(alpha.assertNumber("alpha"), 1, "alpha")) ?? + 1, ColorFormat.hslFunction); } @@ -702,7 +704,8 @@ Value _hwb(List arguments) { whiteness.valueInRange(0, 100, "whiteness"), blackness.valueInRange(0, 100, "blackness"), alpha.andThen((alpha) => - _percentageOrUnitless(alpha.assertNumber("alpha"), 1, "alpha"))); + _percentageOrUnitless(alpha.assertNumber("alpha"), 1, "alpha")) ?? + 1); } Object /* SassString | List */ _parseChannels( diff --git a/lib/src/io/interface.dart b/lib/src/io/interface.dart index 9ba1f96f5..618c3ff6e 100644 --- a/lib/src/io/interface.dart +++ b/lib/src/io/interface.dart @@ -20,7 +20,7 @@ bool get isMacOS => throw ''; bool get hasTerminal => throw ''; /// Whether we're running as JS (browser or Node.js). -bool get isJS => throw ''; +const bool isJS = false; /// Whether we're running as Node.js (not browser or Dart VM). bool get isNode => throw ''; diff --git a/lib/src/io/js.dart b/lib/src/io/js.dart index 5fd3db3e3..48086a844 100644 --- a/lib/src/io/js.dart +++ b/lib/src/io/js.dart @@ -228,7 +228,7 @@ bool get isWindows => process?.platform == 'win32'; bool get isMacOS => process?.platform == 'darwin'; -bool get isJS => true; +const bool isJS = true; /// The fs module object, used to check whether this has been loaded as Node. /// diff --git a/lib/src/io/vm.dart b/lib/src/io/vm.dart index adffcbb32..e4f7f6785 100644 --- a/lib/src/io/vm.dart +++ b/lib/src/io/vm.dart @@ -22,7 +22,7 @@ bool get isMacOS => io.Platform.isMacOS; bool get hasTerminal => io.stdout.hasTerminal; -bool get isJS => false; +const bool isJS = false; bool get isNode => false; diff --git a/lib/src/js/value/color.dart b/lib/src/js/value/color.dart index fb639a6a8..51987f47d 100644 --- a/lib/src/js/value/color.dart +++ b/lib/src/js/value/color.dart @@ -8,19 +8,20 @@ import '../../util/nullable.dart'; import '../../util/number.dart'; import '../../value.dart'; import '../reflection.dart'; +import '../utils.dart'; /// The JavaScript `SassColor` class. final JSClass colorClass = () { var jsClass = createJSClass('sass.SassColor', (Object self, _Channels color) { if (color.red != null) { return SassColor.rgb(fuzzyRound(color.red!), fuzzyRound(color.green!), - fuzzyRound(color.blue!), color.alpha); + fuzzyRound(color.blue!), _handleNullAlpha(color.alpha)); } else if (color.saturation != null) { - return SassColor.hsl( - color.hue!, color.saturation!, color.lightness!, color.alpha); + return SassColor.hsl(color.hue!, color.saturation!, color.lightness!, + _handleNullAlpha(color.alpha)); } else { - return SassColor.hwb( - color.hue!, color.whiteness!, color.blackness!, color.alpha); + return SassColor.hwb(color.hue!, color.whiteness!, color.blackness!, + _handleNullAlpha(color.alpha)); } }); @@ -68,6 +69,12 @@ final JSClass colorClass = () { return jsClass; }(); +/// Converts an undefined [alpha] to 1. +/// +/// This ensures that an explicitly null alpha will produce a deprecation +/// warning when passed to the Dart API. +num? _handleNullAlpha(num? alpha) => isUndefined(alpha) ? 1 : alpha; + @JS() @anonymous class _Channels { diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 0a4286687..bae2e6c3a 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -2456,7 +2456,7 @@ abstract class StylesheetParser extends Parser { red, green, blue, - alpha, + alpha ?? 1, // Don't emit four- or eight-digit hex colors as hex, since that's not // yet well-supported in browsers. alpha == null ? SpanColorFormat(scanner.spanFrom(start)) : null); diff --git a/lib/src/value/color.dart b/lib/src/value/color.dart index d3845559e..8fc088690 100644 --- a/lib/src/value/color.dart +++ b/lib/src/value/color.dart @@ -7,7 +7,10 @@ import 'dart:math' as math; import 'package:meta/meta.dart'; import 'package:source_span/source_span.dart'; +import '../deprecation.dart'; +import '../evaluation_context.dart'; import '../exception.dart'; +import '../io.dart'; import '../util/number.dart'; import '../value.dart'; import '../visitor/interface/value.dart'; @@ -98,20 +101,25 @@ class SassColor extends Value { /// Creates an RGB color. /// + /// Passing `null` to [alpha] is deprecated, and will change behavior in + /// future versions of Dart Sass to represent a [missing component] instead of + /// being equivalent to `1`. Callers who want to create opaque colors should + /// explicitly pass `1` or not pass [alpha] at all. + /// + /// [missing component]: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#missing_color_components + /// /// Throws a [RangeError] if [red], [green], and [blue] aren't between `0` and /// `255`, or if [alpha] isn't between `0` and `1`. - SassColor.rgb(int red, int green, int blue, [num? alpha]) - : this.rgbInternal(red, green, blue, alpha); + SassColor.rgb(int red, int green, int blue, [num? alpha = 1]) + : this.rgbInternal(red, green, blue, _handleNullAlpha(alpha)); /// Like [SassColor.rgb], but also takes a [format] parameter. /// /// @nodoc @internal SassColor.rgbInternal(this._red, this._green, this._blue, - [num? alpha, this.format]) - : _alpha = alpha == null - ? 1 - : fuzzyAssertRange(alpha.toDouble(), 0, 1, "alpha") { + [num alpha = 1, this.format]) + : _alpha = fuzzyAssertRange(alpha.toDouble(), 0, 1, "alpha") { RangeError.checkValueInInterval(red, 0, 255, "red"); RangeError.checkValueInInterval(green, 0, 255, "green"); RangeError.checkValueInInterval(blue, 0, 255, "blue"); @@ -119,31 +127,37 @@ class SassColor extends Value { /// Creates an HSL color. /// + /// Passing `null` to [alpha] is deprecated, and will change behavior in + /// future versions of Dart Sass to represent a [missing component] instead of + /// being equivalent to `1`. Callers who want to create opaque colors should + /// explicitly pass `1` or not pass [alpha] at all. + /// + /// [missing component]: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#missing_color_components + /// /// Throws a [RangeError] if [saturation] or [lightness] aren't between `0` /// and `100`, or if [alpha] isn't between `0` and `1`. - SassColor.hsl(num hue, num saturation, num lightness, [num? alpha]) - : this.hslInternal(hue, saturation, lightness, alpha); + SassColor.hsl(num hue, num saturation, num lightness, [num? alpha = 1]) + : this.hslInternal(hue, saturation, lightness, _handleNullAlpha(alpha)); /// Like [SassColor.hsl], but also takes a [format] parameter. /// /// @nodoc @internal SassColor.hslInternal(num hue, num saturation, num lightness, - [num? alpha, this.format]) + [num alpha = 1, this.format]) : _hue = hue % 360, _saturation = fuzzyAssertRange(saturation.toDouble(), 0, 100, "saturation"), _lightness = fuzzyAssertRange(lightness.toDouble(), 0, 100, "lightness"), - _alpha = alpha == null - ? 1 - : fuzzyAssertRange(alpha.toDouble(), 0, 1, "alpha"); + _alpha = fuzzyAssertRange(alpha.toDouble(), 0, 1, "alpha"); /// Creates an HWB color. /// /// Throws a [RangeError] if [whiteness] or [blackness] aren't between `0` and /// `100`, or if [alpha] isn't between `0` and `1`. - factory SassColor.hwb(num hue, num whiteness, num blackness, [num? alpha]) { + factory SassColor.hwb(num hue, num whiteness, num blackness, + [num? alpha = 1]) { // From https://www.w3.org/TR/css-color-4/#hwb-to-rgb var scaledHue = hue % 360 / 360; var scaledWhiteness = @@ -171,6 +185,21 @@ class SassColor extends Value { toRgb(scaledHue - 1 / 3), alpha); } + /// Prints a deprecation warning if [alpha] is explicitly `null`. + static num _handleNullAlpha(num? alpha) { + if (alpha != null) return alpha; + + warnForDeprecation( + 'Passing null for alpha in the ${isJS ? 'JS' : 'Dart'} API is ' + 'deprecated.\n' + 'To preserve current behavior, pass 1${isJS ? ' or undefined' : ''} ' + 'instead.' + '\n' + 'More info: https://sass-lang.com/d/null-alpha', + Deprecation.nullAlpha); + return 1; + } + SassColor._(this._red, this._green, this._blue, this._hue, this._saturation, this._lightness, this._alpha) : format = null; diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index af651fb26..e5eea47c1 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,3 +1,7 @@ +## 7.2.3 + +* No user-visible changes. + ## 7.2.2 * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index daf988e02..9a962ce74 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: 7.2.2 +version: 7.2.3 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=3.0.0 <4.0.0" dependencies: - sass: 1.64.2 + sass: 1.64.3 dev_dependencies: dartdoc: ^5.0.0 diff --git a/pubspec.yaml b/pubspec.yaml index 5f038212d..20f4e74d8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.64.2 +version: 1.64.3 description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass From a8bd7619c37515ede92d6dd21e50f2369a067ae0 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 1 Aug 2023 17:54:56 -0700 Subject: [PATCH 2/3] Poke CI From 90a722fa2045745e02d5bac2f09b44e5b5bf2ef1 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 1 Aug 2023 18:05:07 -0700 Subject: [PATCH 3/3] Drop sass_api dev version --- pkg/sass_api/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index d38368acb..3fe0ab0f6 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: 8.0.0-dev +version: 8.0.0 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass