UNPKG

5.31 kBJavaScriptView Raw
1import * as Colors from '../src/props.colors.js'
2
3// Mapping of CSS variable names to dictionary keys
4const dictionaryMap = {
5 "size-relative": "relative",
6 "size-fluid": "fluid",
7 "size-header": "header",
8 "size-content": "content",
9 "border-size": "size",
10 "radius-conditional": "conditional",
11 "radius-blob": "blob"
12}
13
14// Map a value to a dictionary key using the dictionaryMap
15const mapToDictionaryKey = (value) => dictionaryMap[value] || value
16
17// Determine the type key based on the metaType
18const getTypeKey = (metaType) => {
19 if (metaType === "size" || metaType === "border-radius") {
20 return metaType === "size" ? "size" : "radius"
21 } else if (metaType === "border-width") {
22 return "border"
23 }
24 return metaType
25}
26
27// Count the occurrences of a character in a string
28const countOccurrences = (str, letter) => (str.match(new RegExp(letter, 'g')) || []).length
29
30// Regular expression to match CSS variable usages
31const cssVarUsageRegex = /var\(--([a-zA-Z0-9-]+)\)/g
32
33// Replace the last occurrence of a pattern with a replacement
34/* https://www.30secondsofcode.org/js/s/replace-last-occurrence/ */
35const replaceLast = (str, pattern, replacement) => {
36 const match =
37 typeof pattern === 'string'
38 ? pattern
39 : (str.match(new RegExp(pattern.source, 'g')) || []).slice(-1)[0]
40 if (!match) return str
41 const last = str.lastIndexOf(match)
42 return last !== -1
43 ? `${str.slice(0, last)}${replacement}${str.slice(last + match.length)}`
44 : str
45}
46
47// Helper function to convert CSS variable name to token reference
48const tokenizeCSSVar = (variableName, metaType) => {
49 const tokenName = replaceLast(variableName, '-', '.')
50 const hyphenCount = countOccurrences(variableName, '-')
51
52 if (hyphenCount > 2 && metaType === "other") {
53 const [firstPart, ...restParts] = tokenName.split('-')
54 return `{${metaType}.${firstPart}.${restParts.join('-')}.value}`
55 }
56
57 return `{${tokenName}.value}`
58};
59
60// Convert CSS variable usages to token references
61const cssVarToToken = (input, metaType) => {
62 if (!input.toString().includes("var")) {
63 return input
64 }
65
66 return input.replace(cssVarUsageRegex, (match, variableName) => {
67 return tokenizeCSSVar(variableName, metaType)
68 })
69};
70
71// Create a token object based on metaType and dictionary key
72const createTokenObject = ({
73 baseObj,
74 mainKey,
75 metaType,
76 dictionarykey,
77 index,
78 token
79}) => {
80 const typeKey = getTypeKey(metaType)
81 const targetObj = baseObj[typeKey] = baseObj[typeKey] || {}
82
83 if (typeKey === "size" || typeKey === "radius") {
84 const shouldReplace = mainKey !== dictionarykey
85 handleKey(targetObj, dictionarykey, index, token, metaType, shouldReplace)
86 } else if (typeKey !== "other") {
87 handleKey(targetObj, dictionarykey, index, token, metaType, true)
88 } else {
89 handleOtherTypes(targetObj, dictionarykey, index, token, metaType)
90 }
91
92 return baseObj
93}
94
95// Handle cases where meta.type != "other"
96function handleKey(targetObj, dictionarykey, index, token, metaType, shouldReplace) {
97 if (shouldReplace) {
98 targetObj[dictionarykey] = targetObj[dictionarykey] || {}
99 targetObj[dictionarykey][index] = { value: token, type: metaType }
100 } else {
101 targetObj[index] = { value: token, type: metaType }
102 }
103}
104
105// Handle cases where meta.type = "other"
106function handleOtherTypes(targetObj, dictionarykey, index, token, metaType) {
107 const keyParts = dictionarykey.split("-")
108
109 if (keyParts.length > 1) {
110 const groupName = keyParts[0]
111 targetObj[groupName] = targetObj[groupName] || {}
112 targetObj[groupName][index] = { value: token, type: metaType }
113
114 const rest = keyParts.slice(1)
115 const subKey = rest.join("-")
116
117 targetObj[groupName][subKey] = targetObj[groupName][subKey] || {}
118 targetObj[groupName][subKey][index] = { value: token, type: metaType }
119 }
120}
121
122// Generate a style dictionary
123export const toStyleDictionary = props => {
124 const colors = Object.keys(Colors)
125 .filter(exportName => exportName !== "default")
126 .map(hueName => hueName.toLowerCase())
127
128 return props.reduce((styledictionarytokens, [key, token]) => {
129 const meta = {}
130
131 const isLength = key.includes('size') && !key.includes('border-size')
132 const isBorder = key.includes('border-size')
133 const isRadius = key.includes('radius')
134 const isShadow = key.includes('shadow')
135 const isColor = colors.some(color => key.includes(color))
136
137 if (isLength) meta.type = 'size'
138 else if (isBorder) meta.type = 'border-width'
139 else if (isRadius) meta.type = 'border-radius'
140 else if (isShadow) meta.type = 'box-shadow'
141 else if (isColor) meta.type = 'color'
142 else meta.type = 'other'
143
144 const keyWithoutPrefix = key.replace('--', '')
145 const keyParts = keyWithoutPrefix.split('-')
146 const mainKey = keyParts.length > 1 ? keyParts.slice(0, -1).join('-') : keyParts[0]
147 const index = keyParts.length > 1 ? keyParts[keyParts.length - 1] : 0
148
149 const dictionarykey = mapToDictionaryKey(mainKey)
150
151 return createTokenObject({
152 baseObj: styledictionarytokens,
153 mainKey,
154 metaType: meta.type,
155 dictionarykey,
156 index,
157 token: cssVarToToken(token, meta.type)
158 })
159 }, {})
160}