@styleframe/transpiler 1.0.0
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/.tsbuildinfo +1 -0
- package/CHANGELOG.md +12 -0
- package/package.json +43 -0
- package/src/constants.ts +4 -0
- package/src/consume/at-rule.test.ts +339 -0
- package/src/consume/at-rule.ts +34 -0
- package/src/consume/consume.test.ts +259 -0
- package/src/consume/consume.ts +60 -0
- package/src/consume/container.test.ts +501 -0
- package/src/consume/container.ts +73 -0
- package/src/consume/css.test.ts +184 -0
- package/src/consume/css.ts +17 -0
- package/src/consume/declarations.test.ts +210 -0
- package/src/consume/declarations.ts +17 -0
- package/src/consume/index.ts +12 -0
- package/src/consume/primitive.test.ts +52 -0
- package/src/consume/primitive.ts +16 -0
- package/src/consume/ref.test.ts +84 -0
- package/src/consume/ref.ts +22 -0
- package/src/consume/root.test.ts +353 -0
- package/src/consume/root.ts +19 -0
- package/src/consume/selector.test.ts +441 -0
- package/src/consume/selector.ts +17 -0
- package/src/consume/theme.test.ts +215 -0
- package/src/consume/theme.ts +15 -0
- package/src/consume/utility.test.ts +696 -0
- package/src/consume/utility.ts +31 -0
- package/src/consume/variable.test.ts +197 -0
- package/src/consume/variable.ts +20 -0
- package/src/defaults.ts +21 -0
- package/src/generator/genAtRuleQuery.test.ts +148 -0
- package/src/generator/genAtRuleQuery.ts +3 -0
- package/src/generator/genDeclaration.test.ts +283 -0
- package/src/generator/genDeclaration.ts +9 -0
- package/src/generator/genDeclarationsBlock.test.ts +278 -0
- package/src/generator/genDeclarationsBlock.ts +7 -0
- package/src/generator/genDeclareVariable.test.ts +323 -0
- package/src/generator/genDeclareVariable.ts +6 -0
- package/src/generator/genInlineAtRule.test.ts +351 -0
- package/src/generator/genInlineAtRule.ts +5 -0
- package/src/generator/genReferenceVariable.test.ts +392 -0
- package/src/generator/genReferenceVariable.ts +5 -0
- package/src/generator/genSafePropertyName.test.ts +489 -0
- package/src/generator/genSafePropertyName.ts +5 -0
- package/src/generator/genSafeVariableName.test.ts +358 -0
- package/src/generator/genSafeVariableName.ts +21 -0
- package/src/generator/genSelector.test.ts +357 -0
- package/src/generator/genSelector.ts +5 -0
- package/src/generator/index.ts +9 -0
- package/src/index.ts +6 -0
- package/src/transpile.test.ts +825 -0
- package/src/transpile.ts +21 -0
- package/src/types.ts +15 -0
- package/src/utils.ts +18 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +7 -0
- package/vite.config.ts +5 -0
|
@@ -0,0 +1,825 @@
|
|
|
1
|
+
import type { Styleframe, StyleframeOptions } from "@styleframe/core";
|
|
2
|
+
import {
|
|
3
|
+
createAtRuleFunction,
|
|
4
|
+
createCssFunction,
|
|
5
|
+
createRefFunction,
|
|
6
|
+
createSelectorFunction,
|
|
7
|
+
createThemeFunction,
|
|
8
|
+
createUtilityFunction,
|
|
9
|
+
createVariableFunction,
|
|
10
|
+
styleframe,
|
|
11
|
+
} from "@styleframe/core";
|
|
12
|
+
import { createFile, transpile } from "./transpile";
|
|
13
|
+
|
|
14
|
+
describe("transpile", () => {
|
|
15
|
+
let instance: Styleframe;
|
|
16
|
+
let variable: ReturnType<typeof createVariableFunction>;
|
|
17
|
+
let ref: ReturnType<typeof createRefFunction>;
|
|
18
|
+
let css: ReturnType<typeof createCssFunction>;
|
|
19
|
+
let selector: ReturnType<typeof createSelectorFunction>;
|
|
20
|
+
let theme: ReturnType<typeof createThemeFunction>;
|
|
21
|
+
let atRule: ReturnType<typeof createAtRuleFunction>;
|
|
22
|
+
let utility: ReturnType<typeof createUtilityFunction>;
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
instance = styleframe();
|
|
26
|
+
({
|
|
27
|
+
variable = variable,
|
|
28
|
+
ref = ref,
|
|
29
|
+
css = css,
|
|
30
|
+
selector = selector,
|
|
31
|
+
theme = theme,
|
|
32
|
+
atRule = atRule,
|
|
33
|
+
utility = utility,
|
|
34
|
+
} = instance);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe("createFile", () => {
|
|
38
|
+
it("should create a file with name and content", () => {
|
|
39
|
+
const file = createFile("test.css", "body { margin: 0; }");
|
|
40
|
+
|
|
41
|
+
expect(file.name).toBe("test.css");
|
|
42
|
+
expect(file.content).toEqual("body { margin: 0; }");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("should create a file with empty content when no content provided", () => {
|
|
46
|
+
const file = createFile("empty.css");
|
|
47
|
+
|
|
48
|
+
expect(file.name).toBe("empty.css");
|
|
49
|
+
expect(file.content).toEqual("");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should create a file with content string", () => {
|
|
53
|
+
const content = ":root {\n\t--color: #000;\n}";
|
|
54
|
+
const file = createFile("variables.css", content);
|
|
55
|
+
|
|
56
|
+
expect(file.name).toBe("variables.css");
|
|
57
|
+
expect(file.content).toEqual(content);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe("transpile", () => {
|
|
62
|
+
it("should transpile an empty Styleframe instance", () => {
|
|
63
|
+
const output = transpile(instance);
|
|
64
|
+
|
|
65
|
+
expect(output.files).toHaveLength(1);
|
|
66
|
+
expect(output.files[0]!.name).toBe("index.css");
|
|
67
|
+
expect(output.files[0]!.content).toEqual("");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("should transpile a simple variable", () => {
|
|
71
|
+
variable("primary-color", "#006cff");
|
|
72
|
+
|
|
73
|
+
const output = transpile(instance);
|
|
74
|
+
|
|
75
|
+
expect(output.files).toHaveLength(1);
|
|
76
|
+
expect(output.files[0]!.name).toBe("index.css");
|
|
77
|
+
expect(output.files[0]!.content).toEqual(`:root {
|
|
78
|
+
\t--primary-color: #006cff;
|
|
79
|
+
}`);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("should transpile multiple variables", () => {
|
|
83
|
+
variable("primary-color", "#006cff");
|
|
84
|
+
variable("secondary-color", "#ff6c00");
|
|
85
|
+
variable("font-size", "16px");
|
|
86
|
+
|
|
87
|
+
const output = transpile(instance);
|
|
88
|
+
const content = output.files[0]!.content;
|
|
89
|
+
|
|
90
|
+
expect(content).toEqual(`:root {
|
|
91
|
+
\t--primary-color: #006cff;
|
|
92
|
+
\t--secondary-color: #ff6c00;
|
|
93
|
+
\t--font-size: 16px;
|
|
94
|
+
}`);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("should transpile selectors", () => {
|
|
98
|
+
selector(".button", {
|
|
99
|
+
padding: "0.5rem 1rem",
|
|
100
|
+
backgroundColor: "#006cff",
|
|
101
|
+
color: "#ffffff",
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const output = transpile(instance);
|
|
105
|
+
const content = output.files[0]!.content;
|
|
106
|
+
|
|
107
|
+
expect(content).toEqual(`.button {
|
|
108
|
+
\tpadding: 0.5rem 1rem;
|
|
109
|
+
\tbackground-color: #006cff;
|
|
110
|
+
\tcolor: #ffffff;
|
|
111
|
+
}`);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("should transpile themes", () => {
|
|
115
|
+
theme("light", ({ variable: v }) => {
|
|
116
|
+
v("background-color", "#ffffff");
|
|
117
|
+
v("text-color", "#000000");
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
theme("dark", ({ variable: v }) => {
|
|
121
|
+
v("background-color", "#000000");
|
|
122
|
+
v("text-color", "#ffffff");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const output = transpile(instance);
|
|
126
|
+
const content = output.files[0]!.content;
|
|
127
|
+
|
|
128
|
+
expect(content).toEqual(`
|
|
129
|
+
|
|
130
|
+
[data-theme="light"] {
|
|
131
|
+
\t--background-color: #ffffff;
|
|
132
|
+
\t--text-color: #000000;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
[data-theme="dark"] {
|
|
136
|
+
\t--background-color: #000000;
|
|
137
|
+
\t--text-color: #ffffff;
|
|
138
|
+
}`);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("should transpile at-rules", () => {
|
|
142
|
+
atRule("media", "(min-width: 768px)", ({ selector: s }) => {
|
|
143
|
+
s(".container", {
|
|
144
|
+
maxWidth: "1200px",
|
|
145
|
+
margin: "0 auto",
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const output = transpile(instance);
|
|
150
|
+
const content = output.files[0]!.content;
|
|
151
|
+
|
|
152
|
+
expect(content).toEqual(`@media (min-width: 768px) {
|
|
153
|
+
\t.container {
|
|
154
|
+
\t\tmax-width: 1200px;
|
|
155
|
+
\t\tmargin: 0 auto;
|
|
156
|
+
\t}
|
|
157
|
+
}`);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("should transpile utilities", () => {
|
|
161
|
+
const createMarginUtility = utility("margin", ({ value }) => ({
|
|
162
|
+
margin: value,
|
|
163
|
+
}));
|
|
164
|
+
|
|
165
|
+
createMarginUtility({
|
|
166
|
+
sm: "8px",
|
|
167
|
+
md: "16px",
|
|
168
|
+
lg: "24px",
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const output = transpile(instance);
|
|
172
|
+
const content = output.files[0]!.content;
|
|
173
|
+
|
|
174
|
+
expect(content).toEqual(`._margin\\:sm {
|
|
175
|
+
\tmargin: 8px;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
._margin\\:md {
|
|
179
|
+
\tmargin: 16px;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
._margin\\:lg {
|
|
183
|
+
\tmargin: 24px;
|
|
184
|
+
}`);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("should transpile with custom options", () => {
|
|
188
|
+
const customOptions: StyleframeOptions = {
|
|
189
|
+
variables: {
|
|
190
|
+
name: ({ name }) => `--app-${name}`,
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const customInstance = styleframe(customOptions);
|
|
195
|
+
const customRoot = customInstance.root;
|
|
196
|
+
const customVariable = createVariableFunction(customRoot, customRoot);
|
|
197
|
+
|
|
198
|
+
customVariable("primary", "#006cff");
|
|
199
|
+
customVariable("secondary", "#ff6c00");
|
|
200
|
+
|
|
201
|
+
const output = transpile(customInstance);
|
|
202
|
+
const content = output.files[0]!.content;
|
|
203
|
+
|
|
204
|
+
expect(content).toEqual(`:root {
|
|
205
|
+
\t--app-primary: #006cff;
|
|
206
|
+
\t--app-secondary: #ff6c00;
|
|
207
|
+
}`);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("should handle variable references", () => {
|
|
211
|
+
const primaryColor = variable("primary-color", "#006cff");
|
|
212
|
+
|
|
213
|
+
selector(".button", {
|
|
214
|
+
backgroundColor: ref(primaryColor),
|
|
215
|
+
border: css`2px solid ${ref(primaryColor)}`,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const output = transpile(instance);
|
|
219
|
+
const content = output.files[0]!.content;
|
|
220
|
+
|
|
221
|
+
expect(content).toEqual(`:root {
|
|
222
|
+
\t--primary-color: #006cff;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.button {
|
|
226
|
+
\tbackground-color: var(--primary-color);
|
|
227
|
+
\tborder: 2px solid var(--primary-color);
|
|
228
|
+
}`);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("should handle nested selectors", () => {
|
|
232
|
+
selector(".card", ({ selector: s }) => {
|
|
233
|
+
s(".title", {
|
|
234
|
+
fontSize: "1.5rem",
|
|
235
|
+
fontWeight: "bold",
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
s("&:hover", {
|
|
239
|
+
transform: "translateY(-2px)",
|
|
240
|
+
boxShadow: "0 4px 8px rgba(0,0,0,0.1)",
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
padding: "1rem",
|
|
245
|
+
borderRadius: "8px",
|
|
246
|
+
};
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const output = transpile(instance);
|
|
250
|
+
const content = output.files[0]!.content;
|
|
251
|
+
|
|
252
|
+
expect(content).toEqual(`.card {
|
|
253
|
+
\tpadding: 1rem;
|
|
254
|
+
\tborder-radius: 8px;
|
|
255
|
+
\t
|
|
256
|
+
\t.title {
|
|
257
|
+
\t\tfont-size: 1.5rem;
|
|
258
|
+
\t\tfont-weight: bold;
|
|
259
|
+
\t}
|
|
260
|
+
\t
|
|
261
|
+
\t&:hover {
|
|
262
|
+
\t\ttransform: translateY(-2px);
|
|
263
|
+
\t\tbox-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
|
264
|
+
\t}
|
|
265
|
+
}`);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("should transpile a complex scenario with utilities, modifiers, themes, and nested structures", () => {
|
|
269
|
+
// Define global variables
|
|
270
|
+
const primaryColor = variable("primary-color", "#006cff");
|
|
271
|
+
const secondaryColor = variable("secondary-color", "#ff6c00");
|
|
272
|
+
variable("font-family", "'Inter', sans-serif");
|
|
273
|
+
variable("spacing-unit", "8px");
|
|
274
|
+
|
|
275
|
+
// Create utilities
|
|
276
|
+
const createPaddingUtility = utility("padding", ({ value }) => ({
|
|
277
|
+
padding: value,
|
|
278
|
+
}));
|
|
279
|
+
|
|
280
|
+
const createFlexUtility = utility("flex", ({ value }) => ({
|
|
281
|
+
display: "flex",
|
|
282
|
+
flexDirection: value,
|
|
283
|
+
}));
|
|
284
|
+
|
|
285
|
+
createPaddingUtility({
|
|
286
|
+
sm: "8px",
|
|
287
|
+
md: "16px",
|
|
288
|
+
lg: "24px",
|
|
289
|
+
xl: "32px",
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
createFlexUtility({
|
|
293
|
+
row: "row",
|
|
294
|
+
col: "column",
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// Create base styles
|
|
298
|
+
selector("*", {
|
|
299
|
+
boxSizing: "border-box",
|
|
300
|
+
margin: "0",
|
|
301
|
+
padding: "0",
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
selector("body", {
|
|
305
|
+
fontFamily: ref("font-family"),
|
|
306
|
+
lineHeight: "1.6",
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// Create component styles with nested selectors and modifiers
|
|
310
|
+
selector(".button", ({ selector: s, variable: v }) => {
|
|
311
|
+
v("button-bg", ref(primaryColor));
|
|
312
|
+
v("button-hover-bg", ref(secondaryColor));
|
|
313
|
+
|
|
314
|
+
s("&:hover", {
|
|
315
|
+
backgroundColor: ref("button-hover-bg"),
|
|
316
|
+
transform: "scale(1.05)",
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
s("&:active", {
|
|
320
|
+
transform: "scale(0.98)",
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
s("&.button--large", {
|
|
324
|
+
padding: "1rem 2rem",
|
|
325
|
+
fontSize: "1.125rem",
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
s("&.button--disabled", {
|
|
329
|
+
opacity: "0.5",
|
|
330
|
+
cursor: "not-allowed",
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
s(".button__icon", {
|
|
334
|
+
marginRight: "0.5rem",
|
|
335
|
+
verticalAlign: "middle",
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
backgroundColor: ref("button-bg"),
|
|
340
|
+
color: "#ffffff",
|
|
341
|
+
padding: "0.75rem 1.5rem",
|
|
342
|
+
border: "none",
|
|
343
|
+
borderRadius: "4px",
|
|
344
|
+
cursor: "pointer",
|
|
345
|
+
transition: "all 0.3s ease",
|
|
346
|
+
fontSize: "1rem",
|
|
347
|
+
};
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// Create card component with complex nesting
|
|
351
|
+
selector(".card", ({ selector: s, variable: v }) => {
|
|
352
|
+
v("card-shadow", "0 2px 8px rgba(0,0,0,0.1)");
|
|
353
|
+
v("card-bg", "#ffffff");
|
|
354
|
+
|
|
355
|
+
s(".card__header", ({ selector: nested }) => {
|
|
356
|
+
nested(".card__title", {
|
|
357
|
+
fontSize: "1.5rem",
|
|
358
|
+
fontWeight: "600",
|
|
359
|
+
color: ref(primaryColor),
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
nested(".card__subtitle", {
|
|
363
|
+
fontSize: "0.875rem",
|
|
364
|
+
color: "#666666",
|
|
365
|
+
marginTop: "0.25rem",
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
return {
|
|
369
|
+
padding: "1.5rem",
|
|
370
|
+
borderBottom: "1px solid #e0e0e0",
|
|
371
|
+
};
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
s(".card__body", {
|
|
375
|
+
padding: "1.5rem",
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
s(".card__footer", ({ selector: nested }) => {
|
|
379
|
+
nested(".button", {
|
|
380
|
+
marginRight: "0.5rem",
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
return {
|
|
384
|
+
padding: "1rem 1.5rem",
|
|
385
|
+
borderTop: "1px solid #e0e0e0",
|
|
386
|
+
display: "flex",
|
|
387
|
+
justifyContent: "flex-end",
|
|
388
|
+
};
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
s("&:hover", {
|
|
392
|
+
boxShadow: "0 4px 16px rgba(0,0,0,0.15)",
|
|
393
|
+
transform: "translateY(-2px)",
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
return {
|
|
397
|
+
backgroundColor: ref("card-bg"),
|
|
398
|
+
boxShadow: ref("card-shadow"),
|
|
399
|
+
borderRadius: "8px",
|
|
400
|
+
overflow: "hidden",
|
|
401
|
+
transition: "all 0.3s ease",
|
|
402
|
+
};
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
// Create themes with overrides
|
|
406
|
+
theme("light", ({ variable: v, selector: s }) => {
|
|
407
|
+
v("bg-primary", "#ffffff");
|
|
408
|
+
v("bg-secondary", "#f5f5f5");
|
|
409
|
+
v("text-primary", "#1a1a1a");
|
|
410
|
+
v("text-secondary", "#666666");
|
|
411
|
+
v("border-color", "#e0e0e0");
|
|
412
|
+
|
|
413
|
+
s(".card", ({ variable: cardVar }) => {
|
|
414
|
+
cardVar("card-bg", ref("bg-primary"));
|
|
415
|
+
cardVar("card-shadow", "0 2px 8px rgba(0,0,0,0.08)");
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
s(".button", ({ variable: btnVar }) => {
|
|
419
|
+
btnVar("button-bg", ref(primaryColor));
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
theme("dark", ({ variable: v, selector: s }) => {
|
|
424
|
+
v("bg-primary", "#1a1a1a");
|
|
425
|
+
v("bg-secondary", "#2d2d2d");
|
|
426
|
+
v("text-primary", "#ffffff");
|
|
427
|
+
v("text-secondary", "#b0b0b0");
|
|
428
|
+
v("border-color", "#404040");
|
|
429
|
+
|
|
430
|
+
s("body", {
|
|
431
|
+
backgroundColor: ref("bg-primary"),
|
|
432
|
+
color: ref("text-primary"),
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
s(".card", ({ variable: cardVar }) => {
|
|
436
|
+
cardVar("card-bg", ref("bg-secondary"));
|
|
437
|
+
cardVar("card-shadow", "0 2px 8px rgba(0,0,0,0.3)");
|
|
438
|
+
|
|
439
|
+
return {
|
|
440
|
+
borderColor: ref("border-color"),
|
|
441
|
+
};
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
s(".button", ({ variable: btnVar }) => {
|
|
445
|
+
btnVar("button-bg", "#0080ff");
|
|
446
|
+
btnVar("button-hover-bg", "#0066cc");
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// Create responsive at-rules
|
|
451
|
+
atRule("media", "(min-width: 768px)", ({ selector: s }) => {
|
|
452
|
+
s(".container", {
|
|
453
|
+
maxWidth: "768px",
|
|
454
|
+
margin: "0 auto",
|
|
455
|
+
padding: "0 1rem",
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
s(".card", {
|
|
459
|
+
maxWidth: "600px",
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
atRule("media", "(min-width: 1024px)", ({ selector: s }) => {
|
|
464
|
+
s(".container", {
|
|
465
|
+
maxWidth: "1024px",
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
s(".grid", {
|
|
469
|
+
display: "grid",
|
|
470
|
+
gridTemplateColumns: "repeat(3, 1fr)",
|
|
471
|
+
gap: "1.5rem",
|
|
472
|
+
});
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// Create print styles
|
|
476
|
+
atRule("media", "print", ({ selector: s }) => {
|
|
477
|
+
s("body", {
|
|
478
|
+
backgroundColor: "#ffffff",
|
|
479
|
+
color: "#000000",
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
s(".button", {
|
|
483
|
+
display: "none",
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
s(".card", {
|
|
487
|
+
boxShadow: "none",
|
|
488
|
+
border: "1px solid #000000",
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
// Create animation keyframes
|
|
493
|
+
atRule("keyframes", "fadeIn", {
|
|
494
|
+
"0%": {
|
|
495
|
+
opacity: "0",
|
|
496
|
+
},
|
|
497
|
+
"100%": {
|
|
498
|
+
opacity: "1",
|
|
499
|
+
},
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
atRule("keyframes", "slideUp", {
|
|
503
|
+
"0%": {
|
|
504
|
+
transform: "translateY(20px)",
|
|
505
|
+
opacity: "0",
|
|
506
|
+
},
|
|
507
|
+
"100%": {
|
|
508
|
+
transform: "translateY(0)",
|
|
509
|
+
opacity: "1",
|
|
510
|
+
},
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
// Create animation classes using the keyframes
|
|
514
|
+
selector(".fade-in", {
|
|
515
|
+
animation: "fadeIn 0.3s ease-in-out",
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
selector(".slide-up", {
|
|
519
|
+
animation: "slideUp 0.5s ease-out",
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
// Transpile the complex scenario
|
|
523
|
+
const output = transpile(instance);
|
|
524
|
+
|
|
525
|
+
expect(output.files).toHaveLength(1);
|
|
526
|
+
expect(output.files[0]!.name).toBe("index.css");
|
|
527
|
+
|
|
528
|
+
const content = output.files[0]!.content;
|
|
529
|
+
|
|
530
|
+
// This is a complex test that validates the structure is correct
|
|
531
|
+
// TODO: Fix keyframes output - currently outputs as [object Object] instead of proper CSS properties
|
|
532
|
+
// Note: The order of items matters - atRules and keyframes come before themes in the output
|
|
533
|
+
expect(content).toEqual(`:root {
|
|
534
|
+
\t--primary-color: #006cff;
|
|
535
|
+
\t--secondary-color: #ff6c00;
|
|
536
|
+
\t--font-family: 'Inter', sans-serif;
|
|
537
|
+
\t--spacing-unit: 8px;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
._padding\\:sm {
|
|
541
|
+
\tpadding: 8px;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
._padding\\:md {
|
|
545
|
+
\tpadding: 16px;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
._padding\\:lg {
|
|
549
|
+
\tpadding: 24px;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
._padding\\:xl {
|
|
553
|
+
\tpadding: 32px;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
._flex\\:row {
|
|
557
|
+
\tdisplay: flex;
|
|
558
|
+
\tflex-direction: row;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
._flex\\:col {
|
|
562
|
+
\tdisplay: flex;
|
|
563
|
+
\tflex-direction: column;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
* {
|
|
567
|
+
\tbox-sizing: border-box;
|
|
568
|
+
\tmargin: 0;
|
|
569
|
+
\tpadding: 0;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
body {
|
|
573
|
+
\tfont-family: var(--font-family);
|
|
574
|
+
\tline-height: 1.6;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.button {
|
|
578
|
+
\t--button-bg: var(--primary-color);
|
|
579
|
+
\t--button-hover-bg: var(--secondary-color);
|
|
580
|
+
\t
|
|
581
|
+
\tbackground-color: var(--button-bg);
|
|
582
|
+
\tcolor: #ffffff;
|
|
583
|
+
\tpadding: 0.75rem 1.5rem;
|
|
584
|
+
\tborder: none;
|
|
585
|
+
\tborder-radius: 4px;
|
|
586
|
+
\tcursor: pointer;
|
|
587
|
+
\ttransition: all 0.3s ease;
|
|
588
|
+
\tfont-size: 1rem;
|
|
589
|
+
\t
|
|
590
|
+
\t&:hover {
|
|
591
|
+
\t\tbackground-color: var(--button-hover-bg);
|
|
592
|
+
\t\ttransform: scale(1.05);
|
|
593
|
+
\t}
|
|
594
|
+
\t
|
|
595
|
+
\t&:active {
|
|
596
|
+
\t\ttransform: scale(0.98);
|
|
597
|
+
\t}
|
|
598
|
+
\t
|
|
599
|
+
\t&.button--large {
|
|
600
|
+
\t\tpadding: 1rem 2rem;
|
|
601
|
+
\t\tfont-size: 1.125rem;
|
|
602
|
+
\t}
|
|
603
|
+
\t
|
|
604
|
+
\t&.button--disabled {
|
|
605
|
+
\t\topacity: 0.5;
|
|
606
|
+
\t\tcursor: not-allowed;
|
|
607
|
+
\t}
|
|
608
|
+
\t
|
|
609
|
+
\t.button__icon {
|
|
610
|
+
\t\tmargin-right: 0.5rem;
|
|
611
|
+
\t\tvertical-align: middle;
|
|
612
|
+
\t}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
.card {
|
|
616
|
+
\t--card-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
617
|
+
\t--card-bg: #ffffff;
|
|
618
|
+
\t
|
|
619
|
+
\tbackground-color: var(--card-bg);
|
|
620
|
+
\tbox-shadow: var(--card-shadow);
|
|
621
|
+
\tborder-radius: 8px;
|
|
622
|
+
\toverflow: hidden;
|
|
623
|
+
\ttransition: all 0.3s ease;
|
|
624
|
+
\t
|
|
625
|
+
\t.card__header {
|
|
626
|
+
\t\tpadding: 1.5rem;
|
|
627
|
+
\t\tborder-bottom: 1px solid #e0e0e0;
|
|
628
|
+
\t\t
|
|
629
|
+
\t\t.card__title {
|
|
630
|
+
\t\t\tfont-size: 1.5rem;
|
|
631
|
+
\t\t\tfont-weight: 600;
|
|
632
|
+
\t\t\tcolor: var(--primary-color);
|
|
633
|
+
\t\t}
|
|
634
|
+
\t\t
|
|
635
|
+
\t\t.card__subtitle {
|
|
636
|
+
\t\t\tfont-size: 0.875rem;
|
|
637
|
+
\t\t\tcolor: #666666;
|
|
638
|
+
\t\t\tmargin-top: 0.25rem;
|
|
639
|
+
\t\t}
|
|
640
|
+
\t}
|
|
641
|
+
\t
|
|
642
|
+
\t.card__body {
|
|
643
|
+
\t\tpadding: 1.5rem;
|
|
644
|
+
\t}
|
|
645
|
+
\t
|
|
646
|
+
\t.card__footer {
|
|
647
|
+
\t\tpadding: 1rem 1.5rem;
|
|
648
|
+
\t\tborder-top: 1px solid #e0e0e0;
|
|
649
|
+
\t\tdisplay: flex;
|
|
650
|
+
\t\tjustify-content: flex-end;
|
|
651
|
+
\t\t
|
|
652
|
+
\t\t.button {
|
|
653
|
+
\t\t\tmargin-right: 0.5rem;
|
|
654
|
+
\t\t}
|
|
655
|
+
\t}
|
|
656
|
+
\t
|
|
657
|
+
\t&:hover {
|
|
658
|
+
\t\tbox-shadow: 0 4px 16px rgba(0,0,0,0.15);
|
|
659
|
+
\t\ttransform: translateY(-2px);
|
|
660
|
+
\t}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
@media (min-width: 768px) {
|
|
664
|
+
\t.container {
|
|
665
|
+
\t\tmax-width: 768px;
|
|
666
|
+
\t\tmargin: 0 auto;
|
|
667
|
+
\t\tpadding: 0 1rem;
|
|
668
|
+
\t}
|
|
669
|
+
\t
|
|
670
|
+
\t.card {
|
|
671
|
+
\t\tmax-width: 600px;
|
|
672
|
+
\t}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
@media (min-width: 1024px) {
|
|
676
|
+
\t.container {
|
|
677
|
+
\t\tmax-width: 1024px;
|
|
678
|
+
\t}
|
|
679
|
+
\t
|
|
680
|
+
\t.grid {
|
|
681
|
+
\t\tdisplay: grid;
|
|
682
|
+
\t\tgrid-template-columns: repeat(3, 1fr);
|
|
683
|
+
\t\tgap: 1.5rem;
|
|
684
|
+
\t}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
@media print {
|
|
688
|
+
\tbody {
|
|
689
|
+
\t\tbackground-color: #ffffff;
|
|
690
|
+
\t\tcolor: #000000;
|
|
691
|
+
\t}
|
|
692
|
+
\t
|
|
693
|
+
\t.button {
|
|
694
|
+
\t\tdisplay: none;
|
|
695
|
+
\t}
|
|
696
|
+
\t
|
|
697
|
+
\t.card {
|
|
698
|
+
\t\tbox-shadow: none;
|
|
699
|
+
\t\tborder: 1px solid #000000;
|
|
700
|
+
\t}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
@keyframes fadeIn {
|
|
704
|
+
\t0%: [object Object];
|
|
705
|
+
\t100%: [object Object];
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
@keyframes slideUp {
|
|
709
|
+
\t0%: [object Object];
|
|
710
|
+
\t100%: [object Object];
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
.fade-in {
|
|
714
|
+
\tanimation: fadeIn 0.3s ease-in-out;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
.slide-up {
|
|
718
|
+
\tanimation: slideUp 0.5s ease-out;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
[data-theme="light"] {
|
|
722
|
+
\t--bg-primary: #ffffff;
|
|
723
|
+
\t--bg-secondary: #f5f5f5;
|
|
724
|
+
\t--text-primary: #1a1a1a;
|
|
725
|
+
\t--text-secondary: #666666;
|
|
726
|
+
\t--border-color: #e0e0e0;
|
|
727
|
+
\t
|
|
728
|
+
\t.card {
|
|
729
|
+
\t\t--card-bg: var(--bg-primary);
|
|
730
|
+
\t\t--card-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
|
731
|
+
\t}
|
|
732
|
+
\t
|
|
733
|
+
\t.button {
|
|
734
|
+
\t\t--button-bg: var(--primary-color);
|
|
735
|
+
\t}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
[data-theme="dark"] {
|
|
739
|
+
\t--bg-primary: #1a1a1a;
|
|
740
|
+
\t--bg-secondary: #2d2d2d;
|
|
741
|
+
\t--text-primary: #ffffff;
|
|
742
|
+
\t--text-secondary: #b0b0b0;
|
|
743
|
+
\t--border-color: #404040;
|
|
744
|
+
\t
|
|
745
|
+
\tbody {
|
|
746
|
+
\t\tbackground-color: var(--bg-primary);
|
|
747
|
+
\t\tcolor: var(--text-primary);
|
|
748
|
+
\t}
|
|
749
|
+
\t
|
|
750
|
+
\t.card {
|
|
751
|
+
\t\t--card-bg: var(--bg-secondary);
|
|
752
|
+
\t\t--card-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
|
753
|
+
\t\t
|
|
754
|
+
\t\tborder-color: var(--border-color);
|
|
755
|
+
\t}
|
|
756
|
+
\t
|
|
757
|
+
\t.button {
|
|
758
|
+
\t\t--button-bg: #0080ff;
|
|
759
|
+
\t\t--button-hover-bg: #0066cc;
|
|
760
|
+
\t}
|
|
761
|
+
}`);
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
it("should maintain output file structure", () => {
|
|
765
|
+
variable("test", "value");
|
|
766
|
+
selector(".test", { color: "red" });
|
|
767
|
+
|
|
768
|
+
const output = transpile(instance);
|
|
769
|
+
|
|
770
|
+
expect(output).toHaveProperty("files");
|
|
771
|
+
expect(Array.isArray(output.files)).toBe(true);
|
|
772
|
+
expect(output.files[0]!).toHaveProperty("name");
|
|
773
|
+
expect(output.files[0]!).toHaveProperty("content");
|
|
774
|
+
expect(typeof output.files[0]!.content).toBe("string");
|
|
775
|
+
|
|
776
|
+
const content = output.files[0]!.content;
|
|
777
|
+
expect(content).toEqual(`:root {
|
|
778
|
+
\t--test: value;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
.test {
|
|
782
|
+
\tcolor: red;
|
|
783
|
+
}`);
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
it("should pass options to consume function", () => {
|
|
787
|
+
const customOptions: StyleframeOptions = {
|
|
788
|
+
variables: {
|
|
789
|
+
name: ({ name }) => `--custom-${name}`,
|
|
790
|
+
},
|
|
791
|
+
};
|
|
792
|
+
|
|
793
|
+
const customInstance = styleframe(customOptions);
|
|
794
|
+
const customRoot = customInstance.root;
|
|
795
|
+
const customVariable = createVariableFunction(customRoot, customRoot);
|
|
796
|
+
const customUtility = createUtilityFunction(customRoot, customRoot);
|
|
797
|
+
|
|
798
|
+
customVariable("color", "#123456");
|
|
799
|
+
|
|
800
|
+
const createSpacingUtility = customUtility("space", ({ value }) => ({
|
|
801
|
+
marginBottom: value,
|
|
802
|
+
}));
|
|
803
|
+
|
|
804
|
+
createSpacingUtility({
|
|
805
|
+
small: "4px",
|
|
806
|
+
large: "16px",
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
const output = transpile(customInstance);
|
|
810
|
+
const content = output.files[0]!.content;
|
|
811
|
+
|
|
812
|
+
expect(content).toEqual(`:root {
|
|
813
|
+
\t--custom-color: #123456;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
._space\\:small {
|
|
817
|
+
\tmargin-bottom: 4px;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
._space\\:large {
|
|
821
|
+
\tmargin-bottom: 16px;
|
|
822
|
+
}`);
|
|
823
|
+
});
|
|
824
|
+
});
|
|
825
|
+
});
|