@styleframe/core 1.0.1 → 1.0.2

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 (48) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/LICENSE +21 -0
  3. package/dist/styleframe.d.ts +301 -0
  4. package/dist/styleframe.js +528 -0
  5. package/dist/styleframe.umd.cjs +1 -0
  6. package/package.json +13 -3
  7. package/.tsbuildinfo +0 -1
  8. package/src/index.ts +0 -5
  9. package/src/styleframe.ts +0 -64
  10. package/src/tokens/atRule.test.ts +0 -1013
  11. package/src/tokens/atRule.ts +0 -67
  12. package/src/tokens/css.test.ts +0 -404
  13. package/src/tokens/css.ts +0 -23
  14. package/src/tokens/declarations.test.ts +0 -584
  15. package/src/tokens/declarations.ts +0 -71
  16. package/src/tokens/index.ts +0 -11
  17. package/src/tokens/modifier.test.ts +0 -90
  18. package/src/tokens/modifier.ts +0 -86
  19. package/src/tokens/recipe.test.ts +0 -105
  20. package/src/tokens/recipe.ts +0 -32
  21. package/src/tokens/ref.test.ts +0 -430
  22. package/src/tokens/ref.ts +0 -24
  23. package/src/tokens/root.test.ts +0 -70
  24. package/src/tokens/root.ts +0 -14
  25. package/src/tokens/selector.test.ts +0 -440
  26. package/src/tokens/selector.ts +0 -47
  27. package/src/tokens/theme.test.ts +0 -338
  28. package/src/tokens/theme.ts +0 -26
  29. package/src/tokens/utility.test.ts +0 -1456
  30. package/src/tokens/utility.ts +0 -92
  31. package/src/tokens/variable.test.ts +0 -235
  32. package/src/tokens/variable.ts +0 -42
  33. package/src/typeGuards.test.ts +0 -33
  34. package/src/typeGuards.ts +0 -98
  35. package/src/types/declarations.ts +0 -42
  36. package/src/types/index.ts +0 -3
  37. package/src/types/options.ts +0 -22
  38. package/src/types/tokens.ts +0 -149
  39. package/src/utils/capitalizeFirst.ts +0 -9
  40. package/src/utils/deepClone.ts +0 -317
  41. package/src/utils/getters.test.ts +0 -399
  42. package/src/utils/getters.ts +0 -36
  43. package/src/utils/index.ts +0 -4
  44. package/src/utils/merge.test.ts +0 -978
  45. package/src/utils/merge.ts +0 -73
  46. package/src/vite-env.d.ts +0 -1
  47. package/tsconfig.json +0 -7
  48. package/vite.config.ts +0 -5
