88 NODE_VALUE_COLOR ,
99 NODE_VALUE_FUNCTION ,
1010 NODE_VALUE_OPERATOR ,
11+ NODE_VALUE_PARENTHESIS ,
1112} from './arena'
1213
1314describe ( 'ValueParser' , ( ) => {
@@ -215,7 +216,7 @@ describe('ValueParser', () => {
215216 expect ( decl ?. values [ 0 ] . children [ 0 ] . text ) . toBe ( '--primary-color' )
216217 } )
217218
218- it ( 'should parse url() function' , ( ) => {
219+ it ( 'should parse url() function with quoted string ' , ( ) => {
219220 const parser = new Parser ( 'body { background: url("image.png"); }' )
220221 const root = parser . parse ( )
221222 const rule = root . first_child
@@ -228,6 +229,90 @@ describe('ValueParser', () => {
228229 expect ( decl ?. values [ 0 ] . children [ 0 ] . type ) . toBe ( NODE_VALUE_STRING )
229230 expect ( decl ?. values [ 0 ] . children [ 0 ] . text ) . toBe ( '"image.png"' )
230231 } )
232+
233+ it ( 'should parse url() function with unquoted URL containing dots' , ( ) => {
234+ const parser = new Parser ( 'body { cursor: url(mycursor.cur); }' )
235+ const root = parser . parse ( )
236+ const rule = root . first_child
237+ const decl = rule ?. first_child ?. next_sibling ?. first_child
238+ const func = decl ?. values [ 0 ]
239+
240+ expect ( func ?. type ) . toBe ( NODE_VALUE_FUNCTION )
241+ expect ( func ?. name ) . toBe ( 'url' )
242+
243+ // URL function should not parse children - content is available via node.value
244+ expect ( func ?. has_children ) . toBe ( false )
245+ expect ( func ?. text ) . toBe ( 'url(mycursor.cur)' )
246+ expect ( func ?. value ) . toBe ( 'mycursor.cur' )
247+ } )
248+
249+ it ( 'should parse src() function with unquoted URL' , ( ) => {
250+ const parser = new Parser ( 'body { content: src(myfont.woff2); }' )
251+ const root = parser . parse ( )
252+ const rule = root . first_child
253+ const decl = rule ?. first_child ?. next_sibling ?. first_child
254+ const func = decl ?. values [ 0 ]
255+
256+ expect ( func ?. type ) . toBe ( NODE_VALUE_FUNCTION )
257+ expect ( func ?. name ) . toBe ( 'src' )
258+ expect ( func ?. has_children ) . toBe ( false )
259+ expect ( func ?. text ) . toBe ( 'src(myfont.woff2)' )
260+ expect ( func ?. value ) . toBe ( 'myfont.woff2' )
261+ } )
262+
263+ it ( 'should parse url() with base64 data URL' , ( ) => {
264+ const parser = new Parser ( 'body { background: url(data:image/png;base64,iVBORw0KGg); }' )
265+ const root = parser . parse ( )
266+ const rule = root . first_child
267+ const decl = rule ?. first_child ?. next_sibling ?. first_child
268+ const func = decl ?. values [ 0 ]
269+
270+ expect ( func ?. type ) . toBe ( NODE_VALUE_FUNCTION )
271+ expect ( func ?. name ) . toBe ( 'url' )
272+ expect ( func ?. has_children ) . toBe ( false )
273+ expect ( func ?. value ) . toBe ( 'data:image/png;base64,iVBORw0KGg' )
274+ } )
275+
276+ it ( 'should parse url() with inline SVG' , ( ) => {
277+ const parser = new Parser ( 'body { background: url(data:image/svg+xml,<svg></svg>); }' )
278+ const root = parser . parse ( )
279+ const rule = root . first_child
280+ const decl = rule ?. first_child ?. next_sibling ?. first_child
281+ const func = decl ?. values [ 0 ]
282+
283+ expect ( func ?. type ) . toBe ( NODE_VALUE_FUNCTION )
284+ expect ( func ?. name ) . toBe ( 'url' )
285+ expect ( func ?. has_children ) . toBe ( false )
286+ expect ( func ?. value ) . toBe ( 'data:image/svg+xml,<svg></svg>' )
287+ } )
288+
289+ it ( 'should provide node.value for other functions like calc()' , ( ) => {
290+ const parser = new Parser ( 'body { width: calc(100% - 20px); }' )
291+ const root = parser . parse ( )
292+ const rule = root . first_child
293+ const decl = rule ?. first_child ?. next_sibling ?. first_child
294+ const func = decl ?. values [ 0 ]
295+
296+ expect ( func ?. type ) . toBe ( NODE_VALUE_FUNCTION )
297+ expect ( func ?. name ) . toBe ( 'calc' )
298+ expect ( func ?. text ) . toBe ( 'calc(100% - 20px)' )
299+ expect ( func ?. value ) . toBe ( '100% - 20px' )
300+ expect ( func ?. has_children ) . toBe ( true ) // calc() parses its children
301+ } )
302+
303+ it ( 'should provide node.value for var() function' , ( ) => {
304+ const parser = new Parser ( 'body { color: var(--primary-color); }' )
305+ const root = parser . parse ( )
306+ const rule = root . first_child
307+ const decl = rule ?. first_child ?. next_sibling ?. first_child
308+ const func = decl ?. values [ 0 ]
309+
310+ expect ( func ?. type ) . toBe ( NODE_VALUE_FUNCTION )
311+ expect ( func ?. name ) . toBe ( 'var' )
312+ expect ( func ?. text ) . toBe ( 'var(--primary-color)' )
313+ expect ( func ?. value ) . toBe ( '--primary-color' )
314+ expect ( func ?. has_children ) . toBe ( true ) // var() parses its children
315+ } )
231316 } )
232317
233318 describe ( 'Complex values' , ( ) => {
@@ -368,4 +453,79 @@ describe('ValueParser', () => {
368453 expect ( operators ?. [ 3 ] . text ) . toBe ( '-' )
369454 } )
370455 } )
456+
457+ describe ( 'Parentheses' , ( ) => {
458+ it ( 'should parse parenthesized expressions in calc()' , ( ) => {
459+ const parser = new Parser ( 'body { width: calc((100% - 50px) / 2); }' )
460+ const root = parser . parse ( )
461+ const rule = root . first_child
462+ const decl = rule ?. first_child ?. next_sibling ?. first_child
463+ const func = decl ?. values [ 0 ]
464+
465+ expect ( func ?. type ) . toBe ( NODE_VALUE_FUNCTION )
466+ expect ( func ?. name ) . toBe ( 'calc' )
467+ expect ( func ?. children ) . toHaveLength ( 3 )
468+
469+ // First child should be a parenthesis node
470+ expect ( func ?. children [ 0 ] . type ) . toBe ( NODE_VALUE_PARENTHESIS )
471+ expect ( func ?. children [ 0 ] . text ) . toBe ( '(100% - 50px)' )
472+
473+ // Check parenthesis content
474+ const parenNode = func ?. children [ 0 ]
475+ expect ( parenNode ?. children ) . toHaveLength ( 3 )
476+ expect ( parenNode ?. children [ 0 ] . type ) . toBe ( NODE_VALUE_DIMENSION )
477+ expect ( parenNode ?. children [ 0 ] . text ) . toBe ( '100%' )
478+ expect ( parenNode ?. children [ 1 ] . type ) . toBe ( NODE_VALUE_OPERATOR )
479+ expect ( parenNode ?. children [ 1 ] . text ) . toBe ( '-' )
480+ expect ( parenNode ?. children [ 2 ] . type ) . toBe ( NODE_VALUE_DIMENSION )
481+ expect ( parenNode ?. children [ 2 ] . text ) . toBe ( '50px' )
482+
483+ // Second child should be division operator
484+ expect ( func ?. children [ 1 ] . type ) . toBe ( NODE_VALUE_OPERATOR )
485+ expect ( func ?. children [ 1 ] . text ) . toBe ( '/' )
486+
487+ // Third child should be number
488+ expect ( func ?. children [ 2 ] . type ) . toBe ( NODE_VALUE_NUMBER )
489+ expect ( func ?. children [ 2 ] . text ) . toBe ( '2' )
490+ } )
491+
492+ it ( 'should parse complex nested parentheses' , ( ) => {
493+ const parser = new Parser ( 'body { width: calc(((100% - var(--x)) / 12 * 6) + (-1 * var(--y))); }' )
494+ const root = parser . parse ( )
495+ const rule = root . first_child
496+ const decl = rule ?. first_child ?. next_sibling ?. first_child
497+ const func = decl ?. values [ 0 ]
498+
499+ expect ( func ?. type ) . toBe ( NODE_VALUE_FUNCTION )
500+ expect ( func ?. name ) . toBe ( 'calc' )
501+
502+ // The calc function should have 3 children: parenthesis + operator + parenthesis
503+ expect ( func ?. children ) . toHaveLength ( 3 )
504+ expect ( func ?. children [ 0 ] . type ) . toBe ( NODE_VALUE_PARENTHESIS )
505+ expect ( func ?. children [ 0 ] . text ) . toBe ( '((100% - var(--x)) / 12 * 6)' )
506+ expect ( func ?. children [ 1 ] . type ) . toBe ( NODE_VALUE_OPERATOR )
507+ expect ( func ?. children [ 1 ] . text ) . toBe ( '+' )
508+ expect ( func ?. children [ 2 ] . type ) . toBe ( NODE_VALUE_PARENTHESIS )
509+ expect ( func ?. children [ 2 ] . text ) . toBe ( '(-1 * var(--y))' )
510+
511+ // Check first parenthesis has nested parenthesis and preserves structure
512+ const firstParen = func ?. children [ 0 ]
513+ expect ( firstParen ?. children ) . toHaveLength ( 5 ) // paren + / + 12 + * + 6
514+ expect ( firstParen ?. children [ 0 ] . type ) . toBe ( NODE_VALUE_PARENTHESIS )
515+ expect ( firstParen ?. children [ 0 ] . text ) . toBe ( '(100% - var(--x))' )
516+
517+ // Check nested parenthesis has function
518+ const nestedParen = firstParen ?. children [ 0 ]
519+ expect ( nestedParen ?. children [ 2 ] . type ) . toBe ( NODE_VALUE_FUNCTION )
520+ expect ( nestedParen ?. children [ 2 ] . name ) . toBe ( 'var' )
521+
522+ // Check second parenthesis has content
523+ const secondParen = func ?. children [ 2 ]
524+ expect ( secondParen ?. children ) . toHaveLength ( 3 ) // -1 * var(--y)
525+ expect ( secondParen ?. children [ 0 ] . type ) . toBe ( NODE_VALUE_NUMBER )
526+ expect ( secondParen ?. children [ 0 ] . text ) . toBe ( '-1' )
527+ expect ( secondParen ?. children [ 2 ] . type ) . toBe ( NODE_VALUE_FUNCTION )
528+ expect ( secondParen ?. children [ 2 ] . name ) . toBe ( 'var' )
529+ } )
530+ } )
371531} )
0 commit comments