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

@tokens-studio/sd-transforms

Package Overview
Dependencies
Maintainers
0
Versions
89
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tokens-studio/sd-transforms - npm Package Compare versions

Comparing version 0.16.1 to 1.0.0

dist/src/preprocessors/add-font-styles.d.ts

3

dist/src/checkAndEvaluateMath.d.ts

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

export declare function checkAndEvaluateMath(expr: string | number | boolean | undefined): string | number | boolean | undefined;
import { DesignToken } from 'style-dictionary/types';
export declare function checkAndEvaluateMath(token: DesignToken): DesignToken['value'];
import { Parser } from 'expr-eval-fork';
import { parse as parse_1, reduceExpression } from '../bundled_CJS_deps/postcss-calc-ast-parser/dist/index.js';
import { parse, reduceExpression } from '@bundled-es-modules/postcss-calc-ast-parser';
const mathChars = ['+', '-', '*', '/'];

@@ -92,3 +91,5 @@ const parser = new Parser();

while ((matchArr = unitRegex.exec(noPixExpr)) !== null) {
foundUnits.add(matchArr.groups.unit);
if (matchArr?.groups) {
foundUnits.add(matchArr.groups.unit);
}
}

@@ -101,3 +102,3 @@ // multiple units (besides px) found, cannot parse the expression

// Remove it here so we can evaluate expressions like 16px + 24px
const calcParsed = parse_1(noPixExpr, { allowInlineCommnets: false });
const calcParsed = parse(noPixExpr, { allowInlineCommnets: false });
// No expression to evaluate, just return it (in case of number as string e.g. '10')

