@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.
Files changed (57) hide show
  1. package/.tsbuildinfo +1 -0
  2. package/CHANGELOG.md +12 -0
  3. package/package.json +43 -0
  4. package/src/constants.ts +4 -0
  5. package/src/consume/at-rule.test.ts +339 -0
  6. package/src/consume/at-rule.ts +34 -0
  7. package/src/consume/consume.test.ts +259 -0
  8. package/src/consume/consume.ts +60 -0
  9. package/src/consume/container.test.ts +501 -0
  10. package/src/consume/container.ts +73 -0
  11. package/src/consume/css.test.ts +184 -0
  12. package/src/consume/css.ts +17 -0
  13. package/src/consume/declarations.test.ts +210 -0
  14. package/src/consume/declarations.ts +17 -0
  15. package/src/consume/index.ts +12 -0
  16. package/src/consume/primitive.test.ts +52 -0
  17. package/src/consume/primitive.ts +16 -0
  18. package/src/consume/ref.test.ts +84 -0
  19. package/src/consume/ref.ts +22 -0
  20. package/src/consume/root.test.ts +353 -0
  21. package/src/consume/root.ts +19 -0
  22. package/src/consume/selector.test.ts +441 -0
  23. package/src/consume/selector.ts +17 -0
  24. package/src/consume/theme.test.ts +215 -0
  25. package/src/consume/theme.ts +15 -0
  26. package/src/consume/utility.test.ts +696 -0
  27. package/src/consume/utility.ts +31 -0
  28. package/src/consume/variable.test.ts +197 -0
  29. package/src/consume/variable.ts +20 -0
  30. package/src/defaults.ts +21 -0
  31. package/src/generator/genAtRuleQuery.test.ts +148 -0
  32. package/src/generator/genAtRuleQuery.ts +3 -0
  33. package/src/generator/genDeclaration.test.ts +283 -0
  34. package/src/generator/genDeclaration.ts +9 -0
  35. package/src/generator/genDeclarationsBlock.test.ts +278 -0
  36. package/src/generator/genDeclarationsBlock.ts +7 -0
  37. package/src/generator/genDeclareVariable.test.ts +323 -0
  38. package/src/generator/genDeclareVariable.ts +6 -0
  39. package/src/generator/genInlineAtRule.test.ts +351 -0
  40. package/src/generator/genInlineAtRule.ts +5 -0
  41. package/src/generator/genReferenceVariable.test.ts +392 -0
  42. package/src/generator/genReferenceVariable.ts +5 -0
  43. package/src/generator/genSafePropertyName.test.ts +489 -0
  44. package/src/generator/genSafePropertyName.ts +5 -0
  45. package/src/generator/genSafeVariableName.test.ts +358 -0
  46. package/src/generator/genSafeVariableName.ts +21 -0
  47. package/src/generator/genSelector.test.ts +357 -0
  48. package/src/generator/genSelector.ts +5 -0
  49. package/src/generator/index.ts +9 -0
  50. package/src/index.ts +6 -0
  51. package/src/transpile.test.ts +825 -0
  52. package/src/transpile.ts +21 -0
  53. package/src/types.ts +15 -0
  54. package/src/utils.ts +18 -0
  55. package/src/vite-env.d.ts +1 -0
  56. package/tsconfig.json +7 -0
  57. package/vite.config.ts +5 -0
