Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@cobalt-ui/core

Package Overview
Dependencies
Maintainers
1
Versions
49
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@cobalt-ui/core - npm Package Compare versions

Comparing version 1.8.1 to 1.9.0

12

CHANGELOG.md
# @cobalt-ui/core
## 1.9.0
### Minor Changes
- [#228](https://github.com/drwpow/cobalt-ui/pull/228) [`1e12d04fb24eebc1df152017935dd65a2c6d7618`](https://github.com/drwpow/cobalt-ui/commit/1e12d04fb24eebc1df152017935dd65a2c6d7618) Thanks [@drwpow](https://github.com/drwpow)! - Add gamut clipping for color tokens
### Patch Changes
- [#228](https://github.com/drwpow/cobalt-ui/pull/228) [`1e12d04fb24eebc1df152017935dd65a2c6d7618`](https://github.com/drwpow/cobalt-ui/commit/1e12d04fb24eebc1df152017935dd65a2c6d7618) Thanks [@drwpow](https://github.com/drwpow)! - Improve Tokens Studio inline aliasing
- [#228](https://github.com/drwpow/cobalt-ui/pull/228) [`1e12d04fb24eebc1df152017935dd65a2c6d7618`](https://github.com/drwpow/cobalt-ui/commit/1e12d04fb24eebc1df152017935dd65a2c6d7618) Thanks [@drwpow](https://github.com/drwpow)! - Make parse options optional for easier use
## 1.8.1

@@ -4,0 +16,0 @@

6

dist/parse/index.d.ts

@@ -20,7 +20,7 @@ import type { ParsedToken } from '../token.js';

/** Configure transformations for color tokens */
color: ParseColorOptions;
color?: ParseColorOptions;
figma?: FigmaParseOptions;
/** Configure plugin lint rules (if any) */
lint: {
rules: Record<string, LintRule>;
lint?: {
rules?: Record<string, LintRule>;
};

@@ -27,0 +27,0 @@ }

@@ -6,3 +6,4 @@ /**

*/
import { parseAlias } from '@cobalt-ui/utils';
import { isAlias, parseAlias } from '@cobalt-ui/utils';
import { parse as culoriParse, rgb } from 'culori';
// I’m not sure this is comprehensive at all but better than nothing

@@ -216,5 +217,56 @@ const FONT_WEIGHTS = {

else {
let value = v.value;
// resolve inline aliases (e.g. `rgba({color.black}, 0.5)`)
if (value.includes('{') && !v.value.startsWith('{')) {
value = resolveAlias(value, path);
if (!value) {
errors.push(`Could not resolve "${v.value}"`);
continue;
}
// note: we did some work earlier to help resolve the aliases, but
// we need to REPLACE them in this scenario so we must do a 2nd pass
const matches = value.match(ALIAS_RE);
for (const match of matches ?? []) {
let currentAlias = parseAlias(match).id;
let resolvedValue;
const aliasHistory = new Set([currentAlias]);
while (!resolvedValue) {
const aliasNode = get(rawTokens, currentAlias.split('.'));
// does this resolve to a $value?
if (aliasNode && aliasNode.value) {
// is this another alias?
if (isAlias(aliasNode.value)) {
currentAlias = parseAlias(aliasNode.value).id;
if (aliasHistory.has(currentAlias)) {
errors.push(`Couldn’t resolve circular alias "${v.value}"`);
break;
}
aliasHistory.add(currentAlias);
continue;
}
resolvedValue = aliasNode.value;
}
break;
}
if (resolvedValue) {
value = value.replace(match, resolvedValue);
}
}
if (!culoriParse(value)) {
// fix `rgba(#000000, 0.3)` scenario specifically (common Tokens Studio version)
// but throw err otherwise
if (value.startsWith('rgb') && value.includes('#')) {
const hexValue = value.match(/#[abcdef0-9]+/i);
if (hexValue && hexValue[0]) {
const rgbVal = rgb(hexValue[0]);
if (rgbVal) {
value = value.replace(hexValue[0], `${rgbVal.r * 100}%, ${rgbVal.g * 100}%, ${rgbVal.b * 100}%`);
}
}
}
}
}
addToken({
$type: 'color',
$value: v.value,
$value: value,
$description: v.description,

@@ -221,0 +273,0 @@ }, tokenPath);

import type { ParsedBorderToken } from '../../token.js';
import type { ParseColorOptions } from './color.js';
export interface ParseBorderOptions {
color: ParseColorOptions;
color?: ParseColorOptions;
}

@@ -6,0 +6,0 @@ /**

import type { ParsedColorToken } from '../../token.js';
export interface ParseColorOptions {
/** Convert color to sRGB hex? (default: true) */
/** Convert color to 8-bit sRGB hexadecimal? (default: false) */
convertToHex?: boolean;
/** Confine colors to gamut? sRGB is smaller but widely-supported; P3 supports more colors but not all users (default: `undefined`) */
gamut?: 'srgb' | 'p3' | undefined;
}

@@ -15,2 +17,2 @@ /**

*/
export declare function normalizeColorValue(value: unknown, options: ParseColorOptions): ParsedColorToken['$value'];
export declare function normalizeColorValue(rawValue: unknown, options?: ParseColorOptions): ParsedColorToken['$value'];

@@ -1,2 +0,2 @@

import { formatHex, formatHex8, parse } from 'culori';
import { clampChroma, formatHex, formatHex8, parse, formatCss } from 'culori';
/**

@@ -11,18 +11,30 @@ * 8.1 Color

*/
export function normalizeColorValue(value, options) {
if (!value) {
export function normalizeColorValue(rawValue, options) {
if (!rawValue) {
throw new Error('missing value');
}
if (typeof value === 'string') {
if (options.convertToHex === true) {
const parsed = parse(value);
if (!parsed) {
throw new Error(`invalid color "${value}"`);
}
return typeof parsed.alpha === 'number' && parsed.alpha < 1 ? formatHex8(parsed) : formatHex(parsed);
if (typeof rawValue === 'string') {
const parsed = parse(rawValue);
if (!parsed) {
throw new Error(`invalid color "${rawValue}"`);
}
return value;
let value = parsed;
let valueEdited = false; // keep track of this to reduce rounding errors
// clamp to sRGB if we’re converting to hex, too!
if (options?.gamut === 'srgb' || options?.convertToHex === true) {
value = clampChroma(parsed, parsed.mode, 'rgb');
valueEdited = true;
}
else if (options?.gamut === 'p3') {
value = clampChroma(parsed, parsed.mode, 'p3');
valueEdited = true;
}
// TODO: in 2.x, only convert to hex if no color loss (e.g. don’t downgrade a 12-bit color `rgb()` to 8-bit hex)
if (options?.convertToHex === true) {
return typeof value.alpha === 'number' && value.alpha < 1 ? formatHex8(value) : formatHex(value);
}
return valueEdited ? formatCss(value) : rawValue; // return the original value if we didn’t modify it; we may introduce nondeterministic rounding errors (the classic JS 0.3333… nonsense, etc.)
}
throw new Error(`expected string, received ${typeof value}`);
throw new Error(`expected string, received ${typeof rawValue}`);
}
//# sourceMappingURL=color.js.map
import type { ParsedGradientToken } from '../../token.js';
import type { ParseColorOptions } from './color.js';
export interface ParseGradientOptions {
color: ParseColorOptions;
color?: ParseColorOptions;
}

@@ -6,0 +6,0 @@ /**

import type { ParsedShadowToken } from '../../token.js';
import type { ParseColorOptions } from './color.js';
export interface ParseShadowOptions {
color: ParseColorOptions;
color?: ParseColorOptions;
}

@@ -6,0 +6,0 @@ /**

{
"name": "@cobalt-ui/core",
"description": "Parser/validator for the Design Tokens Community Group (DTCG) standard.",
"version": "1.8.1",
"version": "1.9.0",
"author": {

@@ -6,0 +6,0 @@ "name": "Drew Powers",

@@ -42,7 +42,7 @@ import { cloneDeep, FG_YELLOW, getAliasID, invalidTokenIDError, isAlias, RESET } from '@cobalt-ui/utils';

/** Configure transformations for color tokens */
color: ParseColorOptions;
color?: ParseColorOptions;
figma?: FigmaParseOptions;
/** Configure plugin lint rules (if any) */
lint: {
rules: Record<string, LintRule>;
lint?: {
rules?: Record<string, LintRule>;
};

@@ -49,0 +49,0 @@ }

@@ -6,3 +6,4 @@ /**

*/
import { parseAlias } from '@cobalt-ui/utils';
import { isAlias, parseAlias } from '@cobalt-ui/utils';
import { parse as culoriParse, rgb } from 'culori';
import type { GradientStop, Group, Token } from '../token.js';

@@ -288,5 +289,8 @@

let order = [values[0], values[1], values[0], values[1]] as [string, string, string, string]; // TL, BR
if (values.length === 3)
{order = [values[0], values[1], values[2], values[1]] as [string, string, string, string];} // TL, TR/BL, BR
else if (values.length === 4) {order = [values[0], values[1], values[2], values[3]] as [string, string, string, string];} // TL, TR, BR, BL
if (values.length === 3) {
order = [values[0], values[1], values[2], values[1]] as [string, string, string, string];
} // TL, TR/BL, BR
else if (values.length === 4) {
order = [values[0], values[1], values[2], values[3]] as [string, string, string, string];
} // TL, TR, BR, BL
addToken({ $type: 'dimension', $value: order[0], $description: v.description }, [...path, `${k}TopLeft`]);

@@ -370,6 +374,61 @@ addToken({ $type: 'dimension', $value: order[1], $description: v.description }, [...path, `${k}TopRight`]);

} else {
let value: string | undefined = v.value;
// resolve inline aliases (e.g. `rgba({color.black}, 0.5)`)
if (value.includes('{') && !v.value.startsWith('{')) {
value = resolveAlias(value, path);
if (!value) {
errors.push(`Could not resolve "${v.value}"`);
continue;
}
// note: we did some work earlier to help resolve the aliases, but
// we need to REPLACE them in this scenario so we must do a 2nd pass
const matches = value.match(ALIAS_RE);
for (const match of matches ?? []) {
let currentAlias = parseAlias(match).id;
let resolvedValue: string | undefined;
const aliasHistory = new Set<string>([currentAlias]);
while (!resolvedValue) {
const aliasNode: any = get(rawTokens, currentAlias.split('.'));
// does this resolve to a $value?
if (aliasNode && aliasNode.value) {
// is this another alias?
if (isAlias(aliasNode.value)) {
currentAlias = parseAlias(aliasNode.value).id;
if (aliasHistory.has(currentAlias)) {
errors.push(`Couldn’t resolve circular alias "${v.value}"`);
break;
}
aliasHistory.add(currentAlias);
continue;
}
resolvedValue = aliasNode.value;
}
break;
}
if (resolvedValue) {
value = value.replace(match, resolvedValue);
}
}
if (!culoriParse(value)) {
// fix `rgba(#000000, 0.3)` scenario specifically (common Tokens Studio version)
// but throw err otherwise
if (value.startsWith('rgb') && value.includes('#')) {
const hexValue = value.match(/#[abcdef0-9]+/i);
if (hexValue && hexValue[0]) {
const rgbVal = rgb(hexValue[0]);
if (rgbVal) {
value = value.replace(hexValue[0], `${rgbVal.r * 100}%, ${rgbVal.g * 100}%, ${rgbVal.b * 100}%`);
}
}
}
}
}
addToken(
{
$type: 'color',
$value: v.value,
$value: value,
$description: v.description,

@@ -447,5 +506,8 @@ },

let order: [string, string, string, string] = [values[0], values[1], values[0], values[1]] as [string, string, string, string]; // TB, RL
if (values.length === 3)
{order = [values[0], values[1], values[2], values[1]] as [string, string, string, string];} // T, RL, B
else if (values.length === 4) {order = [values[0], values[1], values[2], values[3]] as [string, string, string, string];} // T, R, B, L
if (values.length === 3) {
order = [values[0], values[1], values[2], values[1]] as [string, string, string, string];
} // T, RL, B
else if (values.length === 4) {
order = [values[0], values[1], values[2], values[3]] as [string, string, string, string];
} // T, R, B, L
addToken({ $type: 'dimension', $value: order[0], $description: v.description }, [...path, `${k}Top`]);

@@ -452,0 +514,0 @@ addToken({ $type: 'dimension', $value: order[1], $description: v.description }, [...path, `${k}Right`]);

@@ -9,3 +9,3 @@ import type { BorderToken, ParsedBorderToken } from '../../token.js';

export interface ParseBorderOptions {
color: ParseColorOptions;
color?: ParseColorOptions;
}

@@ -12,0 +12,0 @@

@@ -1,7 +0,9 @@

import { formatHex, formatHex8, parse } from 'culori';
import { type Color, clampChroma, formatHex, formatHex8, parse, formatCss } from 'culori';
import type { ParsedColorToken } from '../../token.js';
export interface ParseColorOptions {
/** Convert color to sRGB hex? (default: true) */
/** Convert color to 8-bit sRGB hexadecimal? (default: false) */
convertToHex?: boolean;
/** Confine colors to gamut? sRGB is smaller but widely-supported; P3 supports more colors but not all users (default: `undefined`) */
gamut?: 'srgb' | 'p3' | undefined;
}

@@ -18,17 +20,32 @@

*/
export function normalizeColorValue(value: unknown, options: ParseColorOptions): ParsedColorToken['$value'] {
if (!value) {
export function normalizeColorValue(rawValue: unknown, options?: ParseColorOptions): ParsedColorToken['$value'] {
if (!rawValue) {
throw new Error('missing value');
}
if (typeof value === 'string') {
if (options.convertToHex === true) {
const parsed = parse(value);
if (!parsed) {
throw new Error(`invalid color "${value}"`);
}
return typeof parsed.alpha === 'number' && parsed.alpha < 1 ? formatHex8(parsed) : formatHex(parsed);
if (typeof rawValue === 'string') {
const parsed = parse(rawValue);
if (!parsed) {
throw new Error(`invalid color "${rawValue}"`);
}
return value;
let value = parsed as Color;
let valueEdited = false; // keep track of this to reduce rounding errors
// clamp to sRGB if we’re converting to hex, too!
if (options?.gamut === 'srgb' || options?.convertToHex === true) {
value = clampChroma(parsed, parsed.mode, 'rgb');
valueEdited = true;
} else if (options?.gamut === 'p3') {
value = clampChroma(parsed, parsed.mode, 'p3');
valueEdited = true;
}
// TODO: in 2.x, only convert to hex if no color loss (e.g. don’t downgrade a 12-bit color `rgb()` to 8-bit hex)
if (options?.convertToHex === true) {
return typeof value.alpha === 'number' && value.alpha < 1 ? formatHex8(value) : formatHex(value);
}
return valueEdited ? formatCss(value) : rawValue; // return the original value if we didn’t modify it; we may introduce nondeterministic rounding errors (the classic JS 0.3333… nonsense, etc.)
}
throw new Error(`expected string, received ${typeof value}`);
throw new Error(`expected string, received ${typeof rawValue}`);
}

@@ -6,3 +6,3 @@ import type { GradientStop, ParsedGradientToken } from '../../token.js';

export interface ParseGradientOptions {
color: ParseColorOptions;
color?: ParseColorOptions;
}

@@ -9,0 +9,0 @@

@@ -7,3 +7,3 @@ import type { ParsedShadowToken } from '../../token.js';

export interface ParseShadowOptions {
color: ParseColorOptions;
color?: ParseColorOptions;
}

@@ -10,0 +10,0 @@

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc