mirror of
https://github.com/danog/dart-sass.git
synced 2024-11-26 20:24:42 +01:00
Add a map.deep-remove() function (#1091)
This commit is contained in:
parent
45703ab597
commit
56d979d04d
14
CHANGELOG.md
14
CHANGELOG.md
@ -48,6 +48,20 @@
|
||||
|
||||
[map-deep-merge]: https://sass-lang.com/documentation/modules/map#deep-merge
|
||||
|
||||
* Add a `map.deep-remove()` function. This allows you to remove keys from
|
||||
nested maps by passing multiple keys. For example:
|
||||
|
||||
```
|
||||
map.deep-remove(
|
||||
(color: (primary: red, secondary: blue)),
|
||||
color, primary
|
||||
) // => (color: (secondary: blue))
|
||||
```
|
||||
|
||||
See [the Sass documentation][map-deep-remove] for more details.
|
||||
|
||||
[map-deep-remove]: https://sass-lang.com/documentation/modules/map#deep-remove
|
||||
|
||||
### Dart API
|
||||
|
||||
* Add a `Value.tryMap()` function which returns the `Value` as a `SassMap` if
|
||||
|
@ -23,7 +23,15 @@ final global = UnmodifiableListView([
|
||||
|
||||
/// The Sass map module.
|
||||
final module = BuiltInModule("map", functions: [
|
||||
_get, _set, _merge, _remove, _keys, _values, _hasKey, _deepMerge //
|
||||
_get,
|
||||
_set,
|
||||
_merge,
|
||||
_remove,
|
||||
_keys,
|
||||
_values,
|
||||
_hasKey,
|
||||
_deepMerge,
|
||||
_deepRemove
|
||||
]);
|
||||
|
||||
final _get = _function("get", r"$map, $key, $keys...", (arguments) {
|
||||
@ -72,7 +80,7 @@ final _merge = BuiltInCallable.overloadedFunction("merge", {
|
||||
throw SassScriptException("Expected \$args to contain a map.");
|
||||
}
|
||||
var map2 = args.last.assertMap("map2");
|
||||
return _modify(map1, args.sublist(0, args.length - 1), (oldValue) {
|
||||
return _modify(map1, args.take(args.length - 1), (oldValue) {
|
||||
var nestedMap = oldValue?.tryMap();
|
||||
if (nestedMap == null) return map2;
|
||||
return SassMap({...nestedMap.contents, ...map2.contents});
|
||||
@ -86,6 +94,19 @@ final _deepMerge = _function("deep-merge", r"$map1, $map2", (arguments) {
|
||||
return _deepMergeImpl(map1, map2);
|
||||
});
|
||||
|
||||
final _deepRemove =
|
||||
_function("deep-remove", r"$map, $key, $keys...", (arguments) {
|
||||
var map = arguments[0].assertMap("map");
|
||||
var keys = [arguments[1], ...arguments[2].asList];
|
||||
return _modify(map, keys.take(keys.length - 1), (value) {
|
||||
var nestedMap = value?.tryMap();
|
||||
if (nestedMap?.contents?.containsKey(keys.last) ?? false) {
|
||||
return SassMap(Map.of(nestedMap.contents)..remove(keys.last));
|
||||
}
|
||||
return value;
|
||||
});
|
||||
});
|
||||
|
||||
final _remove = BuiltInCallable.overloadedFunction("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
|
||||
@ -131,36 +152,42 @@ final _hasKey = _function("has-key", r"$map, $key, $keys...", (arguments) {
|
||||
return SassBoolean(map.contents.containsKey(keys.last));
|
||||
});
|
||||
|
||||
/// Updates the specified value in [map] by applying the [modify()] callback to
|
||||
/// Updates the specified value in [map] by applying the [modify] callback to
|
||||
/// it, then returns the resulting map.
|
||||
///
|
||||
/// If more than one key is provided, this means the map targeted for update is
|
||||
/// nested within [map]. The multiple [keys] form a path of nested maps that
|
||||
/// leads to the targeted map. If any value along the path is not a map, and
|
||||
/// [overwrite] is true, this inserts a new map at that key and overwrites the
|
||||
/// current value. Otherwise, this fails and returns [map] with no changes.
|
||||
SassMap _modify(SassMap map, List<Value> keys, Value modify(Value old),
|
||||
[bool overwrite = true]) {
|
||||
SassMap _modifyNestedMap(SassMap map, int index) {
|
||||
/// `modify(null)` returns null, this inserts a new map at that key and
|
||||
/// overwrites the current value. Otherwise, this fails and returns [map] with
|
||||
/// no changes.
|
||||
///
|
||||
/// If no keys are provided, this passes [map] directly to modify and returns
|
||||
/// the result.
|
||||
Value _modify(SassMap map, Iterable<Value> keys, Value modify(Value old)) {
|
||||
var keyIterator = keys.iterator;
|
||||
SassMap _modifyNestedMap(SassMap map, [Value newValue]) {
|
||||
var mutableMap = Map.of(map.contents);
|
||||
var key = keys[index];
|
||||
var key = keyIterator.current;
|
||||
|
||||
if (index == keys.length - 1) {
|
||||
mutableMap[key] = modify(mutableMap[key]);
|
||||
if (!keyIterator.moveNext()) {
|
||||
mutableMap[key] = newValue ?? modify(mutableMap[key]);
|
||||
return SassMap(mutableMap);
|
||||
}
|
||||
|
||||
var nestedMap = mutableMap[key]?.tryMap();
|
||||
if (nestedMap == null && !overwrite) {
|
||||
return SassMap(mutableMap);
|
||||
if (nestedMap == null) {
|
||||
// We pass null to `modify` here to indicate there's no existing value.
|
||||
newValue = modify(null);
|
||||
if (newValue == null) return SassMap(mutableMap);
|
||||
}
|
||||
|
||||
nestedMap ??= const SassMap.empty();
|
||||
mutableMap[key] = _modifyNestedMap(nestedMap, index + 1);
|
||||
mutableMap[key] = _modifyNestedMap(nestedMap, newValue);
|
||||
return SassMap(mutableMap);
|
||||
}
|
||||
|
||||
return _modifyNestedMap(map, 0);
|
||||
return keyIterator.moveNext() ? _modifyNestedMap(map) : modify(map);
|
||||
}
|
||||
|
||||
/// Merges [map1] and [map2], with values in [map2] taking precedence.
|
||||
|
Loading…
Reference in New Issue
Block a user