@cobalt-ui/plugin-css
Advanced tools
Comparing version 1.7.3 to 1.7.4
# @cobalt-ui/plugin-css | ||
## 1.7.4 | ||
### Patch Changes | ||
- [#253](https://github.com/drwpow/cobalt-ui/pull/253) [`2dec1cb`](https://github.com/drwpow/cobalt-ui/commit/2dec1cb427715d6b7eb3c719f3eba8e3bb263517) Thanks [@drwpow](https://github.com/drwpow)! - Bump deps | ||
- Updated dependencies [[`2dec1cb`](https://github.com/drwpow/cobalt-ui/commit/2dec1cb427715d6b7eb3c719f3eba8e3bb263517)]: | ||
- @cobalt-ui/cli@1.11.2 | ||
## 1.7.3 | ||
@@ -4,0 +13,0 @@ |
import type { ParsedToken, Plugin } from '@cobalt-ui/core'; | ||
import defaultTransformer, { type ColorFormat } from './transform/index.js'; | ||
import { CustomNameGenerator, makeNameGenerator } from './utils/token.js'; | ||
import type { CustomNameGenerator } from './utils/token.js'; | ||
import { makeNameGenerator } from './utils/token.js'; | ||
import { type UtilityCSSGroup } from './utils/utility-css.js'; | ||
@@ -5,0 +6,0 @@ export { makeNameGenerator as _INTERNAL_makeNameGenerator, defaultTransformer }; |
@@ -39,9 +39,10 @@ /** | ||
let config; | ||
let filename = options?.filename || './tokens.css'; | ||
const filename = options?.filename || './tokens.css'; | ||
const prefix = options?.prefix || ''; | ||
const generateName = makeNameGenerator(options?.generateName, prefix); | ||
function makeVars({ tokens, indentLv = 0, root = false }) { | ||
function makeVars({ tokens, indentLv = 0, root = false, }) { | ||
const output = []; | ||
if (root) | ||
if (root) { | ||
output.push(indent(':root {', indentLv)); | ||
} | ||
const sortedTokens = Object.entries(tokens).sort((a, b) => a[0].localeCompare(b[0], 'en-us', { numeric: true })); | ||
@@ -51,4 +52,5 @@ for (const [variableName, value] of sortedTokens) { | ||
} | ||
if (root) | ||
if (root) { | ||
output.push(indent('}', indentLv)); | ||
} | ||
return output; | ||
@@ -65,9 +67,11 @@ } | ||
const matches = line.match(HEX_RE); | ||
if (!matches || !matches.length) | ||
if (!matches || !matches.length) { | ||
continue; | ||
} | ||
let newVal = line; | ||
for (const c of matches) { | ||
const rgb = toRGB(c); | ||
if (!rgb) | ||
if (!rgb) { | ||
throw new Error(`invalid color "${c}"`); | ||
} | ||
newVal = newVal.replace(c, formatCss({ ...rgb, mode: 'p3' })); | ||
@@ -104,4 +108,5 @@ hasValidColors = true; // keep track of whether or not actual colors have been generated (we also generate non-color output, so checking for output.length won’t work) | ||
case 'link': { | ||
if (options?.embedFiles) | ||
if (options?.embedFiles) { | ||
value = encode(value, config.outDir); | ||
} | ||
tokenVals[ref] = value; | ||
@@ -111,4 +116,4 @@ break; | ||
case 'typography': { | ||
for (const [k, v] of Object.entries(value)) { | ||
tokenVals[generateName(`${token.id}-${k}`, token)] = v; | ||
for (const k in value) { | ||
tokenVals[generateName(`${token.id}-${k}`, token)] = value[k]; | ||
} | ||
@@ -122,3 +127,3 @@ break; | ||
} | ||
if (token.$extensions && token.$extensions.mode && options?.modeSelectors) { | ||
if (token.$extensions?.mode && options?.modeSelectors) { | ||
const modeSelectors = []; | ||
@@ -135,3 +140,4 @@ if (Array.isArray(options.modeSelectors)) { | ||
} | ||
if (modeSelector.tokens && (!Array.isArray(modeSelector.tokens) || modeSelector.tokens.some((s) => typeof s !== 'string'))) { | ||
if (modeSelector.tokens && | ||
(!Array.isArray(modeSelector.tokens) || modeSelector.tokens.some((s) => typeof s !== 'string'))) { | ||
throw new Error(`modeSelectors[${i}] tokens must be an array of strings`); | ||
@@ -147,24 +153,39 @@ } | ||
else if (typeof options.modeSelectors === 'object') { | ||
for (const [modeID, selector] of Object.entries(options.modeSelectors)) { | ||
for (const modeID in options.modeSelectors) { | ||
const selector = options.modeSelectors[modeID]; | ||
const [groupRoot, modeName] = parseLegacyModeSelector(modeID); | ||
modeSelectors.push({ mode: modeName, tokens: groupRoot ? [`${groupRoot}*`] : undefined, selectors: Array.isArray(selector) ? selector : [selector] }); | ||
modeSelectors.push({ | ||
mode: modeName, | ||
tokens: groupRoot ? [`${groupRoot}*`] : undefined, | ||
selectors: Array.isArray(selector) ? selector : [selector], | ||
}); | ||
} | ||
} | ||
for (const modeSelector of modeSelectors) { | ||
if (!token.$extensions.mode[modeSelector.mode] || (modeSelector.tokens && !isTokenMatch(token.id, modeSelector.tokens))) { | ||
if (!token.$extensions.mode[modeSelector.mode] || | ||
(modeSelector.tokens && !isTokenMatch(token.id, modeSelector.tokens))) { | ||
continue; | ||
} | ||
for (const selector of modeSelector.selectors) { | ||
if (!selectors.includes(selector)) | ||
if (!selectors.includes(selector)) { | ||
selectors.push(selector); | ||
if (!modeVals[selector]) | ||
} | ||
if (!modeVals[selector]) { | ||
modeVals[selector] = {}; | ||
} | ||
let modeVal = await options?.transform?.(token, modeSelector.mode); | ||
if (modeVal === undefined || modeVal === null) { | ||
modeVal = defaultTransformer(token, { colorFormat, generateName, mode: modeSelector.mode, prefix, tokens }); | ||
modeVal = defaultTransformer(token, { | ||
colorFormat, | ||
generateName, | ||
mode: modeSelector.mode, | ||
prefix, | ||
tokens, | ||
}); | ||
} | ||
switch (token.$type) { | ||
case 'link': { | ||
if (options?.embedFiles) | ||
if (options?.embedFiles) { | ||
modeVal = encode(modeVal, config.outDir); | ||
} | ||
modeVals[selector][generateName(token.id, token)] = modeVal; | ||
@@ -174,4 +195,4 @@ break; | ||
case 'typography': { | ||
for (const [k, v] of Object.entries(modeVal)) { | ||
modeVals[selector][generateName(`${token.id}-${k}`, token)] = v; | ||
for (const k in modeVal) { | ||
modeVals[selector][generateName(`${token.id}-${k}`, token)] = modeVal[k]; | ||
} | ||
@@ -190,3 +211,3 @@ break; | ||
// :root vars | ||
let code = []; | ||
const code = []; | ||
code.push('/**'); | ||
@@ -221,3 +242,3 @@ code.push(` * ${metadata.name || 'Design Tokens'}`); | ||
code.push(''); | ||
code.push(indent(`@supports (color: color(display-p3 1 1 1)) {`, 0)); // note: @media (color-gamut: p3) is problematic in most browsers | ||
code.push(indent('@supports (color: color(display-p3 1 1 1)) {', 0)); // note: @media (color-gamut: p3) is problematic in most browsers | ||
code.push(...makeP3(makeVars({ tokens: tokenVals, indentLv: 1, root: true }))); | ||
@@ -252,9 +273,11 @@ for (const selector of selectors) { | ||
function parseLegacyModeSelector(modeID) { | ||
if (!modeID.includes('#')) | ||
if (!modeID.includes('#')) { | ||
throw new Error(`modeSelector key must have "#" character`); | ||
} | ||
const parts = modeID.split('#').map((s) => s.trim()); | ||
if (parts.length > 2) | ||
if (parts.length > 2) { | ||
throw new Error(`modeSelector key must have only 1 "#" character`); | ||
} | ||
return [parts[0], parts[1]]; | ||
} | ||
//# sourceMappingURL=index.js.map |
@@ -1,5 +0,5 @@ | ||
import { ParsedBorderToken, ParsedToken } from '@cobalt-ui/core'; | ||
import { makeNameGenerator } from '../utils/token.js'; | ||
import type { ParsedBorderToken, ParsedToken } from '@cobalt-ui/core'; | ||
import type { makeNameGenerator } from '../utils/token.js'; | ||
import { type ColorFormat } from './color.js'; | ||
export default function transformBorder(token: ParsedBorderToken, { colorFormat, mode, prefix, tokens, generateName }: { | ||
export default function transformBorder(token: ParsedBorderToken, { colorFormat, mode, prefix, tokens, generateName, }: { | ||
colorFormat: ColorFormat; | ||
@@ -6,0 +6,0 @@ mode?: string; |
@@ -6,3 +6,3 @@ import { isAlias } from '@cobalt-ui/utils'; | ||
import transformStrokeStyle from './stroke-style.js'; | ||
export default function transformBorder(token, { colorFormat, mode, prefix, tokens, generateName }) { | ||
export default function transformBorder(token, { colorFormat, mode, prefix, tokens, generateName, }) { | ||
const { value, originalVal } = getMode(token, mode); | ||
@@ -12,7 +12,13 @@ if (typeof originalVal === 'string') { | ||
} | ||
const width = isAlias(originalVal.width) ? varRef(originalVal.width, { property: 'width', prefix, tokens, generateName }) : transformDimension(value.width); | ||
const color = isAlias(originalVal.color) ? varRef(originalVal.color, { property: 'color', prefix, tokens, generateName }) : transformColor(originalVal.color, colorFormat); | ||
const style = isAlias(originalVal.style) ? varRef(originalVal.style, { property: 'style', prefix, tokens, generateName }) : transformStrokeStyle(value.style); | ||
const width = isAlias(originalVal.width) | ||
? varRef(originalVal.width, { property: 'width', prefix, tokens, generateName }) | ||
: transformDimension(value.width); | ||
const color = isAlias(originalVal.color) | ||
? varRef(originalVal.color, { property: 'color', prefix, tokens, generateName }) | ||
: transformColor(originalVal.color, colorFormat); | ||
const style = isAlias(originalVal.style) | ||
? varRef(originalVal.style, { property: 'style', prefix, tokens, generateName }) | ||
: transformStrokeStyle(value.style); | ||
return `${width} ${style} ${color}`; | ||
} | ||
//# sourceMappingURL=border.js.map |
@@ -5,13 +5,13 @@ import type { ParsedColorToken } from '@cobalt-ui/core'; | ||
/** ⚠️ Important! We do NOT want to parse as P3. We want to parse as sRGB, then expand 1:1 to P3. @see https://webkit.org/blog/10042/wide-gamut-color-in-css-with-display-p3/ */ | ||
export declare const toHSL: import("culori/src/converter.js").ConvertFn<"hsl">; | ||
export declare const toHWB: import("culori/src/converter.js").ConvertFn<"hwb">; | ||
export declare const toLab: import("culori/src/converter.js").ConvertFn<"lab">; | ||
export declare const toLch: import("culori/src/converter.js").ConvertFn<"lch">; | ||
export declare const toOklab: import("culori/src/converter.js").ConvertFn<"oklab">; | ||
export declare const toOklch: import("culori/src/converter.js").ConvertFn<"oklch">; | ||
export declare const toP3: import("culori/src/converter.js").ConvertFn<"p3">; | ||
export declare const toRGB: import("culori/src/converter.js").ConvertFn<"rgb">; | ||
export declare const toRGBLinear: import("culori/src/converter.js").ConvertFn<"lrgb">; | ||
export declare const toXYZ50: import("culori/src/converter.js").ConvertFn<"xyz50">; | ||
export declare const toXYZ65: import("culori/src/converter.js").ConvertFn<"xyz65">; | ||
export declare const toHSL: import("culori/src/converter").ConvertFn<"hsl">; | ||
export declare const toHWB: import("culori/src/converter").ConvertFn<"hwb">; | ||
export declare const toLab: import("culori/src/converter").ConvertFn<"lab">; | ||
export declare const toLch: import("culori/src/converter").ConvertFn<"lch">; | ||
export declare const toOklab: import("culori/src/converter").ConvertFn<"oklab">; | ||
export declare const toOklch: import("culori/src/converter").ConvertFn<"oklch">; | ||
export declare const toP3: import("culori/src/converter").ConvertFn<"p3">; | ||
export declare const toRGB: import("culori/src/converter").ConvertFn<"rgb">; | ||
export declare const toRGBLinear: import("culori/src/converter").ConvertFn<"lrgb">; | ||
export declare const toXYZ50: import("culori/src/converter").ConvertFn<"xyz50">; | ||
export declare const toXYZ65: import("culori/src/converter").ConvertFn<"xyz65">; | ||
export default function transformColor(value: ParsedColorToken['$value'], colorFormat: ColorFormat): string; |
@@ -1,2 +0,2 @@ | ||
import { formatCss, formatRgb, clampChroma, formatHex8, formatHex, formatHsl, converter, parse as parseColor } from 'culori'; | ||
import { formatCss, formatRgb, clampChroma, formatHex8, formatHex, formatHsl, converter, parse as parseColor, } from 'culori'; | ||
/** ⚠️ Important! We do NOT want to parse as P3. We want to parse as sRGB, then expand 1:1 to P3. @see https://webkit.org/blog/10042/wide-gamut-color-in-css-with-display-p3/ */ | ||
@@ -19,4 +19,5 @@ export const toHSL = converter('hsl'); | ||
const parsed = parseColor(value); | ||
if (!parsed) | ||
if (!parsed) { | ||
throw new Error(`invalid color "${value}"`); | ||
} | ||
switch (colorFormat) { | ||
@@ -23,0 +24,0 @@ case 'rgb': { |
@@ -1,2 +0,2 @@ | ||
import { ParsedToken } from '@cobalt-ui/core'; | ||
import type { ParsedToken } from '@cobalt-ui/core'; | ||
import transformColor, { type ColorFormat } from './color.js'; | ||
@@ -13,3 +13,3 @@ import transformDimension from './dimension.js'; | ||
export * from './color.js'; | ||
export { transformColor, transformDimension, transformDuration, transformFontFamily, transformFontWeight, transformCubicBezier, transformNumber, transformLink, transformStrokeStyle }; | ||
export { transformColor, transformDimension, transformDuration, transformFontFamily, transformFontWeight, transformCubicBezier, transformNumber, transformLink, transformStrokeStyle, }; | ||
export default function transform(token: ParsedToken, { colorFormat, generateName, mode, prefix, tokens, }: { | ||
@@ -16,0 +16,0 @@ colorFormat: ColorFormat; |
@@ -14,3 +14,3 @@ import { isAlias, kebabinate, parseAlias } from '@cobalt-ui/utils'; | ||
export * from './color.js'; | ||
export { transformColor, transformDimension, transformDuration, transformFontFamily, transformFontWeight, transformCubicBezier, transformNumber, transformLink, transformStrokeStyle }; | ||
export { transformColor, transformDimension, transformDuration, transformFontFamily, transformFontWeight, transformCubicBezier, transformNumber, transformLink, transformStrokeStyle, }; | ||
export default function transform(token, { colorFormat = 'hex', generateName, mode, prefix, tokens, }) { | ||
@@ -93,6 +93,8 @@ switch (token.$type) { | ||
// handle backwards compat for previous versions that didn’t always return array | ||
if (!Array.isArray(value)) | ||
if (!Array.isArray(value)) { | ||
value = [value]; | ||
if (!Array.isArray(originalVal)) | ||
} | ||
if (!Array.isArray(originalVal)) { | ||
originalVal = [originalVal]; | ||
} | ||
return value | ||
@@ -104,7 +106,17 @@ .map((shadow, i) => { | ||
} | ||
const offsetX = isAlias(origShadow.offsetX) ? varRef(origShadow.offsetX, { prefix, tokens, generateName }) : transformDimension(shadow.offsetX); | ||
const offsetY = isAlias(origShadow.offsetY) ? varRef(origShadow.offsetY, { prefix, tokens, generateName }) : transformDimension(shadow.offsetY); | ||
const blur = isAlias(origShadow.blur) ? varRef(origShadow.blur, { prefix, tokens, generateName }) : transformDimension(shadow.blur); | ||
const spread = isAlias(origShadow.spread) ? varRef(origShadow.spread, { prefix, tokens, generateName }) : transformDimension(shadow.spread); | ||
const color = isAlias(origShadow.color) ? varRef(origShadow.color, { prefix, tokens, generateName }) : transformColor(origShadow.color, colorFormat); | ||
const offsetX = isAlias(origShadow.offsetX) | ||
? varRef(origShadow.offsetX, { prefix, tokens, generateName }) | ||
: transformDimension(shadow.offsetX); | ||
const offsetY = isAlias(origShadow.offsetY) | ||
? varRef(origShadow.offsetY, { prefix, tokens, generateName }) | ||
: transformDimension(shadow.offsetY); | ||
const blur = isAlias(origShadow.blur) | ||
? varRef(origShadow.blur, { prefix, tokens, generateName }) | ||
: transformDimension(shadow.blur); | ||
const spread = isAlias(origShadow.spread) | ||
? varRef(origShadow.spread, { prefix, tokens, generateName }) | ||
: transformDimension(shadow.spread); | ||
const color = isAlias(origShadow.color) | ||
? varRef(origShadow.color, { prefix, tokens, generateName }) | ||
: transformColor(origShadow.color, colorFormat); | ||
return `${shadow.inset ? 'inset ' : ''}${offsetX} ${offsetY} ${blur} ${spread} ${color}`; | ||
@@ -125,4 +137,8 @@ }) | ||
} | ||
const color = isAlias(origGradient.color) ? varRef(origGradient.color, { prefix, tokens, generateName }) : transformColor(origGradient.color, colorFormat); | ||
const stop = isAlias(origGradient.position) ? varRef(origGradient.position, { prefix, tokens, generateName }) : `${100 * gradient.position}%`; | ||
const color = isAlias(origGradient.color) | ||
? varRef(origGradient.color, { prefix, tokens, generateName }) | ||
: transformColor(origGradient.color, colorFormat); | ||
const stop = isAlias(origGradient.position) | ||
? varRef(origGradient.position, { prefix, tokens, generateName }) | ||
: `${100 * gradient.position}%`; | ||
return `${color} ${stop}`; | ||
@@ -137,8 +153,14 @@ }) | ||
} | ||
const duration = isAlias(originalVal.duration) ? varRef(originalVal.duration, { prefix, tokens, generateName }) : transformDuration(value.duration); | ||
const duration = isAlias(originalVal.duration) | ||
? varRef(originalVal.duration, { prefix, tokens, generateName }) | ||
: transformDuration(value.duration); | ||
let delay = undefined; | ||
if (value.delay) { | ||
delay = isAlias(originalVal.delay) ? varRef(originalVal.delay, { prefix, tokens, generateName }) : transformDuration(value.delay); | ||
delay = isAlias(originalVal.delay) | ||
? varRef(originalVal.delay, { prefix, tokens, generateName }) | ||
: transformDuration(value.delay); | ||
} | ||
const timingFunction = isAlias(originalVal.timingFunction) ? varRef(originalVal.timingFunction, { prefix, tokens, generateName }) : transformCubicBezier(value.timingFunction); | ||
const timingFunction = isAlias(originalVal.timingFunction) | ||
? varRef(originalVal.timingFunction, { prefix, tokens, generateName }) | ||
: transformCubicBezier(value.timingFunction); | ||
return `${duration} ${delay ?? ''} ${timingFunction}`; | ||
@@ -157,3 +179,4 @@ } | ||
} | ||
for (const [k, v] of Object.entries(value)) { | ||
for (const k in value) { | ||
const v = value[k]; | ||
const formatter = k === 'fontFamily' ? transformFontFamily : (val) => String(val); | ||
@@ -160,0 +183,0 @@ // partial alias (e.g. one property out of the set) |
@@ -1,4 +0,4 @@ | ||
/// <reference types="node" resolution-mode="require"/> | ||
/// <reference types="node" /> | ||
import { URL } from 'node:url'; | ||
/** encode file for CSS */ | ||
export declare function encode(cssURL: string, cwd: URL): string; |
@@ -1,2 +0,2 @@ | ||
import { ParsedToken } from '@cobalt-ui/core'; | ||
import type { ParsedToken } from '@cobalt-ui/core'; | ||
export { isTokenMatch } from '@cobalt-ui/utils'; | ||
@@ -3,0 +3,0 @@ export declare function getMode<T extends { |
@@ -32,3 +32,5 @@ import { isAlias, kebabinate, parseAlias } from '@cobalt-ui/utils'; | ||
const variableId = refID + normalizedSuffix; | ||
const property = token && options?.property && typeof token.$value === 'object' && !!token.$value[options.property] ? options.property : undefined; | ||
const property = token && options?.property && typeof token.$value === 'object' && !!token.$value[options.property] | ||
? options.property | ||
: undefined; | ||
return `var(${options?.generateName?.(variableId, token) ?? defaultNameGenerator(variableId, options?.prefix)}${property ? `-${kebabinate(property)}` : ''})`; | ||
@@ -46,5 +48,3 @@ } | ||
} | ||
else { | ||
return word[0]?.toLocaleUpperCase() + word.slice(1).toLocaleLowerCase(); | ||
} | ||
return word[0]?.toLocaleUpperCase() + word.slice(1).toLocaleLowerCase(); | ||
}) | ||
@@ -51,0 +51,0 @@ .join(''); |
@@ -20,5 +20,6 @@ import { getLocalID, kebabinate } from '@cobalt-ui/utils'; | ||
const value = {}; | ||
if (typeof token.$value === 'string') | ||
continue; // skip aliased token | ||
for (const k of Object.keys(token.$value)) { | ||
if (typeof token.$value === 'string') { | ||
continue; | ||
} // skip aliased token | ||
for (const k in token.$value) { | ||
const property = kebabinate(k); | ||
@@ -58,12 +59,17 @@ value[property] = `var(${refs[token.id]}-${property});`; | ||
let property = ''; | ||
if (token.type === 'border') | ||
if (token.type === 'border') { | ||
property = 'border'; | ||
else if (token.type === 'color') | ||
} | ||
else if (token.type === 'color') { | ||
property = 'border-color'; | ||
else if (token.type === 'dimension') | ||
} | ||
else if (token.type === 'dimension') { | ||
property = 'border-width'; | ||
else if (token.type === 'strokeStyle') | ||
} | ||
else if (token.type === 'strokeStyle') { | ||
property = 'border-style'; | ||
else | ||
continue; // skip invalid token types | ||
} | ||
else { | ||
continue; | ||
} // skip invalid token types | ||
output.push(`.border-${token.partialID} { | ||
@@ -76,12 +82,17 @@ ${property}: ${token.value}; | ||
let property = ''; | ||
if (token.type === 'border') | ||
if (token.type === 'border') { | ||
property = `border-${dir}`; | ||
else if (token.type === 'color') | ||
} | ||
else if (token.type === 'color') { | ||
property = `border-${dir}-color`; | ||
else if (token.type === 'dimension') | ||
} | ||
else if (token.type === 'dimension') { | ||
property = `border-${dir}-width`; | ||
else if (token.type === 'strokeStyle') | ||
} | ||
else if (token.type === 'strokeStyle') { | ||
property = `border-${dir}-style`; | ||
else | ||
continue; // skip invalid token types | ||
} | ||
else { | ||
continue; | ||
} // skip invalid token types | ||
output.push(`.border-${dir}-${token.partialID} { | ||
@@ -98,4 +109,4 @@ ${property}: ${token.value}; | ||
output.push(`.font-${token.partialID} {`); | ||
for (const [k, v] of Object.entries(token.value)) { | ||
output.push(` ${k}: ${v}`); | ||
for (const k in token.value) { | ||
output.push(` ${k}: ${token.value[k]}`); | ||
} | ||
@@ -168,4 +179,5 @@ output.push('}'); | ||
for (const token of selectedTokens) { | ||
if (token.type !== 'shadow') | ||
if (token.type !== 'shadow') { | ||
continue; | ||
} | ||
output.push(`.shadow-${token.partialID} { | ||
@@ -172,0 +184,0 @@ box-shadow: ${token.value}; |
{ | ||
"name": "@cobalt-ui/plugin-css", | ||
"description": "Generate CSS from your design tokens schema (requires @cobalt-ui/cli)", | ||
"version": "1.7.3", | ||
"version": "1.7.4", | ||
"author": { | ||
@@ -29,17 +29,16 @@ "name": "Drew Powers", | ||
"peerDependencies": { | ||
"@cobalt-ui/cli": "^1.x" | ||
"@cobalt-ui/cli": "^1.11.0" | ||
}, | ||
"dependencies": { | ||
"@cobalt-ui/utils": "^1.2.4", | ||
"@types/culori": "^2.0.4", | ||
"@types/culori": "^2.1.0", | ||
"@types/mime": "^3.0.4", | ||
"culori": "^3.3.0", | ||
"culori": "^4.0.1", | ||
"mime": "^3.0.0", | ||
"svgo": "^3.2.0" | ||
"svgo": "^3.3.2", | ||
"@cobalt-ui/utils": "^1.2.6" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^20.11.16", | ||
"vitest": "^1.2.2", | ||
"@cobalt-ui/cli": "^1.7.0", | ||
"@cobalt-ui/core": "^1.7.0" | ||
"yaml": "^2.4.2", | ||
"@cobalt-ui/cli": "^1.11.2", | ||
"@cobalt-ui/core": "^1.11.2" | ||
}, | ||
@@ -49,5 +48,6 @@ "scripts": { | ||
"build:clean": "del dist", | ||
"build:ts": "tsc", | ||
"build:ts": "tsc -p tsconfig.build.json", | ||
"build:license": "node ../../scripts/inject-license.js @cobalt-ui/plugin-css dist/index.js", | ||
"dev": "tsc -w", | ||
"dev": "tsc -p tsconfig.build.json -w", | ||
"lint": "biome check .", | ||
"test": "pnpm run \"/^test:.*/\"", | ||
@@ -54,0 +54,0 @@ "test:js": "vitest run", |
@@ -22,8 +22,8 @@ # @cobalt-ui/plugin-css | ||
// tokens.config.mjs | ||
import pluginCSS from '@cobalt-ui/plugin-css'; | ||
import pluginCSS from "@cobalt-ui/plugin-css"; | ||
/** @type {import('@cobalt-ui/core').Config} */ | ||
/** @type {import("@cobalt-ui/core").Config} */ | ||
export default { | ||
tokens: './tokens.json', | ||
outDir: './tokens/', | ||
tokens: "./tokens.json", | ||
outDir: "./tokens/", | ||
plugins: [pluginCSS()], | ||
@@ -30,0 +30,0 @@ }; |
141
src/index.ts
@@ -1,10 +0,11 @@ | ||
import type {BuildResult, ParsedToken, Plugin, ResolvedConfig} from '@cobalt-ui/core'; | ||
import {indent, isTokenMatch, FG_YELLOW, RESET} from '@cobalt-ui/utils'; | ||
import {formatCss} from 'culori'; | ||
import defaultTransformer, {type ColorFormat, toRGB} from './transform/index.js'; | ||
import {CustomNameGenerator, makeNameGenerator} from './utils/token.js'; | ||
import {encode} from './utils/encode.js'; | ||
import generateUtilityCSS, {type UtilityCSSGroup} from './utils/utility-css.js'; | ||
import type { BuildResult, ParsedToken, Plugin, ResolvedConfig } from '@cobalt-ui/core'; | ||
import { indent, isTokenMatch, FG_YELLOW, RESET } from '@cobalt-ui/utils'; | ||
import { formatCss } from 'culori'; | ||
import defaultTransformer, { type ColorFormat, toRGB } from './transform/index.js'; | ||
import type { CustomNameGenerator } from './utils/token.js'; | ||
import { makeNameGenerator } from './utils/token.js'; | ||
import { encode } from './utils/encode.js'; | ||
import generateUtilityCSS, { type UtilityCSSGroup } from './utils/utility-css.js'; | ||
export {makeNameGenerator as _INTERNAL_makeNameGenerator, defaultTransformer}; | ||
export { makeNameGenerator as _INTERNAL_makeNameGenerator, defaultTransformer }; | ||
export * from './utils/utility-css.js'; | ||
@@ -52,14 +53,22 @@ export * from './utils/token.js'; | ||
let config: ResolvedConfig; | ||
let filename = options?.filename || './tokens.css'; | ||
const filename = options?.filename || './tokens.css'; | ||
const prefix = options?.prefix || ''; | ||
const generateName = makeNameGenerator(options?.generateName, prefix); | ||
function makeVars({tokens, indentLv = 0, root = false}: {tokens: Record<string, string>; indentLv: number; root: boolean}): string[] { | ||
function makeVars({ | ||
tokens, | ||
indentLv = 0, | ||
root = false, | ||
}: { tokens: Record<string, string>; indentLv: number; root: boolean }): string[] { | ||
const output: string[] = []; | ||
if (root) output.push(indent(':root {', indentLv)); | ||
const sortedTokens = Object.entries(tokens).sort((a, b) => a[0].localeCompare(b[0], 'en-us', {numeric: true})); | ||
if (root) { | ||
output.push(indent(':root {', indentLv)); | ||
} | ||
const sortedTokens = Object.entries(tokens).sort((a, b) => a[0].localeCompare(b[0], 'en-us', { numeric: true })); | ||
for (const [variableName, value] of sortedTokens) { | ||
output.push(indent(`${variableName}: ${value};`, indentLv + (root ? 1 : 0))); | ||
} | ||
if (root) output.push(indent('}', indentLv)); | ||
if (root) { | ||
output.push(indent('}', indentLv)); | ||
} | ||
return output; | ||
@@ -77,8 +86,12 @@ } | ||
const matches = line.match(HEX_RE); | ||
if (!matches || !matches.length) continue; | ||
if (!matches || !matches.length) { | ||
continue; | ||
} | ||
let newVal = line; | ||
for (const c of matches) { | ||
const rgb = toRGB(c); | ||
if (!rgb) throw new Error(`invalid color "${c}"`); | ||
newVal = newVal.replace(c, formatCss({...rgb, mode: 'p3'})); | ||
if (!rgb) { | ||
throw new Error(`invalid color "${c}"`); | ||
} | ||
newVal = newVal.replace(c, formatCss({ ...rgb, mode: 'p3' })); | ||
hasValidColors = true; // keep track of whether or not actual colors have been generated (we also generate non-color output, so checking for output.length won’t work) | ||
@@ -97,9 +110,9 @@ } | ||
name: '@cobalt-ui/plugin-css', | ||
config(c): void { | ||
config(c) { | ||
config = c; | ||
}, | ||
async build({tokens, metadata}): Promise<BuildResult[]> { | ||
async build({ tokens, metadata }): Promise<BuildResult[]> { | ||
const tokenIdToRefs: Record<string, string> = {}; | ||
const tokenVals: {[variableName: string]: any} = {}; | ||
const modeVals: {[selector: string]: {[variableName: string]: any}} = {}; | ||
const tokenVals: { [variableName: string]: any } = {}; | ||
const modeVals: { [selector: string]: { [variableName: string]: any } } = {}; | ||
const selectors: string[] = []; | ||
@@ -110,3 +123,3 @@ const colorFormat = options?.colorFormat ?? 'hex'; | ||
if (value === undefined || value === null) { | ||
value = defaultTransformer(token, {prefix, colorFormat, generateName, tokens}); | ||
value = defaultTransformer(token, { prefix, colorFormat, generateName, tokens }); | ||
} | ||
@@ -119,3 +132,5 @@ | ||
case 'link': { | ||
if (options?.embedFiles) value = encode(value as string, config.outDir); | ||
if (options?.embedFiles) { | ||
value = encode(value as string, config.outDir); | ||
} | ||
tokenVals[ref] = value; | ||
@@ -125,4 +140,4 @@ break; | ||
case 'typography': { | ||
for (const [k, v] of Object.entries(value)) { | ||
tokenVals[generateName(`${token.id}-${k}`, token)] = v; | ||
for (const k in value as Record<string, string>) { | ||
tokenVals[generateName(`${token.id}-${k}`, token)] = (value as Record<string, string>)[k]; | ||
} | ||
@@ -136,3 +151,3 @@ break; | ||
} | ||
if (token.$extensions && token.$extensions.mode && options?.modeSelectors) { | ||
if (token.$extensions?.mode && options?.modeSelectors) { | ||
const modeSelectors: ModeSelector[] = []; | ||
@@ -150,3 +165,6 @@ | ||
} | ||
if (modeSelector.tokens && (!Array.isArray(modeSelector.tokens) || modeSelector.tokens.some((s) => typeof s !== 'string'))) { | ||
if ( | ||
modeSelector.tokens && | ||
(!Array.isArray(modeSelector.tokens) || modeSelector.tokens.some((s) => typeof s !== 'string')) | ||
) { | ||
throw new Error(`modeSelectors[${i}] tokens must be an array of strings`); | ||
@@ -162,5 +180,10 @@ } | ||
else if (typeof options.modeSelectors === 'object') { | ||
for (const [modeID, selector] of Object.entries(options.modeSelectors)) { | ||
for (const modeID in options.modeSelectors) { | ||
const selector = options.modeSelectors[modeID]!; | ||
const [groupRoot, modeName] = parseLegacyModeSelector(modeID); | ||
modeSelectors.push({mode: modeName, tokens: groupRoot ? [`${groupRoot}*`] : undefined, selectors: Array.isArray(selector) ? selector : [selector]}); | ||
modeSelectors.push({ | ||
mode: modeName, | ||
tokens: groupRoot ? [`${groupRoot}*`] : undefined, | ||
selectors: Array.isArray(selector) ? selector : [selector], | ||
}); | ||
} | ||
@@ -170,3 +193,6 @@ } | ||
for (const modeSelector of modeSelectors) { | ||
if (!token.$extensions.mode[modeSelector.mode] || (modeSelector.tokens && !isTokenMatch(token.id, modeSelector.tokens))) { | ||
if ( | ||
!token.$extensions.mode[modeSelector.mode] || | ||
(modeSelector.tokens && !isTokenMatch(token.id, modeSelector.tokens)) | ||
) { | ||
continue; | ||
@@ -176,11 +202,26 @@ } | ||
for (const selector of modeSelector.selectors) { | ||
if (!selectors.includes(selector)) selectors.push(selector); | ||
if (!modeVals[selector]) modeVals[selector] = {}; | ||
let modeVal: ReturnType<typeof defaultTransformer> | undefined | null = await options?.transform?.(token, modeSelector.mode); | ||
if (!selectors.includes(selector)) { | ||
selectors.push(selector); | ||
} | ||
if (!modeVals[selector]) { | ||
modeVals[selector] = {}; | ||
} | ||
let modeVal: ReturnType<typeof defaultTransformer> | undefined | null = await options?.transform?.( | ||
token, | ||
modeSelector.mode, | ||
); | ||
if (modeVal === undefined || modeVal === null) { | ||
modeVal = defaultTransformer(token, {colorFormat, generateName, mode: modeSelector.mode, prefix, tokens}); | ||
modeVal = defaultTransformer(token, { | ||
colorFormat, | ||
generateName, | ||
mode: modeSelector.mode, | ||
prefix, | ||
tokens, | ||
}); | ||
} | ||
switch (token.$type) { | ||
case 'link': { | ||
if (options?.embedFiles) modeVal = encode(modeVal as string, config.outDir); | ||
if (options?.embedFiles) { | ||
modeVal = encode(modeVal as string, config.outDir); | ||
} | ||
modeVals[selector]![generateName(token.id, token)] = modeVal; | ||
@@ -190,4 +231,6 @@ break; | ||
case 'typography': { | ||
for (const [k, v] of Object.entries(modeVal)) { | ||
modeVals[selector]![generateName(`${token.id}-${k}`, token)] = v; | ||
for (const k in modeVal as Record<string, string>) { | ||
modeVals[selector]![generateName(`${token.id}-${k}`, token)] = (modeVal as Record<string, string>)[ | ||
k | ||
]; | ||
} | ||
@@ -207,3 +250,3 @@ break; | ||
// :root vars | ||
let code: string[] = []; | ||
const code: string[] = []; | ||
code.push('/**'); | ||
@@ -215,3 +258,3 @@ code.push(` * ${metadata.name || 'Design Tokens'}`); | ||
code.push(''); | ||
code.push(...makeVars({tokens: tokenVals, indentLv: 0, root: true})); | ||
code.push(...makeVars({ tokens: tokenVals, indentLv: 0, root: true })); | ||
@@ -228,3 +271,3 @@ // modes | ||
if (modeVals[selector]) { | ||
const vars = makeVars({tokens: modeVals[selector]!, indentLv: 1, root: wrapper.startsWith('@')}); | ||
const vars = makeVars({ tokens: modeVals[selector]!, indentLv: 1, root: wrapper.startsWith('@') }); | ||
// don’t output empty selectors | ||
@@ -241,10 +284,12 @@ if (vars.length) { | ||
(colorFormat === 'hex' || colorFormat === 'rgb' || colorFormat === 'hsl' || colorFormat === 'hwb') && // only transform for the smaller gamuts | ||
tokens.some((t) => t.$type === 'color' || t.$type === 'border' || t.$type === 'gradient' || t.$type === 'shadow') | ||
tokens.some( | ||
(t) => t.$type === 'color' || t.$type === 'border' || t.$type === 'gradient' || t.$type === 'shadow', | ||
) | ||
) { | ||
code.push(''); | ||
code.push(indent(`@supports (color: color(display-p3 1 1 1)) {`, 0)); // note: @media (color-gamut: p3) is problematic in most browsers | ||
code.push(...makeP3(makeVars({tokens: tokenVals, indentLv: 1, root: true}))); | ||
code.push(indent('@supports (color: color(display-p3 1 1 1)) {', 0)); // note: @media (color-gamut: p3) is problematic in most browsers | ||
code.push(...makeP3(makeVars({ tokens: tokenVals, indentLv: 1, root: true }))); | ||
for (const selector of selectors) { | ||
const wrapper = selector.trim().replace(SELECTOR_BRACKET_RE, ''); | ||
const vars = makeVars({tokens: modeVals[selector]!, indentLv: 2, root: wrapper.startsWith('@')}); | ||
const vars = makeVars({ tokens: modeVals[selector]!, indentLv: 2, root: wrapper.startsWith('@') }); | ||
const p3colors = makeP3(vars); | ||
@@ -263,3 +308,3 @@ // don’t output empty selectors | ||
if (options?.utility && Object.keys(options.utility).length) { | ||
code.push(generateUtilityCSS(options.utility, {refs: tokenIdToRefs, tokens})); | ||
code.push(generateUtilityCSS(options.utility, { refs: tokenIdToRefs, tokens })); | ||
} | ||
@@ -281,6 +326,10 @@ | ||
function parseLegacyModeSelector(modeID: string): [string, string] { | ||
if (!modeID.includes('#')) throw new Error(`modeSelector key must have "#" character`); | ||
if (!modeID.includes('#')) { | ||
throw new Error(`modeSelector key must have "#" character`); | ||
} | ||
const parts = modeID.split('#').map((s) => s.trim()); | ||
if (parts.length > 2) throw new Error(`modeSelector key must have only 1 "#" character`); | ||
if (parts.length > 2) { | ||
throw new Error(`modeSelector key must have only 1 "#" character`); | ||
} | ||
return [parts[0]!, parts[1]!]; | ||
} |
@@ -1,5 +0,6 @@ | ||
import {ParsedBorderToken, ParsedToken} from '@cobalt-ui/core'; | ||
import {isAlias} from '@cobalt-ui/utils'; | ||
import {getMode, makeNameGenerator, varRef} from '../utils/token.js'; | ||
import transformColor, {type ColorFormat} from './color.js'; | ||
import type { ParsedBorderToken, ParsedToken } from '@cobalt-ui/core'; | ||
import { isAlias } from '@cobalt-ui/utils'; | ||
import type { makeNameGenerator } from '../utils/token.js'; | ||
import { getMode, varRef } from '../utils/token.js'; | ||
import transformColor, { type ColorFormat } from './color.js'; | ||
import transformDimension from './dimension.js'; | ||
@@ -10,12 +11,30 @@ import transformStrokeStyle from './stroke-style.js'; | ||
token: ParsedBorderToken, | ||
{colorFormat, mode, prefix, tokens, generateName}: {colorFormat: ColorFormat; mode?: string; prefix?: string; tokens?: ParsedToken[]; generateName?: ReturnType<typeof makeNameGenerator>}, | ||
{ | ||
colorFormat, | ||
mode, | ||
prefix, | ||
tokens, | ||
generateName, | ||
}: { | ||
colorFormat: ColorFormat; | ||
mode?: string; | ||
prefix?: string; | ||
tokens?: ParsedToken[]; | ||
generateName?: ReturnType<typeof makeNameGenerator>; | ||
}, | ||
): string { | ||
const {value, originalVal} = getMode(token, mode); | ||
const { value, originalVal } = getMode(token, mode); | ||
if (typeof originalVal === 'string') { | ||
return varRef(originalVal, {prefix, tokens, generateName}); | ||
return varRef(originalVal, { prefix, tokens, generateName }); | ||
} | ||
const width = isAlias(originalVal.width) ? varRef(originalVal.width, {property: 'width', prefix, tokens, generateName}) : transformDimension(value.width); | ||
const color = isAlias(originalVal.color) ? varRef(originalVal.color, {property: 'color', prefix, tokens, generateName}) : transformColor(originalVal.color, colorFormat); | ||
const style = isAlias(originalVal.style) ? varRef(originalVal.style as string, {property: 'style', prefix, tokens, generateName}) : transformStrokeStyle(value.style); | ||
const width = isAlias(originalVal.width) | ||
? varRef(originalVal.width, { property: 'width', prefix, tokens, generateName }) | ||
: transformDimension(value.width); | ||
const color = isAlias(originalVal.color) | ||
? varRef(originalVal.color, { property: 'color', prefix, tokens, generateName }) | ||
: transformColor(originalVal.color, colorFormat); | ||
const style = isAlias(originalVal.style) | ||
? varRef(originalVal.style as string, { property: 'style', prefix, tokens, generateName }) | ||
: transformStrokeStyle(value.style); | ||
return `${width} ${style} ${color}`; | ||
} |
@@ -1,6 +0,28 @@ | ||
import type {ParsedColorToken} from '@cobalt-ui/core'; | ||
import {formatCss, formatRgb, clampChroma, formatHex8, formatHex, formatHsl, converter, parse as parseColor} from 'culori'; | ||
import type { ParsedColorToken } from '@cobalt-ui/core'; | ||
import { | ||
formatCss, | ||
formatRgb, | ||
clampChroma, | ||
formatHex8, | ||
formatHex, | ||
formatHsl, | ||
converter, | ||
parse as parseColor, | ||
} from 'culori'; | ||
/** normalize all color outputs to format (default: "hex") or specify "none" to keep as-is */ | ||
export type ColorFormat = 'none' | 'hex' | 'rgb' | 'hsl' | 'hwb' | 'srgb-linear' | 'p3' | 'lab' | 'lch' | 'oklab' | 'oklch' | 'xyz-d50' | 'xyz-d65'; | ||
export type ColorFormat = | ||
| 'none' | ||
| 'hex' | ||
| 'rgb' | ||
| 'hsl' | ||
| 'hwb' | ||
| 'srgb-linear' | ||
| 'p3' | ||
| 'lab' | ||
| 'lch' | ||
| 'oklab' | ||
| 'oklch' | ||
| 'xyz-d50' | ||
| 'xyz-d65'; | ||
@@ -25,3 +47,5 @@ /** ⚠️ Important! We do NOT want to parse as P3. We want to parse as sRGB, then expand 1:1 to P3. @see https://webkit.org/blog/10042/wide-gamut-color-in-css-with-display-p3/ */ | ||
const parsed = parseColor(value); | ||
if (!parsed) throw new Error(`invalid color "${value}"`); | ||
if (!parsed) { | ||
throw new Error(`invalid color "${value}"`); | ||
} | ||
switch (colorFormat) { | ||
@@ -28,0 +52,0 @@ case 'rgb': { |
@@ -1,2 +0,2 @@ | ||
import type {ParsedCubicBezierToken} from '@cobalt-ui/core'; | ||
import type { ParsedCubicBezierToken } from '@cobalt-ui/core'; | ||
@@ -3,0 +3,0 @@ /** transform cubic beziér */ |
@@ -1,2 +0,2 @@ | ||
import type {ParsedDimensionToken} from '@cobalt-ui/core'; | ||
import type { ParsedDimensionToken } from '@cobalt-ui/core'; | ||
@@ -3,0 +3,0 @@ /** transform dimension */ |
@@ -1,2 +0,2 @@ | ||
import type {ParsedDurationToken} from '@cobalt-ui/core'; | ||
import type { ParsedDurationToken } from '@cobalt-ui/core'; | ||
@@ -3,0 +3,0 @@ /** transform duration */ |
@@ -1,2 +0,2 @@ | ||
import type {ParsedFontFamilyToken} from '@cobalt-ui/core'; | ||
import type { ParsedFontFamilyToken } from '@cobalt-ui/core'; | ||
@@ -3,0 +3,0 @@ /** transform font family */ |
@@ -1,2 +0,2 @@ | ||
import type {ParsedFontWeightToken} from '@cobalt-ui/core'; | ||
import type { ParsedFontWeightToken } from '@cobalt-ui/core'; | ||
@@ -3,0 +3,0 @@ /** transform font weight */ |
@@ -1,4 +0,4 @@ | ||
import {ParsedToken} from '@cobalt-ui/core'; | ||
import {isAlias, kebabinate, parseAlias} from '@cobalt-ui/utils'; | ||
import transformColor, {type ColorFormat} from './color.js'; | ||
import type { ParsedToken } from '@cobalt-ui/core'; | ||
import { isAlias, kebabinate, parseAlias } from '@cobalt-ui/utils'; | ||
import transformColor, { type ColorFormat } from './color.js'; | ||
import transformDimension from './dimension.js'; | ||
@@ -12,7 +12,17 @@ import transformDuration from './duration.js'; | ||
import transformStrokeStyle from './stroke-style.js'; | ||
import {getMode, type makeNameGenerator, varRef} from '../utils/token.js'; | ||
import { getMode, type makeNameGenerator, varRef } from '../utils/token.js'; | ||
import transformBorder from './border.js'; | ||
export * from './color.js'; | ||
export {transformColor, transformDimension, transformDuration, transformFontFamily, transformFontWeight, transformCubicBezier, transformNumber, transformLink, transformStrokeStyle}; | ||
export { | ||
transformColor, | ||
transformDimension, | ||
transformDuration, | ||
transformFontFamily, | ||
transformFontWeight, | ||
transformCubicBezier, | ||
transformNumber, | ||
transformLink, | ||
transformStrokeStyle, | ||
}; | ||
@@ -38,5 +48,5 @@ export default function transform( | ||
case 'color': { | ||
const {originalVal} = getMode(token, mode); | ||
const { originalVal } = getMode(token, mode); | ||
if (isAlias(originalVal)) { | ||
return varRef(originalVal, {prefix, tokens, generateName}); | ||
return varRef(originalVal, { prefix, tokens, generateName }); | ||
} | ||
@@ -46,5 +56,5 @@ return transformColor(originalVal, colorFormat); // note: use original value because it may have been normalized to hex (which matters if it wasn’t in sRGB gamut to begin with) | ||
case 'dimension': { | ||
const {value, originalVal} = getMode(token, mode); | ||
const { value, originalVal } = getMode(token, mode); | ||
if (isAlias(originalVal)) { | ||
return varRef(originalVal as string, {prefix, tokens, generateName}); | ||
return varRef(originalVal as string, { prefix, tokens, generateName }); | ||
} | ||
@@ -54,5 +64,5 @@ return transformDimension(value); | ||
case 'duration': { | ||
const {value, originalVal} = getMode(token, mode); | ||
const { value, originalVal } = getMode(token, mode); | ||
if (isAlias(originalVal)) { | ||
return varRef(originalVal as string, {prefix, tokens, generateName}); | ||
return varRef(originalVal as string, { prefix, tokens, generateName }); | ||
} | ||
@@ -63,5 +73,5 @@ return transformDuration(value); | ||
case 'fontFamily': { | ||
const {value, originalVal} = getMode(token, mode); | ||
const { value, originalVal } = getMode(token, mode); | ||
if (isAlias(originalVal)) { | ||
return varRef(originalVal as string, {prefix, tokens, generateName}); | ||
return varRef(originalVal as string, { prefix, tokens, generateName }); | ||
} | ||
@@ -71,5 +81,5 @@ return transformFontFamily(value); | ||
case 'fontWeight': { | ||
const {value, originalVal} = getMode(token, mode); | ||
const { value, originalVal } = getMode(token, mode); | ||
if (isAlias(originalVal)) { | ||
return varRef(originalVal as string, {prefix, tokens, generateName}); | ||
return varRef(originalVal as string, { prefix, tokens, generateName }); | ||
} | ||
@@ -79,5 +89,5 @@ return transformFontWeight(value); | ||
case 'cubicBezier': { | ||
const {value, originalVal} = getMode(token, mode); | ||
const { value, originalVal } = getMode(token, mode); | ||
if (isAlias(originalVal)) { | ||
return varRef(originalVal as string, {prefix, tokens, generateName}); | ||
return varRef(originalVal as string, { prefix, tokens, generateName }); | ||
} | ||
@@ -87,5 +97,5 @@ return transformCubicBezier(value); | ||
case 'number': { | ||
const {value, originalVal} = getMode(token, mode); | ||
const { value, originalVal } = getMode(token, mode); | ||
if (isAlias(originalVal)) { | ||
return varRef(originalVal as string, {prefix, tokens, generateName}); | ||
return varRef(originalVal as string, { prefix, tokens, generateName }); | ||
} | ||
@@ -95,5 +105,5 @@ return transformNumber(value); | ||
case 'link': { | ||
const {value, originalVal} = getMode(token, mode); | ||
const { value, originalVal } = getMode(token, mode); | ||
if (isAlias(originalVal)) { | ||
return varRef(originalVal as string, {prefix, tokens, generateName}); | ||
return varRef(originalVal as string, { prefix, tokens, generateName }); | ||
} | ||
@@ -103,5 +113,5 @@ return transformLink(value); | ||
case 'strokeStyle': { | ||
const {value, originalVal} = getMode(token, mode); | ||
const { value, originalVal } = getMode(token, mode); | ||
if (isAlias(originalVal)) { | ||
return varRef(originalVal as string, {prefix, tokens, generateName}); | ||
return varRef(originalVal as string, { prefix, tokens, generateName }); | ||
} | ||
@@ -112,13 +122,17 @@ return transformStrokeStyle(value); | ||
case 'border': { | ||
return transformBorder(token, {mode, prefix, tokens, generateName, colorFormat}); | ||
return transformBorder(token, { mode, prefix, tokens, generateName, colorFormat }); | ||
} | ||
case 'shadow': { | ||
let {value, originalVal} = getMode(token, mode); | ||
let { value, originalVal } = getMode(token, mode); | ||
if (typeof originalVal === 'string') { | ||
return varRef(originalVal, {prefix, tokens, generateName}); | ||
return varRef(originalVal, { prefix, tokens, generateName }); | ||
} | ||
// handle backwards compat for previous versions that didn’t always return array | ||
if (!Array.isArray(value)) value = [value]; | ||
if (!Array.isArray(originalVal)) originalVal = [originalVal]; | ||
if (!Array.isArray(value)) { | ||
value = [value]; | ||
} | ||
if (!Array.isArray(originalVal)) { | ||
originalVal = [originalVal]; | ||
} | ||
@@ -129,9 +143,19 @@ return value | ||
if (typeof origShadow === 'string') { | ||
return varRef(origShadow, {prefix, tokens, generateName}); | ||
return varRef(origShadow, { prefix, tokens, generateName }); | ||
} | ||
const offsetX = isAlias(origShadow.offsetX) ? varRef(origShadow.offsetX, {prefix, tokens, generateName}) : transformDimension(shadow.offsetX); | ||
const offsetY = isAlias(origShadow.offsetY) ? varRef(origShadow.offsetY, {prefix, tokens, generateName}) : transformDimension(shadow.offsetY); | ||
const blur = isAlias(origShadow.blur) ? varRef(origShadow.blur, {prefix, tokens, generateName}) : transformDimension(shadow.blur); | ||
const spread = isAlias(origShadow.spread) ? varRef(origShadow.spread, {prefix, tokens, generateName}) : transformDimension(shadow.spread); | ||
const color = isAlias(origShadow.color) ? varRef(origShadow.color, {prefix, tokens, generateName}) : transformColor(origShadow.color, colorFormat); | ||
const offsetX = isAlias(origShadow.offsetX) | ||
? varRef(origShadow.offsetX, { prefix, tokens, generateName }) | ||
: transformDimension(shadow.offsetX); | ||
const offsetY = isAlias(origShadow.offsetY) | ||
? varRef(origShadow.offsetY, { prefix, tokens, generateName }) | ||
: transformDimension(shadow.offsetY); | ||
const blur = isAlias(origShadow.blur) | ||
? varRef(origShadow.blur, { prefix, tokens, generateName }) | ||
: transformDimension(shadow.blur); | ||
const spread = isAlias(origShadow.spread) | ||
? varRef(origShadow.spread, { prefix, tokens, generateName }) | ||
: transformDimension(shadow.spread); | ||
const color = isAlias(origShadow.color) | ||
? varRef(origShadow.color, { prefix, tokens, generateName }) | ||
: transformColor(origShadow.color, colorFormat); | ||
return `${shadow.inset ? 'inset ' : ''}${offsetX} ${offsetY} ${blur} ${spread} ${color}`; | ||
@@ -142,5 +166,5 @@ }) | ||
case 'gradient': { | ||
const {value, originalVal} = getMode(token, mode); | ||
const { value, originalVal } = getMode(token, mode); | ||
if (typeof originalVal === 'string') { | ||
return varRef(originalVal, {prefix, tokens, generateName}); | ||
return varRef(originalVal, { prefix, tokens, generateName }); | ||
} | ||
@@ -151,6 +175,10 @@ return value | ||
if (typeof origGradient === 'string') { | ||
return varRef(origGradient, {prefix, tokens, generateName}); | ||
return varRef(origGradient, { prefix, tokens, generateName }); | ||
} | ||
const color = isAlias(origGradient.color) ? varRef(origGradient.color, {prefix, tokens, generateName}) : transformColor(origGradient.color, colorFormat); | ||
const stop = isAlias(origGradient.position) ? varRef(origGradient.position as any, {prefix, tokens, generateName}) : `${100 * gradient.position}%`; | ||
const color = isAlias(origGradient.color) | ||
? varRef(origGradient.color, { prefix, tokens, generateName }) | ||
: transformColor(origGradient.color, colorFormat); | ||
const stop = isAlias(origGradient.position) | ||
? varRef(origGradient.position as any, { prefix, tokens, generateName }) | ||
: `${100 * gradient.position}%`; | ||
return `${color} ${stop}`; | ||
@@ -161,31 +189,38 @@ }) | ||
case 'transition': { | ||
const {value, originalVal} = getMode(token, mode); | ||
const { value, originalVal } = getMode(token, mode); | ||
if (typeof originalVal === 'string') { | ||
return varRef(originalVal, {prefix, tokens, generateName}); | ||
return varRef(originalVal, { prefix, tokens, generateName }); | ||
} | ||
const duration = isAlias(originalVal.duration) ? varRef(originalVal.duration, {prefix, tokens, generateName}) : transformDuration(value.duration); | ||
const duration = isAlias(originalVal.duration) | ||
? varRef(originalVal.duration, { prefix, tokens, generateName }) | ||
: transformDuration(value.duration); | ||
let delay: string | undefined = undefined; | ||
if (value.delay) { | ||
delay = isAlias(originalVal.delay) ? varRef(originalVal.delay, {prefix, tokens, generateName}) : transformDuration(value.delay); | ||
delay = isAlias(originalVal.delay) | ||
? varRef(originalVal.delay, { prefix, tokens, generateName }) | ||
: transformDuration(value.delay); | ||
} | ||
const timingFunction = isAlias(originalVal.timingFunction) ? varRef(originalVal.timingFunction as any, {prefix, tokens, generateName}) : transformCubicBezier(value.timingFunction); | ||
const timingFunction = isAlias(originalVal.timingFunction) | ||
? varRef(originalVal.timingFunction as any, { prefix, tokens, generateName }) | ||
: transformCubicBezier(value.timingFunction); | ||
return `${duration} ${delay ?? ''} ${timingFunction}`; | ||
} | ||
case 'typography': { | ||
const {value, originalVal} = getMode(token, mode); | ||
const { value, originalVal } = getMode(token, mode); | ||
const output: Record<string, string> = {}; | ||
// full alias | ||
if (typeof originalVal === 'string') { | ||
const {id} = parseAlias(originalVal); | ||
const { id } = parseAlias(originalVal); | ||
for (const k in value) { | ||
output[kebabinate(k)] = varRef(id, {property: k, prefix, tokens, generateName}); | ||
output[kebabinate(k)] = varRef(id, { property: k, prefix, tokens, generateName }); | ||
} | ||
return output; | ||
} | ||
for (const [k, v] of Object.entries(value)) { | ||
for (const k in value) { | ||
const v = (value as Record<string, string>)[k]!; | ||
const formatter = k === 'fontFamily' ? transformFontFamily : (val: any): string => String(val); | ||
// partial alias (e.g. one property out of the set) | ||
if (isAlias((originalVal as any)[k] as any)) { | ||
const {id} = parseAlias((originalVal as any)[k]); | ||
output[kebabinate(k)] = varRef(id, {property: k, prefix, tokens, generateName}); | ||
const { id } = parseAlias((originalVal as any)[k]); | ||
output[kebabinate(k)] = varRef(id, { property: k, prefix, tokens, generateName }); | ||
} else { | ||
@@ -192,0 +227,0 @@ output[kebabinate(k)] = formatter(v as any); |
@@ -1,2 +0,2 @@ | ||
import type {ParsedLinkToken} from '@cobalt-ui/core'; | ||
import type { ParsedLinkToken } from '@cobalt-ui/core'; | ||
@@ -3,0 +3,0 @@ /** transform file */ |
@@ -1,2 +0,2 @@ | ||
import type {ParsedNumberToken} from '@cobalt-ui/core'; | ||
import type { ParsedNumberToken } from '@cobalt-ui/core'; | ||
@@ -3,0 +3,0 @@ /** transform number */ |
@@ -1,2 +0,2 @@ | ||
import type {ParsedStrokeStyleToken} from '@cobalt-ui/core'; | ||
import type { ParsedStrokeStyleToken } from '@cobalt-ui/core'; | ||
@@ -3,0 +3,0 @@ /** transform stroke style */ |
import mime from 'mime'; | ||
import fs from 'node:fs'; | ||
import {fileURLToPath, URL} from 'node:url'; | ||
import { fileURLToPath, URL } from 'node:url'; | ||
import svgo from 'svgo'; | ||
@@ -12,3 +12,5 @@ | ||
export function encode(cssURL: string, cwd: URL): string { | ||
const filename = fileURLToPath(new URL(cssURL.replace(URL_PREFIX, '').replace(URL_SUFFIX, '').replace(LEADING_SLASH_RE, ''), cwd)); | ||
const filename = fileURLToPath( | ||
new URL(cssURL.replace(URL_PREFIX, '').replace(URL_SUFFIX, '').replace(LEADING_SLASH_RE, ''), cwd), | ||
); | ||
const type = mime.getType(filename); | ||
@@ -15,0 +17,0 @@ |
@@ -1,5 +0,5 @@ | ||
import {ParsedToken} from '@cobalt-ui/core'; | ||
import {isAlias, kebabinate, parseAlias} from '@cobalt-ui/utils'; | ||
import type { ParsedToken } from '@cobalt-ui/core'; | ||
import { isAlias, kebabinate, parseAlias } from '@cobalt-ui/utils'; | ||
export {isTokenMatch} from '@cobalt-ui/utils'; | ||
export { isTokenMatch } from '@cobalt-ui/utils'; | ||
@@ -9,3 +9,6 @@ const DASH_PREFIX_RE = /^-+/; | ||
export function getMode<T extends {id: string; $value: any; $extensions?: any; _original: any}>(token: T, mode?: string): {value: T['$value']; originalVal: T['$value'] | string} { | ||
export function getMode<T extends { id: string; $value: any; $extensions?: any; _original: any }>( | ||
token: T, | ||
mode?: string, | ||
): { value: T['$value']; originalVal: T['$value'] | string } { | ||
if (mode) { | ||
@@ -20,3 +23,3 @@ if (!token.$extensions?.mode || !token.$extensions.mode[mode]) { | ||
} | ||
return {value: token.$value, originalVal: token._original.$value}; | ||
return { value: token.$value, originalVal: token._original.$value }; | ||
} | ||
@@ -53,4 +56,9 @@ | ||
const variableId = refID + normalizedSuffix; | ||
const property = token && options?.property && typeof token.$value === 'object' && !!(token.$value as any)[options.property] ? options.property : undefined; | ||
return `var(${options?.generateName?.(variableId, token) ?? defaultNameGenerator(variableId, options?.prefix)}${property ? `-${kebabinate(property)}` : ''})`; | ||
const property = | ||
token && options?.property && typeof token.$value === 'object' && !!(token.$value as any)[options.property] | ||
? options.property | ||
: undefined; | ||
return `var(${options?.generateName?.(variableId, token) ?? defaultNameGenerator(variableId, options?.prefix)}${ | ||
property ? `-${kebabinate(property)}` : '' | ||
})`; | ||
} | ||
@@ -68,5 +76,4 @@ | ||
return word.toLocaleLowerCase(); | ||
} else { | ||
return word[0]?.toLocaleUpperCase() + word.slice(1).toLocaleLowerCase(); | ||
} | ||
return word[0]?.toLocaleUpperCase() + word.slice(1).toLocaleLowerCase(); | ||
}) | ||
@@ -73,0 +80,0 @@ .join(''); |
@@ -1,4 +0,4 @@ | ||
import type {ParsedToken} from '@cobalt-ui/core'; | ||
import {getLocalID, kebabinate} from '@cobalt-ui/utils'; | ||
import {defaultNameGenerator, isTokenMatch} from './token.js'; | ||
import type { ParsedToken } from '@cobalt-ui/core'; | ||
import { getLocalID, kebabinate } from '@cobalt-ui/utils'; | ||
import { defaultNameGenerator, isTokenMatch } from './token.js'; | ||
@@ -10,3 +10,6 @@ export type UtilityCSSGroup = 'bg' | 'border' | 'font' | 'gap' | 'margin' | 'padding' | 'shadow' | 'text'; | ||
export default function generateUtilityCSS(groups: Record<UtilityCSSGroup, string[]>, {refs, tokens}: {refs: Record<string, string>; tokens: ParsedToken[]}): string { | ||
export default function generateUtilityCSS( | ||
groups: Record<UtilityCSSGroup, string[]>, | ||
{ refs, tokens }: { refs: Record<string, string>; tokens: ParsedToken[] }, | ||
): string { | ||
const output: string[] = []; | ||
@@ -18,7 +21,14 @@ | ||
for (const [group, selectors] of groupEntries) { | ||
const selectedTokens: {id: string; partialID: string; type: ParsedToken['$type']; value: string | Record<string, string>}[] = []; | ||
const selectedTokens: { | ||
id: string; | ||
partialID: string; | ||
type: ParsedToken['$type']; | ||
value: string | Record<string, string>; | ||
}[] = []; | ||
for (const token of tokens) { | ||
const globMatch = isTokenMatch(token.id, selectors); | ||
if (globMatch) { | ||
let partialID = defaultNameGenerator(token.id.replace(globMatch.replace(ENDING_GLOB, ''), '').replace(STARTING_DOT, '')); | ||
let partialID = defaultNameGenerator( | ||
token.id.replace(globMatch.replace(ENDING_GLOB, ''), '').replace(STARTING_DOT, ''), | ||
); | ||
if (!partialID) { | ||
@@ -29,10 +39,12 @@ partialID = getLocalID(token.id); | ||
const value: Record<string, string> = {}; | ||
if (typeof token.$value === 'string') continue; // skip aliased token | ||
for (const k of Object.keys(token.$value)) { | ||
if (typeof token.$value === 'string') { | ||
continue; | ||
} // skip aliased token | ||
for (const k in token.$value) { | ||
const property = kebabinate(k); | ||
value[property] = `var(${refs[token.id]}-${property});`; | ||
} | ||
selectedTokens.push({id: token.id, partialID, type: token.$type, value}); | ||
selectedTokens.push({ id: token.id, partialID, type: token.$type, value }); | ||
} else { | ||
selectedTokens.push({id: token.id, partialID, type: token.$type, value: `var(${refs[token.id]})`}); | ||
selectedTokens.push({ id: token.id, partialID, type: token.$type, value: `var(${refs[token.id]})` }); | ||
} | ||
@@ -65,7 +77,13 @@ } | ||
let property = ''; | ||
if (token.type === 'border') property = 'border'; | ||
else if (token.type === 'color') property = 'border-color'; | ||
else if (token.type === 'dimension') property = 'border-width'; | ||
else if (token.type === 'strokeStyle') property = 'border-style'; | ||
else continue; // skip invalid token types | ||
if (token.type === 'border') { | ||
property = 'border'; | ||
} else if (token.type === 'color') { | ||
property = 'border-color'; | ||
} else if (token.type === 'dimension') { | ||
property = 'border-width'; | ||
} else if (token.type === 'strokeStyle') { | ||
property = 'border-style'; | ||
} else { | ||
continue; | ||
} // skip invalid token types | ||
@@ -79,7 +97,13 @@ output.push(`.border-${token.partialID} { | ||
let property = ''; | ||
if (token.type === 'border') property = `border-${dir}`; | ||
else if (token.type === 'color') property = `border-${dir}-color`; | ||
else if (token.type === 'dimension') property = `border-${dir}-width`; | ||
else if (token.type === 'strokeStyle') property = `border-${dir}-style`; | ||
else continue; // skip invalid token types | ||
if (token.type === 'border') { | ||
property = `border-${dir}`; | ||
} else if (token.type === 'color') { | ||
property = `border-${dir}-color`; | ||
} else if (token.type === 'dimension') { | ||
property = `border-${dir}-width`; | ||
} else if (token.type === 'strokeStyle') { | ||
property = `border-${dir}-style`; | ||
} else { | ||
continue; | ||
} // skip invalid token types | ||
@@ -97,4 +121,4 @@ output.push(`.border-${dir}-${token.partialID} { | ||
output.push(`.font-${token.partialID} {`); | ||
for (const [k, v] of Object.entries(token.value)) { | ||
output.push(` ${k}: ${v}`); | ||
for (const k in token.value) { | ||
output.push(` ${k}: ${token.value[k]}`); | ||
} | ||
@@ -169,3 +193,5 @@ output.push('}'); | ||
for (const token of selectedTokens) { | ||
if (token.type !== 'shadow') continue; | ||
if (token.type !== 'shadow') { | ||
continue; | ||
} | ||
output.push(`.shadow-${token.partialID} { | ||
@@ -172,0 +198,0 @@ box-shadow: ${token.value}; |
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
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
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
136449
3
67
2051
+ Addedis-date-object@1.1.0(transitive)
+ Addedis-regex@1.2.1(transitive)
+ Addedsafe-regex-test@1.1.0(transitive)
+ Addedtype-fest@4.30.1(transitive)
- Removedculori@3.3.0(transitive)
- Removedis-date-object@1.0.5(transitive)
- Removedis-regex@1.2.0(transitive)
- Removedsafe-regex-test@1.0.3(transitive)
- Removedtype-fest@4.30.0(transitive)
Updated@cobalt-ui/utils@^1.2.6
Updated@types/culori@^2.1.0
Updatedculori@^4.0.1
Updatedsvgo@^3.3.2