Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | 21x 7x 14x 14x 7x 7x 21x 21x 21x 21x 21x 21x 7x 7x 7x 7x 1x 7x 1x 7x 2x 7x 7x 1x 10x 10x 9x 9x 2x 7x 7x 7x 10x 1x 3x 3x 3x 1x | import type { Dictionary, FormatFn, TransformedToken } from 'style-dictionary/types';
import type { StyleDictionaryHost } from '../style-dictionary-host.js';
import { flatTokenMetadataFields } from '../utils/flat-token-fields.js';
import { buildVariantExtensions } from '../utils/variant-extensions.js';
/**
* Custom format for Design Token raw format
* Outputs to {theme}.tokens.raw.json
*/
/**
* Split camelCase string into separate words
* @param {string} str - camelCase string (e.g., "electricBlue")
* @returns {string[]} - Array of words (e.g., ["electric", "blue"])
*/
function splitCamelCase(str) {
// Handle empty or single character strings
if (!str || str.length <= 1) {
return [str];
}
// Split on capital letters, but keep the capital letters with their following lowercase letters
// Example: "electricBlue" -> ["electric", "Blue"] -> ["electric", "blue"]
const parts = str.split(/(?=[A-Z])/);
return parts.map((s) => s.charAt(0).toLowerCase() + s.slice(1));
}
/**
* Navigate to a nested path in the tokens object, creating intermediate objects as needed
* Splits camelCase path segments into separate nested levels
* @param {object} tokens - Root tokens object
* @param {string[]} path - Token path array
* @returns {object} - The parent object where the token should be placed
*/
function navigateToPath(tokens, path) {
let current = tokens;
for (let i = 0; i < path.length - 1; i++) {
const segment = path[i];
// Split camelCase segments into separate levels
// e.g., "electricBlue" becomes ["electric", "blue"]
const segments = splitCamelCase(segment);
for (const subSegment of segments) {
Eif (!current[subSegment]) {
current[subSegment] = {};
}
current = current[subSegment];
}
}
return current;
}
/**
* Build a DTCG-compliant token object
* @param {object} token - Style Dictionary token
* @param {*} tokenValue - Pre-computed token value (original or computed)
* @returns {object} - Token object with $type, $value, and optional metadata
*/
function buildTokenObject(token: TransformedToken, tokenValue) {
const tokenObject: Record<string, unknown> = {
$type: token.$type,
$value: tokenValue,
};
// Variant-aware tokens carry resolved values under a dedicated $extensions
// namespace: color scheme (light/dark) under com.salesforce-ux.mode, density
// (comfy/compact) under com.salesforce-ux.density. $value is left as the
// reference.
const variantExtensions = buildVariantExtensions(token);
if (variantExtensions) {
tokenObject.$extensions = variantExtensions;
}
// Add deprecated flag
if (token.$deprecated || token.original?.$deprecated) {
tokenObject.$deprecated = true;
}
// Add description
if (token.$description) {
tokenObject.$description = token.$description;
}
Object.assign(tokenObject, flatTokenMetadataFields(token));
return tokenObject;
}
// ============================================================================
// MAIN FORMATTER
// ============================================================================
const jsonRawFormatter = (dictionary: Dictionary) => {
const tokens = {};
dictionary.allTokens.forEach((token) => {
const tokenValue = token.original?.$value || token.value;
// Skip tokens without values
if (tokenValue === undefined || tokenValue === null) {
return;
}
const current = navigateToPath(tokens, token.path);
const tokenName = token.path[token.path.length - 1];
current[tokenName] = buildTokenObject(token, tokenValue);
});
return JSON.stringify(tokens, null, 2);
};
/**
* Style Dictionary registration for the JSON raw format
*
* @param {StyleDictionary} StyleDictionary
*/
export const jsonRawFormat = (StyleDictionary: StyleDictionaryHost) => {
const formatFn = (({ dictionary }) => jsonRawFormatter(dictionary)) satisfies FormatFn;
// Avoid nested collision warnings - this formatter creates a nested structure
const format = Object.assign(formatFn, { nested: true });
StyleDictionary.registerFormat({
name: 'json/raw',
format,
});
};
// Export internal functions for testing
export const _testExports = {
jsonRawFormatter,
};
|