Skip to content

Commit

Permalink
Deprecate explicitly passing null as an alpha value (#2049)
Browse files Browse the repository at this point in the history
This allows us to reserve null to indicate a missing alpha channel
instead in future versions.
  • Loading branch information
nex3 committed Aug 7, 2023
1 parent 8802c69 commit 4c3bd0e
Show file tree
Hide file tree
Showing 12 changed files with 95 additions and 28 deletions.
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,27 @@

### 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

* Include protocol buffer definitions when uploading the `sass` package to pub.

### 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

* No user-visible changes.
Expand Down
5 changes: 5 additions & 0 deletions lib/src/deprecation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.'),

Expand Down
9 changes: 6 additions & 3 deletions lib/src/functions/color.dart
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,8 @@ Value _rgb(String name, List<Value> 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);
}

Expand Down Expand Up @@ -644,7 +645,8 @@ Value _hsl(String name, List<Value> 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);
}

Expand Down Expand Up @@ -693,7 +695,8 @@ Value _hwb(List<Value> 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<Value> */ _parseChannels(
Expand Down
2 changes: 1 addition & 1 deletion lib/src/io/interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 '';
Expand Down
2 changes: 1 addition & 1 deletion lib/src/io/js.dart
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,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.
///
Expand Down
2 changes: 1 addition & 1 deletion lib/src/io/vm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
17 changes: 12 additions & 5 deletions lib/src/js/value/color.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
});

Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion lib/src/parse/stylesheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2232,7 +2232,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);
Expand Down
55 changes: 42 additions & 13 deletions lib/src/value/color.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -98,52 +101,63 @@ 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");
}

/// 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 =
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions pkg/sass_api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

* All uses of classes from the `tuple` package have been replaced by record
types.

## 7.2.2

* No user-visible changes.

## 7.2.1

Expand Down
4 changes: 2 additions & 2 deletions pkg/sass_api/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ 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

environment:
sdk: ">=3.0.0 <4.0.0"

dependencies:
sass: 1.64.2
sass: 1.64.3

dev_dependencies:
dartdoc: ^5.0.0
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: sass
version: 1.64.3-dev
version: 1.64.3
description: A Sass implementation in Dart.
homepage: https://github.com/sass/dart-sass

Expand Down

0 comments on commit 4c3bd0e

Please sign in to comment.