Merge pull request #750 from sass/function-fixes

Fix a bunch of small spec incompatibilities in built-in functions
This commit is contained in:
Natalie Weizenbaum 2019-07-03 17:55:15 -07:00 committed by GitHub
commit 525958951d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 109 additions and 33 deletions

View File

@ -1,3 +1,48 @@
## 1.22.3
* **Potentially breaking bug fix:** The argument name for the `saturate()`
function is now `$amount`, to match the name in LibSass and originally in Ruby
Sass.
* **Potentially breaking bug fix:** The `invert()` function now properly returns
`#808080` when passed `$weight: 50%`. This matches the behavior in LibSass and
originally in Ruby Sass, as well as being consistent with other nearby values
of `$weight`.
* **Potentially breaking bug fix:** The `invert()` function now throws an error
if it's used [as a plain CSS function][plain-CSS invert] *and* the Sass-only
`$weight` parameter is passed. This never did anything useful, so it's
considered a bug fix rather than a full breaking change.
[plain-CSS invert]: https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/invert
* **Potentially breaking bug fix**: The `str-insert()` function now properly
inserts at the end of the string if the `$index` is `-1`. This matches the
behavior in LibSass and originally in Ruby Sass.
* **Potentially breaking bug fix**: An empty map returned by `map-remove()` is
now treated as identical to the literal value `()`, rather than being treated
as though it had a comma separator. This matches the original behavior in Ruby
Sass.
* The `adjust-color()` function no longer throws an error when a large `$alpha`
value is combined with HSL adjustments.
* The `alpha()` function now produces clearer error messages when the wrong
number of arguments are passed.
* Fix a bug where the `str-slice()` function could produce invalid output when
passed a string that contains characters that aren't represented as a single
byte in UTF-16.
* Improve the error message for an unknown separator name passed to the `join()`
or `append()` functions.
* The `zip()` function no longer deadlocks if passed no arguments.
* The `map-remove()` function can now take a `$key` named argument. This matches
the signature in LibSass and originally in Ruby Sass.
## 1.22.2
### JavaScript API

View File