@@ -0,0 +1,501 @@
1
+ import type {
2
+ DeclarationsBlock,
3
+ Root,
4
+ StyleframeOptions,
5
+ } from "@styleframe/core";
6
+ import {
7
+ createRefFunction,
8
+ createRoot,
9
+ createSelectorFunction,
10
+ createVariableFunction,
11
+ } from "@styleframe/core";
12
+ import { createContainerConsumer } from "./container";
13
+ import { consume } from "./consume";
14
+
15
+ describe("createContainerConsumer", () => {
16
+ let root: Root;
17
+ let variable: ReturnType<typeof createVariableFunction>;
18
+ let ref: ReturnType<typeof createRefFunction>;
19
+ let selector: ReturnType<typeof createSelectorFunction>;
20
+
21
+ const consumeContainer = createContainerConsumer(consume);
22
+ const options: StyleframeOptions = {};
23
+
24
+ beforeEach(() => {
25
+ root = createRoot();
26
+ variable = createVariableFunction(root, root);
27
+ ref = createRefFunction(root, root);
28
+ selector = createSelectorFunction(root, root);
29
+ });
30
+
31
+ it("should handle empty container with only query", () => {
32
+ const result = consumeContainer(
33
+ ".test",
34
+ {
35
+ variables: [],
36
+ declarations: {},
37
+ children: [],
38
+ },
39
+ options,
40
+ );
41
+ expect(result).toBe(".test {}");
42
+ });
43
+
44
+ it("should handle container with only variables", () => {
45
+ const colorVar = variable("color", "#ff0000");
46
+ const result = consumeContainer(
47
+ ".test",
48
+ {
49
+ variables: [colorVar],
50
+ declarations: {},
51
+ children: [],
52
+ },
53
+ options,
54
+ );
55
+ expect(result).toBe(`.test {
56
+ \t--color: #ff0000;
57
+ }`);
58
+ });
59
+
60
+ it("should handle container with only declarations", () => {
61
+ const declarations: DeclarationsBlock = {
62
+ color: "red",
63
+ "font-size": "16px",
64
+ };
65
+ const result = consumeContainer(
66
+ ".test",
67
+ {
68
+ variables: [],
69
+ declarations,
70
+ children: [],
71
+ },
72
+ options,
73
+ );
74
+ expect(result).toBe(`.test {
75
+ \tcolor: red;
76
+ \tfont-size: 16px;
77
+ }`);
78
+ });
79
+
80
+ it("should handle container with only children", () => {
81
+ const childSelector = selector("&:hover", {
82
+ color: "blue",
83
+ });
84
+ const result = consumeContainer(
85
+ ".test",
86
+ {
87
+ variables: [],
88
+ declarations: {},
89
+ children: [childSelector],
90
+ },
91
+ options,
92
+ );
93
+ expect(result).toBe(`.test {
94
+ \t&:hover {
95
+ \t\tcolor: blue;
96
+ \t}
97
+ }`);
98
+ });
99
+
100
+ it("should handle container with variables and declarations", () => {
101
+ const colorVar = variable("primary", "#0066ff");
102
+ const declarations: DeclarationsBlock = {
103
+ color: ref(colorVar),
104
+ };
105
+ const result = consumeContainer(
106
+ ".button",
107
+ {
108
+ variables: [colorVar],
109
+ declarations,
110
+ children: [],
111
+ },
112
+ options,
113
+ );
114
+ expect(result).toBe(`.button {
115
+ \t--primary: #0066ff;
116
+ \t
117
+ \tcolor: var(--primary);
118
+ }`);
119
+ });
120
+
121
+ it("should handle container with variables and children", () => {
122
+ const colorVar = variable("hover-color", "#ff6b6b");
123
+ const childSelector = selector("&:hover", {
124
+ color: ref(colorVar),
125
+ });
126
+ const result = consumeContainer(
127
+ ".card",
128
+ {
129
+ variables: [colorVar],
130
+ declarations: {},
131
+ children: [childSelector],
132
+ },
133
+ options,
134
+ );
135
+ expect(result).toBe(`.card {
136
+ \t--hover-color: #ff6b6b;
137
+ \t
138
+ \t&:hover {
139
+ \t\tcolor: var(--hover-color);
140
+ \t}
141
+ }`);
142
+ });
143
+
144
+ it("should handle container with declarations and children", () => {
145
+ const declarations: DeclarationsBlock = {
146
+ display: "flex",
147
+ "align-items": "center",
148
+ };
149
+ const childSelector = selector("&:focus", {
150
+ outline: "2px solid blue",
151
+ });
152
+ const result = consumeContainer(
153
+ ".component",
154
+ {
155
+ variables: [],
156
+ declarations,
157
+ children: [childSelector],
158
+ },
159
+ options,
160
+ );
161
+ expect(result).toBe(`.component {
162
+ \tdisplay: flex;
163
+ \talign-items: center;
164
+ \t
165
+ \t&:focus {
166
+ \t\toutline: 2px solid blue;
167
+ \t}
168
+ }`);
169
+ });
170
+
171
+ it("should handle container with variables, declarations, and children", () => {
172
+ const sizeVar = variable("size", "1rem");
173
+ const colorVar = variable("text-color", "#333");
174
+ const declarations: DeclarationsBlock = {
175
+ "font-size": ref(sizeVar),
176
+ color: ref(colorVar),
177
+ };
178
+ const hoverSelector = selector("&:hover", {
179
+ transform: "scale(1.05)",
180
+ });
181
+ const focusSelector = selector("&:focus", {
182
+ outline: "2px solid currentColor",
183
+ });
184
+
185
+ const result = consumeContainer(
186
+ ".interactive",
187
+ {
188
+ variables: [sizeVar, colorVar],
189
+ declarations,
190
+ children: [hoverSelector, focusSelector],
191
+ },
192
+ options,
193
+ );
194
+
195
+ const expected = `.interactive {
196
+ \t--size: 1rem;
197
+ \t--text-color: #333;
198
+ \t
199
+ \tfont-size: var(--size);
200
+ \tcolor: var(--text-color);
201
+ \t
202
+ \t&:hover {
203
+ \t\ttransform: scale(1.05);
204
+ \t}
205
+ \t
206
+ \t&:focus {
207
+ \t\toutline: 2px solid currentColor;
208
+ \t}
209
+ }`;
210
+ expect(result).toBe(expected);
211
+ });
212
+
213
+ it("should handle multiple variables of same type", () => {
214
+ const primaryVar = variable("primary", "#0066ff");
215
+ const secondaryVar = variable("secondary", "#ff6b6b");
216
+ const accentVar = variable("accent", "#00cc66");
217
+
218
+ const result = consumeContainer(
219
+ ".theme",
220
+ {
221
+ variables: [primaryVar, secondaryVar, accentVar],
222
+ declarations: {},
223
+ children: [],
224
+ },
225
+ options,
226
+ );
227
+
228
+ const expected = `.theme {
229
+ \t--primary: #0066ff;
230
+ \t--secondary: #ff6b6b;
231
+ \t--accent: #00cc66;
232
+ }`;
233
+ expect(result).toBe(expected);
234
+ });
235
+
236
+ it("should handle multiple children selectors", () => {
237
+ const child1 = selector("& > .item", {
238
+ margin: "0.5rem",
239
+ });
240
+ const child2 = selector("& .nested", {
241
+ padding: "1rem",
242
+ });
243
+ const child3 = selector("&::before", {
244
+ content: '""',
245
+ position: "absolute",
246
+ });
247
+
248
+ const result = consumeContainer(
249
+ ".container",
250
+ {
251
+ variables: [],
252
+ declarations: {},
253
+ children: [child1, child2, child3],
254
+ },
255
+ options,
256
+ );
257
+
258
+ const expected = `.container {
259
+ \t& > .item {
260
+ \t\tmargin: 0.5rem;
261
+ \t}
262
+ \t
263
+ \t& .nested {
264
+ \t\tpadding: 1rem;
265
+ \t}
266
+ \t
267
+ \t&::before {
268
+ \t\tcontent: "";
269
+ \t\tposition: absolute;
270
+ \t}
271
+ }`;
272
+ expect(result).toBe(expected);
273
+ });
274
+
275
+ it("should handle complex query strings", () => {
276
+ const result = consumeContainer(
277
+ "@media (min-width: 768px) and (max-width: 1024px)",
278
+ {
279
+ variables: [],
280
+ declarations: { display: "grid" },
281
+ children: [],
282
+ },
283
+ options,
284
+ );
285
+ expect(result).toBe(`@media (min-width: 768px) and (max-width: 1024px) {
286
+ \tdisplay: grid;
287
+ }`);
288
+ });
289
+
290
+ it("should handle nested container structures", () => {
291
+ const parentChild = selector("& .parent", ({ selector }) => {
292
+ selector("& .deeply-nested", {
293
+ "font-weight": "bold",
294
+ });
295
+
296
+ return {
297
+ position: "relative",
298
+ };
299
+ });
300
+
301
+ const result = consumeContainer(
302
+ ".root",
303
+ {
304
+ variables: [],
305
+ declarations: {},
306
+ children: [parentChild],
307
+ },
308
+ options,
309
+ );
310
+
311
+ const expected = `.root {
312
+ \t& .parent {
313
+ \t\tposition: relative;
314
+ \t\t
315
+ \t\t& .deeply-nested {
316
+ \t\t\tfont-weight: bold;
317
+ \t\t}
318
+ \t}
319
+ }`;
320
+ expect(result).toBe(expected);
321
+ });
322
+
323
+ it("should handle container with custom options prefix", () => {
324
+ const prefixOptions: StyleframeOptions = {
325
+ variables: {
326
+ name: ({ name }) => `--sf-${name}`,
327
+ },
328
+ };
329
+ const colorVar = variable("primary", "#0066ff");
330
+ const declarations: DeclarationsBlock = {
331
+ color: ref(colorVar),
332
+ };
333
+
334
+ const result = consumeContainer(
335
+ ".component",
336
+ {
337
+ variables: [colorVar],
338
+ declarations,
339
+ children: [],
340
+ },
341
+ prefixOptions,
342
+ );
343
+
344
+ expect(result).toBe(`.component {
345
+ \t--sf-primary: #0066ff;
346
+ \t
347
+ \tcolor: var(--sf-primary);
348
+ }`);
349
+ });
350
+
351
+ it("should handle variables with complex values", () => {
352
+ const gradientVar = variable(
353
+ "gradient",
354
+ "linear-gradient(45deg, #ff0000, #0000ff)",
355
+ );
356
+ const shadowVar = variable(
357
+ "shadow",
358
+ "0 2px 4px rgba(0, 0, 0, 0.1), 0 8px 16px rgba(0, 0, 0, 0.1)",
359
+ );
360
+
361
+ const result = consumeContainer(
362
+ ".complex",
363
+ {
364
+ variables: [gradientVar, shadowVar],
365
+ declarations: {},
366
+ children: [],
367
+ },
368
+ options,
369
+ );
370
+
371
+ const expected = `.complex {
372
+ \t--gradient: linear-gradient(45deg, #ff0000, #0000ff);
373
+ \t--shadow: 0 2px 4px rgba(0, 0, 0, 0.1), 0 8px 16px rgba(0, 0, 0, 0.1);
374
+ }`;
375
+ expect(result).toBe(expected);
376
+ });
377
+
378
+ it("should handle declarations with reference values", () => {
379
+ const sizeVar = variable("base-size", "16px");
380
+ const declarations: DeclarationsBlock = {
381
+ "font-size": ref(sizeVar),
382
+ "line-height": ref("line-height-normal", "1.5"),
383
+ padding: ref("spacing", "1rem"),
384
+ };
385
+
386
+ const result = consumeContainer(
387
+ ".text",
388
+ {
389
+ variables: [sizeVar],
390
+ declarations,
391
+ children: [],
392
+ },
393
+ options,
394
+ );
395
+
396
+ const expected = `.text {
397
+ \t--base-size: 16px;
398
+ \t
399
+ \tfont-size: var(--base-size);
400
+ \tline-height: var(--line-height-normal, 1.5);
401
+ \tpadding: var(--spacing, 1rem);
402
+ }`;
403
+ expect(result).toBe(expected);
404
+ });
405
+
406
+ it("should handle :root selector specially", () => {
407
+ const colorVar = variable("primary", "#0066ff");
408
+ const childSelector = selector(".button", {
409
+ color: ref(colorVar),
410
+ });
411
+
412
+ const result = consumeContainer(
413
+ ":root",
414
+ {
415
+ variables: [colorVar],
416
+ declarations: { fontSize: "16px" },
417
+ children: [childSelector],
418
+ },
419
+ options,
420
+ );
421
+
422
+ const expected = `:root {
423
+ \t--primary: #0066ff;
424
+ \t
425
+ \tfont-size: 16px;
426
+ }
427
+
428
+ .button {
429
+ \tcolor: var(--primary);
430
+ }`;
431
+ expect(result).toBe(expected);
432
+ });
433
+
434
+ it("should handle :root with only variables", () => {
435
+ const colorVar = variable("primary", "#0066ff");
436
+
437
+ const result = consumeContainer(
438
+ ":root",
439
+ {
440
+ variables: [colorVar],
441
+ declarations: {},
442
+ children: [],
443
+ },
444
+ options,
445
+ );
446
+
447
+ const expected = `:root {
448
+ \t--primary: #0066ff;
449
+ }`;
450
+ expect(result).toBe(expected);
451
+ });
452
+
453
+ it("should handle :root with only declarations", () => {
454
+ const result = consumeContainer(
455
+ ":root",
456
+ {
457
+ variables: [],
458
+ declarations: { fontSize: "16px" },
459
+ children: [],
460
+ },
461
+ options,
462
+ );
463
+
464
+ const expected = `:root {
465
+ \tfont-size: 16px;
466
+ }`;
467
+ expect(result).toBe(expected);
468
+ });
469
+
470
+ it("should handle :root with only children", () => {
471
+ const childSelector = selector(".button", {
472
+ color: "red",
473
+ });
474
+
475
+ const result = consumeContainer(
476
+ ":root",
477
+ {
478
+ variables: [],
479
+ declarations: {},
480
+ children: [childSelector],
481
+ },
482
+ options,
483
+ );
484
+
485
+ const expected = `.button {
486
+ \tcolor: red;
487
+ }`;
488
+ expect(result).toBe(expected);
489
+ });
490
+
491
+ it("should handle container with undefined properties gracefully", () => {
492
+ const result = consumeContainer(
493
+ ".test",
494
+ {
495
+ // All properties are optional, so we can omit them
496
+ },
497
+ options,
498
+ );
499
+ expect(result).toBe(".test {}");
500
+ });
501
+ });
@@ -0,0 +1,73 @@
1
+ import type {
2
+ ContainerChild,
3
+ DeclarationsBlock,
4
+ StyleframeOptions,
5
+ Variable,
6
+ } from "@styleframe/core";
7
+ import type { ConsumeFunction } from "../types";
8
+ import { addIndentToLine, indentLines } from "../utils";
9
+ import { createDeclarationsConsumer } from "./declarations";
10
+ import { createVariableConsumer } from "./variable";
11
+ import { genSelector } from "../generator";
12
+
13
+ /**
14
+ * Base function for consuming container-like structures (Selector, AtRule)
15
+ */
16
+ export function createContainerConsumer(consume: ConsumeFunction) {
17
+ const consumeVariable = createVariableConsumer(consume);
18
+ const consumeDeclarations = createDeclarationsConsumer(consume);
19
+
20
+ return function consumeContainer(
21
+ query: string,
22
+ instance: {
23
+ variables?: Variable[];
24
+ declarations?: DeclarationsBlock;
25
+ children?: ContainerChild[];
26
+ },
27
+ options: StyleframeOptions,
28
+ ): string {
29
+ const { variables, declarations, children } = instance;
30
+ const isRoot = query === ":root";
31
+
32
+ const consumedVariables = (variables ?? []).map((variable) =>
33
+ consumeVariable(variable, options),
34
+ );
35
+
36
+ const consumedDeclarations = consumeDeclarations(
37
+ declarations ?? {},
38
+ options,
39
+ );
40
+
41
+ const consumedChildren = (children ?? []).map((child) =>
42
+ consume(child, options),
43
+ );
44
+
45
+ const hasVariables = consumedVariables.length > 0;
46
+ const hasDeclarations = consumedDeclarations.length > 0;
47
+ const hasChildren = consumedChildren.length > 0;
48
+
49
+ if (isRoot) {
50
+ return `${
51
+ hasVariables || hasDeclarations
52
+ ? genSelector(query, [
53
+ ...consumedVariables,
54
+ ...(hasVariables && hasDeclarations ? [""] : []),
55
+ ...consumedDeclarations,
56
+ ])
57
+ : ""
58
+ }${
59
+ hasChildren && (hasVariables || hasDeclarations) ? "\n\n" : ""
60
+ }${consumedChildren.join("\n\n")}`;
61
+ }
62
+
63
+ return genSelector(query, [
64
+ ...consumedVariables,
65
+ ...(hasVariables && (hasChildren || hasDeclarations) ? [""] : []),
66
+ ...consumedDeclarations,
67
+ ...(hasDeclarations && hasChildren ? [""] : []),
68
+ ...consumedChildren.flatMap((child, index) =>
69
+ index === consumedChildren.length - 1 ? [child] : [child, ""],
70
+ ),
71
+ ]);
72
+ };
73
+ }