@@ -121,14 +122,57 @@ if (calcParsed.nodes.length === 1 && calcParsed.nodes[0].type === 'Number') {

}
function checkAndEvaluateMath(expr) {
if (typeof expr !== 'string') {
export function checkAndEvaluateMath(token) {
const expr = token.$value ?? token.value;
const type = token.$type ?? token.type;
if (!['string', 'object'].includes(typeof expr)) {
return expr;
}
const exprs = splitMultiIntoSingleValues(expr);
const reducedExprs = exprs.map(_expr => parseAndReduce(_expr));
if (reducedExprs.length === 1) {
return reducedExprs[0];
const resolveMath = (expr) => {
if (typeof expr !== 'string') {
return expr;
}
const exprs = splitMultiIntoSingleValues(expr);
const reducedExprs = exprs.map(_expr => parseAndReduce(_expr));
if (reducedExprs.length === 1) {
return reducedExprs[0];
}
return reducedExprs.join(' ');
};
const transformProp = (val, prop) => {
val[prop] = resolveMath(val[prop]);
return val;
};
let transformed = expr;
switch (type) {
case 'typography':
case 'border': {
transformed = transformed;
// double check that expr is still an object and not already shorthand transformed to a string
if (typeof expr === 'object') {
Object.keys(transformed).forEach(prop => {
transformed = transformProp(transformed, prop);
});
}
break;
}
case 'shadow': {
transformed = transformed;
const transformShadow = (shadowVal) => {
// double check that expr is still an object and not already shorthand transformed to a string
if (typeof expr === 'object') {
Object.keys(shadowVal).forEach(prop => {
shadowVal = transformProp(shadowVal, prop);
});
}
return shadowVal;
};
if (Array.isArray(transformed)) {
transformed = transformed.map(transformShadow);
}
transformed = transformShadow(transformed);
break;
}
default:
transformed = resolveMath(transformed);
}
return reducedExprs.join(' ');
return transformed;
}
export { checkAndEvaluateMath };

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

function darken(color, colorSpace, amount) {
export function darken(color, colorSpace, amount) {
switch (colorSpace) {

@@ -43,3 +43,1 @@ case 'lch': {

}
export { darken };

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

function lighten(color, colorSpace, amount) {
export function lighten(color, colorSpace, amount) {
switch (colorSpace) {

@@ -43,3 +43,1 @@ case 'lch': {

}
export { lighten };
import Color from 'colorjs.io';
function mix(color, colorSpace, amount, mixColor) {
export function mix(color, colorSpace, amount, mixColor) {
const mixValue = Math.max(0, Math.min(1, Number(amount)));
return new Color(color.mix(mixColor, mixValue, { space: colorSpace }).toString());
}
export { mix };

@@ -6,3 +6,2 @@ import Color from 'colorjs.io';

import { lighten } from './lighten.js';
// Users using UIColor swift format are blocked from using such transform in

@@ -25,3 +24,3 @@ // combination with this color modify transform when using references.

}
function modifyColor(baseColor, modifier) {
export function modifyColor(baseColor, modifier) {
if (baseColor === undefined) {

@@ -72,3 +71,1 @@ return baseColor;

}
export { modifyColor };

@@ -1,11 +0,7 @@

import usesReferences from '../../bundled_CJS_deps/style-dictionary/lib/utils/references/usesReferences.js';
import '../../bundled_CJS_deps/style-dictionary/lib/utils/references/getReferences.js';
import '../../bundled_CJS_deps/style-dictionary/lib/utils/references/resolveReferences.js';
import '../../bundled_CJS_deps/style-dictionary/lib/utils/references/outputReferencesFilter.js';
import { usesReferences } from 'style-dictionary/utils';
import { modifyColor } from './modifyColor.js';
/**
* Helper: Transforms color tokens with tokens studio color modifiers
*/
function transformColorModifiers(token, options) {
export function transformColorModifiers(token, options) {
const modifier = token.$extensions['studio.tokens']?.modify;

@@ -23,3 +19,1 @@ // If some of the modifier props contain references or the modifier itself is a reference

}
export { transformColorModifiers };

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

function transparentize(color, amount) {
export function transparentize(color, amount) {
const _color = color;

@@ -6,3 +6,1 @@ _color.alpha = Math.max(0, Math.min(1, Number(amount)));

}
export { transparentize };

@@ -0,4 +1,5 @@

import { DesignToken } from 'style-dictionary/types';
/**
* Helper: Transforms typography object to typography shorthand for Jetpack Compose
*/
export declare function transformTypographyForCompose(value: Record<string, string> | undefined): string | undefined;
export declare function transformTypographyForCompose(token: DesignToken): DesignToken['value'];

@@ -1,10 +0,9 @@

import { transformFontWeights } from '../transformFontWeights.js';
import { transformFontWeight } from '../transformFontWeight.js';
/**
* Helper: Transforms typography object to typography shorthand for Jetpack Compose
*/
function transformTypographyForCompose(value) {
if (value === undefined) {
return value;
}
export function transformTypographyForCompose(token) {
const val = token.$value ?? token.value;
if (val === undefined)
return undefined;
/**

@@ -30,7 +29,9 @@ * Mapping between https://docs.tokens.studio/available-tokens/typography-tokens

*/
return `${Object.entries(value).reduce((acc, [propName, val]) => `${acc}${textStylePropertiesMapping[propName]
? `${propName === 'fontWeight' ? transformFontWeights(val) : val}\n`
return `${Object.entries(val).reduce((acc, [propName, v]) => `${acc}${textStylePropertiesMapping[propName]
? `${propName === 'fontWeight'
? transformFontWeight({
value: v,
})
: v}\n`
: ''}`, 'TextStyle(\n')})`;
}
export { transformTypographyForCompose };

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

import { DesignToken } from 'style-dictionary/types';
/**

@@ -6,2 +7,2 @@ * Helper: Transforms hex rgba colors used in figma tokens:

*/
export declare function transformHEXRGBaForCSS(value: string | undefined): string | undefined;
export declare function transformHEXRGBaForCSS(token: DesignToken): DesignToken['value'];

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

import { parseToRgba } from 'color2k';
import Color from 'colorjs.io';
/**

@@ -8,19 +7,43 @@ * Helper: Transforms hex rgba colors used in figma tokens:

*/
function transformHEXRGBaForCSS(value) {
if (value === undefined) {
return value;
}
const regex = /rgba\(\s*(?<hex>#.+?)\s*,\s*(?<alpha>\d*(\.\d*|%)*)\s*\)/g;
return value.replace(regex, (match, hex, alpha) => {
try {
const [r, g, b] = parseToRgba(hex);
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
export function transformHEXRGBaForCSS(token) {
const val = token.$value ?? token.value;
const type = token.$type ?? token.type;
if (val === undefined)
return undefined;
const transformHEXRGBa = (val) => {
const regex = /rgba\(\s*(?<hex>#.+?)\s*,\s*(?<alpha>\d*(\.\d*|%)*)\s*\)/g;
return val.replace(regex, (match, hex, alpha) => {
try {
const [r, g, b] = new Color(hex).srgb;
return `rgba(${r * 255}, ${g * 255}, ${b * 255}, ${alpha})`;
}
catch (e) {
console.warn(`Tried parsing "${hex}" as a hex value, but failed.`);
return match;
}
});
};
const transformProp = (val, prop) => {
if (val[prop] !== undefined) {
val[prop] = transformHEXRGBa(val[prop]);
}
catch (e) {
console.warn(`Tried parsing "${hex}" as a hex value, but failed.`);
return match;
return val;
};
let transformed = val;
switch (type) {
case 'border':
case 'shadow': {
transformed = transformed;
if (Array.isArray(transformed)) {
transformed = transformed.map(item => transformProp(item, 'color'));
}
else {
transformed = transformProp(transformed, 'color');
}
break;
}
});
default:
transformed = transformHEXRGBa(val);
}
return transformed;
}
export { transformHEXRGBaForCSS };

@@ -0,4 +1,5 @@

import { DesignToken } from 'style-dictionary/types';
/**
* Helper: Transforms letter spacing % to em
*/
export declare function transformLetterSpacingForCSS(value: string | number | undefined): string | undefined;
export declare function transformLetterSpacingForCSS(token: DesignToken): DesignToken['value'];
import { percentageToDecimal } from '../utils/percentageToDecimal.js';
/**
* Helper: Transforms letter spacing % to em
*/
function transformLetterSpacingForCSS(value) {
if (value === undefined) {
return value;
export function transformLetterSpacingForCSS(token) {
const val = token.$value ?? token.value;
const type = token.$type ?? token.type;
if (val === undefined)
return undefined;
const transformLetterSpacing = (letterspacing) => {
const decimal = percentageToDecimal(letterspacing);
return typeof decimal === 'string' || isNaN(decimal) ? `${letterspacing}` : `${decimal}em`;
};
if (type === 'typography') {
if (val.letterSpacing !== undefined) {
return {
...val,
letterSpacing: transformLetterSpacing(val.letterSpacing),
};
}
return val;
}
const decimal = percentageToDecimal(value);
return typeof decimal === 'string' || isNaN(decimal) ? `${value}` : `${decimal}em`;
return transformLetterSpacing(val);
}
export { transformLetterSpacingForCSS };

@@ -1,6 +0,10 @@

/**
* Helper: Transforms boxShadow object to shadow shorthand
* This currently works fine if every value uses an alias,
* but if any one of these use a raw value, it will not be transformed.
*/
export declare function transformShadowForCSS(shadow: Record<string, string | undefined> | undefined | string): string | undefined;
import { SingleBoxShadowToken } from '@tokens-studio/types';
export declare function transformShadow(value: SingleBoxShadowToken['value']): string | import("@tokens-studio/types").TokenBoxshadowValue | {
type: string | undefined;
color: string;
x: string | number;
y: string | number;
blur: string | number;
spread: string | number;
blendMode?: string;
}[] | undefined;

@@ -1,24 +0,15 @@

import { checkAndEvaluateMath } from '../checkAndEvaluateMath.js';
import { transformDimension } from '../transformDimension.js';
import { transformHEXRGBaForCSS } from './transformHEXRGBa.js';
import { isNothing } from '../utils/is-nothing.js';
/**
* Helper: Transforms boxShadow object to shadow shorthand
* This currently works fine if every value uses an alias,
* but if any one of these use a raw value, it will not be transformed.
*/
function transformShadowForCSS(shadow) {
if (typeof shadow !== 'object') {
return shadow;
export function transformShadow(value) {
const alignShadowType = (val) => {
return val === 'innerShadow' || val === 'inset' ? 'inset' : undefined;
};
if (Array.isArray(value)) {
return value.map(v => ({
...v,
type: alignShadowType(v.type),
}));
}
let { x, y, blur, spread } = shadow;
const { color, type } = shadow;
x = transformDimension(checkAndEvaluateMath(x));
y = transformDimension(checkAndEvaluateMath(y));
blur = transformDimension(checkAndEvaluateMath(blur));
spread = transformDimension(checkAndEvaluateMath(spread));
return `${type === 'innerShadow' ? 'inset ' : ''}${isNothing(x) ? 0 : x} ${isNothing(y) ? 0 : y} ${isNothing(blur) ? 0 : blur}${isNothing(spread) ? ' ' : ` ${spread} `}${transformHEXRGBaForCSS(color) ?? 'rgba(0, 0, 0, 1)'}`.trim();
if (typeof value === 'object') {
value.type = alignShadowType(value.type);
}
return value;
}
export { transformShadowForCSS };

@@ -1,19 +0,39 @@

export { registerTransforms } from './registerTransforms.js';
export { transforms } from './registerTransforms.js';
export { expandComposites } from './parsers/expand-composites.js';
export { excludeParentKeys } from './parsers/exclude-parent-keys.js';
export { addFontStyles } from './parsers/add-font-styles.js';
export { parseTokens } from './parsers/parse-tokens.js';
export { register, getTransforms } from './register.js';
export { addFontStyles } from './preprocessors/add-font-styles.js';
export { alignTypes } from './preprocessors/align-types.js';
export { excludeParentKeys } from './preprocessors/exclude-parent-keys.js';
export { parseTokens } from './preprocessors/parse-tokens.js';
export { mapDescriptionToComment } from './mapDescriptionToComment.js';
export { checkAndEvaluateMath } from './checkAndEvaluateMath.js';
export { transformDimension } from './transformDimension.js';
export { transformFontWeights } from './transformFontWeights.js';
export { transformFontWeight } from './transformFontWeight.js';
export { transformColorModifiers } from './color-modifiers/transformColorModifiers.js';
export { transformLineHeight } from './transformLineHeight.js';
export { transformShadowForCSS } from './css/transformShadow.js';
export { transformBorderForCSS } from './css/transformBorder.js';
export { transformTypographyForCSS } from './css/transformTypography.js';
export { transformOpacity } from './transformOpacity.js';
export { transformHEXRGBaForCSS } from './css/transformHEXRGBa.js';
export { transformLetterSpacingForCSS } from './css/transformLetterSpacing.js';
export { transformShadow } from './css/transformShadow.js';
export { transformTypographyForCompose } from './compose/transformTypography.js';
export { permutateThemes } from './permutateThemes.js';
/**
* Some of the Tokens Studio typography/shadow props need to be aligned
* when expanding them through StyleDictionary expand
*/
export declare const expandTypesMap: {
typography: {
paragraphSpacing: string;
paragraphIndent: string;
textDecoration: string;
textCase: string;
};
/**
* boxShadow/x/y are not shadow/offsetX/offsetY here because the SD expand on global level
* happens before these types are aligned in sd-transforms preprocessor
*/
boxShadow: {
x: string;
y: string;
blur: string;
spread: string;
};
};

@@ -1,18 +0,39 @@

export { registerTransforms, transforms } from './registerTransforms.js';
export { expandComposites } from './parsers/expand-composites.js';
export { excludeParentKeys } from './parsers/exclude-parent-keys.js';
export { addFontStyles } from './parsers/add-font-styles.js';
export { parseTokens } from './parsers/parse-tokens.js';
export { register, getTransforms } from './register.js';
export { addFontStyles } from './preprocessors/add-font-styles.js';
export { alignTypes } from './preprocessors/align-types.js';
export { excludeParentKeys } from './preprocessors/exclude-parent-keys.js';
export { parseTokens } from './preprocessors/parse-tokens.js';
export { mapDescriptionToComment } from './mapDescriptionToComment.js';
export { checkAndEvaluateMath } from './checkAndEvaluateMath.js';
export { transformDimension } from './transformDimension.js';
export { transformFontWeights } from './transformFontWeights.js';
export { transformFontWeight } from './transformFontWeight.js';
export { transformColorModifiers } from './color-modifiers/transformColorModifiers.js';
export { transformLineHeight } from './transformLineHeight.js';
export { transformShadowForCSS } from './css/transformShadow.js';
export { transformBorderForCSS } from './css/transformBorder.js';
export { transformTypographyForCSS } from './css/transformTypography.js';
export { transformOpacity } from './transformOpacity.js';
export { transformHEXRGBaForCSS } from './css/transformHEXRGBa.js';
export { transformLetterSpacingForCSS } from './css/transformLetterSpacing.js';
export { transformShadow } from './css/transformShadow.js';
export { transformTypographyForCompose } from './compose/transformTypography.js';
export { permutateThemes } from './permutateThemes.js';
/**
* Some of the Tokens Studio typography/shadow props need to be aligned
* when expanding them through StyleDictionary expand
*/
export const expandTypesMap = {
typography: {
paragraphSpacing: 'dimension',
paragraphIndent: 'dimension',
textDecoration: 'other',
textCase: 'other',
},
/**
* boxShadow/x/y are not shadow/offsetX/offsetY here because the SD expand on global level
* happens before these types are aligned in sd-transforms preprocessor
*/
boxShadow: {
x: 'dimension',
y: 'dimension',
blur: 'dimension',
spread: 'dimension',
},
};

@@ -5,9 +5,8 @@ /**

*/
function mapDescriptionToComment(token) {
export function mapDescriptionToComment(token) {
// intentional mutation of the original object
const _t = token;
_t.comment = _t.description.replace(/\r?\n|\r/g, ' '); // Strip out newline and carriage returns, replacing them with space
// Replace carriage returns with just newlines
_t.comment = _t.description.replace(/\r?\n|\r/g, '\n');
return _t;
}
export { mapDescriptionToComment };
function mapThemesToSetsObject(themes) {
return Object.fromEntries(themes.map(theme => [theme.name, filterTokenSets(theme.selectedTokenSets)]));
}
function permutateThemes(themes, { separator = '-' } = {}) {
export function permutateThemes(themes, { separator = '-' } = {}) {
if (themes.some(theme => theme.group)) {

@@ -55,3 +55,1 @@ // Sort themes by groups

}
export { permutateThemes };

@@ -0,4 +1,5 @@

import { DesignToken } from 'style-dictionary/types';
/**
* Helper: Transforms dimensions to px
*/
export declare function transformDimension(value: string | undefined | number): string | undefined;
export declare function transformDimension(token: DesignToken): DesignToken['value'];

@@ -1,17 +0,57 @@

/**
* Helper: Transforms dimensions to px
*/
function transformDimension(value) {
if (value === undefined) {
return value;
}
function _transformDimension(dim) {
// Check if the value is numeric with isNaN, this supports string values as well
// Check if value is not empty string, since this is also not considered "NaN"
// Check if the value, when parsed (since it can also be number), does not equal 0
if (!isNaN(value) && value !== '' && parseFloat(value) !== 0) {
return `${value}px`;
if (!isNaN(dim) && dim !== '' && parseFloat(dim) !== 0) {
return `${dim}px`;
}
return `${value}`;
return `${dim}`;
}
export { transformDimension };
/**
* Helper: Transforms dimensions to px
*/
export function transformDimension(token) {
const val = (token.$value ?? token.value);
const type = token.$type ?? token.type;
if (val === undefined)
return undefined;
const transformProp = (val, prop) => {
if (val[prop] !== undefined) {
val[prop] = _transformDimension(val[prop]);
}
return val;
};
let transformed = val;
switch (type) {
case 'typography': {
transformed = transformed;
transformed = transformProp(transformed, 'fontSize');
break;
}
case 'shadow': {
transformed = transformed;
const transformShadow = (shadowVal) => {
['offsetX', 'offsetY', 'blur', 'spread'].forEach(prop => {
shadowVal = transformProp(shadowVal, prop);
});
return shadowVal;
};
if (Array.isArray(transformed)) {
transformed = transformed.map(transformShadow);
}
else {
transformed = transformShadow(transformed);
}
break;
}
case 'border': {
transformed = transformed;
transformed = transformProp(transformed, 'width');
break;
}
default:
transformed = transformed;
transformed = _transformDimension(transformed);
}
return transformed;
}

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

import { DesignToken } from 'style-dictionary/types';
/**

@@ -6,2 +7,2 @@ * Helper: Transforms line-height % to unit-less decimal value

*/
export declare function transformLineHeight(value: string | number | undefined): string | number | undefined;
export declare function transformLineHeight(token: DesignToken): DesignToken['value'];
import { percentageToDecimal } from './utils/percentageToDecimal.js';
/**

@@ -8,10 +7,21 @@ * Helper: Transforms line-height % to unit-less decimal value

*/
function transformLineHeight(value) {
if (value === undefined) {
return value;
export function transformLineHeight(token) {
const val = token.$value ?? token.value;
const type = token.$type ?? token.type;
if (val === undefined)
return undefined;
const transformLH = (lineHeight) => {
const decimal = percentageToDecimal(lineHeight);
return typeof decimal === 'string' || isNaN(decimal) ? `${lineHeight}` : decimal;
};
if (type === 'typography') {
if (val.lineHeight !== undefined) {
return {
...val,
lineHeight: transformLH(val.lineHeight),
};
}
return val;
}
const decimal = percentageToDecimal(value);
return typeof decimal === 'string' || isNaN(decimal) ? `${value}` : decimal;
return transformLH(val);
}
export { transformLineHeight };
import { percentageToDecimal } from './utils/percentageToDecimal.js';
/**

@@ -8,3 +7,3 @@ * Helper: Transforms opacity % to a decimal point number

*/
function transformOpacity(value) {
export function transformOpacity(value) {
if (value === undefined) {

@@ -16,3 +15,1 @@ return value;

}
export { transformOpacity };

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

import { SingleBorderToken, SingleBoxShadowToken, SingleCompositionToken, SingleToken, SingleTypographyToken } from '@tokens-studio/types';
export type Expandables = SingleCompositionToken | SingleTypographyToken | SingleBorderToken | SingleBoxShadowToken;
export declare const expandablesAsStringsArr: string[];
export type ExpandablesAsStrings = (typeof expandablesAsStringsArr)[number];
export type ExpandFilter<T extends SingleToken> = (token: T, filePath?: string) => boolean;
export interface ExpandOptions {
typography?: boolean | ExpandFilter<SingleTypographyToken>;
border?: boolean | ExpandFilter<SingleBorderToken>;
shadow?: boolean | ExpandFilter<SingleBoxShadowToken>;
composition?: boolean | ExpandFilter<SingleCompositionToken>;
}
export type ColorModifierFormat = 'hex' | 'hsl' | 'lch' | 'p3' | 'srgb';

@@ -17,6 +6,8 @@ export interface ColorModifierOptions {

export interface TransformOptions {
platform?: 'css' | 'compose';
name?: string;
withSDBuiltins?: boolean;
alwaysAddFontStyle?: boolean;
expand?: ExpandOptions | false;
excludeParentKeys?: boolean;
['ts/color/modifiers']?: ColorModifierOptions;
}

@@ -1,3 +0,1 @@

const expandablesAsStringsArr = ['composition', 'typography', 'border', 'boxShadow'];
export { expandablesAsStringsArr };
export {};

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

function isNothing(value) {
export function isNothing(value) {
if (value == null || value === '') {

@@ -7,3 +7,1 @@ return true;

}
export { isNothing };

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

function percentageToDecimal(value) {
export function percentageToDecimal(value) {
if (!`${value}`.endsWith('%')) {

@@ -9,3 +9,1 @@ return `${value}`;

}
export { percentageToDecimal };
{
"name": "@tokens-studio/sd-transforms",
"version": "0.16.1",
"version": "1.0.0",
"description": "Custom transforms for Style-Dictionary, to work with Design Tokens that are exported from Tokens Studio",

@@ -13,9 +13,4 @@ "license": "MIT",

"exports": {
".": {
"require": "./dist/src/index.cjs",
"import": "./dist/src/index.js"
}
".": "./dist/index.js"
},
"main": "./dist/src/index.cjs",
"module": "./dist/src/index.js",
"files": [

@@ -25,3 +20,3 @@ "dist"

"scripts": {
"build": "rimraf dist && rollup -c rollup.config.mjs && node ./handle-bundled-cjs-deps.js",
"build": "tsc",
"format": "npm run format:eslint && npm run format:prettier",

@@ -42,20 +37,17 @@ "format:eslint": "eslint --ext .ts,.html . --fix",

},
"types": "./dist/src/index.d.ts",
"dependencies": {
"@bundled-es-modules/deepmerge": "^4.3.1",
"@bundled-es-modules/postcss-calc-ast-parser": "^0.1.6",
"@tokens-studio/types": "^0.4.0",
"color2k": "^2.0.1",
"colorjs.io": "^0.4.3",
"deepmerge": "^4.3.1",
"expr-eval-fork": "^2.0.2",
"is-mergeable-object": "^1.1.1",
"postcss-calc-ast-parser": "^0.1.4"
"is-mergeable-object": "^1.1.1"
},
"peerDependencies": {
"style-dictionary": "^4.0.0-prerelease.27"
"style-dictionary": "^4.0.0"
},
"devDependencies": {
"@changesets/cli": "^2.26.0",
"@esm-bundle/chai": "^4.3.4-fix.0",
"@rollup/plugin-commonjs": "^24.1.0",
"@rollup/plugin-typescript": "^11.0.0",
"@changesets/cli": "^2.27.6",
"@types/chai": "^4.3.16",
"@types/mocha": "^10.0.7",
"@types/tinycolor2": "^1.4.6",

@@ -65,10 +57,8 @@ "@typescript-eslint/eslint-plugin": "^5.54.0",

"@web/dev-server-esbuild": "^0.3.3",
"@web/dev-server-rollup": "^0.5.0",
"@web/test-runner": "^0.15.1",
"@web/test-runner-playwright": "^0.9.0",
"@web/test-runner": "^0.18.2",
"@web/test-runner-playwright": "^0.11.0",
"chai": "^5.1.1",
"eslint": "^8.32.0",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-mocha": "^10.2.0",
"glob": "^10.2.6",
"eslint-plugin-mocha": "^10.4.3",
"hanbi": "^1.0.3",

@@ -78,10 +68,8 @@ "husky": "^8.0.0",

"npm-run-all": "^4.1.5",
"prettier": "^2.8.3",
"prettier": "^3.3.2",
"prettier-package-json": "^2.8.0",
"rimraf": "^4.1.3",
"rollup": "^3.18.0",
"tinycolor2": "^1.6.0",
"ts-mocha": "^10.0.0",
"ts-node": "^10.9.1",
"typescript": "^5.3.3"
"typescript": "^5.5.2"
},

@@ -94,3 +82,3 @@ "keywords": [

"engines": {
"node": ">=17.0.0"
"node": ">=18.0.0"
},

@@ -97,0 +85,0 @@ "prettier": {

@@ -5,2 +5,4 @@ # Style Dictionary Transforms for Tokens Studio

> Note: this README contains examples that assume latest version of this package & v4 style-dictionary latest prerelease.
## Table of contents

@@ -20,4 +22,2 @@

> This library is currently in beta.
This package contains custom transforms for [Style-Dictionary](https://amzn.github.io/style-dictionary/#/),

@@ -28,4 +28,3 @@ to work with Design Tokens that are exported from [Tokens Studio](https://tokens.studio/):

- Expands composition tokens into multiple, optionally also does so for typography, border and shadow tokens -> parser
- Optionally excludes parent keys from your tokens file, e.g. when using single-file export from Tokens Studio Figma plugin -> parser
- Optionally excludes parent keys from your tokens file, e.g. when using single-file export from Tokens Studio Figma plugin -> preprocessor
- Maps token descriptions to comments -> `ts/descriptionToComment`

@@ -43,6 +42,3 @@ - Check and evaluate Math expressions (transitive) -> `ts/resolveMath`

- Transform colors to `rgba()` format -> `ts/color/css/hexrgba`
- Transform font family into valid CSS, adding single quotes if necessary -> `ts/typography/css/fontFamily`
- Transform typography objects to CSS shorthand -> `ts/typography/css/shorthand`
- Transform Tokens Studio shadow objects to CSS shadow shorthand -> `ts/shadow/css/shorthand`
- Transform border objects to CSS border shorthand -> `ts/border/css/shorthand`
- Transform shadow "type" property "innerShadow" to "inset" -> `ts/shadow/innerShadow`

@@ -75,14 +71,13 @@ Android:

| >= **4.0.0**-prerelease.**27** | >= **0.16.0** |
| >= **4.0.0** | >= **1.0.0** |
This may seem a little tedious, but the reason is because `sd-transforms` is still in alpha, and Style Dictionary v4 is still being worked on, iteratively doing lots of breaking changes.
Now that Style Dictionary v4 is released, and `sd-transforms` v1 is released and out of alpha state,both APIs are stable and the recommendation is to use these.
This will be much simpler when Style Dictionary v4 is released, at that point `sd-transforms` v1 will be released and be out of alpha.
Both APIs will be stable then.
## Usage
> Note: this library is available both in CJS and ESM
> [!NOTE]
> This library is only available in ESM
```js
import { registerTransforms } from '@tokens-studio/sd-transforms';
import { register } from '@tokens-studio/sd-transforms';
import StyleDictionary from 'style-dictionary';

@@ -92,6 +87,8 @@

// that is installed as a dependency of this package.
registerTransforms(StyleDictionary);
register(StyleDictionary);
const sd = StyleDictionary.extend({
source: ['**/*.json'], // <-- make sure to have this match your token files!!!
const sd = new StyleDictionary({
// make sure to have source match your token files!
// be careful about accidentally matching your package.json or similar files that are not tokens
source: ['tokens/**/*.json'],
preprocessors: ['tokens-studio'], // <-- since 0.16.0 this must be explicit

@@ -113,8 +110,6 @@ platforms: {

sd.cleanAllPlatforms();
sd.buildAllPlatforms();
await sd.cleanAllPlatforms();
await sd.buildAllPlatforms();
```
> You can also import as ES Modules if needed.
To run it use the following command

@@ -126,12 +121,9 @@

> From Style-Dictionary `4.0.0-prerelease.18`, [`transformGroup` and `transforms` can now be combined in a platform inside your config](https://github.com/amzn/style-dictionary/blob/v4/CHANGELOG.md#400-prerelease18).
### Using the preprocessor
If you want to use `excludeParentKeys`, `expand` or allow this package to extract the `fontStyle` from the `fontWeight` e.g. `regular italic`,
you must add the `'tokens-studio'` preprocessor explicitly in the configuration:
You must add the `'tokens-studio'` preprocessor explicitly in the configuration:
```json
{
"source": ["**/*.tokens.json"],
"source": ["tokens/**/*.json"],
"preprocessors": ["tokens-studio"],

@@ -142,2 +134,36 @@ "platforms": {}

This allows `fontStyles` to be extracted when they are embedded in `fontWeights`, aligns Tokens Studio token types with DTCG token types, and allows excluding parent keys for single-file Tokens Studio exports.
### Using "Expand"
> Expand used to be an sd-transforms exclusive feature but has moved to Style Dictionary itself under a slightly different API.
> This made sense due to object-value tokens being part of the DTCG spec and no longer a Tokens Studio specific feature.
When using the [expand feature of Style Dictionary](https://v4.styledictionary.com/reference/config/#expand) to expand object-value (composite) tokens,
you should pass our additional `expandTypesMap` for Tokens Studio tokens, because these are slightly different from the DTCG tokens:
- shadow tokens are called `boxShadow` and their `offsetX` and `offsetY` props are called `x` and `y` respectively.
- typography tokens have some additional properties in Tokens Studio:
- `paragraphSpacing` -> dimension
- `paragraphIndent` -> dimension
- `textDecoration` -> other
- `textCase` -> other
Due to the Style Dictionary object-value tokens expansion happening before custom preprocessors such as the sd-transforms preprocessor,
which aligns Tokens Studio token types with DTCG token types, this has to be configured like so:
```js
import StyleDictionary from 'style-dictionary';
import { expandTypesMap } from '@tokens-studio/sd-transforms';
const sd = new StyleDictionary({
source: ['tokens/**/*.json'],
preprocessors: ['tokens-studio'],
expand: {
typesMap: expandTypesMap,
},
platforms: {},
});
```
### Using the transforms

@@ -147,3 +173,3 @@

{
"source": ["**/*.tokens.json"],
"source": ["tokens/**/*.json"],
"preprocessors": ["tokens-studio"],

@@ -162,9 +188,9 @@ "platforms": {

},
"css": {
"scss": {
"transforms": ["ts/size/px", "ts/opacity", "name/kebab"],
"buildPath": "build/css/",
"buildPath": "build/scss/",
"files": [
{
"destination": "variables.css",
"format": "css/variables"
"destination": "variables.scss",
"format": "scss/variables"
}

@@ -195,2 +221,3 @@ ]

> [!NOTE]
> From Style-Dictionary `4.0.0-prerelease.18`, [`transformGroup` and `transforms` can now be combined in a platform inside your config](https://github.com/amzn/style-dictionary/blob/v4/CHANGELOG.md#400-prerelease18).

@@ -202,3 +229,3 @@

```js
import { transforms } from '@tokens-studio/sd-transforms';
import { getTransforms } from '@tokens-studio/sd-transforms';
import StyleDictionary from 'style-dictionary';

@@ -211,22 +238,20 @@

name: 'custom/tokens-studio',
transforms: [...transforms, 'name/constant'].filter(transform => transform !== 'ts/size/px'),
// default value for platform is css, specifies which Tokens Studio transforms for which platform to grab
transforms: [...getTransforms({ platform: 'css' }), 'name/constant'].filter(
transform => transform !== 'ts/size/px',
),
});
```
> Note that you can also manually grab some of the SD built-in transforms by using `StyleDictionary.hooks.transformGroups` or `StyleDictionary.hooks.transforms`
### Options
You can pass options to the `registerTransforms` function.
You can pass options to the `register` function.
```js
registerTransforms(StyleDictionary, {
expand: {
composition: false,
typography: true,
// Note: when using Style-Dictionary v4.0.0-prerelease.2 or higher, filePath no longer gets passed
// as an argument, because preprocessors work on the full dictionary rather than per file (parsers)
border: (token, filePath) =>
token.value.width !== 0 && filePath.startsWith(path.resolve('tokens/core')),
shadow: false,
},
register(StyleDictionary, {
excludeParentKeys: true,
platform: 'css',
name: 'tokens-studio',
'ts/color/modifiers': {

@@ -240,16 +265,15 @@ format: 'hex',

| name | type | required | default | description |
| ----------------------------- | ------------------------ | -------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| excludeParentKeys | boolean | ❌ | `false` | Whether or not to exclude parent keys from your token files |
| alwaysAddFontStyle | boolean | ❌ | `false` | Whether or not to always add a 'normal' fontStyle property to typography tokens which do not have explicit fontStyle |
| expand | boolean \| ExpandOptions | ❌ | See props below | `false` to not register the parser at all. By default, expands composition tokens. Optionally, border, shadow and typography as well. |
| expand.composition | boolean \| ExpandFilter | ❌ | `true` | Whether or not to expand compositions. Also allows a filter callback function to conditionally expand per token/filePath |
| expand.typography | boolean \| ExpandFilter | ❌ | `false` | Whether or not to expand typography. Also allows a filter callback function to conditionally expand per token/filePath |
| expand.shadow | boolean \| ExpandFilter | ❌ | `false` | Whether or not to expand shadows. Also allows a filter callback function to conditionally expand per token/filePath |
| expand.border | boolean \| ExpandFilter | ❌ | `false` | Whether or not to expand borders. Also allows a filter callback function to conditionally expand per token/filePath |
| ['ts/color/modifiers'] | ColorModifierOptions | ❌ | See props below | Color modifier options |
| ['ts/color/modifiers'].format | ColorModifierFormat | ❌ | `undefined` | Color modifier output format override ('hex' \| 'hsl' \| 'lch' \| 'p3' \| 'srgb'), uses local format or modifier space as default |
| name | type | required | default | description |
| ----------------------------- | -------------------- | -------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| excludeParentKeys | boolean | ❌ | `false` | Whether or not to exclude parent keys from your token files |
| platform | `'css'\|'compose'` | ❌ | `css` | Which platform to use the transforms for. |
| name | string | ❌ | `tokens-studio` | Under which name to register the `transformGroup` |
| withSDBuiltins | boolean | ❌ | `true` | Whether to append the Style Dictionary built-in `transformGroup` transforms for the configured platform into the `tokens-studio` transformGroup. |
| alwaysAddFontStyle | boolean | ❌ | `false` | Whether or not to always add a 'normal' fontStyle property to typography tokens which do not have explicit fontStyle |
| ['ts/color/modifiers'] | ColorModifierOptions | ❌ | See props below | Color modifier options |
| ['ts/color/modifiers'].format | ColorModifierFormat | ❌ | `undefined` | Color modifier output format override ('hex' \| 'hsl' \| 'lch' \| 'p3' \| 'srgb'), uses local format or modifier space as default |
> Note: you can also import and use the `parseTokens` function to run the parsing steps on your tokens object manually.
> Handy if you have your own parsers set up (e.g. for JS files), and you want the parser-based features like composites-expansion to work there too.
> [!NOTE]
> You can also import and use the `parseTokens` function to run the parsing steps on your tokens object manually.
> Handy if you have your own preprocessors set up (e.g. for JS files), and you want the preprocessor-based features like composites-expansion to work there too.

@@ -267,7 +291,7 @@ ## Theming

```cjs
import { registerTransforms } from '@tokens-studio/sd-transforms';
import { register } from '@tokens-studio/sd-transforms';
import StyleDictionary from 'style-dictionary';
import { promises } from 'fs';
registerTransforms(StyleDictionary, {
register(StyleDictionary, {
/* options here if needed */

@@ -297,7 +321,8 @@ });

configs.forEach(cfg => {
const sd = StyleDictionary.extend(cfg);
sd.cleanAllPlatforms(); // optionally, cleanup files first..
sd.buildAllPlatforms();
});
async function cleanAndBuild(cfg) {
const sd = new StyleDictionary(cfg);
await sd.cleanAllPlatforms(); // optionally, cleanup files first..
await sd.buildAllPlatforms();
}
await Promise.all(configs.map(cleanAndBuild));
}

@@ -402,7 +427,7 @@

```js
import { registerTransforms, permutateThemes } from '@tokens-studio/sd-transforms';
import { register, permutateThemes } from '@tokens-studio/sd-transforms';
import StyleDictionary from 'style-dictionary';
import { promises } from 'fs';
registerTransforms(StyleDictionary, {
register(StyleDictionary, {
/* options here if needed */

@@ -431,7 +456,8 @@ });

configs.forEach(cfg => {
const sd = StyleDictionary.extend(cfg);
sd.cleanAllPlatforms(); // optionally, cleanup files first..
sd.buildAllPlatforms();
});
async function cleanAndBuild(cfg) {
const sd = new StyleDictionary(cfg);
await sd.cleanAllPlatforms(); // optionally, cleanup files first..
await sd.buildAllPlatforms();
}
await Promise.all(configs.map(cleanAndBuild));
}

@@ -450,2 +476,4 @@

Also converts carriage return `\r` into `\n` and `\r\n` into just `\n`.
**matches**: All tokens that have a description property.

@@ -742,7 +770,7 @@

### ts/typography/css/fontFamily
### ts/shadow/innerShadow
This transforms font-family token values into valid CSS, adding single quotes if necessary.
This transforms shadow tokens to ensure that the `type` property gets converted from `innerShadow` to `inset` (CSS compatible).
**matches**: `token.type` is `'fontFamilies'`
**matches**: `token.type` is `'shadow'`

@@ -754,36 +782,9 @@ #### before

"token": {
"type": "fontFamilies",
"value": "Arial Black, Times New Roman, Foo, sans-serif"
}
}
```
#### after
```json
{
"token": {
"type": "fontFamilies",
"value": "'Arial Black', 'Times New Roman', Foo, sans-serif"
}
}
```
### ts/typography/css/shorthand
This transforms typography tokens into a valid CSS shorthand
**matches**: `token.type` is `'typography'`
#### before
```json
{
"token": {
"type": "typography",
"type": "color",
"value": {
"fontWeight": "500",
"fontSize": "20px",
"lineHeight": "1.5",
"fontFamily": "Arial"
"offsetX": "0",
"offsetY": "4px",
"blur": "10px",
"color": "#000",
"type": "innerShadow"
}

@@ -799,25 +800,9 @@ }

"token": {
"value": "500 20px/1.5 Arial"
}
}
```
### ts/shadow/css/shorthand
This transforms shadow tokens into a valid CSS shadow shorthand
**matches**: `token.type` is `'boxShadow'`
#### before
```json
{
"token": {
"type": "boxShadow",
"type": "color",
"value": {
"x": "5px",
"y": "3px",
"blur": "6px",
"spread": "2px",
"color": "#000000"
"offsetX": "0",
"offsetY": "4px",
"blur": "10px",
"color": "#000",
"type": "inset"
}

@@ -828,43 +813,2 @@ }

#### after
```json
{
"token": {
"value": "5px 3px 6px 2px #000000"
}
}
```
### ts/border/css/shorthand
This transforms border tokens into a valid CSS border shorthand
**matches**: `token.type` is `'border'`
#### before
```json
{
"token": {
"type": "border",
"value": {
"width": "5",
"style": "dashed",
"color": "rgba(#000000, 1)"
}
}
}
```
#### after
```json
{
"token": {
"value": "5px dashed rgba(0, 0, 0, 1)"
}
}
```
## Not sure how to fix your issue?

@@ -871,0 +815,0 @@

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