-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstyle-dictionary.config.js
More file actions
125 lines (107 loc) · 4.24 KB
/
Copy pathstyle-dictionary.config.js
File metadata and controls
125 lines (107 loc) · 4.24 KB
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
import StyleDictionary from 'style-dictionary';
import fs from 'node:fs';
import path from 'node:path';
// -------------------------------------------------------
// Helpers
// -------------------------------------------------------
function getTokenFiles(dir) {
return fs.readdirSync(dir, { withFileTypes: true })
.sort((a, b) => a.name.localeCompare(b.name))
.flatMap(entry => {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) return getTokenFiles(fullPath);
if (entry.name.endsWith('.json')) return [fullPath];
return [];
});
}
function pathToKebab(parts) {
return parts
.filter(p => p !== 'default')
.map(p => p.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase())
.join('-');
}
function refToVar(ref) {
return ref.replace(/\{([^}]+)\}/g, (_, p) => `var(--${pathToKebab(p.split('.'))})`);
}
function compositeLayerToCSS(obj) {
const { inset, ...props } = obj;
const values = Object.values(props).map(v => refToVar(String(v)));
return `${inset ? 'inset ' : ''}${values.join(' ')}`;
}
// -------------------------------------------------------
// Transforms
// -------------------------------------------------------
StyleDictionary.registerTransform({
name: 'name/kebab/strip-default',
type: 'name',
transform(token) {
return pathToKebab(token.path);
},
});
StyleDictionary.registerTransformGroup({
name: 'custom/css',
transforms: ['name/kebab/strip-default'],
});
// -------------------------------------------------------
// Format — @layer tokens
// -------------------------------------------------------
StyleDictionary.registerFormat({
name: 'css/layer-tokens',
format({ dictionary, file }) {
const allVars = dictionary.allTokens.map(t => {
const name = pathToKebab(t.path);
const orig = t.original?.$value ?? t.original?.value;
let value;
if (Array.isArray(orig)) value = orig.map(compositeLayerToCSS).join(', ');
else if (orig !== null && typeof orig === 'object') value = compositeLayerToCSS(orig);
else value = refToVar(String(orig ?? t.$value ?? t.value));
return ` --${name}: ${value};`;
}).join('\n');
const header = '/* Do not edit directly, this file was auto-generated. */';
return `${header}\n\n/* ${file.destination} */\n@layer tokens {\n :root {\n${allVars}\n }\n}\n`;
},
});
// -------------------------------------------------------
// Config
// -------------------------------------------------------
const componentDir = './assets/tokens/components';
const tokenFiles = getTokenFiles('./assets/tokens');
const rel = file => path.relative(componentDir, file).replace(/\.json$/, '');
// Tokens reference @uncinq/component-tokens and @uncinq/design-tokens, so both
// trees must be included for the references to resolve.
for (const pkg of ['@uncinq/design-tokens', '@uncinq/component-tokens']) {
if (!fs.existsSync(`./node_modules/${pkg}`)) {
throw new Error(`Missing ${pkg} — run npm install first.`);
}
}
await new StyleDictionary({
usesDtcg: true,
log: { warnings: 'disabled', errors: { brokenReferences: 'console' } },
include: [
...getTokenFiles('./node_modules/@uncinq/design-tokens/tokens'),
...getTokenFiles('./node_modules/@uncinq/component-tokens/tokens'),
],
source: tokenFiles,
platforms: {
css: {
transformGroup: 'custom/css',
buildPath: 'assets/css/tokens/components/',
files: tokenFiles.map(file => ({
destination: `${rel(file)}.css`,
format: 'css/layer-tokens',
filter: t => t.filePath === file,
})),
},
},
}).buildAllPlatforms();
// -------------------------------------------------------
// Barrel — assets/css/tokens/places.css imports every generated component token
// file. Token files are discovered from disk, so modules/places.css imports this
// barrel once and adding a token JSON needs no manual edit. Each file declares
// its own @layer tokens, so a plain @import is enough.
// -------------------------------------------------------
fs.writeFileSync(
'./assets/css/tokens/places.css',
'/* places.css — barrel, do not edit */\n' +
tokenFiles.map(file => `@import "./components/${rel(file)}.css";`).join('\n') + '\n',
);