Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
import { cssCalc } from './js/css-calc';
import { isGradient, resolveGradient } from './js/css-gradient';
import { cssVar } from './js/css-var';
import { isColor } from './js/resolve';
import {
extractDashedIdent,
isColor,
resolveLengthInPixels,
splitValue
} from './js/util';
Expand Down
4 changes: 2 additions & 2 deletions src/js/css-gradient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
*/

import { createCacheKey, getCache, setCache } from './cache';
import { resolveColor } from './resolve';
import { isString } from './common';
import { isColor, resolveColor } from './resolve';
import { MatchedRegExp, Options } from './typedef';
import { isColor, splitValue } from './util';
import { splitValue } from './util';

/* constants */
import {
Expand Down
2 changes: 1 addition & 1 deletion src/js/css-var.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ 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 './util';
import { isColor } from './resolve';
import { Options } from './typedef';

/* constants */
Expand Down
39 changes: 39 additions & 0 deletions src/js/resolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import { createCacheKey, getCache, setCache } from './cache';
import {
NAMED_COLORS,
convertRgbToHex,
resolveColorFunc,
resolveColorMix,
Expand All @@ -24,21 +25,27 @@ import {
import {
FN_COLOR,
FN_MIX,
SYN_COLOR_TYPE,
SYN_FN_CALC,
SYN_FN_LIGHT_DARK,
SYN_FN_REL,
SYN_FN_VAR,
SYN_MIX,
VAL_COMP,
VAL_SPEC
} from './constant';
const NAMESPACE = 'resolve';
const RGB_TRANSPARENT = 'rgba(0, 0, 0, 0)';

/* regexp */
const REG_COLOR = new RegExp(`^(?:${SYN_COLOR_TYPE})$`);
const REG_FN_CALC = new RegExp(SYN_FN_CALC);
const REG_FN_COLOR =
/^(?:(?:ok)?l(?:ab|ch)|color(?:-mix)?|hsla?|hwb|rgba?|var)\(/;
const REG_FN_LIGHT_DARK = new RegExp(SYN_FN_LIGHT_DARK);
const REG_FN_REL = new RegExp(SYN_FN_REL);
const REG_FN_VAR = new RegExp(SYN_FN_VAR);
const REG_MIX = new RegExp(SYN_MIX);

/**
* resolve color
Expand Down Expand Up @@ -311,3 +318,35 @@ export const resolve = (value: string, opt: Options = {}): string | null => {
opt.nullable = false;
return resolveColor(value, opt);
};

/**
* is color
* @param value - CSS value
* @param [opt] - options
* @returns result
*/
export const isColor = (value: unknown, opt: Options = {}): boolean => {
if (!isString(value)) {
return false;
}
const str = value.toLowerCase().trim();
if (!str) {
return false;
}
if (/^[a-z]+$/.test(str)) {
return (
str === 'currentcolor' ||
str === 'transparent' ||
Object.hasOwn(NAMED_COLORS, str)
);
}
if (REG_COLOR.test(str) || REG_MIX.test(str)) {
return true;
}
if (REG_FN_COLOR.test(str)) {
const colorOpt = { ...opt, nullable: true };
if (!colorOpt.format) colorOpt.format = VAL_SPEC;
return !!resolveColor(str, colorOpt);
}
return false;
};
39 changes: 0 additions & 39 deletions src/js/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@
import { TokenType, tokenize } from '@csstools/css-tokenizer';
import { CacheItem, createCacheKey, getCache, setCache } from './cache';
import { isString } from './common';
import { resolveColor } from './resolve';
import { Options } from './typedef';

/* constants */
import { NAMED_COLORS } from './color';
import { SYN_COLOR_TYPE, SYN_MIX, VAL_SPEC } from './constant';
const {
CloseParen: PAREN_CLOSE,
Comma: COMMA,
Expand All @@ -30,10 +27,6 @@ const DEG = 360;
const DEG_HALF = 180;

/* regexp */
const REG_COLOR = new RegExp(`^(?:${SYN_COLOR_TYPE})$`);
const REG_FN_COLOR =
/^(?:(?:ok)?l(?:ab|ch)|color(?:-mix)?|hsla?|hwb|rgba?|var)\(/;
const REG_MIX = new RegExp(SYN_MIX);
const REG_DASHED_IDENT = /--[\w-]+/g;
const REG_COMMA = /^,$/;
const REG_SLASH = /^\/$/;
Expand Down Expand Up @@ -166,38 +159,6 @@ export const extractDashedIdent = (value: string): string[] => {
return res;
};

/**
* is color
* @param value - CSS value
* @param [opt] - options
* @returns result
*/
export const isColor = (value: unknown, opt: Options = {}): boolean => {
if (!isString(value)) {
return false;
}
const str = value.toLowerCase().trim();
if (!str) {
return false;
}
if (/^[a-z]+$/.test(str)) {
return (
str === 'currentcolor' ||
str === 'transparent' ||
Object.hasOwn(NAMED_COLORS, str)
);
}
if (REG_COLOR.test(str) || REG_MIX.test(str)) {
return true;
}
if (REG_FN_COLOR.test(str)) {
const colorOpt = { ...opt, nullable: true };
if (!colorOpt.format) colorOpt.format = VAL_SPEC;
return !!resolveColor(str, colorOpt);
}
return false;
};

/**
* round to specified precision
* @param value - numeric value
Expand Down
101 changes: 101 additions & 0 deletions test/resolve.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1840,3 +1840,104 @@ describe('resolve', () => {
assert.strictEqual(res2, '', 'result');
});
});

describe('is color', () => {
const func = api.isColor;

it('should get false', () => {
const res = func();
assert.strictEqual(res, false, 'result');
});

it('should get false', () => {
const res = func('');
assert.strictEqual(res, false, 'result');
});

it('should get false', () => {
const res = func('foo');
assert.strictEqual(res, false, 'result');
});

it('should get true', () => {
const res = func('red');
assert.strictEqual(res, true, 'result');
});

it('should get true', () => {
const res = func('currentcolor');
assert.strictEqual(res, true, 'result');
});

it('should get true', () => {
const res = func('transparent');
assert.strictEqual(res, true, 'result');
});

it('should get true', () => {
const res = func('color(srgb 0 127.5 0)');
assert.strictEqual(res, true, 'result');
});

it('should get true', () => {
const res = func('color-mix(in oklab, red, blue)');
assert.strictEqual(res, true, 'result');
});

it('should get true', () => {
const res = func('rgb(from rebeccapurple r g b)');
assert.strictEqual(res, true, 'result');
});

it('should get false', () => {
const res = func('rgb(from rebeccapurple l a b)');
assert.strictEqual(res, false, 'result');
});

it('should get true', () => {
const res = func('var(--foo)');
assert.strictEqual(res, true, 'result');
});

it('should get true', () => {
const res = func(
'color-mix(in oklab, green, color-mix(in srgb, red, transparent))',
{
format: 'computedColor'
}
);
assert.strictEqual(res, true, 'result');
});

it('should get false', () => {
const res = func(
// Invalid color space
'color-mix(in oklab, green, color-mix(in rgb, red, transparent))',
{
format: 'computedColor'
}
);
assert.strictEqual(res, false, 'result');
});

it('should get true', () => {
const res = func(
// Missing close paren should be okay
'color-mix(in oklab, green, color-mix(in srgb, red, transparent)',
{
format: 'computedColor'
}
);
assert.strictEqual(res, true, 'result');
});

it('should get false', () => {
const res = func('url(var(--foo))');
assert.strictEqual(res, false, 'result');
});

it('should get false', () => {
const res = func('radial-gradient(transparent, var(--custom-color))');
assert.strictEqual(res, false, 'result');
});
});
101 changes: 0 additions & 101 deletions test/util.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,107 +221,6 @@ describe('extract dashed-ident tokens', () => {
});
});

describe('is color', () => {
const func = util.isColor;

it('should get false', () => {
const res = func();
assert.strictEqual(res, false, 'result');
});

it('should get false', () => {
const res = func('');
assert.strictEqual(res, false, 'result');
});

it('should get false', () => {
const res = func('foo');
assert.strictEqual(res, false, 'result');
});

it('should get true', () => {
const res = func('red');
assert.strictEqual(res, true, 'result');
});

it('should get true', () => {
const res = func('currentcolor');
assert.strictEqual(res, true, 'result');
});

it('should get true', () => {
const res = func('transparent');
assert.strictEqual(res, true, 'result');
});

it('should get true', () => {
const res = func('color(srgb 0 127.5 0)');
assert.strictEqual(res, true, 'result');
});

it('should get true', () => {
const res = func('color-mix(in oklab, red, blue)');
assert.strictEqual(res, true, 'result');
});

it('should get true', () => {
const res = func('rgb(from rebeccapurple r g b)');
assert.strictEqual(res, true, 'result');
});

it('should get false', () => {
const res = func('rgb(from rebeccapurple l a b)');
assert.strictEqual(res, false, 'result');
});

it('should get true', () => {
const res = func('var(--foo)');
assert.strictEqual(res, true, 'result');
});

it('should get true', () => {
const res = func(
'color-mix(in oklab, green, color-mix(in srgb, red, transparent))',
{
format: 'computedColor'
}
);
assert.strictEqual(res, true, 'result');
});

it('should get false', () => {
const res = func(
// Invalid color space
'color-mix(in oklab, green, color-mix(in rgb, red, transparent))',
{
format: 'computedColor'
}
);
assert.strictEqual(res, false, 'result');
});

it('should get true', () => {
const res = func(
// Missing close paren should be okay
'color-mix(in oklab, green, color-mix(in srgb, red, transparent)',
{
format: 'computedColor'
}
);
assert.strictEqual(res, true, 'result');
});

it('should get false', () => {
const res = func('url(var(--foo))');
assert.strictEqual(res, false, 'result');
});

it('should get false', () => {
const res = func('radial-gradient(transparent, var(--custom-color))');
assert.strictEqual(res, false, 'result');
});
});

describe('round to specified precision', () => {
const func = util.roundToPrecision;

Expand Down
Loading