diff --git a/lib/ValuesParser.js b/lib/ValuesParser.js index 82d9e5a..1542b00 100644 --- a/lib/ValuesParser.js +++ b/lib/ValuesParser.js @@ -148,6 +148,8 @@ module.exports = class ValuesParser extends Parser { if (Punctuation.chars.includes(type)) { Punctuation.fromTokens(tokens, this); + } else if (type === 'word' && Operator.test(tokens, this)) { + Operator.fromTokens(tokens, this); } else if (Func.test(tokens)) { Func.fromTokens(tokens, this); } else if (this.options.interpolation && Interpolation.test(tokens, this)) { diff --git a/lib/nodes/Operator.js b/lib/nodes/Operator.js index 2fac9af..5034ca6 100644 --- a/lib/nodes/Operator.js +++ b/lib/nodes/Operator.js @@ -14,6 +14,7 @@ const Node = require('./Node'); const operators = ['+', '-', '/', '*', '%', '=', '<=', '>=', '<', '>']; const operRegex = new RegExp(`([/|*}])`); +const compactRegex = /^[*/]\b/; class Operator extends Node { constructor(options) { @@ -33,6 +34,13 @@ class Operator extends Node { return operRegex; } + static test(tokens, parser) { + const [first] = tokens; + const [, value] = first; + const { lastNode } = parser; + return lastNode && lastNode.type === 'func' && compactRegex.test(value); + } + static tokenize(tokens, parser) { const [first, ...rest] = tokens; const [, value, startLine, , endLine, endChar] = first; diff --git a/test/fixtures/func.js b/test/fixtures/func.js index c58e3bf..562b243 100644 --- a/test/fixtures/func.js +++ b/test/fixtures/func.js @@ -29,8 +29,10 @@ module.exports = { 'lCH(40% 68.8 34.5 / 50%)', 'hwb(90deg 0% 0% / 0.5)', 'calc(-0.5 * var(foo))', + 'calc(var(--foo)*var(--bar))', 'calc(1px + -2vw - 4px)', 'calc(((768px - 100vw) / 2) - 15px)', + 'calc(((768px - 100vw)/2) - 15px)', 'bar(baz(black, 10%), 10%)', '-webkit-linear-gradient(0)', 'var(--foo)', diff --git a/test/snapshots/func.test.js.md b/test/snapshots/func.test.js.md index 4591cb5..ec7a017 100644 --- a/test/snapshots/func.test.js.md +++ b/test/snapshots/func.test.js.md @@ -5114,3 +5114,1372 @@ Generated by [AVA](https://avajs.dev). [Symbol(isClean)]: false, }, ] + +## calc(-0.5*var(foo)) + +> Snapshot 1 + + 'calc(-0.5*var(foo))' + +> Snapshot 2 + + 'calc(-0.5*var(foo))' + +> Snapshot 3 + + [ + Func { + isColor: false, + isVar: false, + name: 'calc', + nodes: [ + Numeric { + parent: [Circular], + raws: { + after: '', + before: '', + }, + source: { + end: { + column: 1, + line: 1, + offset: 0, + }, + input: Input { + css: '-0.5*var(foo)', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'numeric', + unit: '', + value: '-0.5', + [Symbol(isClean)]: false, + }, + Operator { + parent: [Circular], + raws: { + after: '', + before: '', + }, + source: { + end: { + column: 1, + line: 1, + offset: 0, + }, + input: Input { + css: '-0.5*var(foo)', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'operator', + value: '*', + [Symbol(isClean)]: false, + }, + Func { + isColor: false, + isVar: false, + name: 'var', + nodes: [ + Word { + isColor: false, + isHex: false, + isUrl: false, + isVariable: false, + parent: [Circular], + raws: { + after: '', + before: '', + }, + source: { + end: { + column: 1, + line: 1, + offset: 0, + }, + input: Input { + css: 'foo', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'word', + value: 'foo', + [Symbol(isClean)]: false, + }, + ], + params: '(foo)', + parent: [Circular], + raws: { + after: '', + before: '', + semicolon: false, + }, + source: { + end: { + column: 9, + line: 1, + offset: 8, + }, + input: Input { + css: '-0.5*var(foo)', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'func', + [Symbol(isClean)]: false, + }, + ], + params: '(-0.5*var(foo))', + raws: { + after: '', + before: '', + semicolon: false, + }, + source: { + end: { + column: 19, + line: 1, + offset: 18, + }, + input: Input { + css: 'calc(-0.5*var(foo))', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'func', + [Symbol(isClean)]: false, + }, + ] + +## calc(var(--foo)) + +> Snapshot 1 + + 'calc(var(--foo))' + +> Snapshot 2 + + 'calc(var(--foo))' + +> Snapshot 3 + + [ + Func { + isColor: false, + isVar: false, + name: 'calc', + nodes: [ + Func { + isColor: false, + isVar: true, + name: 'var', + nodes: [ + Word { + isColor: false, + isHex: false, + isUrl: false, + isVariable: true, + parent: [Circular], + raws: { + after: '', + before: '', + }, + source: { + end: { + column: 1, + line: 1, + offset: 0, + }, + input: Input { + css: '--foo', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'word', + value: '--foo', + [Symbol(isClean)]: false, + }, + ], + params: '(--foo)', + parent: [Circular], + raws: { + after: '', + before: '', + semicolon: false, + }, + source: { + end: { + column: 4, + line: 1, + offset: 3, + }, + input: Input { + css: 'var(--foo)', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'func', + [Symbol(isClean)]: false, + }, + ], + params: '(var(--foo))', + raws: { + after: '', + before: '', + semicolon: false, + }, + source: { + end: { + column: 16, + line: 1, + offset: 15, + }, + input: Input { + css: 'calc(var(--foo))', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'func', + [Symbol(isClean)]: false, + }, + ] + +## calc(var(--foo) * var(--bar)) + +> Snapshot 1 + + 'calc(var(--foo) * var(--bar))' + +> Snapshot 2 + + 'calc(var(--foo) * var(--bar))' + +> Snapshot 3 + + [ + Func { + isColor: false, + isVar: false, + name: 'calc', + nodes: [ + Func { + isColor: false, + isVar: true, + name: 'var', + nodes: [ + Word { + isColor: false, + isHex: false, + isUrl: false, + isVariable: true, + parent: [Circular], + raws: { + after: '', + before: '', + }, + source: { + end: { + column: 1, + line: 1, + offset: 0, + }, + input: Input { + css: '--foo', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'word', + value: '--foo', + [Symbol(isClean)]: false, + }, + ], + params: '(--foo)', + parent: [Circular], + raws: { + after: '', + before: '', + semicolon: false, + }, + source: { + end: { + column: 4, + line: 1, + offset: 3, + }, + input: Input { + css: 'var(--foo) * var(--bar)', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'func', + [Symbol(isClean)]: false, + }, + Operator { + parent: [Circular], + raws: { + after: '', + before: ' ', + }, + source: { + end: { + column: 12, + line: 1, + offset: 11, + }, + input: Input { + css: 'var(--foo) * var(--bar)', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 12, + line: 1, + offset: 11, + }, + }, + type: 'operator', + value: '*', + [Symbol(isClean)]: false, + }, + Func { + isColor: false, + isVar: true, + name: 'var', + nodes: [ + Word { + isColor: false, + isHex: false, + isUrl: false, + isVariable: true, + parent: [Circular], + raws: { + after: '', + before: '', + }, + source: { + end: { + column: 1, + line: 1, + offset: 0, + }, + input: Input { + css: '--bar', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'word', + value: '--bar', + [Symbol(isClean)]: false, + }, + ], + params: '(--bar)', + parent: [Circular], + raws: { + after: '', + before: ' ', + semicolon: false, + }, + source: { + end: { + column: 17, + line: 1, + offset: 16, + }, + input: Input { + css: 'var(--foo) * var(--bar)', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 14, + line: 1, + offset: 13, + }, + }, + type: 'func', + [Symbol(isClean)]: false, + }, + ], + params: '(var(--foo) * var(--bar))', + raws: { + after: '', + before: '', + semicolon: false, + }, + source: { + end: { + column: 29, + line: 1, + offset: 28, + }, + input: Input { + css: 'calc(var(--foo) * var(--bar))', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'func', + [Symbol(isClean)]: false, + }, + ] + +## calc(var(--foo)* var(--bar)) + +> Snapshot 1 + + 'calc(var(--foo)* var(--bar))' + +> Snapshot 2 + + 'calc(var(--foo)* var(--bar))' + +> Snapshot 3 + + [ + Func { + isColor: false, + isVar: false, + name: 'calc', + nodes: [ + Func { + isColor: false, + isVar: true, + name: 'var', + nodes: [ + Word { + isColor: false, + isHex: false, + isUrl: false, + isVariable: true, + parent: [Circular], + raws: { + after: '', + before: '', + }, + source: { + end: { + column: 1, + line: 1, + offset: 0, + }, + input: Input { + css: '--foo', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'word', + value: '--foo', + [Symbol(isClean)]: false, + }, + ], + params: '(--foo)', + parent: [Circular], + raws: { + after: '', + before: '', + semicolon: false, + }, + source: { + end: { + column: 4, + line: 1, + offset: 3, + }, + input: Input { + css: 'var(--foo)* var(--bar)', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'func', + [Symbol(isClean)]: false, + }, + Operator { + parent: [Circular], + raws: { + after: '', + before: '', + }, + source: { + end: { + column: 11, + line: 1, + offset: 10, + }, + input: Input { + css: 'var(--foo)* var(--bar)', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 11, + line: 1, + offset: 10, + }, + }, + type: 'operator', + value: '*', + [Symbol(isClean)]: false, + }, + Func { + isColor: false, + isVar: true, + name: 'var', + nodes: [ + Word { + isColor: false, + isHex: false, + isUrl: false, + isVariable: true, + parent: [Circular], + raws: { + after: '', + before: '', + }, + source: { + end: { + column: 1, + line: 1, + offset: 0, + }, + input: Input { + css: '--bar', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'word', + value: '--bar', + [Symbol(isClean)]: false, + }, + ], + params: '(--bar)', + parent: [Circular], + raws: { + after: '', + before: ' ', + semicolon: false, + }, + source: { + end: { + column: 16, + line: 1, + offset: 15, + }, + input: Input { + css: 'var(--foo)* var(--bar)', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 13, + line: 1, + offset: 12, + }, + }, + type: 'func', + [Symbol(isClean)]: false, + }, + ], + params: '(var(--foo)* var(--bar))', + raws: { + after: '', + before: '', + semicolon: false, + }, + source: { + end: { + column: 28, + line: 1, + offset: 27, + }, + input: Input { + css: 'calc(var(--foo)* var(--bar))', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'func', + [Symbol(isClean)]: false, + }, + ] + +## calc(var(--foo)*var(--bar)) + +> Snapshot 1 + + 'calc(var(--foo)*var(--bar))' + +> Snapshot 2 + + 'calc(var(--foo)*var(--bar))' + +> Snapshot 3 + + [ + Func { + isColor: false, + isVar: false, + name: 'calc', + nodes: [ + Func { + isColor: false, + isVar: true, + name: 'var', + nodes: [ + Word { + isColor: false, + isHex: false, + isUrl: false, + isVariable: true, + parent: [Circular], + raws: { + after: '', + before: '', + }, + source: { + end: { + column: 1, + line: 1, + offset: 0, + }, + input: Input { + css: '--foo', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'word', + value: '--foo', + [Symbol(isClean)]: false, + }, + ], + params: '(--foo)', + parent: [Circular], + raws: { + after: '', + before: '', + semicolon: false, + }, + source: { + end: { + column: 4, + line: 1, + offset: 3, + }, + input: Input { + css: 'var(--foo)*var(--bar)', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'func', + [Symbol(isClean)]: false, + }, + Operator { + parent: [Circular], + raws: { + after: '', + before: '', + }, + source: { + end: { + column: 11, + line: 1, + offset: 10, + }, + input: Input { + css: 'var(--foo)*var(--bar)', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 11, + line: 1, + offset: 10, + }, + }, + type: 'operator', + value: '*var', + [Symbol(isClean)]: false, + }, + Punctuation { + parent: [Circular], + raws: { + after: '', + before: '', + }, + source: { + end: { + column: 15, + line: 1, + offset: 14, + }, + input: Input { + css: 'var(--foo)*var(--bar)', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 15, + line: 1, + offset: 14, + }, + }, + type: 'punctuation', + value: '(', + [Symbol(isClean)]: false, + }, + Word { + isColor: false, + isHex: false, + isUrl: false, + isVariable: true, + parent: [Circular], + raws: { + after: '', + before: '', + }, + source: { + end: { + column: 1, + line: 1, + offset: 0, + }, + input: Input { + css: 'var(--foo)*var(--bar)', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'word', + value: '--bar', + [Symbol(isClean)]: false, + }, + Punctuation { + parent: [Circular], + raws: { + after: '', + before: '', + }, + source: { + end: { + column: 15, + line: 1, + offset: 14, + }, + input: Input { + css: 'var(--foo)*var(--bar)', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 15, + line: 1, + offset: 14, + }, + }, + type: 'punctuation', + value: ')', + [Symbol(isClean)]: false, + }, + ], + params: '(var(--foo)*var(--bar))', + raws: { + after: '', + before: '', + semicolon: false, + }, + source: { + end: { + column: 27, + line: 1, + offset: 26, + }, + input: Input { + css: 'calc(var(--foo)*var(--bar))', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'func', + [Symbol(isClean)]: false, + }, + ] + +## calc(((768px - 100vw)/2) - 15px) + +> Snapshot 1 + + 'calc(((768px - 100vw)/2) - 15px)' + +> Snapshot 2 + + 'calc(((768px - 100vw)/2) - 15px)' + +> Snapshot 3 + + [ + Func { + isColor: false, + isVar: false, + name: 'calc', + nodes: [ + Punctuation { + parent: [Circular], + raws: { + after: '', + before: '', + }, + source: { + end: { + column: 1, + line: 1, + offset: 0, + }, + input: Input { + css: '((768px - 100vw)/2) - 15px', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'punctuation', + value: '(', + [Symbol(isClean)]: false, + }, + Punctuation { + parent: [Circular], + raws: { + after: '', + before: '', + }, + source: { + end: { + column: 2, + line: 1, + offset: 1, + }, + input: Input { + css: '((768px - 100vw)/2) - 15px', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 2, + line: 1, + offset: 1, + }, + }, + type: 'punctuation', + value: '(', + [Symbol(isClean)]: false, + }, + Numeric { + parent: [Circular], + raws: { + after: '', + before: '', + }, + source: { + end: { + column: 1, + line: 1, + offset: 0, + }, + input: Input { + css: '((768px - 100vw)/2) - 15px', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'numeric', + unit: 'px', + value: '768', + [Symbol(isClean)]: false, + }, + Operator { + parent: [Circular], + raws: { + after: '', + before: ' ', + }, + source: { + end: { + column: 7, + line: 1, + offset: 6, + }, + input: Input { + css: '((768px - 100vw)/2) - 15px', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 7, + line: 1, + offset: 6, + }, + }, + type: 'operator', + value: '-', + [Symbol(isClean)]: false, + }, + Numeric { + parent: [Circular], + raws: { + after: '', + before: ' ', + }, + source: { + end: { + column: 9, + line: 1, + offset: 8, + }, + input: Input { + css: '((768px - 100vw)/2) - 15px', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 9, + line: 1, + offset: 8, + }, + }, + type: 'numeric', + unit: 'vw', + value: '100', + [Symbol(isClean)]: false, + }, + Punctuation { + parent: [Circular], + raws: { + after: '', + before: '', + }, + source: { + end: { + column: 2, + line: 1, + offset: 1, + }, + input: Input { + css: '((768px - 100vw)/2) - 15px', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 2, + line: 1, + offset: 1, + }, + }, + type: 'punctuation', + value: ')', + [Symbol(isClean)]: false, + }, + Operator { + parent: [Circular], + raws: { + after: '', + before: '', + }, + source: { + end: { + column: 17, + line: 1, + offset: 16, + }, + input: Input { + css: '((768px - 100vw)/2) - 15px', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 17, + line: 1, + offset: 16, + }, + }, + type: 'operator', + value: '/', + [Symbol(isClean)]: false, + }, + Numeric { + parent: [Circular], + raws: { + after: '', + before: '', + }, + source: { + end: { + column: 17, + line: 1, + offset: 16, + }, + input: Input { + css: '((768px - 100vw)/2) - 15px', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 17, + line: 1, + offset: 16, + }, + }, + type: 'numeric', + unit: '', + value: '2', + [Symbol(isClean)]: false, + }, + Punctuation { + parent: [Circular], + raws: { + after: '', + before: '', + }, + source: { + end: { + column: 19, + line: 1, + offset: 18, + }, + input: Input { + css: '((768px - 100vw)/2) - 15px', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 19, + line: 1, + offset: 18, + }, + }, + type: 'punctuation', + value: ')', + [Symbol(isClean)]: false, + }, + Operator { + parent: [Circular], + raws: { + after: '', + before: ' ', + }, + source: { + end: { + column: 21, + line: 1, + offset: 20, + }, + input: Input { + css: '((768px - 100vw)/2) - 15px', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 21, + line: 1, + offset: 20, + }, + }, + type: 'operator', + value: '-', + [Symbol(isClean)]: false, + }, + Numeric { + parent: [Circular], + raws: { + after: '', + before: ' ', + }, + source: { + end: { + column: 23, + line: 1, + offset: 22, + }, + input: Input { + css: '((768px - 100vw)/2) - 15px', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 23, + line: 1, + offset: 22, + }, + }, + type: 'numeric', + unit: 'px', + value: '15', + [Symbol(isClean)]: false, + }, + ], + params: '(((768px - 100vw)/2) - 15px)', + raws: { + after: '', + before: '', + semicolon: false, + }, + source: { + end: { + column: 32, + line: 1, + offset: 31, + }, + input: Input { + css: 'calc(((768px - 100vw)/2) - 15px)', + hasBOM: false, + id: '', + [Symbol(fromOffset cache)]: [ + 0, + ], + }, + start: { + column: 1, + line: 1, + offset: 0, + }, + }, + type: 'func', + [Symbol(isClean)]: false, + }, + ] diff --git a/test/snapshots/func.test.js.snap b/test/snapshots/func.test.js.snap index 610dc5c..774c50b 100644 Binary files a/test/snapshots/func.test.js.snap and b/test/snapshots/func.test.js.snap differ