diff --git a/src/index.ts b/src/index.ts index 72304de..c62c3c4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,9 +5,8 @@ * @see {@link https://github.com/asamuzaK/cssColor/blob/main/LICENSE} */ -import { cssCalc } from './js/css-calc'; +import { cssCalc, cssVar } from './js/css-calc-var'; import { isGradient, resolveGradient } from './js/css-gradient'; -import { cssVar } from './js/css-var'; import { isColor } from './js/resolve'; import { extractDashedIdent, diff --git a/src/js/convert.ts b/src/js/convert.ts index 1d925e0..1850176 100644 --- a/src/js/convert.ts +++ b/src/js/convert.ts @@ -16,8 +16,7 @@ import { parseColorValue } from './color'; import { isString } from './common'; -import { cssCalc } from './css-calc'; -import { resolveVar } from './css-var'; +import { cssCalc, resolveVar } from './css-calc-var'; import { resolveRelativeColor } from './relative-color'; import { resolveColor } from './resolve'; import { ColorChannels, ComputedColorChannels, Options } from './typedef'; diff --git a/src/js/css-calc.ts b/src/js/css-calc-var.ts similarity index 82% rename from src/js/css-calc.ts rename to src/js/css-calc-var.ts index 9c5d251..219c905 100644 --- a/src/js/css-calc.ts +++ b/src/js/css-calc-var.ts @@ -1,18 +1,19 @@ /** - * css-calc + * css-calc-var */ import { calc, conversionOptions as CalcOptions } from '@csstools/css-calc'; import { CSSToken, TokenType, tokenize } from '@csstools/css-tokenizer'; import { createCacheKey, getCache, setCache } from './cache'; import { isString, isStringOrNumber } from './common'; -import { resolveVar } from './css-var'; +import { isColor } from './resolve'; // ๐Ÿ‘ˆ ๅ…ˆใปใฉ็งปๅ‹•ใ—ใŸ isColor import { resolveLengthInPixels, roundToPrecision } from './util'; import { MatchedRegExp, Options } from './typedef'; /* constants */ import { ANGLE, + FN_VAR, LENGTH, NUM, SYN_FN_CALC, @@ -21,16 +22,18 @@ import { SYN_FN_VAR_START, VAL_SPEC } from './constant'; + const { CloseParen: PAREN_CLOSE, Comment: COMMENT, Dimension: DIM, EOF, Function: FUNC, + Ident: IDENT, OpenParen: PAREN_OPEN, Whitespace: W_SPACE } = TokenType; -const NAMESPACE = 'css-calc'; +const NAMESPACE = 'css-calc-var'; /* numeric constants */ const TRIA = 3; @@ -48,8 +51,8 @@ const REG_PAREN_OPEN = /\($/; const REG_TYPE_DIM = new RegExp(`^(${NUM})(${ANGLE}|${LENGTH})$`); const REG_TYPE_DIM_PCT = new RegExp(`^(${NUM})(${ANGLE}|${LENGTH}|%)$`); const REG_TYPE_PCT = new RegExp(`^(${NUM})%$`); +const REG_CSS_WIDE_KEYWORD = /^(?:inherit|initial|revert(?:-layer)?|unset)$/; -/* type definitions */ /** * @type CalcASTNode - AST node for calc() */ @@ -851,12 +854,12 @@ export const resolveDimension = ( }; /** - * parse tokens + * parse CSS calc() tokens * @param tokens - CSS tokens * @param [opt] - options * @returns parsed tokens */ -export const parseTokens = ( +export const parseCalcTokens = ( tokens: CSSToken[], opt: Options = {} ): string[] => { @@ -975,7 +978,7 @@ export const cssCalc = (value: string, opt: Options = {}): string => { return cachedResult.item as string; } const tokens = tokenize({ css: value }); - const values = parseTokens(tokens, opt); + const values = parseCalcTokens(tokens, opt); let resolvedValue: string = calc(values.join(''), { toCanonicalUnits: true }); @@ -1006,3 +1009,201 @@ export const cssCalc = (value: string, opt: Options = {}): string => { setCache(cacheKey, resolvedValue); return resolvedValue; }; + +/** + * resolve custom property + * @param tokens - CSS tokens + * @param [opt] - options + * @returns result - [tokens, resolvedValue] + */ +export function resolveCustomProperty( + tokens: CSSToken[], + opt: Options = {} +): [CSSToken[], string] { + if (!Array.isArray(tokens)) { + throw new TypeError(`${tokens} is not an array.`); + } + const { customProperty = {} } = opt; + const items: string[] = []; + while (tokens.length) { + const token = tokens.shift(); + if (!token) { + break; + } + if (!Array.isArray(token)) { + throw new TypeError(`${token} is not an array.`); + } + const [type, value] = token as [TokenType, string]; + // end of var() + if (type === PAREN_CLOSE) { + break; + } + // nested var() + if (value === FN_VAR) { + const [, item] = resolveCustomProperty(tokens, opt); + if (item) { + items.push(item); + } + } else if (type === IDENT) { + if (value.startsWith('--')) { + let item; + if (Object.hasOwn(customProperty, value)) { + item = customProperty[value] as string; + } else if (typeof customProperty.callback === 'function') { + item = customProperty.callback(value); + } + if (item) { + items.push(item); + } + } else if (value) { + items.push(value); + } + } + } + let resolveAsColor = false; + if (items.length > 1) { + resolveAsColor = isColor(items[items.length - 1]); + } + let resolvedValue = ''; + for (let item of items) { + item = item.trim(); + if (REG_FN_VAR.test(item)) { + // recurse resolveVar() + const resolvedItem = resolveVar(item, opt); + if (isString(resolvedItem)) { + if (!resolveAsColor || isColor(resolvedItem)) { + resolvedValue = resolvedItem; + } + } + } else if (REG_FN_CALC.test(item)) { + item = cssCalc(item, opt); + if (!resolveAsColor || isColor(item)) { + resolvedValue = item; + } + } else if (item && !REG_CSS_WIDE_KEYWORD.test(item)) { + if (!resolveAsColor || isColor(item)) { + resolvedValue = item; + } + } + if (resolvedValue) { + break; + } + } + return [tokens, resolvedValue]; +} + +/** + * parse CSS var() tokens + * @param tokens - CSS tokens + * @param [opt] - options + * @returns parsed tokens + */ +export function parseVarTokens( + tokens: CSSToken[], + opt: Options = {} +): string[] | null { + const res: string[] = []; + while (tokens.length) { + const token = tokens.shift(); + if (!token) break; + const [type = '', value = ''] = token as [TokenType, string]; + if (value === FN_VAR) { + const [, resolvedValue] = resolveCustomProperty(tokens, opt); + if (!resolvedValue) { + return null; + } + res.push(resolvedValue); + } else { + switch (type) { + case PAREN_CLOSE: { + if (res.length) { + if (res[res.length - 1] === ' ') { + res[res.length - 1] = value; + } else { + res.push(value); + } + } else { + res.push(value); + } + break; + } + case W_SPACE: { + if (res.length) { + const lastValue = res[res.length - 1]; + if ( + isString(lastValue) && + !lastValue.endsWith('(') && + lastValue !== ' ' + ) { + res.push(value); + } + } + break; + } + default: { + if (type !== COMMENT && type !== EOF) { + res.push(value); + } + } + } + } + } + return res; +} + +/** + * resolve CSS var() + * @param value - CSS value including var() + * @param [opt] - options + * @returns resolved value + */ +export function resolveVar(value: string, opt: Options = {}): string | null { + const { format = '' } = opt; + if (isString(value)) { + if (!REG_FN_VAR.test(value) || format === VAL_SPEC) { + return value; + } + value = value.trim(); + } else { + throw new TypeError(`${value} is not a string.`); + } + const cacheKey: string = createCacheKey( + { + namespace: NAMESPACE, + name: 'resolveVar', + value + }, + opt + ); + const cachedResult = getCache(cacheKey); + if (cachedResult !== false) { + return cachedResult.item as string | null; + } + const tokens = tokenize({ css: value }); + const values = parseVarTokens(tokens, opt); + if (Array.isArray(values)) { + let color = values.join(''); + if (REG_FN_CALC.test(color)) { + color = cssCalc(color, opt); + } + setCache(cacheKey, color); + return color; + } else { + setCache(cacheKey, null); + return null; + } +} + +/** + * CSS var() + * @param value - CSS value including var() + * @param [opt] - options + * @returns resolved value + */ +export const cssVar = (value: string, opt: Options = {}): string => { + const resolvedValue = resolveVar(value, opt); + if (isString(resolvedValue)) { + return resolvedValue; + } + return ''; +}; diff --git a/src/js/css-var.ts b/src/js/css-var.ts deleted file mode 100644 index 764789b..0000000 --- a/src/js/css-var.ts +++ /dev/null @@ -1,224 +0,0 @@ -/** - * css-var - */ - -import { CSSToken, TokenType, tokenize } from '@csstools/css-tokenizer'; -import { createCacheKey, getCache, setCache } from './cache'; -import { isString } from './common'; -import { cssCalc } from './css-calc'; -import { isColor } from './resolve'; -import { Options } from './typedef'; - -/* constants */ -import { FN_VAR, SYN_FN_CALC, SYN_FN_VAR, VAL_SPEC } from './constant'; -const { - CloseParen: PAREN_CLOSE, - Comment: COMMENT, - EOF, - Ident: IDENT, - Whitespace: W_SPACE -} = TokenType; -const NAMESPACE = 'css-var'; - -/* regexp */ -const REG_FN_CALC = new RegExp(SYN_FN_CALC); -const REG_FN_VAR = new RegExp(SYN_FN_VAR); -const REG_CSS_WIDE_KEYWORD = /^(?:inherit|initial|revert(?:-layer)?|unset)$/; - -/** - * resolve custom property - * @param tokens - CSS tokens - * @param [opt] - options - * @returns result - [tokens, resolvedValue] - */ -export function resolveCustomProperty( - tokens: CSSToken[], - opt: Options = {} -): [CSSToken[], string] { - if (!Array.isArray(tokens)) { - throw new TypeError(`${tokens} is not an array.`); - } - const { customProperty = {} } = opt; - const items: string[] = []; - while (tokens.length) { - const token = tokens.shift(); - if (!token) { - break; - } - if (!Array.isArray(token)) { - throw new TypeError(`${token} is not an array.`); - } - const [type, value] = token as [TokenType, string]; - // end of var() - if (type === PAREN_CLOSE) { - break; - } - // nested var() - if (value === FN_VAR) { - const [, item] = resolveCustomProperty(tokens, opt); - if (item) { - items.push(item); - } - } else if (type === IDENT) { - if (value.startsWith('--')) { - let item; - if (Object.hasOwn(customProperty, value)) { - item = customProperty[value] as string; - } else if (typeof customProperty.callback === 'function') { - item = customProperty.callback(value); - } - if (item) { - items.push(item); - } - } else if (value) { - items.push(value); - } - } - } - let resolveAsColor = false; - if (items.length > 1) { - resolveAsColor = isColor(items[items.length - 1]); - } - let resolvedValue = ''; - for (let item of items) { - item = item.trim(); - if (REG_FN_VAR.test(item)) { - // recurse resolveVar() - const resolvedItem = resolveVar(item, opt); - if (isString(resolvedItem)) { - if (!resolveAsColor || isColor(resolvedItem)) { - resolvedValue = resolvedItem; - } - } - } else if (REG_FN_CALC.test(item)) { - item = cssCalc(item, opt); - if (!resolveAsColor || isColor(item)) { - resolvedValue = item; - } - } else if (item && !REG_CSS_WIDE_KEYWORD.test(item)) { - if (!resolveAsColor || isColor(item)) { - resolvedValue = item; - } - } - if (resolvedValue) { - break; - } - } - return [tokens, resolvedValue]; -} - -/** - * parse tokens - * @param tokens - CSS tokens - * @param [opt] - options - * @returns parsed tokens - */ -export function parseTokens( - tokens: CSSToken[], - opt: Options = {} -): string[] | null { - const res: string[] = []; - while (tokens.length) { - const token = tokens.shift(); - if (!token) break; - const [type = '', value = ''] = token as [TokenType, string]; - if (value === FN_VAR) { - const [, resolvedValue] = resolveCustomProperty(tokens, opt); - if (!resolvedValue) { - return null; - } - res.push(resolvedValue); - } else { - switch (type) { - case PAREN_CLOSE: { - if (res.length) { - if (res[res.length - 1] === ' ') { - res[res.length - 1] = value; - } else { - res.push(value); - } - } else { - res.push(value); - } - break; - } - case W_SPACE: { - if (res.length) { - const lastValue = res[res.length - 1]; - if ( - isString(lastValue) && - !lastValue.endsWith('(') && - lastValue !== ' ' - ) { - res.push(value); - } - } - break; - } - default: { - if (type !== COMMENT && type !== EOF) { - res.push(value); - } - } - } - } - } - return res; -} - -/** - * resolve CSS var() - * @param value - CSS value including var() - * @param [opt] - options - * @returns resolved value - */ -export function resolveVar(value: string, opt: Options = {}): string | null { - const { format = '' } = opt; - if (isString(value)) { - if (!REG_FN_VAR.test(value) || format === VAL_SPEC) { - return value; - } - value = value.trim(); - } else { - throw new TypeError(`${value} is not a string.`); - } - const cacheKey: string = createCacheKey( - { - namespace: NAMESPACE, - name: 'resolveVar', - value - }, - opt - ); - const cachedResult = getCache(cacheKey); - if (cachedResult !== false) { - return cachedResult.item as string | null; - } - const tokens = tokenize({ css: value }); - const values = parseTokens(tokens, opt); - if (Array.isArray(values)) { - let color = values.join(''); - if (REG_FN_CALC.test(color)) { - color = cssCalc(color, opt); - } - setCache(cacheKey, color); - return color; - } else { - setCache(cacheKey, null); - return null; - } -} - -/** - * CSS var() - * @param value - CSS value including var() - * @param [opt] - options - * @returns resolved value - */ -export const cssVar = (value: string, opt: Options = {}): string => { - const resolvedValue = resolveVar(value, opt); - if (isString(resolvedValue)) { - return resolvedValue; - } - return ''; -}; diff --git a/src/js/relative-color.ts b/src/js/relative-color.ts index 939adf2..814ddae 100644 --- a/src/js/relative-color.ts +++ b/src/js/relative-color.ts @@ -11,7 +11,7 @@ import { CSSToken, TokenType, tokenize } from '@csstools/css-tokenizer'; import { createCacheKey, getCache, setCache } from './cache'; import { NAMED_COLORS, convertColorToRgb } from './color'; import { isString, isStringOrNumber } from './common'; -import { resolveDimension, serializeCalc } from './css-calc'; +import { resolveDimension, serializeCalc } from './css-calc-var'; import { resolveColor } from './resolve'; import { roundToPrecision, splitValue } from './util'; import { diff --git a/src/js/resolve.ts b/src/js/resolve.ts index fbda5f7..329425f 100644 --- a/src/js/resolve.ts +++ b/src/js/resolve.ts @@ -11,8 +11,7 @@ import { resolveColorValue } from './color'; import { isString } from './common'; -import { cssCalc } from './css-calc'; -import { resolveVar } from './css-var'; +import { cssCalc, resolveVar } from './css-calc-var'; import { resolveRelativeColor } from './relative-color'; import { splitValue } from './util'; import { diff --git a/test/css-calc.test.ts b/test/css-calc-var.test.ts similarity index 73% rename from test/css-calc.test.ts rename to test/css-calc-var.test.ts index f3c0902..aed6d10 100644 --- a/test/css-calc.test.ts +++ b/test/css-calc-var.test.ts @@ -7,7 +7,7 @@ import { afterEach, assert, beforeEach, describe, it } from 'vitest'; /* test */ import { genCache } from '../src/js/cache'; -import * as csscalc from '../src/js/css-calc'; +import * as css from '../src/js/css-calc-var'; beforeEach(() => { genCache.clear(); @@ -18,7 +18,7 @@ afterEach(() => { }); describe('Calculator', () => { - const { Calculator } = csscalc; + const { Calculator } = css; it('should create instance', () => { const cal = new Calculator(); @@ -651,7 +651,7 @@ describe('Calculator', () => { }); describe('sort calc values', () => { - const func = csscalc.sortCalcValues; + const func = css.sortCalcValues; it('should throw', () => { assert.throws(() => func(), Error, 'Unexpected array length 0.'); @@ -790,7 +790,7 @@ describe('sort calc values', () => { }); describe('serialize calc', () => { - const func = csscalc.serializeCalc; + const func = css.serializeCalc; it('should throw', () => { assert.throws(() => func(), TypeError, 'undefined is not a string.'); @@ -877,7 +877,7 @@ describe('serialize calc', () => { }); describe('resolve dimension', () => { - const func = csscalc.resolveDimension; + const func = css.resolveDimension; it('should throw', () => { assert.throws(() => func(), TypeError, 'undefined is not an array.'); @@ -1162,8 +1162,8 @@ describe('resolve dimension', () => { }); }); -describe('parse tokens', () => { - const func = csscalc.parseTokens; +describe('parse CSS calc() tokens', () => { + const func = css.parseCalcTokens; it('should throw', () => { assert.throws(() => func(), TypeError, 'undefined is not an array.'); @@ -1299,7 +1299,7 @@ describe('parse tokens', () => { }); describe('CSS calc()', () => { - const func = csscalc.cssCalc; + const func = css.cssCalc; it('should throw', () => { assert.throws(() => func(), TypeError, 'undefined is not a string.'); @@ -1784,7 +1784,7 @@ describe('CSS calc()', () => { }); describe('serialize calc edge cases', () => { - const func = csscalc.serializeCalc; + const func = css.serializeCalc; it('should handle incomplete top-level tokens', () => { const res = func('calc(1) +', { format: 'specifiedValue' }); @@ -1814,3 +1814,743 @@ describe('serialize calc edge cases', () => { ); }); }); + +describe('resolve CSS variable', () => { + const func = css.resolveCustomProperty; + + it('should throw', () => { + assert.throws(() => func(), TypeError, 'undefined is not an array.'); + }); + + it('should throw', () => { + assert.throws(() => func(['foo']), TypeError, 'foo is not an array.'); + }); + + it('should get value', () => { + const res = func([[]]); + assert.deepEqual(res, [[], ''], 'result'); + }); + + it('should get value', () => { + const res = func([ + ['ident-token', '--foo'], + [')-token', ')'] + ]); + assert.deepEqual(res, [[], ''], 'result'); + }); + + it('should get value', () => { + const res = func([ + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', 'red'], + [')-token', ')'] + ]); + assert.deepEqual(res, [[], 'red'], 'result'); + }); + + it('should get value', () => { + const res = func([ + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', 'red'], + [')-token', ')'], + [')-token', ')'] + ]); + assert.deepEqual(res, [[[')-token', ')']], 'red'], 'result'); + }); + + it('should get value', () => { + const res = func( + [ + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', 'red'], + [')-token', ')'], + [')-token', ')'] + ], + { + customProperty: { + '--foo': 'blue' + } + } + ); + assert.deepEqual(res, [[[')-token', ')']], 'blue'], 'result'); + }); + + it('should get value', () => { + const getPropertyValue = v => { + let res; + switch (v) { + case '--foo': + res = 'blue'; + break; + case '--bar': + res = 'green'; + break; + default: + } + return res; + }; + const res = func( + [ + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', 'red'], + [')-token', ')'], + [')-token', ')'] + ], + { + customProperty: { + callback: getPropertyValue + } + } + ); + assert.deepEqual(res, [[[')-token', ')']], 'blue'], 'result'); + }); + + it('should get value', () => { + const getPropertyValue = v => { + let res; + switch (v) { + case '--bar': + res = 'green'; + break; + case '--baz': + res = 'yellow'; + break; + default: + } + return res; + }; + const res = func( + [ + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', 'red'], + [')-token', ')'], + [')-token', ')'] + ], + { + customProperty: { + callback: getPropertyValue + } + } + ); + assert.deepEqual(res, [[[')-token', ')']], 'red'], 'result'); + }); + + it('should get value', () => { + const res = func( + [ + ['ident-token', '--FOO'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', 'red'], + [')-token', ')'], + [')-token', ')'] + ], + { + customProperty: { + '--foo': 'blue' + } + } + ); + assert.deepEqual(res, [[[')-token', ')']], 'red'], 'result'); + }); + + it('should get value', () => { + const res = func( + [ + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', 'red'], + [')-token', ')'], + [')-token', ')'] + ], + { + customProperty: { + '--foo': '20px' + } + } + ); + assert.deepEqual(res, [[[')-token', ')']], 'red'], 'result'); + }); + + it('should get value', () => { + const res = func( + [ + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', 'red'], + [')-token', ')'], + [')-token', ')'] + ], + { + customProperty: { + '--bar': 'blue' + } + } + ); + assert.deepEqual(res, [[[')-token', ')']], 'red'], 'result'); + }); + + it('should get value', () => { + const res = func( + [ + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', 'red'], + [')-token', ')'], + [')-token', ')'] + ], + { + customProperty: { + '--foo': 'currentColor' + } + } + ); + assert.deepEqual(res, [[[')-token', ')']], 'currentColor'], 'result'); + }); + + it('should get value', () => { + const res = func( + [ + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', 'red'], + [')-token', ')'], + [')-token', ')'] + ], + { + customProperty: { + '--foo': 'var(--bar)', + '--bar': 'blue' + } + } + ); + assert.deepEqual(res, [[[')-token', ')']], 'blue'], 'result'); + }); + + it('should get value', () => { + const res = func( + [ + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', 'red'], + [')-token', ')'], + [')-token', ')'] + ], + { + customProperty: { + '--foo': 'var(--bar)', + '--bar': 'bar' + } + } + ); + assert.deepEqual(res, [[[')-token', ')']], 'red'], 'result'); + }); + + it('should get value', () => { + const res = func( + [ + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', 'red'], + [')-token', ')'], + [')-token', ')'] + ], + { + customProperty: { + '--foo': 'var(--bar)', + '--bar': 'initial' + } + } + ); + assert.deepEqual(res, [[[')-token', ')']], 'red'], 'result'); + }); + + it('should get value', () => { + const res = func( + [ + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', '10px'], + [')-token', ')'], + [')-token', ')'] + ], + { + customProperty: { + '--foo': 'var(--bar)', + '--bar': '20px' + } + } + ); + assert.deepEqual(res, [[[')-token', ')']], '20px'], 'result'); + }); + + it('should get value', () => { + const res = func( + [ + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', 'red'], + [')-token', ')'], + [')-token', ')'] + ], + { + customProperty: { + '--foo': 'rgb(255 0 calc(255 / 2))' + } + } + ); + assert.deepEqual(res, [[[')-token', ')']], 'rgb(255 0 127.5)'], 'result'); + }); + + it('should get value', () => { + const res = func( + [ + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', 'red'], + [')-token', ')'], + [')-token', ')'] + ], + { + customProperty: { + '--foo': 'color-mix(in oklab, rgb(255 0 0), green)' + } + } + ); + assert.deepEqual( + res, + [[[')-token', ')']], 'color-mix(in oklab, rgb(255 0 0), green)'], + 'result' + ); + }); + + it('should get value', () => { + const res = func( + [ + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', 'red'], + [')-token', ')'], + [')-token', ')'] + ], + { + customProperty: { + '--foo': 'transparent' + } + } + ); + assert.deepEqual(res, [[[')-token', ')']], 'transparent'], 'result'); + }); + + it('should get value', () => { + const res = func( + [ + ['function-token', 'var('], + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', 'red'], + [')-token', ')'], + [')-token', ')'] + ], + { + customProperty: { + '--foo': 'blue' + } + } + ); + assert.deepEqual(res, [[], 'blue'], 'result'); + }); + + it('should get value', () => { + const res = func( + [ + ['function-token', 'var('], + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', 'red'], + [')-token', ')'], + [')-token', ')'] + ], + { + customProperty: { + '--foo': 'calc(1 / 2)' + } + } + ); + assert.deepEqual(res, [[], 'red'], 'result'); + }); + + it('should get value', () => { + const res = func( + [ + ['function-token', 'var('], + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', '1'], + [')-token', ')'], + [')-token', ')'] + ], + { + customProperty: { + '--foo': 'calc(1 / 2)' + } + } + ); + assert.deepEqual(res, [[], '0.5'], 'result'); + }); + + it('should get value', () => { + const res = func( + [ + ['function-token', 'var('], + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', 'red'], + [')-token', ')'], + [')-token', ')'] + ], + { + customProperty: { + '--foo': 'initial' + } + } + ); + assert.deepEqual(res, [[], 'red'], 'result'); + }); + + it('should break loop if token is falsy', () => { + const res = func([undefined] as any); + assert.deepEqual(res, [[], ''], 'result'); + }); +}); + +describe('parse CSS var() tokens', () => { + const func = css.parseVarTokens; + + it('should get null object', () => { + const res = func([ + ['function-token', 'var('], + ['ident-token', '--foo'], + [')-token', ')'] + ]); + assert.deepEqual(res, null, 'result'); + }); + + it('should get value', () => { + const res = func([[]]); + assert.deepEqual(res, [''], 'result'); + }); + + it('should get value', () => { + const res = func([ + ['function-token', 'var('], + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', 'red'], + [')-token', ')'] + ]); + assert.deepEqual(res, ['red'], 'result'); + }); + + it('should get value', () => { + const res = func([ + ['function-token', 'calc('], + ['whitespace-token', ' '], + ['function-token', 'var('], + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', 'red'], + [')-token', ')'], + ['whitespace-token', ' '], + [')-token', ')'] + ]); + assert.deepEqual(res, ['calc(', 'red', ')'], 'result'); + }); + + it('should get value', () => { + const res = func([ + ['whitespace-token', ' '], + ['function-token', 'calc('], + ['whitespace-token', ' '], + ['function-token', 'var('], + ['ident-token', '--foo'], + ['comma-token', ','], + ['whitespace-token', ' '], + ['ident-token', 'red'], + [')-token', ')'], + ['whitespace-token', ' '], + [')-token', ')'] + ]); + assert.deepEqual(res, ['calc(', 'red', ')'], 'result'); + }); + + it('should get value', () => { + const res = func([ + ['ident-token', '--foo'], + [')-token', ')'], + ['whitespace-token', ' '], + [')-token', ')'] + ]); + assert.deepEqual(res, ['--foo', ')', ')'], 'result'); + }); + + it('should get value', () => { + const res = func([[')-token', ')']]); + assert.deepEqual(res, [')'], 'result'); + }); + + it('should break loop if token is falsy', () => { + const res = func([undefined] as any); + assert.deepEqual(res, [], 'result'); + }); +}); + +describe('resolve CSS var()', () => { + const func = css.resolveVar; + + it('should throw', () => { + assert.throws(() => func(), TypeError, 'undefined is not a string.'); + }); + + it('should get value', () => { + const res = func(''); + assert.strictEqual(res, '', 'result'); + }); + + it('should get value', () => { + const res = func('foo'); + assert.strictEqual(res, 'foo', 'result'); + }); + + it('should get value', () => { + const res = func('var(--foo)', { + format: 'specifiedValue' + }); + assert.strictEqual(res, 'var(--foo)', 'result'); + }); + + it('should get null object', () => { + const res = func('var(--foo)'); + assert.deepEqual(res, null, 'result'); + + const res2 = func('var(--foo)'); + assert.deepEqual(res2, null, 'result'); + }); + + it('should get value', () => { + const res = func('var(--foo)', { + customProperty: { + '--foo': 'red' + } + }); + assert.strictEqual(res, 'red', 'result'); + + const res2 = func('var(--foo)', { + customProperty: { + '--foo': 'red' + } + }); + assert.strictEqual(res2, 'red', 'result'); + }); + + it('should get value', () => { + const getPropertyValue = v => { + let res; + switch (v) { + case '--foo': + res = 'blue'; + break; + case '--bar': + res = 'green'; + break; + case '--baz': + res = 'yellow'; + break; + default: + } + return res; + }; + const res = func('var(--foo)', { + customProperty: { + callback: getPropertyValue + } + }); + assert.strictEqual(res, 'blue', 'result'); + + const res2 = func('var(--foo)', { + customProperty: { + callback: getPropertyValue + } + }); + assert.strictEqual(res2, 'blue', 'result'); + }); + + it('should get value', () => { + const colorMap = { + '--foo': 'blue', + '--bar': 'green', + '--baz': 'yellow' + }; + const getPropertyValue = v => { + const res = colorMap[v]; + return res; + }; + const res = func('var(--qux, red)', { + customProperty: { + callback: getPropertyValue + } + }); + assert.strictEqual(res, 'red', 'result'); + + colorMap['--qux'] = 'cyan'; + const res2 = func('var(--qux, red)', { + customProperty: { + callback: getPropertyValue + } + }); + assert.strictEqual(res2, 'cyan', 'result'); + + colorMap['--qux'] = 'teal'; + const res3 = func('var(--qux, red)', { + customProperty: { + callback: getPropertyValue + } + }); + assert.strictEqual(res3, 'teal', 'result'); + }); + + it('should get value', () => { + const res = func('rgb( 0 calc( var( --max-rgb ) * var( --half ) ) 0 )', { + customProperty: { + '--half': '.5', + '--max-rgb': '255' + } + }); + assert.strictEqual(res, 'rgb(0 127.5 0)', 'result'); + + const res2 = func('rgb( 0 calc( var( --max-rgb ) * var( --half ) ) 0 )', { + customProperty: { + '--half': '.5', + '--max-rgb': '255' + } + }); + assert.strictEqual(res2, 'rgb(0 127.5 0)', 'result'); + }); +}); + +describe('CSS var()', () => { + const func = css.cssVar; + + it('should get value', () => { + const res = func(''); + assert.strictEqual(res, '', 'result'); + }); + + it('should get value', () => { + const res = func('var(--foo)', { + format: 'specifiedValue' + }); + assert.strictEqual(res, 'var(--foo)', 'result'); + }); + + it('should get empty string', () => { + const res = func('var(--foo)'); + assert.strictEqual(res, '', 'result'); + }); + + it('should get value', () => { + const res = func('var(--foo)', { + customProperty: { + '--foo': 'red' + } + }); + assert.strictEqual(res, 'red', 'result'); + }); + + it('should get value', () => { + const getPropertyValue = v => { + let res; + switch (v) { + case '--foo': + res = 'blue'; + break; + case '--bar': + res = 'green'; + break; + case '--baz': + res = 'yellow'; + break; + default: + } + return res; + }; + const res = func('var(--foo)', { + customProperty: { + callback: getPropertyValue + } + }); + assert.strictEqual(res, 'blue', 'result'); + }); + + it('should get value', () => { + const colorMap = { + '--foo': 'blue', + '--bar': 'green', + '--baz': 'yellow' + }; + const getPropertyValue = v => { + const res = colorMap[v]; + return res; + }; + const res = func('var(--qux, red)', { + customProperty: { + callback: getPropertyValue + } + }); + assert.strictEqual(res, 'red', 'result'); + + colorMap['--qux'] = 'teal'; + const res2 = func('var(--qux, red)', { + customProperty: { + callback: getPropertyValue + } + }); + assert.strictEqual(res2, 'teal', 'result'); + }); + + it('should get value', () => { + const res = func('rgb( 0 calc( var( --max-rgb ) * var( --half ) ) 0 )', { + customProperty: { + '--half': '.5', + '--max-rgb': '255' + } + }); + assert.strictEqual(res, 'rgb(0 127.5 0)', 'result'); + }); +}); diff --git a/test/css-var.test.ts b/test/css-var.test.ts deleted file mode 100644 index df7d9b5..0000000 --- a/test/css-var.test.ts +++ /dev/null @@ -1,758 +0,0 @@ -/** - * css-var.test - */ - -/* api */ -import { afterEach, assert, beforeEach, describe, it } from 'vitest'; - -/* test */ -import { genCache } from '../src/js/cache'; -import * as custom from '../src/js/css-var'; - -beforeEach(() => { - genCache.clear(); -}); - -afterEach(() => { - genCache.clear(); -}); - -describe('resolve CSS variable', () => { - const func = custom.resolveCustomProperty; - - it('should throw', () => { - assert.throws(() => func(), TypeError, 'undefined is not an array.'); - }); - - it('should throw', () => { - assert.throws(() => func(['foo']), TypeError, 'foo is not an array.'); - }); - - it('should get value', () => { - const res = func([[]]); - assert.deepEqual(res, [[], ''], 'result'); - }); - - it('should get value', () => { - const res = func([ - ['ident-token', '--foo'], - [')-token', ')'] - ]); - assert.deepEqual(res, [[], ''], 'result'); - }); - - it('should get value', () => { - const res = func([ - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', 'red'], - [')-token', ')'] - ]); - assert.deepEqual(res, [[], 'red'], 'result'); - }); - - it('should get value', () => { - const res = func([ - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', 'red'], - [')-token', ')'], - [')-token', ')'] - ]); - assert.deepEqual(res, [[[')-token', ')']], 'red'], 'result'); - }); - - it('should get value', () => { - const res = func( - [ - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', 'red'], - [')-token', ')'], - [')-token', ')'] - ], - { - customProperty: { - '--foo': 'blue' - } - } - ); - assert.deepEqual(res, [[[')-token', ')']], 'blue'], 'result'); - }); - - it('should get value', () => { - const getPropertyValue = v => { - let res; - switch (v) { - case '--foo': - res = 'blue'; - break; - case '--bar': - res = 'green'; - break; - default: - } - return res; - }; - const res = func( - [ - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', 'red'], - [')-token', ')'], - [')-token', ')'] - ], - { - customProperty: { - callback: getPropertyValue - } - } - ); - assert.deepEqual(res, [[[')-token', ')']], 'blue'], 'result'); - }); - - it('should get value', () => { - const getPropertyValue = v => { - let res; - switch (v) { - case '--bar': - res = 'green'; - break; - case '--baz': - res = 'yellow'; - break; - default: - } - return res; - }; - const res = func( - [ - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', 'red'], - [')-token', ')'], - [')-token', ')'] - ], - { - customProperty: { - callback: getPropertyValue - } - } - ); - assert.deepEqual(res, [[[')-token', ')']], 'red'], 'result'); - }); - - it('should get value', () => { - const res = func( - [ - ['ident-token', '--FOO'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', 'red'], - [')-token', ')'], - [')-token', ')'] - ], - { - customProperty: { - '--foo': 'blue' - } - } - ); - assert.deepEqual(res, [[[')-token', ')']], 'red'], 'result'); - }); - - it('should get value', () => { - const res = func( - [ - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', 'red'], - [')-token', ')'], - [')-token', ')'] - ], - { - customProperty: { - '--foo': '20px' - } - } - ); - assert.deepEqual(res, [[[')-token', ')']], 'red'], 'result'); - }); - - it('should get value', () => { - const res = func( - [ - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', 'red'], - [')-token', ')'], - [')-token', ')'] - ], - { - customProperty: { - '--bar': 'blue' - } - } - ); - assert.deepEqual(res, [[[')-token', ')']], 'red'], 'result'); - }); - - it('should get value', () => { - const res = func( - [ - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', 'red'], - [')-token', ')'], - [')-token', ')'] - ], - { - customProperty: { - '--foo': 'currentColor' - } - } - ); - assert.deepEqual(res, [[[')-token', ')']], 'currentColor'], 'result'); - }); - - it('should get value', () => { - const res = func( - [ - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', 'red'], - [')-token', ')'], - [')-token', ')'] - ], - { - customProperty: { - '--foo': 'var(--bar)', - '--bar': 'blue' - } - } - ); - assert.deepEqual(res, [[[')-token', ')']], 'blue'], 'result'); - }); - - it('should get value', () => { - const res = func( - [ - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', 'red'], - [')-token', ')'], - [')-token', ')'] - ], - { - customProperty: { - '--foo': 'var(--bar)', - '--bar': 'bar' - } - } - ); - assert.deepEqual(res, [[[')-token', ')']], 'red'], 'result'); - }); - - it('should get value', () => { - const res = func( - [ - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', 'red'], - [')-token', ')'], - [')-token', ')'] - ], - { - customProperty: { - '--foo': 'var(--bar)', - '--bar': 'initial' - } - } - ); - assert.deepEqual(res, [[[')-token', ')']], 'red'], 'result'); - }); - - it('should get value', () => { - const res = func( - [ - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', '10px'], - [')-token', ')'], - [')-token', ')'] - ], - { - customProperty: { - '--foo': 'var(--bar)', - '--bar': '20px' - } - } - ); - assert.deepEqual(res, [[[')-token', ')']], '20px'], 'result'); - }); - - it('should get value', () => { - const res = func( - [ - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', 'red'], - [')-token', ')'], - [')-token', ')'] - ], - { - customProperty: { - '--foo': 'rgb(255 0 calc(255 / 2))' - } - } - ); - assert.deepEqual(res, [[[')-token', ')']], 'rgb(255 0 127.5)'], 'result'); - }); - - it('should get value', () => { - const res = func( - [ - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', 'red'], - [')-token', ')'], - [')-token', ')'] - ], - { - customProperty: { - '--foo': 'color-mix(in oklab, rgb(255 0 0), green)' - } - } - ); - assert.deepEqual( - res, - [[[')-token', ')']], 'color-mix(in oklab, rgb(255 0 0), green)'], - 'result' - ); - }); - - it('should get value', () => { - const res = func( - [ - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', 'red'], - [')-token', ')'], - [')-token', ')'] - ], - { - customProperty: { - '--foo': 'transparent' - } - } - ); - assert.deepEqual(res, [[[')-token', ')']], 'transparent'], 'result'); - }); - - it('should get value', () => { - const res = func( - [ - ['function-token', 'var('], - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', 'red'], - [')-token', ')'], - [')-token', ')'] - ], - { - customProperty: { - '--foo': 'blue' - } - } - ); - assert.deepEqual(res, [[], 'blue'], 'result'); - }); - - it('should get value', () => { - const res = func( - [ - ['function-token', 'var('], - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', 'red'], - [')-token', ')'], - [')-token', ')'] - ], - { - customProperty: { - '--foo': 'calc(1 / 2)' - } - } - ); - assert.deepEqual(res, [[], 'red'], 'result'); - }); - - it('should get value', () => { - const res = func( - [ - ['function-token', 'var('], - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', '1'], - [')-token', ')'], - [')-token', ')'] - ], - { - customProperty: { - '--foo': 'calc(1 / 2)' - } - } - ); - assert.deepEqual(res, [[], '0.5'], 'result'); - }); - - it('should get value', () => { - const res = func( - [ - ['function-token', 'var('], - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', 'red'], - [')-token', ')'], - [')-token', ')'] - ], - { - customProperty: { - '--foo': 'initial' - } - } - ); - assert.deepEqual(res, [[], 'red'], 'result'); - }); - - it('should break loop if token is falsy', () => { - const res = func([undefined] as any); - assert.deepEqual(res, [[], ''], 'result'); - }); -}); - -describe('parse tokens', () => { - const func = custom.parseTokens; - - it('should get null object', () => { - const res = func([ - ['function-token', 'var('], - ['ident-token', '--foo'], - [')-token', ')'] - ]); - assert.deepEqual(res, null, 'result'); - }); - - it('should get value', () => { - const res = func([[]]); - assert.deepEqual(res, [''], 'result'); - }); - - it('should get value', () => { - const res = func([ - ['function-token', 'var('], - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', 'red'], - [')-token', ')'] - ]); - assert.deepEqual(res, ['red'], 'result'); - }); - - it('should get value', () => { - const res = func([ - ['function-token', 'calc('], - ['whitespace-token', ' '], - ['function-token', 'var('], - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', 'red'], - [')-token', ')'], - ['whitespace-token', ' '], - [')-token', ')'] - ]); - assert.deepEqual(res, ['calc(', 'red', ')'], 'result'); - }); - - it('should get value', () => { - const res = func([ - ['whitespace-token', ' '], - ['function-token', 'calc('], - ['whitespace-token', ' '], - ['function-token', 'var('], - ['ident-token', '--foo'], - ['comma-token', ','], - ['whitespace-token', ' '], - ['ident-token', 'red'], - [')-token', ')'], - ['whitespace-token', ' '], - [')-token', ')'] - ]); - assert.deepEqual(res, ['calc(', 'red', ')'], 'result'); - }); - - it('should get value', () => { - const res = func([ - ['ident-token', '--foo'], - [')-token', ')'], - ['whitespace-token', ' '], - [')-token', ')'] - ]); - assert.deepEqual(res, ['--foo', ')', ')'], 'result'); - }); - - it('should get value', () => { - const res = func([[')-token', ')']]); - assert.deepEqual(res, [')'], 'result'); - }); - - it('should break loop if token is falsy', () => { - const res = func([undefined] as any); - assert.deepEqual(res, [], 'result'); - }); -}); - -describe('resolve CSS var()', () => { - const func = custom.resolveVar; - - it('should throw', () => { - assert.throws(() => func(), TypeError, 'undefined is not a string.'); - }); - - it('should get value', () => { - const res = func(''); - assert.strictEqual(res, '', 'result'); - }); - - it('should get value', () => { - const res = func('foo'); - assert.strictEqual(res, 'foo', 'result'); - }); - - it('should get value', () => { - const res = func('var(--foo)', { - format: 'specifiedValue' - }); - assert.strictEqual(res, 'var(--foo)', 'result'); - }); - - it('should get null object', () => { - const res = func('var(--foo)'); - assert.deepEqual(res, null, 'result'); - - const res2 = func('var(--foo)'); - assert.deepEqual(res2, null, 'result'); - }); - - it('should get value', () => { - const res = func('var(--foo)', { - customProperty: { - '--foo': 'red' - } - }); - assert.strictEqual(res, 'red', 'result'); - - const res2 = func('var(--foo)', { - customProperty: { - '--foo': 'red' - } - }); - assert.strictEqual(res2, 'red', 'result'); - }); - - it('should get value', () => { - const getPropertyValue = v => { - let res; - switch (v) { - case '--foo': - res = 'blue'; - break; - case '--bar': - res = 'green'; - break; - case '--baz': - res = 'yellow'; - break; - default: - } - return res; - }; - const res = func('var(--foo)', { - customProperty: { - callback: getPropertyValue - } - }); - assert.strictEqual(res, 'blue', 'result'); - - const res2 = func('var(--foo)', { - customProperty: { - callback: getPropertyValue - } - }); - assert.strictEqual(res2, 'blue', 'result'); - }); - - it('should get value', () => { - const colorMap = { - '--foo': 'blue', - '--bar': 'green', - '--baz': 'yellow' - }; - const getPropertyValue = v => { - const res = colorMap[v]; - return res; - }; - const res = func('var(--qux, red)', { - customProperty: { - callback: getPropertyValue - } - }); - assert.strictEqual(res, 'red', 'result'); - - colorMap['--qux'] = 'cyan'; - const res2 = func('var(--qux, red)', { - customProperty: { - callback: getPropertyValue - } - }); - assert.strictEqual(res2, 'cyan', 'result'); - - colorMap['--qux'] = 'teal'; - const res3 = func('var(--qux, red)', { - customProperty: { - callback: getPropertyValue - } - }); - assert.strictEqual(res3, 'teal', 'result'); - }); - - it('should get value', () => { - const res = func('rgb( 0 calc( var( --max-rgb ) * var( --half ) ) 0 )', { - customProperty: { - '--half': '.5', - '--max-rgb': '255' - } - }); - assert.strictEqual(res, 'rgb(0 127.5 0)', 'result'); - - const res2 = func('rgb( 0 calc( var( --max-rgb ) * var( --half ) ) 0 )', { - customProperty: { - '--half': '.5', - '--max-rgb': '255' - } - }); - assert.strictEqual(res2, 'rgb(0 127.5 0)', 'result'); - }); -}); - -describe('CSS var()', () => { - const func = custom.cssVar; - - it('should get value', () => { - const res = func(''); - assert.strictEqual(res, '', 'result'); - }); - - it('should get value', () => { - const res = func('var(--foo)', { - format: 'specifiedValue' - }); - assert.strictEqual(res, 'var(--foo)', 'result'); - }); - - it('should get empty string', () => { - const res = func('var(--foo)'); - assert.strictEqual(res, '', 'result'); - }); - - it('should get value', () => { - const res = func('var(--foo)', { - customProperty: { - '--foo': 'red' - } - }); - assert.strictEqual(res, 'red', 'result'); - }); - - it('should get value', () => { - const getPropertyValue = v => { - let res; - switch (v) { - case '--foo': - res = 'blue'; - break; - case '--bar': - res = 'green'; - break; - case '--baz': - res = 'yellow'; - break; - default: - } - return res; - }; - const res = func('var(--foo)', { - customProperty: { - callback: getPropertyValue - } - }); - assert.strictEqual(res, 'blue', 'result'); - }); - - it('should get value', () => { - const colorMap = { - '--foo': 'blue', - '--bar': 'green', - '--baz': 'yellow' - }; - const getPropertyValue = v => { - const res = colorMap[v]; - return res; - }; - const res = func('var(--qux, red)', { - customProperty: { - callback: getPropertyValue - } - }); - assert.strictEqual(res, 'red', 'result'); - - colorMap['--qux'] = 'teal'; - const res2 = func('var(--qux, red)', { - customProperty: { - callback: getPropertyValue - } - }); - assert.strictEqual(res2, 'teal', 'result'); - }); - - it('should get value', () => { - const res = func('rgb( 0 calc( var( --max-rgb ) * var( --half ) ) 0 )', { - customProperty: { - '--half': '.5', - '--max-rgb': '255' - } - }); - assert.strictEqual(res, 'rgb(0 127.5 0)', 'result'); - }); -});