@@ -47,7 +47,7 @@ describe('StyleProcessor', () => {
47
47
} ) ;
48
48
49
49
test ( 'parses custom properties' , ( ) => {
50
- const result = parser . process ( '$my-gap $( my-gap, 2x)' ) ;
50
+ const result = parser . process ( '$my-gap ($ my-gap, 2x)' ) ;
51
51
expect ( result . groups [ 0 ] . values ) . toEqual ( [
52
52
'var(--my-gap)' ,
53
53
'var(--my-gap, calc(2 * var(--gap)))' ,
@@ -195,6 +195,174 @@ describe('StyleProcessor', () => {
195
195
] ) ;
196
196
} ) ;
197
197
198
+ test ( 'parses new custom property with fallback syntax' , ( ) => {
199
+ const result = parser . process (
200
+ '($custom-margin, 1x) ($theme-color, #purple)' ,
201
+ ) ;
202
+ expect ( result . groups [ 0 ] . values ) . toEqual ( [
203
+ 'var(--custom-margin, var(--gap))' ,
204
+ 'var(--theme-color, var(--purple-color))' ,
205
+ ] ) ;
206
+ } ) ;
207
+
208
+ test ( 'parses new custom property syntax in complex expressions' , ( ) => {
209
+ const result = parser . process ( '(100% - (2 * ($custom-gap, 1x)))' ) ;
210
+ expect ( result . groups [ 0 ] . values ) . toEqual ( [
211
+ 'calc(100% - calc(2 * var(--custom-gap, var(--gap))))' ,
212
+ ] ) ;
213
+ } ) ;
214
+
215
+ test ( 'distinguishes between functions and custom property fallbacks' , ( ) => {
216
+ // Test function with custom property as first argument
217
+ const result1 = parser . process ( 'sum($my-prop, 2x)' ) ;
218
+ expect ( result1 . groups [ 0 ] . values ) . toEqual ( [
219
+ 'calc(var(--my-prop) + calc(2 * var(--gap)))' ,
220
+ ] ) ;
221
+
222
+ // Test custom property with fallback
223
+ const result2 = parser . process ( '($my-prop, 2x)' ) ;
224
+ expect ( result2 . groups [ 0 ] . values ) . toEqual ( [
225
+ 'var(--my-prop, calc(2 * var(--gap)))' ,
226
+ ] ) ;
227
+
228
+ // Test multiple scenarios in one expression
229
+ const result3 = parser . process ( 'sum($a, $b) ($fallback-prop, 1x)' ) ;
230
+ expect ( result3 . groups [ 0 ] . values ) . toEqual ( [
231
+ 'calc(var(--a) + var(--b))' ,
232
+ 'var(--fallback-prop, var(--gap))' ,
233
+ ] ) ;
234
+
235
+ // Test edge case: function with custom property fallback as argument
236
+ const result4 = parser . process ( 'sum(($prop-a, 1x), ($prop-b, 2x))' ) ;
237
+ expect ( result4 . groups [ 0 ] . values ) . toEqual ( [
238
+ 'calc(var(--prop-a, var(--gap)) + var(--prop-b, calc(2 * var(--gap))))' ,
239
+ ] ) ;
240
+
241
+ // Test color functions with custom properties
242
+ const result5 = parser . process ( 'rgb($red, $green, $blue)' ) ;
243
+ expect ( result5 . groups [ 0 ] . colors ) . toEqual ( [
244
+ 'rgb(var(--red),var(--green),var(--blue))' ,
245
+ ] ) ;
246
+
247
+ // Test generic function (not user-defined)
248
+ const result6 = parser . process ( 'min($width, 100%)' ) ;
249
+ expect ( result6 . groups [ 0 ] . values ) . toEqual ( [ 'min(var(--width), 100%)' ] ) ;
250
+
251
+ // Test critical edge case: ensure no ambiguity in parsing order
252
+ // Function name 'sum' vs custom property fallback starting with 'sum'
253
+ const result7 = parser . process ( 'sum($a, $b) ($sum, fallback)' ) ;
254
+ expect ( result7 . groups [ 0 ] . values ) . toEqual ( [
255
+ 'calc(var(--a) + var(--b))' , // Function call
256
+ 'var(--sum, fallback)' , // Custom property fallback (not a function)
257
+ ] ) ;
258
+ } ) ;
259
+
260
+ test ( 'validates CSS custom property names correctly' , ( ) => {
261
+ // Valid names
262
+ const result1 = parser . process (
263
+ '$valid-name $_underscore $hyphen-ok $abc123' ,
264
+ ) ;
265
+ expect ( result1 . groups [ 0 ] . values ) . toEqual ( [
266
+ 'var(--valid-name)' ,
267
+ 'var(--_underscore)' ,
268
+ 'var(--hyphen-ok)' ,
269
+ 'var(--abc123)' ,
270
+ ] ) ;
271
+
272
+ // Invalid names (should become modifiers)
273
+ const result2 = parser . process ( '$123invalid $-123invalid $0test $-' ) ;
274
+ expect ( result2 . groups [ 0 ] . mods ) . toEqual ( [
275
+ '$123invalid' ,
276
+ '$-123invalid' ,
277
+ '$0test' ,
278
+ '$-' ,
279
+ ] ) ;
280
+
281
+ // Edge case: single character names
282
+ const result3 = parser . process ( '$a $_ $1' ) ;
283
+ expect ( result3 . groups [ 0 ] . values ) . toEqual ( [ 'var(--a)' , 'var(--_)' ] ) ;
284
+ expect ( result3 . groups [ 0 ] . mods ) . toEqual ( [ '$1' ] ) ;
285
+ } ) ;
286
+
287
+ test ( 'comprehensive collision testing for edge cases' , ( ) => {
288
+ // Test 1: Auto-calc vs custom property fallback - similar patterns
289
+ const result1 = parser . process ( '(100% - 2x) ($gap, 1x)' ) ;
290
+ expect ( result1 . groups [ 0 ] . values ) . toEqual ( [
291
+ 'calc(100% - calc(2 * var(--gap)))' , // Auto-calc
292
+ 'var(--gap, var(--gap))' , // Custom property fallback
293
+ ] ) ;
294
+
295
+ // Test 2: URL with comma vs custom property fallback
296
+ // NOTE: URLs merge with following tokens for background layers
297
+ const result2 = parser . process (
298
+ 'url("img,with,comma.png") ($fallback, auto)' ,
299
+ ) ;
300
+ expect ( result2 . groups [ 0 ] . values ) . toEqual ( [
301
+ 'url("img,with,comma.png") var(--fallback, auto)' , // URL merges with following token
302
+ ] ) ;
303
+
304
+ // Test 3: Quoted strings that look like custom properties
305
+ const result3 = parser . process (
306
+ '"($not-a-prop, value)" ($real-prop, fallback)' ,
307
+ ) ;
308
+ expect ( result3 . groups [ 0 ] . values ) . toEqual ( [
309
+ '"($not-a-prop, value)"' , // Quoted string (not processed)
310
+ 'var(--real-prop, fallback)' , // Custom property fallback
311
+ ] ) ;
312
+
313
+ // Test 4: Color function with similar pattern
314
+ const result4 = parser . process (
315
+ 'rgb($red, $green, $blue) ($color-fallback, #fff)' ,
316
+ ) ;
317
+ expect ( result4 . groups [ 0 ] . colors ) . toEqual ( [
318
+ 'rgb(var(--red),var(--green),var(--blue))' ,
319
+ ] ) ;
320
+ expect ( result4 . groups [ 0 ] . values ) . toEqual ( [
321
+ 'var(--color-fallback, var(--fff-color, #fff))' ,
322
+ ] ) ;
323
+
324
+ // Test 5: Nested parentheses with custom properties
325
+ const result5 = parser . process ( '(($outer, 10px) + ($inner, 5px))' ) ;
326
+ expect ( result5 . groups [ 0 ] . values ) . toEqual ( [
327
+ 'calc(var(--outer, 10px) + var(--inner, 5px))' ,
328
+ ] ) ;
329
+
330
+ // Test 6: Invalid custom property patterns (should not match)
331
+ const result6 = parser . process (
332
+ '(not-a-prop, value) (@invalid-syntax, bad)' ,
333
+ ) ;
334
+ expect ( result6 . groups [ 0 ] . values ) . toEqual ( [
335
+ 'calc(not-a-prop, value)' , // Auto-calc (no $ prefix)
336
+ 'var(--invalid-syntax, bad)' , // @ is valid custom property prefix
337
+ ] ) ;
338
+
339
+ // Test 7: Edge case with spaces and special characters
340
+ // NOTE: Extra spaces cause the pattern to not match, falling back to auto-calc
341
+ const result7 = parser . process ( '( $spaced , fallback ) ($compact,nospace)' ) ;
342
+ expect ( result7 . groups [ 0 ] . values ) . toEqual ( [
343
+ 'calc(var(--spaced), fallback)' , // Extra spaces -> auto-calc, not custom property
344
+ 'var(--compact, nospace)' , // No spaces are fine
345
+ ] ) ;
346
+
347
+ // Test 8: Edge cases with regex boundaries
348
+ // Now properly validates CSS custom property names
349
+ const result8 = parser . process (
350
+ '($123invalid, fallback) ($valid-name, fallback) ($_underscore, fallback) ($hyphen-ok, fallback)' ,
351
+ ) ;
352
+ expect ( result8 . groups [ 0 ] . values ) . toEqual ( [
353
+ 'calc($123invalid, fallback)' , // Invalid (starts with number) -> auto-calc
354
+ 'var(--valid-name, fallback)' , // Valid
355
+ 'var(--_underscore, fallback)' , // Valid (underscore allowed)
356
+ 'var(--hyphen-ok, fallback)' , // Valid (letter followed by hyphen)
357
+ ] ) ;
358
+
359
+ // Test 9: Comma separation in complex scenarios
360
+ const result9 = parser . process ( '($prop1, fallback), ($prop2, fallback)' ) ;
361
+ expect ( result9 . groups . length ) . toBe ( 2 ) ; // Should create two groups
362
+ expect ( result9 . groups [ 0 ] . values ) . toEqual ( [ 'var(--prop1, fallback)' ] ) ;
363
+ expect ( result9 . groups [ 1 ] . values ) . toEqual ( [ 'var(--prop2, fallback)' ] ) ;
364
+ } ) ;
365
+
198
366
test ( 'skips invalid functions while parsing (for example missing closing parenthesis)' , ( ) => {
199
367
const warnSpy = jest . spyOn ( console , 'warn' ) . mockImplementation ( ( ) => { } ) ;
200
368
0 commit comments