Commit
+169 -2 +/-4 browse
1 | diff --git a/build/props.js b/build/props.js |
2 | index fc8a4f0..c8f74e2 100644 |
3 | --- a/build/props.js |
4 | +++ b/build/props.js |
5 | @@ -22,6 +22,7 @@ import {buildPropsStylesheet} from './to-stylesheet.js' |
6 | import {toTokens} from './to-tokens.js' |
7 | import {toObject} from './to-object.js' |
8 | import {toFigmaTokens} from './to-figmatokens.js' |
9 | + import {toStyleDictionary} from './to-style-dictionary.js' |
10 | |
11 | const [,,prefix='',useWhere,customSubject='',filePrefix=''] = process.argv |
12 | |
13 | @@ -77,6 +78,11 @@ const designtokens = toTokens(jsonbundle) |
14 | const JSONtokens = fs.createWriteStream('../open-props.tokens.json') |
15 | JSONtokens.end(JSON.stringify(Object.fromEntries(designtokens), null, 2)) |
16 | |
17 | + // gen style-dictionary tokens |
18 | + const styledictionarytokens = toStyleDictionary(jsonbundle) |
19 | + const StyleDictionaryTokens = fs.createWriteStream('../open-props.style-dictionary-tokens.json') |
20 | + StyleDictionaryTokens.end(JSON.stringify(styledictionarytokens, null, 2)) |
21 | + |
22 | // gen figma tokens |
23 | const figmatokens = toFigmaTokens(jsonbundle) |
24 | const FigmaTokens = fs.createWriteStream('../open-props.figma-tokens.json') |
25 | diff --git a/build/to-style-dictionary.js b/build/to-style-dictionary.js |
26 | new file mode 100644 |
27 | index 0000000..875aa31 |
28 | --- /dev/null |
29 | +++ b/build/to-style-dictionary.js |
30 | @@ -0,0 +1,160 @@ |
31 | + import * as Colors from '../src/props.colors.js' |
32 | + |
33 | + // Mapping of CSS variable names to dictionary keys |
34 | + const dictionaryMap = { |
35 | + "size-relative": "relative", |
36 | + "size-fluid": "fluid", |
37 | + "size-header": "header", |
38 | + "size-content": "content", |
39 | + "border-size": "size", |
40 | + "radius-conditional": "conditional", |
41 | + "radius-blob": "blob" |
42 | + } |
43 | + |
44 | + // Map a value to a dictionary key using the dictionaryMap |
45 | + const mapToDictionaryKey = (value) => dictionaryMap[value] || value |
46 | + |
47 | + // Determine the type key based on the metaType |
48 | + const getTypeKey = (metaType) => { |
49 | + if (metaType === "size" || metaType === "border-radius") { |
50 | + return metaType === "size" ? "size" : "radius" |
51 | + } else if (metaType === "border-width") { |
52 | + return "border" |
53 | + } |
54 | + return metaType |
55 | + } |
56 | + |
57 | + // Count the occurrences of a character in a string |
58 | + const countOccurrences = (str, letter) => (str.match(new RegExp(letter, 'g')) || []).length |
59 | + |
60 | + // Regular expression to match CSS variable usages |
61 | + const cssVarUsageRegex = /var\(--([a-zA-Z0-9-]+)\)/g |
62 | + |
63 | + // Replace the last occurrence of a pattern with a replacement |
64 | + /* https://www.30secondsofcode.org/js/s/replace-last-occurrence/ */ |
65 | + const replaceLast = (str, pattern, replacement) => { |
66 | + const match = |
67 | + typeof pattern === 'string' |
68 | + ? pattern |
69 | + : (str.match(new RegExp(pattern.source, 'g')) || []).slice(-1)[0] |
70 | + if (!match) return str |
71 | + const last = str.lastIndexOf(match) |
72 | + return last !== -1 |
73 | + ? `${str.slice(0, last)}${replacement}${str.slice(last + match.length)}` |
74 | + : str |
75 | + } |
76 | + |
77 | + // Helper function to convert CSS variable name to token reference |
78 | + const tokenizeCSSVar = (variableName, metaType) => { |
79 | + const tokenName = replaceLast(variableName, '-', '.') |
80 | + const hyphenCount = countOccurrences(variableName, '-') |
81 | + |
82 | + if (hyphenCount > 2 && metaType === "other") { |
83 | + const [firstPart, ...restParts] = tokenName.split('-') |
84 | + return `{${metaType}.${firstPart}.${restParts.join('-')}.value}` |
85 | + } |
86 | + |
87 | + return `{${tokenName}.value}` |
88 | + }; |
89 | + |
90 | + // Convert CSS variable usages to token references |
91 | + const cssVarToToken = (input, metaType) => { |
92 | + if (!input.toString().includes("var")) { |
93 | + return input |
94 | + } |
95 | + |
96 | + return input.replace(cssVarUsageRegex, (match, variableName) => { |
97 | + return tokenizeCSSVar(variableName, metaType) |
98 | + }) |
99 | + }; |
100 | + |
101 | + // Create a token object based on metaType and dictionary key |
102 | + const createTokenObject = ({ |
103 | + baseObj, |
104 | + mainKey, |
105 | + metaType, |
106 | + dictionarykey, |
107 | + index, |
108 | + token |
109 | + }) => { |
110 | + const typeKey = getTypeKey(metaType) |
111 | + const targetObj = baseObj[typeKey] = baseObj[typeKey] || {} |
112 | + |
113 | + if (typeKey === "size" || typeKey === "radius") { |
114 | + const shouldReplace = mainKey !== dictionarykey |
115 | + handleKey(targetObj, dictionarykey, index, token, metaType, shouldReplace) |
116 | + } else if (typeKey !== "other") { |
117 | + handleKey(targetObj, dictionarykey, index, token, metaType, true) |
118 | + } else { |
119 | + handleOtherTypes(targetObj, dictionarykey, index, token, metaType) |
120 | + } |
121 | + |
122 | + return baseObj |
123 | + } |
124 | + |
125 | + // Handle cases where meta.type != "other" |
126 | + function handleKey(targetObj, dictionarykey, index, token, metaType, shouldReplace) { |
127 | + if (shouldReplace) { |
128 | + targetObj[dictionarykey] = targetObj[dictionarykey] || {} |
129 | + targetObj[dictionarykey][index] = { value: token, type: metaType } |
130 | + } else { |
131 | + targetObj[index] = { value: token, type: metaType } |
132 | + } |
133 | + } |
134 | + |
135 | + // Handle cases where meta.type = "other" |
136 | + function handleOtherTypes(targetObj, dictionarykey, index, token, metaType) { |
137 | + const keyParts = dictionarykey.split("-") |
138 | + |
139 | + if (keyParts.length > 1) { |
140 | + const groupName = keyParts[0] |
141 | + targetObj[groupName] = targetObj[groupName] || {} |
142 | + targetObj[groupName][index] = { value: token, type: metaType } |
143 | + |
144 | + const rest = keyParts.slice(1) |
145 | + const subKey = rest.join("-") |
146 | + |
147 | + targetObj[groupName][subKey] = targetObj[groupName][subKey] || {} |
148 | + targetObj[groupName][subKey][index] = { value: token, type: metaType } |
149 | + } |
150 | + } |
151 | + |
152 | + // Generate a style dictionary |
153 | + export const toStyleDictionary = props => { |
154 | + const colors = Object.keys(Colors) |
155 | + .filter(exportName => exportName !== "default") |
156 | + .map(hueName => hueName.toLowerCase()) |
157 | + |
158 | + return props.reduce((styledictionarytokens, [key, token]) => { |
159 | + const meta = {} |
160 | + |
161 | + const isLength = key.includes('size') && !key.includes('border-size') |
162 | + const isBorder = key.includes('border-size') |
163 | + const isRadius = key.includes('radius') |
164 | + const isShadow = key.includes('shadow') |
165 | + const isColor = colors.some(color => key.includes(color)) |
166 | + |
167 | + if (isLength) meta.type = 'size' |
168 | + else if (isBorder) meta.type = 'border-width' |
169 | + else if (isRadius) meta.type = 'border-radius' |
170 | + else if (isShadow) meta.type = 'box-shadow' |
171 | + else if (isColor) meta.type = 'color' |
172 | + else meta.type = 'other' |
173 | + |
174 | + const keyWithoutPrefix = key.replace('--', '') |
175 | + const keyParts = keyWithoutPrefix.split('-') |
176 | + const mainKey = keyParts.length > 1 ? keyParts.slice(0, -1).join('-') : keyParts[0] |
177 | + const index = keyParts.length > 1 ? keyParts[keyParts.length - 1] : 0 |
178 | + |
179 | + const dictionarykey = mapToDictionaryKey(mainKey) |
180 | + |
181 | + return createTokenObject({ |
182 | + baseObj: styledictionarytokens, |
183 | + mainKey, |
184 | + metaType: meta.type, |
185 | + dictionarykey, |
186 | + index, |
187 | + token: cssVarToToken(token, meta.type) |
188 | + }) |
189 | + }, {}) |
190 | + } |
191 | diff --git a/package-lock.json b/package-lock.json |
192 | index 4e0659a..1efa6ab 100644 |
193 | --- a/package-lock.json |
194 | +++ b/package-lock.json |
195 | @@ -1,12 +1,12 @@ |
196 | { |
197 | "name": "open-props", |
198 | - "version": "1.5.16", |
199 | + "version": "1.6.8", |
200 | "lockfileVersion": 2, |
201 | "requires": true, |
202 | "packages": { |
203 | "": { |
204 | "name": "open-props", |
205 | - "version": "1.5.16", |
206 | + "version": "1.6.8", |
207 | "license": "MIT", |
208 | "devDependencies": { |
209 | "ava": "^3.15.0", |
210 | diff --git a/package.json b/package.json |
211 | index b711098..709f2ef 100644 |
212 | --- a/package.json |
213 | +++ b/package.json |
214 | @@ -171,6 +171,7 @@ |
215 | "./json": "./open-props.tokens.json", |
216 | "./tokens": "./open-props.tokens.json", |
217 | "./design-tokens": "./open-props.tokens.json", |
218 | + "./style-dictionary-tokens": "./open-props.style-dictionary-tokens.json", |
219 | "./postcss/brand": "./src/extra/brand.css", |
220 | "./postcss/theme": "./src/extra/theme.css", |
221 | "./postcss/theme-dark": "./src/extra/theme-dark.css", |