@@ -9,26 +9,51 @@ setting, and getting items from nested maps.
9
9
10
10
* [ Background] ( #background )
11
11
* [ Summary] ( #summary )
12
- * [ Syntax ] ( #syntax )
12
+ * [ Functions ] ( #functions )
13
13
* [ ` get() ` ] ( #get )
14
14
* [ ` has-key() ` ] ( #has-key )
15
15
* [ ` set() ` ] ( #set )
16
16
* [ ` merge() ` ] ( #merge )
17
+ * [ ` deep-merge() ` ] ( #deep-merge )
17
18
18
19
## Background
19
20
20
21
> This section is non-normative.
21
22
22
- The current map inspection and manipulation functions don't provide any built-in
23
- support for managing nested maps. Projects often build thir own tooling, but
24
- the results are inconsistent, and often slow.
23
+ Variables have always been a key feature of the Sass language. But these days,
24
+ design systems and component libraries form the basis of most CSS projects --
25
+ with well organized _ design tokens_ as the foundation. While Individual token
26
+ variables can be quite useful, the ability to group tokens into strucutred and
27
+ meaningful relationships is essential for creating resilient systems.
28
+
29
+ There are many ways to group tokens. The popular Style Dictionary recommends
30
+ a deep nesting of _ category_ , _ type_ , _ item_ , _ sub-item_ , and _ state_ . Other
31
+ taxonomies also include concepts like _ theme_ , or even _ operating system_ . Most
32
+ of the existing tools rely on YAML or JSON objects to achieve that nested
33
+ structure, at the expense of other important information. YAML and JSON are not
34
+ design languages, and do not understand fundamental CSS concepts like color or
35
+ length.
36
+
37
+ With Sass, we don't have to make that tradeoff. We already support nestable map
38
+ structures, and the ability to interact with them programmatically -- adding or
39
+ removing properties, accessing values, and looping over entire structures. But
40
+ current built-in functions don't provide much support for managing nested maps.
41
+ Projects often build thir own tooling.
42
+
43
+ The results are inconsistent across projects, difficult to re-use, and often
44
+ slow to compile. Implementing core support for nested maps could change all that.
25
45
26
46
## Summary
27
47
28
48
> This section is non-normative.
29
49
30
- This proposal updates the ` sass:map ` module to better support inspection and
31
- manipulation of nested maps.
50
+ This proposal updates existing map functions with better support for inspection
51
+ and manipulation of nested maps, as well as adding new functions to the
52
+ ` sass:map ` module. For existing legacy functions (` get() ` , ` has-key() ` ,
53
+ ` merge() ` ) the new behavior will be accessible through both the ` sass:map `
54
+ module, and global legacy names (` map-get() ` , ` map-has-key() ` , ` map-merge() ` ).
55
+ New functions (` set() ` , ` deep-merge() ` ) will only be available inside the
56
+ ` sass:map ` module.
32
57
33
58
The ` has-key() ` and ` get() ` functions both accept multiple ` $keys... ` :
34
59
@@ -89,17 +114,65 @@ $nav: map.set($nav, 'color', 'hover', 'search', green);
89
114
$nav : map .set ($nav , ' color' , ' hover' , ' logo' , orange );
90
115
```
91
116
92
- ## Syntax
117
+ And finally, a new ` deep-merge() ` function in the ` sass:map ` module allows
118
+ merging two or more nested maps. This works much like the existing ` merge() `
119
+ function, but when both maps have a nested-map at the same key, those nested
120
+ maps are also merged:
121
+
122
+ ``` scss
123
+ @use ' sass:map' ;
124
+
125
+ $nav : (
126
+ ' bg' : ' gray' ,
127
+ ' color' : (
128
+ ' hover' : (
129
+ ' search' : yellow ,
130
+ ' home' : red ,
131
+ ' filter' : blue ,
132
+ ),
133
+ ),
134
+ );
135
+
136
+ $update : (
137
+ ' bg' : white ,
138
+ ' color' : (
139
+ ' hover' : (
140
+ ' search' : green ,
141
+ ' logo' : orange ,
142
+ )
143
+ )
144
+ );
145
+
146
+ $nav : map .deep-merge ($nav , $update );
147
+
148
+ // $nav: (
149
+ // 'bg': white,
150
+ // 'color': (
151
+ // 'hover': (
152
+ // 'search': green,
153
+ // 'home': red,
154
+ // 'filter': blue,
155
+ // 'logo': orange,
156
+ // ),
157
+ // ),
158
+ // );
159
+ ```
160
+
161
+ ## Functions
93
162
94
163
### ` get() `
95
164
96
165
```
97
166
get($map, $keys...)
98
167
```
99
168
169
+ * If ` $map ` is not a map, throw an error.
170
+
171
+ * If ` $keys ` is empty, throw an error.
172
+
100
173
* Let ` key ` be the first (or only) element in ` $keys `
101
174
102
- * If ` $map ` does not have a key with the same name as ` key ` , throw an error .
175
+ * If ` $map ` does not have a key equal to ` key ` , return ` null ` .
103
176
104
177
* Let ` value ` be the value assigned to ` key ` in ` $map `
105
178
@@ -117,6 +190,10 @@ get($map, $keys...)
117
190
has-key($map, $keys...)
118
191
```
119
192
193
+ * If ` $map ` is not a map, throw an error.
194
+
195
+ * If ` $keys ` is empty, throw an error.
196
+
120
197
* Let ` key ` be the first (or only) element in ` $keys `
121
198
122
199
* If ` $map ` does not have a key with the same name as ` key ` , return boolean
@@ -135,22 +212,22 @@ has-key($map, $keys...)
135
212
### ` set() `
136
213
137
214
```
138
- set($map1 , $keys ..., $value )
215
+ set($map , $args ...)
139
216
```
140
217
141
- * If ` $map1 ` is not a map, throw an error.
218
+ * If ` $map ` is not a map, throw an error.
142
219
143
- * If fewer than three arguments are provided, throw an error .
220
+ * Let ` map ` be an empty map .
144
221
145
- * Let ` map ` be an empty map .
222
+ * Let ` set-key ` be the first item in arglist ` $args ` .
146
223
147
- * If there is more than one argument in arglist ` $keys ` :
224
+ * Let ` remaining ` be a slice of all the other items in arglist ` $args ` .
148
225
149
- * Let ` set-key ` be the first element in ` $keys ` .
226
+ * If there are no items in ` remaining ` , throw an error .
150
227
151
- * Let ` keys ` be a slice of all ` $keys ` elements except the first.
228
+ * If there is more than one argument in ` remaining ` :
152
229
153
- * If ` $map1 ` has a key ` current-key ` with the same name as ` set-key ` :
230
+ * If ` $map ` has a key ` current-key ` that is equal to ` set-key ` :
154
231
155
232
* Let ` current ` be the value of ` current-key ` .
156
233
@@ -159,46 +236,87 @@ set($map1, $keys..., $value)
159
236
* Let ` current ` be an empty map.
160
237
161
238
* Let ` set-value ` be the result of calling ` set() ` with ` current ` and expanded
162
- ` keys ` as arguments.
239
+ ` remaining ` as arguments.
163
240
164
- > This will error if ` current ` is not a map, but we stil have nested ` keys `
241
+ > This will error if ` current ` is not a map, but we stil have ` remaining `
165
242
166
243
* Otherwise:
167
244
168
- * Let ` set-key ` be the only element in ` $keys `
245
+ * Let ` set-value ` be the only item in ` remaining `
169
246
170
- * Let ` set-value ` be the value of ` $value `
247
+ * Return a copy of ` map ` with ` set-key ` set to ` set-value ` , overriding an
248
+ existing value for ` set-key ` if one exists.
171
249
172
- * Add a key with name ` set-key ` and value ` set-value ` to ` map `
250
+ ### ` merge() `
173
251
174
- * For each ` key ` , ` value ` pair in ` $map1 `
252
+ This proposal add a new overload to the existing ` merge() ` function:
175
253
176
- * If a key with name ` key ` already exists in ` map ` , do nothing.
254
+ ```
255
+ merge($map1, $args...)
256
+ ```
177
257
178
- * Otherwise, add a key to ` map ` with name ` key ` and value ` value ` .
258
+ * If ` $args ` is empty, throw an error
179
259
180
- * Return the value ` map `
260
+ * Let ` map2 ` be the last item in arglist ` $args `
181
261
182
- ### ` merge() `
262
+ * If either ` $map1 ` or ` map2 ` is not a map, throw an error.
263
+
264
+ * If arglist ` $args ` has more than one item:
265
+
266
+ * Let ` get-keys ` be a slice of all except the last item in ` args ` .
267
+
268
+ * Let ` sub ` be the result of calling ` get() ` with ` sub ` and expanded ` get-keys `
269
+ as arguments.
270
+
271
+ * Let ` sub-merged ` be the result of calling ` merge() ` with ` sub ` and ` map2 `
272
+ as argumets.
273
+
274
+ * Let ` set-args ` be the result of appending ` sub-merged ` to the list ` keys `
275
+
276
+ * Return the result of calling ` set() ` with ` $map1 ` and expanded ` set-args `
277
+ as arguments.
278
+
279
+ * Otherwise:
280
+
281
+ * Let ` map ` be a copy of ` $map1 ` .
282
+
283
+ * For each ` key ` , ` value ` in ` map2 ` :
284
+
285
+ * Replace ` map ` with the result of calling ` set() ` with ` map ` , ` key ` , and
286
+ ` value ` as arguments.
287
+
288
+ * Return ` map ` .
289
+
290
+ ### ` deep-merge() `
183
291
184
292
```
185
- merge($map1, $keys ..., $map2 )
293
+ deep- merge($maps ...)
186
294
```
187
295
188
- * If only one argument is provided , throw an error.
296
+ * If the length of ` $maps ` is less than two , throw an error.
189
297
190
- * If either the first (` $map1 ` ) or last (` $map2 ` ) argument is not a map, throw
191
- an error.
298
+ * If any of the items in ` $maps ` are not maps, throw an error.
192
299
193
- * Let ` map ` be an empty map.
300
+ * Let ` merged ` be an empty map.
301
+
302
+ * For each ` map ` in ` maps ` :
303
+
304
+ * for each ` key ` , ` value ` in ` map ` :
305
+
306
+ * If ` value ` is a map:
307
+
308
+ * Let ` current ` be the result of calling ` get() ` with ` merged ` and ` key ` as
309
+ arguments.
194
310
195
- * For each ` key ` , ` new ` pair in ` $map2 ` :
311
+ * If ` current ` is a map :
196
312
197
- * If ` $map1 ` has a key with the same name as ` key `
313
+ * Let ` deep ` be the result of calling ` deep-merge() ` with ` current ` and
314
+ ` value ` as arguments.
198
315
199
- * Let ` keys ` be the result of appending ` key ` to the argument list ` $keys ` .
316
+ * Replace ` map ` with the result of calling ` set() ` with ` map ` , ` key ` , and
317
+ ` deep ` as arguments.
200
318
201
- * Update ` map ` to be the result of calling ` set() ` with ` map ` ,
202
- expanded ` keys ` , and ` value ` as arguments.
319
+ * Replace ` merged ` with the result of calling ` merge ` with ` merged ` and ` map `
320
+ as arguments.
203
321
204
- * Return the value of ` map `
322
+ * Return ` merged ` .
0 commit comments