@terrazzo/plugin-css 2.0.0-alpha.7 → 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/index.d.ts +91 -25
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +386 -305
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -1,59 +1,124 @@
|
|
|
1
|
-
import { kebabCase, validateCustomTransform } from "@terrazzo/token-tools";
|
|
2
1
|
import { generateShorthand, makeCSSVar, transformCSSValue } from "@terrazzo/token-tools/css";
|
|
3
2
|
import wcmatch from "wildcard-match";
|
|
3
|
+
import { kebabCase } from "@terrazzo/token-tools";
|
|
4
4
|
|
|
5
5
|
//#region src/lib.ts
|
|
6
|
+
const PLUGIN_NAME = "@terrazzo/plugin-css";
|
|
6
7
|
const FORMAT_ID = "css";
|
|
7
8
|
const FILE_PREFIX = `/* -------------------------------------------
|
|
8
9
|
* Autogenerated by ⛋ Terrazzo. DO NOT EDIT!
|
|
9
10
|
* ------------------------------------------- */`;
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Convert CSSRules into a formatted, indented CSS string.
|
|
13
|
+
* The reason we’re using this homemade version instead of something like css-tree is:
|
|
14
|
+
*
|
|
15
|
+
* 1. css-tree doesn’t support comments :(
|
|
16
|
+
* 2. we are only generating PARTIALS, not full CSS (the user controls the
|
|
17
|
+
* wrapper). So with a proper AST, we’d be hacking it a little anyway because
|
|
18
|
+
* we never really have a true, valid, finalized document.
|
|
19
|
+
* 3. we want @terrazzo/plugin-css to run in the browser AND be lean (i.e. don’t
|
|
20
|
+
* load Prettier or 25MB of wasm).
|
|
21
|
+
* 4. we only have to deal with a small subset of CSS—this doesn’t have to be robust
|
|
22
|
+
* by any means (even future additions won’t push the limits of the spec).
|
|
23
|
+
*/
|
|
24
|
+
function printRules(nodes, { indentChar = " ", indentLv = 0 } = {}) {
|
|
25
|
+
let output = "";
|
|
26
|
+
for (const node of nodes) {
|
|
27
|
+
if (output && node.type === "Rule") output += "\n";
|
|
28
|
+
output += printNode(node, {
|
|
29
|
+
indentChar,
|
|
30
|
+
indentLv
|
|
31
|
+
});
|
|
26
32
|
}
|
|
27
|
-
return output.
|
|
33
|
+
return output.trim();
|
|
28
34
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (
|
|
34
|
-
output
|
|
35
|
-
|
|
36
|
-
output
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
35
|
+
/** Internal printer for individual nodes */
|
|
36
|
+
function printNode(node, { indentChar, indentLv }) {
|
|
37
|
+
let output = "";
|
|
38
|
+
const indent = indentChar.repeat(indentLv);
|
|
39
|
+
if (node.type === "Declaration") {
|
|
40
|
+
if (node.comment) output += `${indent}/* ${node.comment} */\n`;
|
|
41
|
+
output += `${indent}${node.property}: ${node.value};\n`;
|
|
42
|
+
return output;
|
|
43
|
+
}
|
|
44
|
+
if (!node.prelude.length || !node.children.length) return output;
|
|
45
|
+
const mediaQueryWithDecls = node.children.some((s) => s.type === "Declaration") ? node.prelude.find((s) => s.startsWith("@")) : void 0;
|
|
46
|
+
if (mediaQueryWithDecls) {
|
|
47
|
+
const nonMedia = node.prelude.filter((s) => s !== mediaQueryWithDecls);
|
|
48
|
+
output += `${indent}${mediaQueryWithDecls} {\n`;
|
|
49
|
+
output += printNode(rule([":root"], node.children), {
|
|
50
|
+
indentChar,
|
|
51
|
+
indentLv: indentLv + 1
|
|
52
|
+
});
|
|
53
|
+
output += `${indent}}\n\n`;
|
|
54
|
+
output += printNode(rule(nonMedia, node.children), {
|
|
55
|
+
indentChar,
|
|
56
|
+
indentLv
|
|
57
|
+
});
|
|
58
|
+
return output;
|
|
46
59
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
60
|
+
let childOutput = "";
|
|
61
|
+
for (const child of node.children) childOutput += printNode(child, {
|
|
62
|
+
indentChar,
|
|
63
|
+
indentLv: indentLv + 1
|
|
64
|
+
});
|
|
65
|
+
childOutput = childOutput.trim();
|
|
66
|
+
if (!childOutput) return output;
|
|
67
|
+
output += `${indent}${node.prelude.join(", ")} {\n`;
|
|
68
|
+
output += `${indentChar.repeat(indentLv + 1)}${childOutput}\n`;
|
|
69
|
+
output += `${indent}}\n`;
|
|
70
|
+
return output;
|
|
71
|
+
}
|
|
72
|
+
/** Infer indentation preferences from a user-defined wrapping method. */
|
|
73
|
+
function getIndentFromPrepare(prepare) {
|
|
74
|
+
const str = "//css//";
|
|
75
|
+
const output = prepare(str).replace(/\/\*.*\*\//g, "");
|
|
76
|
+
let indentChar = " ";
|
|
77
|
+
let indentLv = 0;
|
|
78
|
+
let lineStartChar = 0;
|
|
79
|
+
for (let i = 0; i < output.length; i++) if (output[i] === "{") {
|
|
80
|
+
lineStartChar = i + 1;
|
|
81
|
+
indentLv++;
|
|
82
|
+
} else if (output[i] === "}") indentLv--;
|
|
83
|
+
else if (output[i] === "\n") lineStartChar = i + 1;
|
|
84
|
+
else if (output[i] === str[0] && output.slice(i).startsWith(str)) {
|
|
85
|
+
indentChar = output.slice(lineStartChar, i);
|
|
86
|
+
indentChar = indentChar.slice(0, Math.floor(indentChar.length / indentLv));
|
|
87
|
+
break;
|
|
51
88
|
}
|
|
52
|
-
return
|
|
89
|
+
return {
|
|
90
|
+
indentChar: indentChar || " ",
|
|
91
|
+
indentLv
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/** Syntactic sugar over Rule boilerplate */
|
|
95
|
+
function rule(prelude, children = []) {
|
|
96
|
+
return {
|
|
97
|
+
type: "Rule",
|
|
98
|
+
prelude,
|
|
99
|
+
children
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/** Syntactic sugar over Declaration boilerplate */
|
|
103
|
+
function decl(property, value, comment) {
|
|
104
|
+
return {
|
|
105
|
+
type: "Declaration",
|
|
106
|
+
property,
|
|
107
|
+
value,
|
|
108
|
+
comment
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/** Does a node list contain a root-level declaration with this property? */
|
|
112
|
+
function hasDecl(list, property) {
|
|
113
|
+
return list.some((d) => d.type === "Declaration" && d.property === property);
|
|
114
|
+
}
|
|
115
|
+
/** Add a declaration only if it’s unique (note: CSS, by design, allows duplication—it’s how fallbacks happen. Only use this if fallbacks aren’t needed. */
|
|
116
|
+
function addDeclUnique(list, declaration) {
|
|
117
|
+
if (!hasDecl(list, declaration.property)) list.push(declaration);
|
|
53
118
|
}
|
|
54
119
|
|
|
55
120
|
//#endregion
|
|
56
|
-
//#region src/
|
|
121
|
+
//#region src/utility-css.ts
|
|
57
122
|
const GROUP_REGEX = {
|
|
58
123
|
bg: /(^bg-|-bg-)/,
|
|
59
124
|
border: /(^border-|-border-)/,
|
|
@@ -65,38 +130,39 @@ const GROUP_REGEX = {
|
|
|
65
130
|
text: /(^text-|-text-)/
|
|
66
131
|
};
|
|
67
132
|
/** Make CSS class name from transformed token */
|
|
68
|
-
function
|
|
69
|
-
return `.${prefix}${subgroup || ""}-${kebabCase(token.token.id).replace(GROUP_REGEX[prefix], "")}
|
|
133
|
+
function makePrelude(token, prefix, subgroup) {
|
|
134
|
+
return [`.${prefix}${subgroup || ""}-${kebabCase(token.token.id).replace(GROUP_REGEX[prefix], "")}`];
|
|
70
135
|
}
|
|
71
136
|
function makeVarValue(token) {
|
|
72
|
-
return
|
|
137
|
+
return makeCSSVar(token.localID ?? token.token.id, { wrapVar: true });
|
|
73
138
|
}
|
|
74
|
-
function generateUtilityCSS(groups, tokens) {
|
|
75
|
-
const
|
|
139
|
+
function generateUtilityCSS(groups, tokens, { logger }) {
|
|
140
|
+
const root = [];
|
|
76
141
|
const groupEntries = Object.entries(groups);
|
|
77
142
|
groupEntries.sort((a, b) => a[0].localeCompare(b[0]));
|
|
78
143
|
for (const [group, selectors] of groupEntries) {
|
|
79
144
|
const selectorMatcher = wcmatch(selectors);
|
|
80
145
|
const matchingTokens = tokens.filter((token) => selectorMatcher(token.token.id));
|
|
81
146
|
if (!matchingTokens.length) {
|
|
82
|
-
|
|
147
|
+
logger.warn({
|
|
148
|
+
group: "plugin",
|
|
149
|
+
label: PLUGIN_NAME,
|
|
150
|
+
message: `utility group "${group}" matched 0 tokens: ${JSON.stringify(selectors)}`
|
|
151
|
+
});
|
|
83
152
|
break;
|
|
84
153
|
}
|
|
85
154
|
switch (group) {
|
|
86
155
|
case "bg":
|
|
87
156
|
for (const token of matchingTokens) {
|
|
88
|
-
const
|
|
157
|
+
const prelude = makePrelude(token, "bg");
|
|
89
158
|
switch (token.token.$type) {
|
|
90
159
|
case "color":
|
|
91
|
-
|
|
92
|
-
selectors: [selector],
|
|
93
|
-
declarations: { "background-color": makeVarValue(token) }
|
|
94
|
-
});
|
|
160
|
+
root.push(rule(prelude, [decl("background-color", makeVarValue(token))]));
|
|
95
161
|
break;
|
|
96
|
-
case "gradient":
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
162
|
+
case "gradient": {
|
|
163
|
+
const value = decl("background-image", `linear-gradient(${makeCSSVar(token.localID ?? token.token.id, { wrapVar: true })})`);
|
|
164
|
+
root.push(rule(prelude, [value]));
|
|
165
|
+
}
|
|
100
166
|
}
|
|
101
167
|
}
|
|
102
168
|
break;
|
|
@@ -108,10 +174,7 @@ function generateUtilityCSS(groups, tokens) {
|
|
|
108
174
|
dimension: "border-width",
|
|
109
175
|
strokeStyle: "border-style"
|
|
110
176
|
}[token.token.$type];
|
|
111
|
-
if (property)
|
|
112
|
-
selectors: [makeSelector(token, "border")],
|
|
113
|
-
declarations: { [property]: makeVarValue(token) }
|
|
114
|
-
});
|
|
177
|
+
if (property) root.push(rule(makePrelude(token, "border"), [decl(property, makeVarValue(token))]));
|
|
115
178
|
}
|
|
116
179
|
for (const token of matchingTokens) for (const side of [
|
|
117
180
|
"top",
|
|
@@ -125,200 +188,202 @@ function generateUtilityCSS(groups, tokens) {
|
|
|
125
188
|
dimension: `border-${side}-width`,
|
|
126
189
|
strokeStyle: `border-${side}-style`
|
|
127
190
|
}[token.token.$type];
|
|
128
|
-
if (property)
|
|
129
|
-
selectors: [makeSelector(token, "border", `-${side}`)],
|
|
130
|
-
declarations: { [property]: makeVarValue(token) }
|
|
131
|
-
});
|
|
191
|
+
if (property) root.push(rule(makePrelude(token, "border", `-${side}`), [decl(property, makeVarValue(token))]));
|
|
132
192
|
}
|
|
133
193
|
break;
|
|
134
194
|
case "font":
|
|
135
195
|
for (const token of matchingTokens) {
|
|
136
|
-
const
|
|
196
|
+
const prelude = makePrelude(token, "font");
|
|
137
197
|
if (token.token.$type === "typography" && token.type === "MULTI_VALUE") {
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
output.push({
|
|
141
|
-
selectors: [selector],
|
|
142
|
-
declarations
|
|
143
|
-
});
|
|
198
|
+
const value = Object.keys(token.value).map((property) => decl(property, makeCSSVar(`${token.localID ?? token.token.id}-${property}`, { wrapVar: true })));
|
|
199
|
+
root.push(rule(prelude, value));
|
|
144
200
|
} else {
|
|
145
201
|
const property = {
|
|
146
202
|
dimension: "font-size",
|
|
147
203
|
fontFamily: "font-family",
|
|
148
204
|
fontWeight: "font-weight"
|
|
149
205
|
}[token.token.$type];
|
|
150
|
-
if (property)
|
|
151
|
-
selectors: [selector],
|
|
152
|
-
declarations: { [property]: makeVarValue(token) }
|
|
153
|
-
});
|
|
206
|
+
if (property) root.push(rule(prelude, [decl(property, makeVarValue(token))]));
|
|
154
207
|
}
|
|
155
208
|
}
|
|
156
209
|
break;
|
|
157
210
|
case "layout": {
|
|
158
211
|
const filteredTokens = matchingTokens.filter((t) => t.token.$type === "dimension");
|
|
159
|
-
for (const token of filteredTokens)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
});
|
|
163
|
-
for (const token of filteredTokens) output.push({
|
|
164
|
-
selectors: [makeSelector(token, "gap", "-col")],
|
|
165
|
-
declarations: { "column-gap": makeVarValue(token) }
|
|
166
|
-
});
|
|
167
|
-
for (const token of filteredTokens) output.push({
|
|
168
|
-
selectors: [makeSelector(token, "gap", "-row")],
|
|
169
|
-
declarations: { "row-gap": makeVarValue(token) }
|
|
170
|
-
});
|
|
212
|
+
for (const token of filteredTokens) root.push(rule(makePrelude(token, "gap"), [decl("gap", makeVarValue(token))]));
|
|
213
|
+
for (const token of filteredTokens) root.push(rule(makePrelude(token, "gap", "-col"), [decl("column-gap", makeVarValue(token))]));
|
|
214
|
+
for (const token of filteredTokens) root.push(rule(makePrelude(token, "gap", "-row"), [decl("row-gap", makeVarValue(token))]));
|
|
171
215
|
for (const prefix of ["m", "p"]) {
|
|
172
216
|
const property = prefix === "m" ? "margin" : "padding";
|
|
173
|
-
for (const token of filteredTokens)
|
|
174
|
-
selectors: [makeSelector(token, prefix, "a")],
|
|
175
|
-
declarations: { [property]: makeVarValue(token) }
|
|
176
|
-
});
|
|
217
|
+
for (const token of filteredTokens) root.push(rule(makePrelude(token, prefix, "a"), [decl(property, makeVarValue(token))]));
|
|
177
218
|
for (const token of filteredTokens) {
|
|
178
219
|
const value = makeVarValue(token);
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
[`${property}-bottom`]: value,
|
|
189
|
-
[`${property}-top`]: value
|
|
190
|
-
}
|
|
191
|
-
});
|
|
220
|
+
root.push(rule(makePrelude(token, prefix, "x"), [
|
|
221
|
+
decl(`${property}-inline`, value),
|
|
222
|
+
decl(`${property}-left`, value),
|
|
223
|
+
decl(`${property}-right`, value)
|
|
224
|
+
]), rule(makePrelude(token, prefix, "y"), [
|
|
225
|
+
decl(`${property}-block`, value),
|
|
226
|
+
decl(`${property}-bottom`, value),
|
|
227
|
+
decl(`${property}-top`, value)
|
|
228
|
+
]));
|
|
192
229
|
}
|
|
193
230
|
for (const side of [
|
|
194
231
|
"top",
|
|
195
232
|
"right",
|
|
196
233
|
"bottom",
|
|
197
234
|
"left"
|
|
198
|
-
]) for (const token of filteredTokens)
|
|
199
|
-
selectors: [makeSelector(token, prefix, side[0])],
|
|
200
|
-
declarations: { [`${property}-${side}`]: makeVarValue(token) }
|
|
201
|
-
});
|
|
235
|
+
]) for (const token of filteredTokens) root.push(rule(makePrelude(token, prefix, side[0]), [decl(`${property}-${side}`, makeVarValue(token))]));
|
|
202
236
|
for (const token of filteredTokens) {
|
|
203
237
|
const value = makeVarValue(token);
|
|
204
|
-
|
|
205
|
-
selectors: [makeSelector(token, prefix, "s")],
|
|
206
|
-
declarations: { [`${property}-inline-start`]: value }
|
|
207
|
-
}, {
|
|
208
|
-
selectors: [makeSelector(token, prefix, "e")],
|
|
209
|
-
declarations: { [`${property}-inline-end`]: value }
|
|
210
|
-
});
|
|
238
|
+
root.push(rule(makePrelude(token, prefix, "bs"), [decl(`${property}-block-start`, value)]), rule(makePrelude(token, prefix, "be"), [decl(`${property}-block-end`, value)]), rule(makePrelude(token, prefix, "is"), [decl(`${property}-inline-start`, value)]), rule(makePrelude(token, prefix, "ie"), [decl(`${property}-inline-end`, value)]));
|
|
211
239
|
}
|
|
212
240
|
}
|
|
213
241
|
break;
|
|
214
242
|
}
|
|
215
243
|
case "shadow":
|
|
216
|
-
for (const token of matchingTokens) if (token.token.$type === "shadow")
|
|
217
|
-
selectors: [makeSelector(token, "shadow")],
|
|
218
|
-
declarations: { "box-shadow": makeVarValue(token) }
|
|
219
|
-
});
|
|
244
|
+
for (const token of matchingTokens) if (token.token.$type === "shadow") root.push(rule(makePrelude(token, "shadow"), [decl("box-shadow", makeVarValue(token))]));
|
|
220
245
|
break;
|
|
221
246
|
case "text":
|
|
222
247
|
for (const token of matchingTokens) {
|
|
223
|
-
const
|
|
248
|
+
const prelude = makePrelude(token, "text");
|
|
224
249
|
const value = makeVarValue(token);
|
|
225
250
|
switch (token.token.$type) {
|
|
226
251
|
case "color":
|
|
227
|
-
|
|
228
|
-
selectors: [selector],
|
|
229
|
-
declarations: { color: value }
|
|
230
|
-
});
|
|
252
|
+
root.push(rule(prelude, [decl("color", value)]));
|
|
231
253
|
break;
|
|
232
254
|
case "gradient":
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
"-webkit-text-fill-color": { value: "transparent" }
|
|
239
|
-
}
|
|
240
|
-
});
|
|
255
|
+
root.push(rule(prelude, [
|
|
256
|
+
decl("background", `-webkit-linear-gradient(${value})`),
|
|
257
|
+
decl("-webkit-background-clip", "text"),
|
|
258
|
+
decl("-webkit-text-fill-color", "transparent")
|
|
259
|
+
]));
|
|
241
260
|
break;
|
|
242
261
|
}
|
|
243
262
|
}
|
|
244
263
|
break;
|
|
245
264
|
default:
|
|
246
|
-
|
|
265
|
+
logger.warn({
|
|
266
|
+
group: "plugin",
|
|
267
|
+
label: PLUGIN_NAME,
|
|
268
|
+
message: `unknown utility CSS group "${group}", ignoring`
|
|
269
|
+
});
|
|
247
270
|
break;
|
|
248
271
|
}
|
|
249
272
|
}
|
|
250
|
-
return
|
|
273
|
+
return root;
|
|
251
274
|
}
|
|
252
275
|
|
|
253
276
|
//#endregion
|
|
254
|
-
//#region src/build
|
|
277
|
+
//#region src/build.ts
|
|
255
278
|
const P3_MQ = "@media (color-gamut: p3)";
|
|
256
279
|
const REC2020_MQ = "@media (color-gamut: rec2020)";
|
|
257
|
-
function
|
|
258
|
-
|
|
280
|
+
function buildCSS({ logger, getTransforms, exclude, utility, permutations, modeSelectors, baseSelector, baseScheme }) {
|
|
281
|
+
if (permutations?.length) {
|
|
282
|
+
let output$1 = "";
|
|
283
|
+
for (const p of permutations) {
|
|
284
|
+
if (typeof p.prepare !== "function") logger.error({
|
|
285
|
+
group: "plugin",
|
|
286
|
+
label: PLUGIN_NAME,
|
|
287
|
+
message: "prepare(css) must be a function!"
|
|
288
|
+
});
|
|
289
|
+
const tokens = getTransforms({
|
|
290
|
+
format: FORMAT_ID,
|
|
291
|
+
input: p.input
|
|
292
|
+
});
|
|
293
|
+
if (!tokens.length) continue;
|
|
294
|
+
const root = [];
|
|
295
|
+
const hdrColors = {
|
|
296
|
+
p3: [],
|
|
297
|
+
rec2020: []
|
|
298
|
+
};
|
|
299
|
+
for (const token of tokens) {
|
|
300
|
+
const localID = makeCSSVar(token.localID ?? token.token.id);
|
|
301
|
+
const aliasTokens = token.token.aliasedBy?.length ? getTransforms({
|
|
302
|
+
format: FORMAT_ID,
|
|
303
|
+
id: token.token.aliasedBy,
|
|
304
|
+
input: p.input
|
|
305
|
+
}) : [];
|
|
306
|
+
if (token.type === "SINGLE_VALUE") addDeclUnique(root, decl(localID, token.value, token.token.$description));
|
|
307
|
+
else if (token.value.srgb && token.value.p3 && token.value.rec2020) {
|
|
308
|
+
addDeclUnique(root, decl(localID, token.value.srgb, token.token.$description));
|
|
309
|
+
if (token.value.p3 !== token.value.srgb) {
|
|
310
|
+
addDeclUnique(hdrColors.p3, decl(localID, token.value.p3, token.token.$description));
|
|
311
|
+
addDeclUnique(hdrColors.rec2020, decl(localID, token.value.rec2020, token.token.$description));
|
|
312
|
+
for (const alias of aliasTokens) if (alias.localID && typeof alias.value === "string") {
|
|
313
|
+
const aliasID = makeCSSVar(alias.localID);
|
|
314
|
+
addDeclUnique(hdrColors.p3, decl(aliasID, alias.value, alias.token.$description));
|
|
315
|
+
addDeclUnique(hdrColors.rec2020, decl(aliasID, alias.value, alias.token.$description));
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
} else {
|
|
319
|
+
for (const [name, subValue] of Object.entries(token.value)) addDeclUnique(root, decl(`${localID}-${name}`, subValue, token.token.$description));
|
|
320
|
+
const shorthand = generateShorthand({
|
|
321
|
+
token: {
|
|
322
|
+
...token.token,
|
|
323
|
+
$value: token.value
|
|
324
|
+
},
|
|
325
|
+
localID
|
|
326
|
+
});
|
|
327
|
+
if (shorthand) addDeclUnique(root, decl(localID, shorthand, token.token.$description));
|
|
328
|
+
}
|
|
329
|
+
for (const alias of aliasTokens) if (alias.localID && typeof alias.value === "string") addDeclUnique(root, decl(alias.localID, alias.value, token.token.$description));
|
|
330
|
+
}
|
|
331
|
+
const indentRules = getIndentFromPrepare(p.prepare);
|
|
332
|
+
if (output$1) output$1 += "\n";
|
|
333
|
+
output$1 += `${p.prepare(printRules(root, indentRules))}\n`;
|
|
334
|
+
for (const gamut of ["p3", "rec2020"]) if (hdrColors[gamut].length) {
|
|
335
|
+
output$1 += `\n@media (color-gamut: ${gamut}) {\n`;
|
|
336
|
+
output$1 += indentRules.indentChar;
|
|
337
|
+
output$1 += p.prepare(printRules(hdrColors[gamut], indentRules)).replace(/\n(?!\n)/g, `\n${indentRules.indentChar}`);
|
|
338
|
+
output$1 += "\n}\n";
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
if (utility && Object.keys(utility).length) {
|
|
342
|
+
if (output$1) output$1 += "\n\n";
|
|
343
|
+
output$1 += generateUtilityCSS(utility, getTransforms({ format: FORMAT_ID }), { logger });
|
|
344
|
+
}
|
|
345
|
+
return output$1;
|
|
346
|
+
}
|
|
347
|
+
let output = "";
|
|
259
348
|
const rootTokens = getTransforms({
|
|
260
349
|
format: FORMAT_ID,
|
|
261
350
|
mode: "."
|
|
262
351
|
});
|
|
263
352
|
if (rootTokens.length) {
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
};
|
|
274
|
-
const rec2020Rule = {
|
|
275
|
-
selectors: [baseSelector],
|
|
276
|
-
nestedQuery: REC2020_MQ,
|
|
277
|
-
declarations: {}
|
|
278
|
-
};
|
|
279
|
-
rules.push(rootRule, p3Rule, rec2020Rule);
|
|
353
|
+
const rules = [
|
|
354
|
+
rule([baseSelector], []),
|
|
355
|
+
rule([P3_MQ], [rule([baseSelector])]),
|
|
356
|
+
rule([REC2020_MQ], [rule([baseSelector])])
|
|
357
|
+
];
|
|
358
|
+
const rootRule = rules[0];
|
|
359
|
+
const p3Rule = rules[1].children[0];
|
|
360
|
+
const rec2020Rule = rules[2].children[0];
|
|
361
|
+
if (baseScheme) rootRule.children.unshift(decl("color-scheme", baseScheme));
|
|
280
362
|
const shouldExclude = wcmatch(exclude ?? []);
|
|
281
363
|
for (const token of rootTokens) {
|
|
282
364
|
if (shouldExclude(token.token.id)) continue;
|
|
283
365
|
const localID = token.localID ?? token.token.id;
|
|
284
366
|
const aliasTokens = token.token.aliasedBy?.length ? getTransforms({
|
|
285
367
|
format: FORMAT_ID,
|
|
286
|
-
id: token.token.aliasedBy
|
|
368
|
+
id: token.token.aliasedBy,
|
|
369
|
+
mode: "."
|
|
287
370
|
}) : [];
|
|
288
|
-
if (token.type === "SINGLE_VALUE") rootRule.
|
|
289
|
-
value: token.value,
|
|
290
|
-
description: token.token.$description
|
|
291
|
-
};
|
|
371
|
+
if (token.type === "SINGLE_VALUE") addDeclUnique(rootRule.children, decl(localID, token.value, token.token.$description));
|
|
292
372
|
else if (token.value.srgb && token.value.p3 && token.value.rec2020) {
|
|
293
|
-
rootRule.
|
|
294
|
-
value: token.value.srgb,
|
|
295
|
-
description: token.token.$description
|
|
296
|
-
};
|
|
373
|
+
addDeclUnique(rootRule.children, decl(localID, token.value.srgb, token.token.$description));
|
|
297
374
|
if (token.value.p3 !== token.value.srgb) {
|
|
298
|
-
p3Rule.
|
|
299
|
-
|
|
300
|
-
description: token.token.$description
|
|
301
|
-
};
|
|
302
|
-
rec2020Rule.declarations[localID] = {
|
|
303
|
-
value: token.value.rec2020,
|
|
304
|
-
description: token.token.$description
|
|
305
|
-
};
|
|
375
|
+
addDeclUnique(p3Rule.children, decl(localID, token.value.p3, token.token.$description));
|
|
376
|
+
addDeclUnique(rec2020Rule.children, decl(localID, token.value.rec2020, token.token.$description));
|
|
306
377
|
for (const alias of aliasTokens) if (alias.localID && typeof alias.value === "string") {
|
|
307
|
-
p3Rule.
|
|
308
|
-
|
|
309
|
-
description: token.token.$description
|
|
310
|
-
};
|
|
311
|
-
rec2020Rule.declarations[alias.localID] ??= {
|
|
312
|
-
value: alias.value,
|
|
313
|
-
description: token.token.$description
|
|
314
|
-
};
|
|
378
|
+
addDeclUnique(p3Rule.children, decl(alias.localID, alias.value, token.token.$description));
|
|
379
|
+
addDeclUnique(rec2020Rule.children, decl(alias.localID, alias.value, token.token.$description));
|
|
315
380
|
}
|
|
316
381
|
}
|
|
317
382
|
} else if (token.type === "MULTI_VALUE") {
|
|
318
|
-
for (const [name, value] of Object.entries(token.value))
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
383
|
+
for (const [name, value] of Object.entries(token.value)) {
|
|
384
|
+
const property = name === "." ? localID : [localID, name].join("-");
|
|
385
|
+
addDeclUnique(rootRule.children, decl(property, value, token.token.$description));
|
|
386
|
+
}
|
|
322
387
|
const shorthand = generateShorthand({
|
|
323
388
|
token: {
|
|
324
389
|
...token.token,
|
|
@@ -326,78 +391,40 @@ function buildFormat({ getTransforms, exclude, utility, modeSelectors, baseSelec
|
|
|
326
391
|
},
|
|
327
392
|
localID
|
|
328
393
|
});
|
|
329
|
-
if (shorthand) rootRule.
|
|
330
|
-
value: shorthand,
|
|
331
|
-
description: token.token.$description
|
|
332
|
-
};
|
|
394
|
+
if (shorthand) addDeclUnique(rootRule.children, decl(token.localID ?? token.token.id, shorthand, token.token.$description));
|
|
333
395
|
}
|
|
334
396
|
}
|
|
397
|
+
output += printRules(rules);
|
|
335
398
|
}
|
|
336
|
-
for (const
|
|
337
|
-
if (!selectors.length) continue;
|
|
399
|
+
for (const selector of modeSelectors ?? []) {
|
|
338
400
|
const selectorTokens = getTransforms({
|
|
339
401
|
format: FORMAT_ID,
|
|
340
|
-
id: tokens,
|
|
341
|
-
mode
|
|
402
|
+
id: selector.tokens,
|
|
403
|
+
mode: selector.mode
|
|
342
404
|
});
|
|
343
405
|
if (!selectorTokens.length) continue;
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
const selectorP3Rule = {
|
|
350
|
-
selectors,
|
|
351
|
-
nestedQuery: P3_MQ,
|
|
352
|
-
declarations: {}
|
|
353
|
-
};
|
|
354
|
-
const selectorRec2020Rule = {
|
|
355
|
-
selectors,
|
|
356
|
-
nestedQuery: REC2020_MQ,
|
|
357
|
-
declarations: {}
|
|
406
|
+
const modeRule = rule(selector.selectors);
|
|
407
|
+
if (selector.scheme) modeRule.children.unshift(decl("color-scheme", selector.scheme));
|
|
408
|
+
const hdrColors = {
|
|
409
|
+
p3: [],
|
|
410
|
+
rec2020: []
|
|
358
411
|
};
|
|
359
|
-
const selectorAliasDeclarations = {};
|
|
360
|
-
rules.push(selectorRule, selectorP3Rule, selectorRec2020Rule);
|
|
361
412
|
for (const token of selectorTokens) {
|
|
362
413
|
const localID = token.localID ?? token.token.id;
|
|
363
414
|
const aliasTokens = token.token.aliasedBy?.length ? getTransforms({
|
|
364
415
|
format: FORMAT_ID,
|
|
365
416
|
id: token.token.aliasedBy
|
|
366
417
|
}) : [];
|
|
367
|
-
if (token.type === "SINGLE_VALUE")
|
|
368
|
-
value: token.value,
|
|
369
|
-
description: token.token.$description
|
|
370
|
-
};
|
|
418
|
+
if (token.type === "SINGLE_VALUE") addDeclUnique(modeRule.children, decl(localID, token.value, token.token.$description));
|
|
371
419
|
else if (token.value.srgb && token.value.p3 && token.value.rec2020) {
|
|
372
|
-
|
|
373
|
-
value: token.value.srgb,
|
|
374
|
-
description: token.token.$description
|
|
375
|
-
};
|
|
420
|
+
addDeclUnique(modeRule.children, decl(localID, token.value.srgb, token.token.$description));
|
|
376
421
|
if (token.value.p3 !== token.value.srgb) {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
};
|
|
381
|
-
selectorRec2020Rule.declarations[localID] = {
|
|
382
|
-
value: token.value.rec2020,
|
|
383
|
-
description: token.token.$description
|
|
384
|
-
};
|
|
385
|
-
for (const alias of aliasTokens) if (alias.localID && typeof alias.value === "string") {
|
|
386
|
-
selectorP3Rule.declarations[alias.localID] ??= {
|
|
387
|
-
value: alias.value,
|
|
388
|
-
description: token.token.$description
|
|
389
|
-
};
|
|
390
|
-
selectorRec2020Rule.declarations[alias.localID] ??= {
|
|
391
|
-
value: alias.value,
|
|
392
|
-
description: token.token.$description
|
|
393
|
-
};
|
|
394
|
-
}
|
|
422
|
+
addDeclUnique(hdrColors.p3, decl(localID, token.value.p3, token.token.$description));
|
|
423
|
+
addDeclUnique(hdrColors.rec2020, decl(localID, token.value.rec2020, token.token.$description));
|
|
424
|
+
for (const alias of aliasTokens) if (alias.localID && typeof alias.value === "string") for (const gamut of ["p3", "rec2020"]) addDeclUnique(hdrColors[gamut], decl(alias.localID, alias.value, token.token.$description));
|
|
395
425
|
}
|
|
396
426
|
} else {
|
|
397
|
-
for (const [name,
|
|
398
|
-
value: subvalue,
|
|
399
|
-
description: token.token.$description
|
|
400
|
-
};
|
|
427
|
+
for (const [name, subValue] of Object.entries(token.value)) addDeclUnique(modeRule.children, decl(`${localID}-${name}`, subValue, token.token.$description));
|
|
401
428
|
const shorthand = generateShorthand({
|
|
402
429
|
token: {
|
|
403
430
|
...token.token,
|
|
@@ -405,107 +432,161 @@ function buildFormat({ getTransforms, exclude, utility, modeSelectors, baseSelec
|
|
|
405
432
|
},
|
|
406
433
|
localID
|
|
407
434
|
});
|
|
408
|
-
if (shorthand)
|
|
409
|
-
value: shorthand,
|
|
410
|
-
description: token.token.$description
|
|
411
|
-
};
|
|
435
|
+
if (shorthand) addDeclUnique(modeRule.children, decl(localID, shorthand, token.token.$description));
|
|
412
436
|
}
|
|
413
|
-
for (const alias of aliasTokens) if (alias.localID && typeof alias.value === "string")
|
|
414
|
-
value: alias.value,
|
|
415
|
-
description: token.token.$description
|
|
416
|
-
};
|
|
437
|
+
for (const alias of aliasTokens) if (alias.localID && typeof alias.value === "string") addDeclUnique(modeRule.children, decl(alias.localID, alias.value, token.token.$description));
|
|
417
438
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
439
|
+
if (output) output += "\n\n";
|
|
440
|
+
output += printRules([
|
|
441
|
+
modeRule,
|
|
442
|
+
rule([P3_MQ], [rule(selector.selectors, hdrColors.p3)]),
|
|
443
|
+
rule([REC2020_MQ], [rule(selector.selectors, hdrColors.rec2020)])
|
|
444
|
+
]);
|
|
422
445
|
}
|
|
423
|
-
if (utility && Object.keys(utility).length)
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
446
|
+
if (utility && Object.keys(utility).length) {
|
|
447
|
+
if (output) output += "\n\n";
|
|
448
|
+
output += printRules(generateUtilityCSS(utility, getTransforms({
|
|
449
|
+
format: FORMAT_ID,
|
|
450
|
+
mode: "."
|
|
451
|
+
}), { logger }));
|
|
452
|
+
}
|
|
453
|
+
return output;
|
|
428
454
|
}
|
|
429
455
|
|
|
430
456
|
//#endregion
|
|
431
|
-
//#region src/
|
|
432
|
-
function
|
|
433
|
-
const { exclude, variableName, modeSelectors, transform: customTransform, utility, legacyHex, skipBuild, baseScheme } = options ?? {};
|
|
434
|
-
const filename = options?.filename ?? options?.fileName ?? "index.css";
|
|
435
|
-
const baseSelector = options?.baseSelector ?? ":root";
|
|
457
|
+
//#region src/transform.ts
|
|
458
|
+
function transformCSS({ transform: { context: { logger }, resolver, getTransforms, setTransform, tokens: baseTokens }, options: { permutations, exclude: userExclude, legacyHex, transform: customTransform, variableName } }) {
|
|
436
459
|
function transformName(token) {
|
|
437
460
|
const customName = variableName?.(token);
|
|
438
461
|
if (customName !== void 0) {
|
|
439
|
-
if (typeof customName !== "string")
|
|
462
|
+
if (typeof customName !== "string") logger.error({
|
|
463
|
+
group: "plugin",
|
|
464
|
+
label: PLUGIN_NAME,
|
|
465
|
+
message: `variableName() must return a string; received ${customName}`
|
|
466
|
+
});
|
|
440
467
|
return customName;
|
|
441
468
|
}
|
|
442
469
|
return makeCSSVar(token.id);
|
|
443
470
|
}
|
|
444
471
|
const transformAlias = (token) => `var(${transformName(token)})`;
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
for (const mode of Object.keys(token.mode)) {
|
|
456
|
-
if (customTransform) {
|
|
457
|
-
const value = customTransform(token, mode);
|
|
458
|
-
if (value !== void 0 && value !== null) {
|
|
459
|
-
validateCustomTransform(value, { $type: token.$type });
|
|
460
|
-
setTransform(id, {
|
|
461
|
-
format: FORMAT_ID,
|
|
462
|
-
localID,
|
|
463
|
-
value,
|
|
464
|
-
mode,
|
|
465
|
-
meta: { "token-listing": { name: localID } }
|
|
466
|
-
});
|
|
467
|
-
continue;
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
const transformedValue = transformCSSValue(token, {
|
|
471
|
-
mode,
|
|
472
|
+
const exclude = userExclude ? wcmatch(userExclude) : void 0;
|
|
473
|
+
if (permutations?.length) {
|
|
474
|
+
for (const p of permutations) {
|
|
475
|
+
const input = p.input;
|
|
476
|
+
const ignore = p.ignore ? wcmatch(p.ignore) : void 0;
|
|
477
|
+
try {
|
|
478
|
+
const tokens = resolver.apply(input);
|
|
479
|
+
for (const token of Object.values(tokens)) {
|
|
480
|
+
if (ignore?.(token.id) || exclude?.(token.id)) continue;
|
|
481
|
+
const value = customTransform?.(token) ?? transformCSSValue(token, {
|
|
472
482
|
tokensSet: tokens,
|
|
473
483
|
transformAlias,
|
|
474
484
|
color: { legacyHex }
|
|
475
485
|
});
|
|
476
|
-
if (
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
setTransform(id, {
|
|
486
|
+
if (value && isDifferentValue(value, getTransforms({
|
|
487
|
+
format: FORMAT_ID,
|
|
488
|
+
id: token.id
|
|
489
|
+
})[0]?.value)) {
|
|
490
|
+
const localID = transformName(token);
|
|
491
|
+
setTransform(token.id, {
|
|
483
492
|
format: FORMAT_ID,
|
|
493
|
+
value,
|
|
484
494
|
localID,
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
meta: { "token-listing": { name: listingName } }
|
|
495
|
+
input,
|
|
496
|
+
meta: { "token-listing": { name: localID } }
|
|
488
497
|
});
|
|
489
498
|
}
|
|
490
499
|
}
|
|
500
|
+
} catch (err) {
|
|
501
|
+
logger.error({
|
|
502
|
+
group: "plugin",
|
|
503
|
+
label: PLUGIN_NAME,
|
|
504
|
+
message: `There was an error trying to apply input ${resolver.getPermutationID(input)}.`,
|
|
505
|
+
continueOnError: true
|
|
506
|
+
});
|
|
507
|
+
throw err;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
for (const token of Object.values(baseTokens)) {
|
|
513
|
+
if (exclude?.(token.id)) continue;
|
|
514
|
+
for (const mode of Object.keys(token.mode)) {
|
|
515
|
+
const value = customTransform?.(token, ".") ?? transformCSSValue({
|
|
516
|
+
...token,
|
|
517
|
+
...token.mode[mode]
|
|
518
|
+
}, {
|
|
519
|
+
tokensSet: baseTokens,
|
|
520
|
+
transformAlias,
|
|
521
|
+
color: { legacyHex }
|
|
522
|
+
});
|
|
523
|
+
if (value) {
|
|
524
|
+
const localID = transformName(token);
|
|
525
|
+
setTransform(token.id, {
|
|
526
|
+
format: FORMAT_ID,
|
|
527
|
+
localID,
|
|
528
|
+
value,
|
|
529
|
+
mode,
|
|
530
|
+
meta: { "token-listing": { name: localID } }
|
|
531
|
+
});
|
|
491
532
|
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
/** Is the transformed value different from the base value? */
|
|
537
|
+
function isDifferentValue(value, baseValue) {
|
|
538
|
+
if (!value || !baseValue || typeof value !== typeof baseValue) return true;
|
|
539
|
+
if (typeof value === "string" && typeof baseValue === "string") return value !== baseValue;
|
|
540
|
+
const keysA = Object.keys(value);
|
|
541
|
+
const keysB = Object.keys(baseValue);
|
|
542
|
+
if (keysA.length !== keysB.length) return true;
|
|
543
|
+
if (!keysA.every((k) => keysB.includes(k) && value[k] === baseValue[k])) return true;
|
|
544
|
+
return false;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
//#endregion
|
|
548
|
+
//#region src/index.ts
|
|
549
|
+
function cssPlugin(options) {
|
|
550
|
+
const { utility, skipBuild, baseScheme } = options ?? {};
|
|
551
|
+
const filename = options?.filename ?? options?.fileName ?? "index.css";
|
|
552
|
+
const baseSelector = options?.baseSelector ?? ":root";
|
|
553
|
+
return {
|
|
554
|
+
name: PLUGIN_NAME,
|
|
555
|
+
config(_config, context) {
|
|
556
|
+
if (options?.permutations && (options?.modeSelectors || options?.baseSelector || options?.baseScheme)) context.logger.error({
|
|
557
|
+
group: "plugin",
|
|
558
|
+
label: PLUGIN_NAME,
|
|
559
|
+
message: "Permutations option is incompatible with modeSelectors, baseSelector, and baseScheme."
|
|
560
|
+
});
|
|
561
|
+
},
|
|
562
|
+
async transform(transformOptions) {
|
|
563
|
+
if (transformOptions.getTransforms({
|
|
564
|
+
format: FORMAT_ID,
|
|
565
|
+
id: "*"
|
|
566
|
+
}).length) return;
|
|
567
|
+
transformCSS({
|
|
568
|
+
transform: transformOptions,
|
|
569
|
+
options: options ?? {}
|
|
570
|
+
});
|
|
492
571
|
},
|
|
493
|
-
async build({ getTransforms, outputFile }) {
|
|
572
|
+
async build({ getTransforms, outputFile, context }) {
|
|
494
573
|
if (skipBuild === true) return;
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
exclude,
|
|
574
|
+
let contents = `${FILE_PREFIX}\n\n`;
|
|
575
|
+
contents += buildCSS({
|
|
576
|
+
exclude: options?.exclude,
|
|
498
577
|
getTransforms,
|
|
499
|
-
|
|
578
|
+
permutations: options?.permutations,
|
|
579
|
+
modeSelectors: options?.modeSelectors,
|
|
500
580
|
utility,
|
|
501
581
|
baseSelector,
|
|
502
|
-
baseScheme
|
|
503
|
-
|
|
504
|
-
|
|
582
|
+
baseScheme,
|
|
583
|
+
logger: context.logger
|
|
584
|
+
});
|
|
585
|
+
outputFile(filename, contents.replace(/\n*$/, "\n"));
|
|
505
586
|
}
|
|
506
587
|
};
|
|
507
588
|
}
|
|
508
589
|
|
|
509
590
|
//#endregion
|
|
510
|
-
export { FILE_PREFIX, FORMAT_ID, cssPlugin as default, printRules };
|
|
591
|
+
export { FILE_PREFIX, FORMAT_ID, PLUGIN_NAME, addDeclUnique, decl, cssPlugin as default, getIndentFromPrepare, hasDecl, printNode, printRules, rule };
|
|
511
592
|
//# sourceMappingURL=index.js.map
|