forked from argyleink/open-props
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathto-style-dictionary.js
161 lines (135 loc) · 5.22 KB
/
to-style-dictionary.js
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import * as Colors from '../src/props.colors.js'
// Mapping of CSS variable names to dictionary keys
const dictionaryMap = {
"size-px": "px",
"size-relative": "relative",
"size-fluid": "fluid",
"size-header": "header",
"size-content": "content",
"border-size": "size",
"radius-conditional": "conditional",
"radius-blob": "blob"
}
// Map a value to a dictionary key using the dictionaryMap
const mapToDictionaryKey = (value) => dictionaryMap[value] || value
// Determine the type key based on the metaType
const getTypeKey = (metaType) => {
if (metaType === "size" || metaType === "border-radius") {
return metaType === "size" ? "size" : "radius"
} else if (metaType === "border-width") {
return "border"
}
return metaType
}
// Count the occurrences of a character in a string
const countOccurrences = (str, letter) => (str.match(new RegExp(letter, 'g')) || []).length
// Regular expression to match CSS variable usages
const cssVarUsageRegex = /var\(--([a-zA-Z0-9-]+)\)/g
// Replace the last occurrence of a pattern with a replacement
/* https://www.30secondsofcode.org/js/s/replace-last-occurrence/ */
const replaceLast = (str, pattern, replacement) => {
const match =
typeof pattern === 'string'
? pattern
: (str.match(new RegExp(pattern.source, 'g')) || []).slice(-1)[0]
if (!match) return str
const last = str.lastIndexOf(match)
return last !== -1
? `${str.slice(0, last)}${replacement}${str.slice(last + match.length)}`
: str
}
// Helper function to convert CSS variable name to token reference
const tokenizeCSSVar = (variableName, metaType) => {
const tokenName = replaceLast(variableName, '-', '.')
const hyphenCount = countOccurrences(variableName, '-')
if (hyphenCount > 2 && metaType === "other") {
const [firstPart, ...restParts] = tokenName.split('-')
return `{${metaType}.${firstPart}.${restParts.join('-')}.value}`
}
return `{${tokenName}.value}`
};
// Convert CSS variable usages to token references
const cssVarToToken = (input, metaType) => {
if (!input.toString().includes("var")) {
return input
}
return input.replace(cssVarUsageRegex, (match, variableName) => {
return tokenizeCSSVar(variableName, metaType)
})
};
// Create a token object based on metaType and dictionary key
const createTokenObject = ({
baseObj,
mainKey,
metaType,
dictionarykey,
index,
token
}) => {
const typeKey = getTypeKey(metaType)
const targetObj = baseObj[typeKey] = baseObj[typeKey] || {}
if (typeKey === "size" || typeKey === "radius") {
const shouldReplace = mainKey !== dictionarykey
handleKey(targetObj, dictionarykey, index, token, metaType, shouldReplace)
} else if (typeKey !== "other") {
handleKey(targetObj, dictionarykey, index, token, metaType, true)
} else {
handleOtherTypes(targetObj, dictionarykey, index, token, metaType)
}
return baseObj
}
// Handle cases where meta.type != "other"
function handleKey(targetObj, dictionarykey, index, token, metaType, shouldReplace) {
if (shouldReplace) {
targetObj[dictionarykey] = targetObj[dictionarykey] || {}
targetObj[dictionarykey][index] = { value: token, type: metaType }
} else {
targetObj[index] = { value: token, type: metaType }
}
}
// Handle cases where meta.type = "other"
function handleOtherTypes(targetObj, dictionarykey, index, token, metaType) {
const keyParts = dictionarykey.split("-")
if (keyParts.length > 1) {
const groupName = keyParts[0]
targetObj[groupName] = targetObj[groupName] || {}
targetObj[groupName][index] = { value: token, type: metaType }
const rest = keyParts.slice(1)
const subKey = rest.join("-")
targetObj[groupName][subKey] = targetObj[groupName][subKey] || {}
targetObj[groupName][subKey][index] = { value: token, type: metaType }
}
}
// Generate a style dictionary
export const toStyleDictionary = props => {
const colors = Object.keys(Colors)
.filter(exportName => exportName !== "default")
.map(hueName => hueName.toLowerCase())
return props.reduce((styledictionarytokens, [key, token]) => {
const meta = {}
const isLength = key.includes('size') && !key.includes('border-size')
const isBorder = key.includes('border-size')
const isRadius = key.includes('radius')
const isShadow = key.includes('shadow')
const isColor = colors.some(color => key.includes(color))
if (isLength) meta.type = 'size'
else if (isBorder) meta.type = 'border-width'
else if (isRadius) meta.type = 'border-radius'
else if (isShadow) meta.type = 'box-shadow'
else if (isColor) meta.type = 'color'
else meta.type = 'other'
const keyWithoutPrefix = key.replace('--', '')
const keyParts = keyWithoutPrefix.split('-')
const mainKey = keyParts.length > 1 ? keyParts.slice(0, -1).join('-') : keyParts[0]
const index = keyParts.length > 1 ? keyParts[keyParts.length - 1] : 0
const dictionarykey = mapToDictionaryKey(mainKey)
return createTokenObject({
baseObj: styledictionarytokens,
mainKey,
metaType: meta.type,
dictionarykey,
index,
token: cssVarToToken(token, meta.type)
})
}, {})
}