@ -47,16 +47,20 @@ final global = UnmodifiableListView([
}
}),
BuiltInCallable("invert", r"$color, $weight: 50%", (arguments) {
BuiltInCallable("invert", r"$color, $weight: 100%", (arguments) {
var weight = arguments[1].assertNumber("weight");
if (arguments[0] is SassNumber) {
if (weight.value != 100 || !weight.hasUnit("%")) {
throw "Only one argument may be passed to the plain-CSS invert() "
"function.";
}
return _functionString("invert", arguments.take(1));
}
var color = arguments[0].assertColor("color");
var weight = arguments[1].assertNumber("weight");
var inverse = color.changeRgb(
red: 255 - color.red, green: 255 - color.green, blue: 255 - color.blue);
if (weight.value == 50) return inverse;
return _mixColors(inverse, color, weight);
}),
@ -130,8 +134,8 @@ final global = UnmodifiableListView([
}),
BuiltInCallable.overloaded("saturate", {
r"$number": (arguments) {
var number = arguments[0].assertNumber("number");
r"$amount": (arguments) {
var number = arguments[0].assertNumber("amount");
return SassString("saturate(${number.toCssString()})", quotes: false);
},
r"$color, $amount": (arguments) {
@ -171,17 +175,23 @@ final global = UnmodifiableListView([
return SassNumber(color.alpha);
},
r"$args...": (arguments) {
if (arguments[0].asList.every((argument) =>
argument is SassString &&
!argument.hasQuotes &&
argument.text.contains(_microsoftFilterStart))) {
var argList = arguments[0].asList;
if (argList.isNotEmpty &&
argList.every((argument) =>
argument is SassString &&
!argument.hasQuotes &&
argument.text.contains(_microsoftFilterStart))) {
// Suport the proprietary Microsoft alpha() function.
return _functionString("alpha", arguments);
}
assert(arguments.length != 1);
throw SassScriptException(
"Only 1 argument allowed, but ${arguments.length} were passed.");
assert(argList.length != 1);
if (argList.isEmpty) {
throw SassScriptException("Missing argument \$color.");
} else {
throw SassScriptException(
"Only 1 argument allowed, but ${argList.length} were passed.");
}
}
}),
@ -206,8 +216,14 @@ final module = BuiltInModule("color", [
// ### RGB
_red, _green, _blue, _mix,
BuiltInCallable("invert", r"$color, $weight: 50%", (arguments) {
BuiltInCallable("invert", r"$color, $weight: 100%", (arguments) {
var weight = arguments[1].assertNumber("weight");
if (arguments[0] is SassNumber) {
if (weight.value != 100 || !weight.hasUnit("%")) {
throw "Only one argument may be passed to the plain-CSS invert() "
"function.";
}
var result = _functionString("invert", arguments.take(1));
warn("Passing a number to color.invert() is deprecated.\n"
"\n"
@ -216,10 +232,8 @@ final module = BuiltInModule("color", [
}
var color = arguments[0].assertColor("color");
var weight = arguments[1].assertNumber("weight");
var inverse = color.changeRgb(
red: 255 - color.red, green: 255 - color.green, blue: 255 - color.blue);
if (weight.value == 50) return inverse;
return _mixColors(inverse, color, weight);
}),
@ -400,7 +414,7 @@ final _adjust = BuiltInCallable("adjust", r"$color, $kwargs...", (arguments) {
hue: color.hue + (hue ?? 0),
saturation: (color.saturation + (saturation ?? 0)).clamp(0, 100),
lightness: (color.lightness + (lightness ?? 0)).clamp(0, 100),
alpha: color.alpha + (alpha ?? 0));
alpha: (color.alpha + (alpha ?? 0)).clamp(0, 1));
} else if (alpha != null) {
return color.changeAlpha((color.alpha + (alpha ?? 0)).clamp(0, 1));
} else {

View File

@ -63,7 +63,7 @@ final _join = BuiltInCallable(
separator = ListSeparator.comma;
} else {
throw SassScriptException(
'\$$separator: Must be "space", "comma", or "auto".');
'\$separator: Must be "space", "comma", or "auto".');
}
var bracketed = bracketedParam is SassString && bracketedParam.text == 'auto'
@ -91,7 +91,7 @@ final _append =
separator = ListSeparator.comma;
} else {
throw SassScriptException(
'\$$separator: Must be "space", "comma", or "auto".');
'\$separator: Must be "space", "comma", or "auto".');
}
var newList = [...list.asList, value];
@ -100,6 +100,10 @@ final _append =
final _zip = BuiltInCallable("zip", r"$lists...", (arguments) {
var lists = arguments[0].asList.map((list) => list.asList).toList();
if (lists.isEmpty) {
return const SassList.empty(separator: ListSeparator.comma);
}
var i = 0;
var results = <SassList>[];
while (lists.every((list) => i != list.length)) {

View File

@ -36,14 +36,23 @@ final _merge = BuiltInCallable("merge", r"$map1, $map2", (arguments) {
return SassMap({...map1.contents, ...map2.contents});
});
final _remove = BuiltInCallable("remove", r"$map, $keys...", (arguments) {
var map = arguments[0].assertMap("map");
var keys = arguments[1];
var mutableMap = Map.of(map.contents);
for (var key in keys.asList) {
mutableMap.remove(key);
final _remove = BuiltInCallable.overloaded("remove", {
// Because the signature below has an explicit `$key` argument, it doesn't
// allow zero keys to be passed. We want to allow that case, so we add an
// explicit overload for it.
r"$map": (arguments) => arguments[0].assertMap("map"),
// The first argument has special handling so that the $key parameter can be
// passed by name.
r"$map, $key, $keys...": (arguments) {
var map = arguments[0].assertMap("map");
var keys = [arguments[1], ...arguments[2].asList];
var mutableMap = Map.of(map.contents);
for (var key in keys) {
mutableMap.remove(key);
}
return SassMap(mutableMap);
}
return SassMap(mutableMap);
});
final _keys = BuiltInCallable(

View File

@ -64,10 +64,13 @@ final _insert =
// the `$insert` string is at `$index` in the result, which means that we
// want to insert before `$index` if it's positive and after if it's
// negative.
if (indexInt < 0) indexInt++;
if (indexInt < 0) {
// +1 because negative indexes start counting from -1 rather than 0, and
// another +1 because we want to insert *after* that index.
indexInt = string.sassLength + indexInt + 2;
}
var codepointIndex = _codepointForIndex(indexInt, string.sassLength);
var codeUnitIndex =
codepointIndexToCodeUnitIndex(string.text, codepointIndex);
return SassString(
@ -113,7 +116,7 @@ final _slice =
return SassString(
string.text.substring(
codepointIndexToCodeUnitIndex(string.text, startCodepoint),
codepointIndexToCodeUnitIndex(string.text, endCodepoint) + 1),
codepointIndexToCodeUnitIndex(string.text, endCodepoint + 1)),
quotes: string.hasQuotes);
});

View File

@ -10,7 +10,8 @@ import 'external/value.dart' as ext;
class SassMap extends Value implements ext.SassMap {
final Map<Value, Value> contents;
ListSeparator get separator => ListSeparator.comma;
ListSeparator get separator =>
contents.isEmpty ? ListSeparator.undecided : ListSeparator.comma;
List<Value> get asList {
var result = <Value>[];

View File

@ -1,5 +1,5 @@
name: sass
version: 1.22.2
version: 1.22.3
description: A Sass implementation in Dart.
author: Dart Team <misc@dartlang.org>
homepage: https://github.com/sass/dart-sass

View File

@ -15,7 +15,7 @@ main() {
SassMap value;
setUp(() => value = parseValue("(a: b, c: d)") as SassMap);
test("is comma-separated", () {
test("has an undecided separator", () {
expect(value.separator, equals(ListSeparator.comma));
});
@ -143,8 +143,8 @@ main() {
SassMap value;
setUp(() => value = parseValue("map-remove((a: b), a)") as SassMap);
test("is comma-separated", () {
expect(value.separator, equals(ListSeparator.comma));
test("has an undecided separator", () {
expect(value.separator, equals(ListSeparator.undecided));
});
test("returns its contents as a map", () {