@@ -1,440 +0,0 @@
1
- import { styleframe } from "../styleframe";
2
- import { isAtRule, isSelector } from "../typeGuards";
3
- import type { Root, Selector } from "../types";
4
- import { createRoot } from "./root";
5
- import { createSelectorFunction } from "./selector";
6
-
7
- describe("createSelectorFunction", () => {
8
- let root: Root;
9
- let selector: ReturnType<typeof createSelectorFunction>;
10
-
11
- beforeEach(() => {
12
- root = createRoot();
13
- selector = createSelectorFunction(root, root);
14
- });
15
-
16
- describe("basic selector creation", () => {
17
- it("should create a selector with correct properties", () => {
18
- const result = selector(".button", {
19
- padding: "0.5rem 1rem",
20
- backgroundColor: "#006cff",
21
- color: "white",
22
- });
23
-
24
- expect(result).toEqual({
25
- type: "selector",
26
- query: ".button",
27
- variables: [],
28
- declarations: {
29
- padding: "0.5rem 1rem",
30
- backgroundColor: "#006cff",
31
- color: "white",
32
- },
33
- children: [],
34
- });
35
- });
36
-
37
- it("should add selector to root children", () => {
38
- const result = selector(".card", {
39
- padding: "1rem",
40
- borderRadius: "8px",
41
- });
42
-
43
- expect(root.children).toHaveLength(1);
44
- expect(root.children[0]).toBe(result);
45
- });
46
-
47
- it("should handle different selector types", () => {
48
- // Class selector
49
- const classSelector = selector(".button", { color: "blue" });
50
- // ID selector
51
- const idSelector = selector("#header", { fontSize: "2rem" });
52
- // Element selector
53
- const elementSelector = selector("p", { lineHeight: "1.5" });
54
- // Attribute selector
55
- const attributeSelector = selector('[data-theme="dark"]', {
56
- background: "black",
57
- });
58
-
59
- expect(classSelector.query).toBe(".button");
60
- expect(idSelector.query).toBe("#header");
61
- expect(elementSelector.query).toBe("p");
62
- expect(attributeSelector.query).toBe('[data-theme="dark"]');
63
- expect(root.children).toHaveLength(4);
64
- });
65
- });
66
-
67
- describe("inline nesting", () => {
68
- it("should handle pseudo-class nesting with &", () => {
69
- const result = selector(".button", {
70
- padding: "0.5rem 1rem",
71
- backgroundColor: "#006cff",
72
- "&:hover": {
73
- backgroundColor: "#0056cc",
74
- },
75
- "&:active": {
76
- backgroundColor: "#004099",
77
- },
78
- });
79
-
80
- // The nested selectors should be removed from main declarations
81
- expect(result.declarations).not.toHaveProperty("&:hover");
82
- expect(result.declarations).not.toHaveProperty("&:active");
83
- expect(result.declarations).toEqual({
84
- padding: "0.5rem 1rem",
85
- backgroundColor: "#006cff",
86
- });
87
-
88
- // Should have nested selectors in the selector's children
89
- expect(result.children).toHaveLength(2); // 2 nested selectors
90
-
91
- // Check that nested selectors were created
92
- const hoverSelector = result.children
93
- .filter(isSelector)
94
- .find((child) => child.query === "&:hover");
95
- const activeSelector = result.children
96
- .filter(isSelector)
97
- .find((child) => child.query === "&:active");
98
-
99
- expect(hoverSelector).toBeDefined();
100
- expect(activeSelector).toBeDefined();
101
- expect(hoverSelector?.declarations).toEqual({
102
- backgroundColor: "#0056cc",
103
- });
104
- expect(activeSelector?.declarations).toEqual({
105
- backgroundColor: "#004099",
106
- });
107
- });
108
-
109
- it("should handle pseudo-element nesting", () => {
110
- const result = selector(".button", {
111
- position: "relative",
112
- "&::before": {
113
- content: '""',
114
- display: "inline-block",
115
- marginRight: "0.5rem",
116
- },
117
- });
118
-
119
- expect(result.declarations).not.toHaveProperty("&::before");
120
- expect(result.declarations).toEqual({ position: "relative" });
121
- expect(result.children).toHaveLength(1); // 1 nested selector
122
-
123
- const beforeSelector = result.children
124
- .filter(isSelector)
125
- .find((child) => child.query === "&::before");
126
- expect(beforeSelector?.declarations).toEqual({
127
- content: '""',
128
- display: "inline-block",
129
- marginRight: "0.5rem",
130
- });
131
- });
132
-
133
- it("should handle child selector nesting", () => {
134
- const result = selector(".card", {
135
- padding: "1rem",
136
- borderRadius: "8px",
137
- ".card-title": {
138
- fontSize: "1.5rem",
139
- fontWeight: "bold",
140
- },
141
- ".card-content": {
142
- marginTop: "0.5rem",
143
- },
144
- });
145
-
146
- expect(result.declarations).not.toHaveProperty(".card-title");
147
- expect(result.declarations).not.toHaveProperty(".card-content");
148
- expect(result.declarations).toEqual({
149
- padding: "1rem",
150
- borderRadius: "8px",
151
- });
152
- expect(result.children).toHaveLength(2); // 2 nested selectors
153
-
154
- const titleSelector = result.children
155
- .filter(isSelector)
156
- .find((child) => child.query === ".card-title");
157
- const contentSelector = result.children
158
- .filter(isSelector)
159
- .find((child) => child.query === ".card-content");
160
-
161
- expect(titleSelector?.declarations).toEqual({
162
- fontSize: "1.5rem",
163
- fontWeight: "bold",
164
- });
165
- expect(contentSelector?.declarations).toEqual({
166
- marginTop: "0.5rem",
167
- });
168
- });
169
-
170
- it("should handle complex nested selectors", () => {
171
- const result = selector(".navigation", {
172
- display: "flex",
173
- "& > li": {
174
- listStyle: "none",
175
- },
176
- "& a:not(.active)": {
177
- color: "gray",
178
- },
179
- '&[aria-expanded="true"]': {
180
- backgroundColor: "lightblue",
181
- },
182
- });
183
-
184
- expect(result.children).toHaveLength(3); // 3 nested selectors
185
- expect(result.declarations).toEqual({ display: "flex" });
186
- });
187
- });
188
-
189
- describe("callback nesting", () => {
190
- it("should execute callback with proper context", () => {
191
- const callback = vi.fn();
192
-
193
- selector(".card", callback);
194
-
195
- expect(callback).toHaveBeenCalledTimes(1);
196
- expect(callback).toHaveBeenCalledWith({
197
- atRule: expect.any(Function),
198
- variable: expect.any(Function),
199
- selector: expect.any(Function),
200
- keyframes: expect.any(Function),
201
- media: expect.any(Function),
202
- css: expect.any(Function),
203
- ref: expect.any(Function),
204
- });
205
- });
206
-
207
- it("should create nested selectors via callback", () => {
208
- const result = selector(".card", ({ selector: nestedSelector }) => {
209
- nestedSelector(".card-title", {
210
- fontSize: "1.5rem",
211
- fontWeight: "bold",
212
- });
213
-
214
- nestedSelector(".card-content", {
215
- marginTop: "0.5rem",
216
- });
217
-
218
- return {
219
- padding: "1rem",
220
- borderRadius: "8px",
221
- };
222
- });
223
-
224
- // Should have nested selectors
225
- expect(result.children).toHaveLength(2); // 2 nested selectors created via callback
226
-
227
- const titleSelector = result.children
228
- .filter(isSelector)
229
- .find((child) => child.query === ".card-title");
230
- const contentSelector = result.children
231
- .filter(isSelector)
232
- .find((child) => child.query === ".card-content");
233
-
234
- expect(titleSelector?.declarations).toEqual({
235
- fontSize: "1.5rem",
236
- fontWeight: "bold",
237
- });
238
- expect(contentSelector?.declarations).toEqual({
239
- marginTop: "0.5rem",
240
- });
241
- });
242
-
243
- it("should allow creating variables in callback", () => {
244
- const callback = vi.fn(({ variable }) => {
245
- const color = variable("card-color", "#ffffff");
246
- expect(color.name).toBe("card-color");
247
- });
248
-
249
- selector(".card", callback);
250
-
251
- expect(callback).toHaveBeenCalled();
252
- });
253
-
254
- it("should handle deeply nested selectors", () => {
255
- const result = selector(".menu", ({ selector: nestedSelector }) => {
256
- nestedSelector(".menu-item", ({ selector: deepNestedSelector }) => {
257
- deepNestedSelector(".menu-link", {
258
- textDecoration: "none",
259
- });
260
-
261
- return {
262
- padding: "0.5rem",
263
- };
264
- });
265
-
266
- return {
267
- display: "block",
268
- };
269
- });
270
-
271
- // Check that deep nesting is properly handled
272
- expect(result.children).toHaveLength(1);
273
-
274
- const menuItem = result.children[0] as Selector;
275
- expect(menuItem.query).toBe(".menu-item");
276
- expect(menuItem.children).toHaveLength(1);
277
-
278
- const menuLink = menuItem.children[0] as Selector;
279
- expect(menuLink.query).toBe(".menu-link");
280
- expect(menuLink.declarations).toEqual({
281
- textDecoration: "none",
282
- });
283
- });
284
- });
285
-
286
- describe("mixed nesting approaches", () => {
287
- it("should handle both inline and callback nesting together", () => {
288
- const result = selector(".component", ({ selector: nestedSelector }) => {
289
- nestedSelector(".component-header", {
290
- fontWeight: "bold",
291
- });
292
-
293
- return {
294
- display: "flex",
295
- "&:hover": {
296
- opacity: 0.8,
297
- },
298
- };
299
- });
300
-
301
- expect(result.children).toHaveLength(2); // inline nested + callback nested
302
- expect(result.declarations).toEqual({ display: "flex" });
303
-
304
- const hoverSelector = result.children
305
- .filter(isSelector)
306
- .find((child) => child.query === "&:hover");
307
- const headerSelector = result.children
308
- .filter(isSelector)
309
- .find((child) => child.query === ".component-header");
310
-
311
- expect(hoverSelector?.declarations).toEqual({ opacity: 0.8 });
312
- expect(headerSelector?.declarations).toEqual({
313
- fontWeight: "bold",
314
- });
315
- });
316
- });
317
-
318
- describe("media queries", () => {
319
- it("should handle media query parsing", () => {
320
- // This test is for future implementation
321
- const result = selector(".responsive", {
322
- width: "100%",
323
- "@media (min-width: 768px)": {
324
- width: "50%",
325
- },
326
- });
327
-
328
- expect(result.declarations).not.toHaveProperty(
329
- "@media (min-width: 768px)",
330
- );
331
- expect(result.declarations).toEqual({
332
- width: "100%",
333
- });
334
- expect(result.children).toHaveLength(1); // 1 media query selector
335
-
336
- const mediaQuerySelector = result.children
337
- .filter(isAtRule)
338
- .find(
339
- (child) =>
340
- child.identifier === "media" && child.rule === "(min-width: 768px)",
341
- );
342
- expect(mediaQuerySelector?.declarations).toEqual({
343
- width: "50%",
344
- });
345
- });
346
- });
347
-
348
- describe("working with nested selectors as root", () => {
349
- it("should work when root is a selector", () => {
350
- const parentSelectorInstance: Selector = {
351
- type: "selector",
352
- query: ".parent",
353
- variables: [],
354
- declarations: {},
355
- children: [],
356
- };
357
-
358
- const nestedSelectorFn = createSelectorFunction(
359
- parentSelectorInstance,
360
- root,
361
- );
362
- const result = nestedSelectorFn(".child", {
363
- color: "blue",
364
- });
365
-
366
- expect(result.query).toBe(".child");
367
- expect(parentSelectorInstance.children).toHaveLength(1);
368
- expect(parentSelectorInstance.children[0]).toBe(result);
369
- });
370
- });
371
-
372
- describe("integration with styleframe", () => {
373
- it("should work with styleframe instance", () => {
374
- const s = styleframe();
375
-
376
- const button = s.selector(".button", {
377
- padding: "0.5rem 1rem",
378
- backgroundColor: "#006cff",
379
- });
380
-
381
- const card = s.selector(".card", {
382
- borderRadius: "8px",
383
- });
384
-
385
- expect(button.query).toBe(".button");
386
- expect(card.query).toBe(".card");
387
- });
388
-
389
- it("should work with variables from styleframe", () => {
390
- const s = styleframe();
391
-
392
- // This would need the ref function to be implemented
393
- const primaryColor = s.variable("primary", "#006cff");
394
- const button = s.selector(".button", {
395
- backgroundColor: s.ref(primaryColor),
396
- });
397
-
398
- expect(primaryColor.name).toBe("primary");
399
- expect(primaryColor.value).toBe("#006cff");
400
- expect(button.declarations).toEqual({
401
- backgroundColor: {
402
- type: "reference",
403
- name: "primary",
404
- fallback: undefined,
405
- },
406
- });
407
- });
408
- });
409
-
410
- describe("edge cases", () => {
411
- it("should handle empty declarations", () => {
412
- const result = selector(".empty", {});
413
-
414
- expect(result.declarations).toEqual({});
415
- expect(result.children).toHaveLength(0);
416
- });
417
-
418
- it("should handle complex selector strings", () => {
419
- const complexSelector =
420
- "div.container > .item:nth-child(2n+1):not(.disabled)";
421
- const result = selector(complexSelector, {
422
- display: "block",
423
- });
424
-
425
- expect(result.query).toBe(complexSelector);
426
- });
427
-
428
- it("should preserve declaration order", () => {
429
- const result = selector(".ordered", {
430
- a: "1",
431
- b: "2",
432
- c: "3",
433
- d: "4",
434
- });
435
-
436
- const keys = Object.keys(result.declarations);
437
- expect(keys).toEqual(["a", "b", "c", "d"]);
438
- });
439
- });
440
- });
@@ -1,47 +0,0 @@
1
- import { isContainer } from "../typeGuards";
2
- import type {
3
- Container,
4
- DeclarationsBlock,
5
- DeclarationsCallback,
6
- Root,
7
- Selector,
8
- } from "../types";
9
- import {
10
- createDeclarationsCallbackContext,
11
- parseDeclarationsBlock,
12
- } from "./declarations";
13
-
14
- export function createSelectorFunction(parent: Container, root: Root) {
15
- return function selector(
16
- query: string,
17
- declarationsOrCallback:
18
- | DeclarationsBlock
19
- | Container
20
- | DeclarationsCallback,
21
- ): Selector {
22
- const instance: Selector = {
23
- type: "selector",
24
- query,
25
- declarations: {},
26
- variables: [],
27
- children: [],
28
- };
29
-
30
- const callbackContext = createDeclarationsCallbackContext(instance, root);
31
- if (typeof declarationsOrCallback === "function") {
32
- instance.declarations = declarationsOrCallback(callbackContext) ?? {};
33
- } else if (isContainer(declarationsOrCallback)) {
34
- instance.variables = declarationsOrCallback.variables;
35
- instance.declarations = declarationsOrCallback.declarations;
36
- instance.children = declarationsOrCallback.children;
37
- } else {
38
- instance.declarations = declarationsOrCallback;
39
- }
40
-
41
- parseDeclarationsBlock(instance.declarations, callbackContext);
42
-
43
- parent.children.push(instance);
44
-
45
- return instance;
46
- };
47
- }