From 5466dd76a2ac2a68034de20386807ab1f60b0ecb Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 13 Sep 2022 16:09:56 -0700 Subject: [PATCH] Give SassScriptException a name parameter (#1798) This avoids the need to copy around the same `_exception()` helper all over the place. --- CHANGELOG.md | 7 +++++++ lib/src/exception.dart | 8 +++++++- lib/src/value.dart | 32 ++++++++++++++------------------ lib/src/value/calculation.dart | 14 +++++--------- lib/src/value/number.dart | 22 +++++++++------------- lib/src/value/string.dart | 11 ++++------- pkg/sass_api/CHANGELOG.md | 7 +++++++ pkg/sass_api/pubspec.yaml | 4 ++-- pubspec.yaml | 2 +- 9 files changed, 56 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 566d20f85..d59652f7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.55.0 + +### Dart API + +* Add an optional `argumentName` parameter to `SassScriptException()` to make it + easier to throw exceptions associated with particular argument names. + ## 1.54.9 * Fix an incorrect span in certain `@media` query deprecation warnings. diff --git a/lib/src/exception.dart b/lib/src/exception.dart index 1fd79c347..05f6941a9 100644 --- a/lib/src/exception.dart +++ b/lib/src/exception.dart @@ -165,7 +165,13 @@ class SassScriptException { /// The error message. final String message; - SassScriptException(this.message); + /// Creates a [SassScriptException] with the given [message]. + /// + /// The [argumentName] is the name of the Sass function argument that + /// triggered this exception. If it's not null, it's automatically included in + /// [message]. + SassScriptException(String message, [String? argumentName]) + : message = argumentName == null ? message : "\$$argumentName: $message"; String toString() => "$message\n\nBUG: This should include a source span!"; } diff --git a/lib/src/value.dart b/lib/src/value.dart index 94ccea8cb..23197ff43 100644 --- a/lib/src/value.dart +++ b/lib/src/value.dart @@ -121,9 +121,9 @@ abstract class Value { /// argument name (without the `$`). It's used for error reporting. int sassIndexToListIndex(Value sassIndex, [String? name]) { var index = sassIndex.assertNumber(name).assertInt(name); - if (index == 0) throw _exception("List index may not be 0.", name); + if (index == 0) throw SassScriptException("List index may not be 0.", name); if (index.abs() > lengthAsList) { - throw _exception( + throw SassScriptException( "Invalid index $sassIndex for a list with $lengthAsList elements.", name); } @@ -139,35 +139,35 @@ abstract class Value { /// If this came from a function argument, [name] is the argument name /// (without the `$`). It's used for error reporting. SassBoolean assertBoolean([String? name]) => - throw _exception("$this is not a boolean.", name); + throw SassScriptException("$this is not a boolean.", name); /// Throws a [SassScriptException] if [this] isn't a calculation. /// /// If this came from a function argument, [name] is the argument name /// (without the `$`). It's used for error reporting. SassCalculation assertCalculation([String? name]) => - throw _exception("$this is not a calculation.", name); + throw SassScriptException("$this is not a calculation.", name); /// Throws a [SassScriptException] if [this] isn't a color. /// /// If this came from a function argument, [name] is the argument name /// (without the `$`). It's used for error reporting. SassColor assertColor([String? name]) => - throw _exception("$this is not a color.", name); + throw SassScriptException("$this is not a color.", name); /// Throws a [SassScriptException] if [this] isn't a function reference. /// /// If this came from a function argument, [name] is the argument name /// (without the `$`). It's used for error reporting. SassFunction assertFunction([String? name]) => - throw _exception("$this is not a function reference.", name); + throw SassScriptException("$this is not a function reference.", name); /// Throws a [SassScriptException] if [this] isn't a map. /// /// If this came from a function argument, [name] is the argument name /// (without the `$`). It's used for error reporting. SassMap assertMap([String? name]) => - throw _exception("$this is not a map.", name); + throw SassScriptException("$this is not a map.", name); /// Returns [this] as a [SassMap] if it is one (including empty lists, which /// count as empty maps) or returns `null` if it's not. @@ -178,14 +178,14 @@ abstract class Value { /// If this came from a function argument, [name] is the argument name /// (without the `$`). It's used for error reporting. SassNumber assertNumber([String? name]) => - throw _exception("$this is not a number.", name); + throw SassScriptException("$this is not a number.", name); /// Throws a [SassScriptException] if [this] isn't a string. /// /// If this came from a function argument, [name] is the argument name /// (without the `$`). It's used for error reporting. SassString assertString([String? name]) => - throw _exception("$this is not a string.", name); + throw SassScriptException("$this is not a string.", name); /// Converts a `selector-parse()`-style input into a string that can be /// parsed. @@ -196,7 +196,7 @@ abstract class Value { var string = _selectorStringOrNull(); if (string != null) return string; - throw _exception( + throw SassScriptException( "$this is not a valid selector: it must be a string,\n" "a list of strings, or a list of lists of strings.", name); @@ -384,10 +384,6 @@ abstract class Value { /// won't reflect the user's output settings. [toCssString] should be used /// instead to convert [this] to CSS. String toString() => serializeValue(this, inspect: true); - - /// Throws a [SassScriptException] with the given [message]. - SassScriptException _exception(String message, [String? name]) => - SassScriptException(name == null ? message : "\$$name: $message"); } /// Extension methods that are only visible through the `sass_api` package. @@ -414,7 +410,7 @@ extension SassApiValue on Value { // TODO(nweiz): colorize this if we're running in an environment where // that works. throwWithTrace( - _exception(error.toString().replaceFirst("Error: ", ""), name), + SassScriptException(error.toString().replaceFirst("Error: ", ""), name), stackTrace); } } @@ -437,7 +433,7 @@ extension SassApiValue on Value { // TODO(nweiz): colorize this if we're running in an environment where // that works. throwWithTrace( - _exception(error.toString().replaceFirst("Error: ", ""), name), + SassScriptException(error.toString().replaceFirst("Error: ", ""), name), stackTrace); } } @@ -460,7 +456,7 @@ extension SassApiValue on Value { // TODO(nweiz): colorize this if we're running in an environment where // that works. throwWithTrace( - _exception(error.toString().replaceFirst("Error: ", ""), name), + SassScriptException(error.toString().replaceFirst("Error: ", ""), name), stackTrace); } } @@ -483,7 +479,7 @@ extension SassApiValue on Value { // TODO(nweiz): colorize this if we're running in an environment where // that works. throwWithTrace( - _exception(error.toString().replaceFirst("Error: ", ""), name), + SassScriptException(error.toString().replaceFirst("Error: ", ""), name), stackTrace); } } diff --git a/lib/src/value/calculation.dart b/lib/src/value/calculation.dart index ececb7783..209ad7a8d 100644 --- a/lib/src/value/calculation.dart +++ b/lib/src/value/calculation.dart @@ -235,11 +235,11 @@ class SassCalculation extends Value { return arg; } else if (arg is SassString) { if (!arg.hasQuotes) return arg; - throw _exception("Quoted string $arg can't be used in a calculation."); + throw SassScriptException("Quoted string $arg can't be used in a calculation."); } else if (arg is SassCalculation) { return arg.name == 'calc' ? arg.arguments[0] : arg; } else if (arg is Value) { - throw _exception("Value $arg can't be used in a calculation."); + throw SassScriptException("Value $arg can't be used in a calculation."); } else { throw ArgumentError("Unexpected calculation argument $arg."); } @@ -255,7 +255,7 @@ class SassCalculation extends Value { for (var arg in args) { if (arg is! SassNumber) continue; if (arg.numeratorUnits.length > 1 || arg.denominatorUnits.isNotEmpty) { - throw _exception("Number $arg isn't compatible with CSS calculations."); + throw SassScriptException("Number $arg isn't compatible with CSS calculations."); } } @@ -267,7 +267,7 @@ class SassCalculation extends Value { var number2 = args[j]; if (number2 is! SassNumber) continue; if (number1.hasPossiblyCompatibleUnits(number2)) continue; - throw _exception("$number1 and $number2 are incompatible."); + throw SassScriptException("$number1 and $number2 are incompatible."); } } } @@ -280,7 +280,7 @@ class SassCalculation extends Value { .any((arg) => arg is SassString || arg is CalculationInterpolation)) { return; } - throw _exception( + throw SassScriptException( "$expectedLength arguments required, but only ${args.length} " "${pluralize('was', args.length, plural: 'were')} passed."); } @@ -319,10 +319,6 @@ class SassCalculation extends Value { listEquals(arguments, other.arguments); int get hashCode => name.hashCode ^ listHash(arguments); - - /// Throws a [SassScriptException] with the given [message]. - static SassScriptException _exception(String message, [String? name]) => - SassScriptException(name == null ? message : "\$$name: $message"); } /// A binary operation that can appear in a [SassCalculation]. diff --git a/lib/src/value/number.dart b/lib/src/value/number.dart index 47d311574..e4448059e 100644 --- a/lib/src/value/number.dart +++ b/lib/src/value/number.dart @@ -332,7 +332,7 @@ abstract class SassNumber extends Value { int assertInt([String? name]) { var integer = fuzzyAsInt(value); if (integer != null) return integer; - throw _exception("$this is not an int.", name); + throw SassScriptException("$this is not an int.", name); } /// If [value] is between [min] and [max], returns it. @@ -344,7 +344,7 @@ abstract class SassNumber extends Value { num valueInRange(num min, num max, [String? name]) { var result = fuzzyCheckRange(value, min, max); if (result != null) return result; - throw _exception( + throw SassScriptException( "Expected $this to be within $min$unitString and $max$unitString.", name); } @@ -361,7 +361,7 @@ abstract class SassNumber extends Value { num valueInRangeWithUnit(num min, num max, String name, String unit) { var result = fuzzyCheckRange(value, min, max); if (result != null) return result; - throw _exception( + throw SassScriptException( "Expected $this to be within $min$unit and $max$unit.", name); } @@ -395,7 +395,7 @@ abstract class SassNumber extends Value { /// (without the `$`). It's used for error reporting. void assertUnit(String unit, [String? name]) { if (hasUnit(unit)) return; - throw _exception('Expected $this to have unit "$unit".', name); + throw SassScriptException('Expected $this to have unit "$unit".', name); } /// Throws a [SassScriptException] unless [this] has no units. @@ -404,7 +404,7 @@ abstract class SassNumber extends Value { /// (without the `$`). It's used for error reporting. void assertNoUnits([String? name]) { if (!hasUnits) return; - throw _exception('Expected $this to have no units.', name); + throw SassScriptException('Expected $this to have no units.', name); } /// Returns a copy of this number, converted to the units represented by @@ -601,16 +601,16 @@ abstract class SassNumber extends Value { if (!hasUnits || !otherHasUnits) { message.write(" (one has units and the other doesn't)"); } - return _exception("$message.", name); + return SassScriptException("$message.", name); } else if (!otherHasUnits) { - return _exception("Expected $this to have no units.", name); + return SassScriptException("Expected $this to have no units.", name); } else { if (newNumerators.length == 1 && newDenominators.isEmpty) { var type = _typesByUnit[newNumerators.first]; if (type != null) { // If we're converting to a unit of a named type, use that type name // and make it clear exactly which units are convertible. - return _exception( + return SassScriptException( "Expected $this to have ${a(type)} unit " "(${_unitsByType[type]!.join(', ')}).", name); @@ -619,7 +619,7 @@ abstract class SassNumber extends Value { var unit = pluralize('unit', newNumerators.length + newDenominators.length); - return _exception( + return SassScriptException( "Expected $this to have $unit " "${_unitString(newNumerators, newDenominators)}.", name); @@ -946,8 +946,4 @@ abstract class SassNumber extends Value { var innerMap = _conversions[unit]; return innerMap == null ? 1 : 1 / innerMap.values.first; } - - /// Throws a [SassScriptException] with the given [message]. - SassScriptException _exception(String message, [String? name]) => - SassScriptException(name == null ? message : "\$$name: $message"); } diff --git a/lib/src/value/string.dart b/lib/src/value/string.dart index cc4c9c4bc..2ded47bd7 100644 --- a/lib/src/value/string.dart +++ b/lib/src/value/string.dart @@ -170,9 +170,10 @@ class SassString extends Value { /// argument name (without the `$`). It's used for error reporting. int sassIndexToRuneIndex(Value sassIndex, [String? name]) { var index = sassIndex.assertNumber(name).assertInt(name); - if (index == 0) throw _exception("String index may not be 0.", name); - if (index.abs() > sassLength) { - throw _exception( + if (index == 0) { + throw SassScriptException("String index may not be 0.", name); + } else if (index.abs() > sassLength) { + throw SassScriptException( "Invalid index $sassIndex for a string with $sassLength characters.", name); } @@ -199,8 +200,4 @@ class SassString extends Value { bool operator ==(Object other) => other is SassString && text == other.text; int get hashCode => _hashCache ??= text.hashCode; - - /// Throws a [SassScriptException] with the given [message]. - SassScriptException _exception(String message, [String? name]) => - SassScriptException(name == null ? message : "\$$name: $message"); } diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index bbcab2bb6..7b35160c9 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,3 +1,10 @@ +## 3.1.0 + +### Dart API + +* Add an optional `argumentName` parameter to `SassScriptException()` to make it + easier to throw exceptions associated with particular argument names. + ## 3.0.4 * `UnaryOperationExpression`s with operator `not` now include a correct span, diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index bf8ff8e8d..ac509de08 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: 3.0.4 +version: 3.1.0 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=2.17.0 <3.0.0" dependencies: - sass: 1.54.9 + sass: 1.55.0 dev_dependencies: dartdoc: ^5.0.0 diff --git a/pubspec.yaml b/pubspec.yaml index 15b5bab9a..92542939c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.54.9 +version: 1.55.0 description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass