@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.
- package/CHANGELOG.md +6 -0
- package/LICENSE +21 -0
- package/dist/styleframe.d.ts +301 -0
- package/dist/styleframe.js +528 -0
- package/dist/styleframe.umd.cjs +1 -0
- package/package.json +13 -3
- package/.tsbuildinfo +0 -1
- package/src/index.ts +0 -5
- package/src/styleframe.ts +0 -64
- package/src/tokens/atRule.test.ts +0 -1013
- package/src/tokens/atRule.ts +0 -67
- package/src/tokens/css.test.ts +0 -404
- package/src/tokens/css.ts +0 -23
- package/src/tokens/declarations.test.ts +0 -584
- package/src/tokens/declarations.ts +0 -71
- package/src/tokens/index.ts +0 -11
- package/src/tokens/modifier.test.ts +0 -90
- package/src/tokens/modifier.ts +0 -86
- package/src/tokens/recipe.test.ts +0 -105
- package/src/tokens/recipe.ts +0 -32
- package/src/tokens/ref.test.ts +0 -430
- package/src/tokens/ref.ts +0 -24
- package/src/tokens/root.test.ts +0 -70
- package/src/tokens/root.ts +0 -14
- package/src/tokens/selector.test.ts +0 -440
- package/src/tokens/selector.ts +0 -47
- package/src/tokens/theme.test.ts +0 -338
- package/src/tokens/theme.ts +0 -26
- package/src/tokens/utility.test.ts +0 -1456
- package/src/tokens/utility.ts +0 -92
- package/src/tokens/variable.test.ts +0 -235
- package/src/tokens/variable.ts +0 -42
- package/src/typeGuards.test.ts +0 -33
- package/src/typeGuards.ts +0 -98
- package/src/types/declarations.ts +0 -42
- package/src/types/index.ts +0 -3
- package/src/types/options.ts +0 -22
- package/src/types/tokens.ts +0 -149
- package/src/utils/capitalizeFirst.ts +0 -9
- package/src/utils/deepClone.ts +0 -317
- package/src/utils/getters.test.ts +0 -399
- package/src/utils/getters.ts +0 -36
- package/src/utils/index.ts +0 -4
- package/src/utils/merge.test.ts +0 -978
- package/src/utils/merge.ts +0 -73
- package/src/vite-env.d.ts +0 -1
- package/tsconfig.json +0 -7
- package/vite.config.ts +0 -5
|
@@ -1,584 +0,0 @@
|
|
|
1
|
-
import type { AtRule, Container, Root, Selector, Variable } from "../types";
|
|
2
|
-
import { createAtRuleFunction } from "./atRule";
|
|
3
|
-
import {
|
|
4
|
-
createDeclarationsCallbackContext,
|
|
5
|
-
parseDeclarationsBlock,
|
|
6
|
-
} from "./declarations";
|
|
7
|
-
import { createRoot } from "./root";
|
|
8
|
-
import { createSelectorFunction } from "./selector";
|
|
9
|
-
import { createVariableFunction } from "./variable";
|
|
10
|
-
|
|
11
|
-
describe("parseDeclarationsBlock", () => {
|
|
12
|
-
let mockContext: any;
|
|
13
|
-
|
|
14
|
-
beforeEach(() => {
|
|
15
|
-
mockContext = {
|
|
16
|
-
selector: vi.fn(),
|
|
17
|
-
variable: vi.fn(),
|
|
18
|
-
keyframes: vi.fn(),
|
|
19
|
-
atRule: vi.fn(),
|
|
20
|
-
media: vi.fn(),
|
|
21
|
-
css: vi.fn(),
|
|
22
|
-
ref: vi.fn(),
|
|
23
|
-
};
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
describe("nested selector parsing", () => {
|
|
27
|
-
it("should parse selectors starting with &", () => {
|
|
28
|
-
const declarations = {
|
|
29
|
-
color: "blue",
|
|
30
|
-
"&:hover": { color: "red" },
|
|
31
|
-
"&::before": { content: '""' },
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
parseDeclarationsBlock(declarations, mockContext);
|
|
35
|
-
|
|
36
|
-
expect(mockContext.selector).toHaveBeenCalledTimes(2);
|
|
37
|
-
expect(mockContext.selector).toHaveBeenCalledWith("&:hover", {
|
|
38
|
-
color: "red",
|
|
39
|
-
});
|
|
40
|
-
expect(mockContext.selector).toHaveBeenCalledWith("&::before", {
|
|
41
|
-
content: '""',
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// Should remove nested selectors from original declarations
|
|
45
|
-
expect(declarations).not.toHaveProperty("&:hover");
|
|
46
|
-
expect(declarations).not.toHaveProperty("&::before");
|
|
47
|
-
expect(declarations).toHaveProperty("color", "blue");
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("should parse selectors starting with .", () => {
|
|
51
|
-
const declarations = {
|
|
52
|
-
padding: "1rem",
|
|
53
|
-
".child": { fontSize: "14px" },
|
|
54
|
-
".another-child": { margin: "0.5rem" },
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
parseDeclarationsBlock(declarations, mockContext);
|
|
58
|
-
|
|
59
|
-
expect(mockContext.selector).toHaveBeenCalledTimes(2);
|
|
60
|
-
expect(mockContext.selector).toHaveBeenCalledWith(".child", {
|
|
61
|
-
fontSize: "14px",
|
|
62
|
-
});
|
|
63
|
-
expect(mockContext.selector).toHaveBeenCalledWith(".another-child", {
|
|
64
|
-
margin: "0.5rem",
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it("should parse selectors starting with :", () => {
|
|
69
|
-
const declarations = {
|
|
70
|
-
display: "block",
|
|
71
|
-
":hover": { opacity: 0.8 },
|
|
72
|
-
":focus": { outline: "2px solid blue" },
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
parseDeclarationsBlock(declarations, mockContext);
|
|
76
|
-
|
|
77
|
-
expect(mockContext.selector).toHaveBeenCalledTimes(2);
|
|
78
|
-
expect(mockContext.selector).toHaveBeenCalledWith(":hover", {
|
|
79
|
-
opacity: 0.8,
|
|
80
|
-
});
|
|
81
|
-
expect(mockContext.selector).toHaveBeenCalledWith(":focus", {
|
|
82
|
-
outline: "2px solid blue",
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it("should not parse regular CSS properties", () => {
|
|
87
|
-
const declarations = {
|
|
88
|
-
color: "blue",
|
|
89
|
-
fontSize: "16px",
|
|
90
|
-
marginTop: "1rem",
|
|
91
|
-
backgroundColor: "#ffffff",
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
parseDeclarationsBlock(declarations, mockContext);
|
|
95
|
-
|
|
96
|
-
expect(mockContext.selector).not.toHaveBeenCalled();
|
|
97
|
-
// All properties should remain
|
|
98
|
-
expect(Object.keys(declarations)).toHaveLength(4);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("should handle mixed declarations", () => {
|
|
102
|
-
const declarations = {
|
|
103
|
-
padding: "1rem",
|
|
104
|
-
color: "blue",
|
|
105
|
-
"&:hover": { color: "red" },
|
|
106
|
-
fontSize: "16px",
|
|
107
|
-
".child": { margin: "0.5rem" },
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
parseDeclarationsBlock(declarations, mockContext);
|
|
111
|
-
|
|
112
|
-
expect(mockContext.selector).toHaveBeenCalledTimes(2);
|
|
113
|
-
|
|
114
|
-
// Regular properties should remain
|
|
115
|
-
expect(declarations).toHaveProperty("padding", "1rem");
|
|
116
|
-
expect(declarations).toHaveProperty("color", "blue");
|
|
117
|
-
expect(declarations).toHaveProperty("fontSize", "16px");
|
|
118
|
-
|
|
119
|
-
// Nested selectors should be removed
|
|
120
|
-
expect(declarations).not.toHaveProperty("&:hover");
|
|
121
|
-
expect(declarations).not.toHaveProperty(".child");
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
describe("media query handling", () => {
|
|
126
|
-
it("should handle media queries", () => {
|
|
127
|
-
const declarations = {
|
|
128
|
-
width: "100%",
|
|
129
|
-
"@media (min-width: 768px)": {
|
|
130
|
-
width: "50%",
|
|
131
|
-
},
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
parseDeclarationsBlock(declarations, mockContext);
|
|
135
|
-
|
|
136
|
-
expect(declarations).not.toHaveProperty("@media (min-width: 768px)");
|
|
137
|
-
expect(mockContext.atRule).toHaveBeenCalledTimes(1);
|
|
138
|
-
expect(mockContext.atRule).toHaveBeenCalledWith(
|
|
139
|
-
"media",
|
|
140
|
-
"(min-width: 768px)",
|
|
141
|
-
{ width: "50%" },
|
|
142
|
-
);
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
describe("edge cases", () => {
|
|
147
|
-
it("should handle non-object nested values", () => {
|
|
148
|
-
const declarations = {
|
|
149
|
-
color: "blue",
|
|
150
|
-
"&:hover": "not-an-object",
|
|
151
|
-
".child": { fontSize: "14px" },
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
parseDeclarationsBlock(declarations, mockContext);
|
|
155
|
-
|
|
156
|
-
// Should only process object values
|
|
157
|
-
expect(mockContext.selector).toHaveBeenCalledTimes(1);
|
|
158
|
-
expect(mockContext.selector).toHaveBeenCalledWith(".child", {
|
|
159
|
-
fontSize: "14px",
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
// Non-object nested value should remain
|
|
163
|
-
expect(declarations).toHaveProperty("&:hover", "not-an-object");
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it("should handle empty nested objects", () => {
|
|
167
|
-
const declarations = {
|
|
168
|
-
color: "blue",
|
|
169
|
-
"&:hover": {},
|
|
170
|
-
".child": {},
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
parseDeclarationsBlock(declarations, mockContext);
|
|
174
|
-
|
|
175
|
-
expect(mockContext.selector).toHaveBeenCalledTimes(2);
|
|
176
|
-
expect(mockContext.selector).toHaveBeenCalledWith("&:hover", {});
|
|
177
|
-
expect(mockContext.selector).toHaveBeenCalledWith(".child", {});
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
describe("createDeclarationsCallbackContext", () => {
|
|
183
|
-
let root: Root;
|
|
184
|
-
let selector: Selector;
|
|
185
|
-
let media: AtRule;
|
|
186
|
-
|
|
187
|
-
beforeEach(() => {
|
|
188
|
-
root = createRoot();
|
|
189
|
-
selector = {
|
|
190
|
-
type: "selector",
|
|
191
|
-
query: ".test",
|
|
192
|
-
variables: [],
|
|
193
|
-
declarations: {},
|
|
194
|
-
children: [],
|
|
195
|
-
};
|
|
196
|
-
media = {
|
|
197
|
-
type: "at-rule",
|
|
198
|
-
identifier: "media",
|
|
199
|
-
rule: "(min-width: 768px)",
|
|
200
|
-
variables: [],
|
|
201
|
-
declarations: {},
|
|
202
|
-
children: [],
|
|
203
|
-
} as unknown as AtRule;
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
describe("basic context creation", () => {
|
|
207
|
-
it("should create context with all required functions for root container", () => {
|
|
208
|
-
const context = createDeclarationsCallbackContext(root, root);
|
|
209
|
-
|
|
210
|
-
expect(context).toHaveProperty("variable");
|
|
211
|
-
expect(context).toHaveProperty("selector");
|
|
212
|
-
expect(context).toHaveProperty("media");
|
|
213
|
-
expect(context).toHaveProperty("atRule");
|
|
214
|
-
expect(typeof context.variable).toBe("function");
|
|
215
|
-
expect(typeof context.selector).toBe("function");
|
|
216
|
-
expect(typeof context.media).toBe("function");
|
|
217
|
-
expect(typeof context.atRule).toBe("function");
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
it("should create context with all required functions for selector container", () => {
|
|
221
|
-
const context = createDeclarationsCallbackContext(selector, root);
|
|
222
|
-
|
|
223
|
-
expect(context).toHaveProperty("variable");
|
|
224
|
-
expect(context).toHaveProperty("selector");
|
|
225
|
-
expect(context).toHaveProperty("media");
|
|
226
|
-
expect(context).toHaveProperty("atRule");
|
|
227
|
-
expect(typeof context.variable).toBe("function");
|
|
228
|
-
expect(typeof context.selector).toBe("function");
|
|
229
|
-
expect(typeof context.media).toBe("function");
|
|
230
|
-
expect(typeof context.atRule).toBe("function");
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
it("should create context with all required functions for media container", () => {
|
|
234
|
-
const context = createDeclarationsCallbackContext(media, root);
|
|
235
|
-
|
|
236
|
-
expect(context).toHaveProperty("variable");
|
|
237
|
-
expect(context).toHaveProperty("selector");
|
|
238
|
-
expect(context).toHaveProperty("media");
|
|
239
|
-
expect(context).toHaveProperty("atRule");
|
|
240
|
-
expect(typeof context.variable).toBe("function");
|
|
241
|
-
expect(typeof context.selector).toBe("function");
|
|
242
|
-
expect(typeof context.media).toBe("function");
|
|
243
|
-
expect(typeof context.atRule).toBe("function");
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
describe("function creation with proper context", () => {
|
|
248
|
-
it("should create variable function bound to correct parent and root", () => {
|
|
249
|
-
const context = createDeclarationsCallbackContext(selector, root);
|
|
250
|
-
|
|
251
|
-
const testVar = context.variable("test-var", "test-value");
|
|
252
|
-
|
|
253
|
-
expect(testVar.name).toBe("test-var");
|
|
254
|
-
expect(testVar.value).toBe("test-value");
|
|
255
|
-
expect(selector.variables).toContainEqual(testVar);
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
it("should create selector function bound to correct parent and root", () => {
|
|
259
|
-
const context = createDeclarationsCallbackContext(root, root);
|
|
260
|
-
|
|
261
|
-
const testSelector = context.selector(".test-selector", {
|
|
262
|
-
color: "red",
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
expect(testSelector.query).toBe(".test-selector");
|
|
266
|
-
expect(testSelector.declarations).toEqual({ color: "red" });
|
|
267
|
-
expect(root.children).toContainEqual(testSelector);
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
it("should create media function bound to correct parent and root", () => {
|
|
271
|
-
const context = createDeclarationsCallbackContext(root, root);
|
|
272
|
-
|
|
273
|
-
const testMedia = context.media("(min-width: 768px)", {
|
|
274
|
-
fontSize: "18px",
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
expect(testMedia.type).toBe("at-rule");
|
|
278
|
-
expect(testMedia.identifier).toBe("media");
|
|
279
|
-
expect(testMedia.rule).toBe("(min-width: 768px)");
|
|
280
|
-
expect(testMedia.declarations).toEqual({ fontSize: "18px" });
|
|
281
|
-
expect(root.children).toContainEqual(testMedia);
|
|
282
|
-
});
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
describe("nested context functionality", () => {
|
|
286
|
-
it("should support nested variable creation in selector context", () => {
|
|
287
|
-
const context = createDeclarationsCallbackContext(selector, root);
|
|
288
|
-
|
|
289
|
-
const nestedVar = context.variable("nested-var", "#006cff");
|
|
290
|
-
|
|
291
|
-
expect(nestedVar.name).toBe("nested-var");
|
|
292
|
-
expect(nestedVar.value).toBe("#006cff");
|
|
293
|
-
expect(selector.variables).toContainEqual(nestedVar);
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
it("should support nested selector creation in media context", () => {
|
|
297
|
-
const context = createDeclarationsCallbackContext(media, root);
|
|
298
|
-
|
|
299
|
-
const nestedSelector = context.selector(".nested", {
|
|
300
|
-
padding: "1rem",
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
expect(nestedSelector.query).toBe(".nested");
|
|
304
|
-
expect(nestedSelector.declarations).toEqual({ padding: "1rem" });
|
|
305
|
-
expect(media.children).toContainEqual(nestedSelector);
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
it("should support nested media creation in selector context", () => {
|
|
309
|
-
const context = createDeclarationsCallbackContext(selector, root);
|
|
310
|
-
|
|
311
|
-
const nestedMedia = context.media("(hover: hover)", {
|
|
312
|
-
opacity: "0.8",
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
expect(nestedMedia.type).toBe("at-rule");
|
|
316
|
-
expect(nestedMedia.identifier).toBe("media");
|
|
317
|
-
expect(nestedMedia.rule).toBe("(hover: hover)");
|
|
318
|
-
expect(nestedMedia.declarations).toEqual({ opacity: "0.8" });
|
|
319
|
-
expect(selector.children).toContainEqual(nestedMedia);
|
|
320
|
-
});
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
describe("function independence", () => {
|
|
324
|
-
it("should create independent function instances for different contexts", () => {
|
|
325
|
-
const context1 = createDeclarationsCallbackContext(root, root);
|
|
326
|
-
const context2 = createDeclarationsCallbackContext(selector, root);
|
|
327
|
-
|
|
328
|
-
expect(context1.variable).not.toBe(context2.variable);
|
|
329
|
-
expect(context1.selector).not.toBe(context2.selector);
|
|
330
|
-
expect(context1.media).not.toBe(context2.media);
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
it("should create functions that operate on different parent containers", () => {
|
|
334
|
-
const root1 = createRoot();
|
|
335
|
-
const root2 = createRoot();
|
|
336
|
-
|
|
337
|
-
const context1 = createDeclarationsCallbackContext(root1, root1);
|
|
338
|
-
const context2 = createDeclarationsCallbackContext(root2, root2);
|
|
339
|
-
|
|
340
|
-
context1.variable("var1", "value1");
|
|
341
|
-
context2.variable("var2", "value2");
|
|
342
|
-
|
|
343
|
-
expect(root1.variables).toHaveLength(1);
|
|
344
|
-
expect(root2.variables).toHaveLength(1);
|
|
345
|
-
expect(root1.variables[0]).not.toEqual(root2.variables[0]);
|
|
346
|
-
});
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
describe("container type handling", () => {
|
|
350
|
-
it("should work with all Container union types", () => {
|
|
351
|
-
const containers: Container[] = [root, selector, media];
|
|
352
|
-
|
|
353
|
-
containers.forEach((container) => {
|
|
354
|
-
const context = createDeclarationsCallbackContext(container, root);
|
|
355
|
-
|
|
356
|
-
expect(context).toHaveProperty("variable");
|
|
357
|
-
expect(context).toHaveProperty("selector");
|
|
358
|
-
expect(context).toHaveProperty("media");
|
|
359
|
-
});
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
it("should maintain proper parent-child relationships for different container types", () => {
|
|
363
|
-
const contexts = [
|
|
364
|
-
{ parent: root, name: "root" },
|
|
365
|
-
{ parent: selector, name: "selector" },
|
|
366
|
-
{ parent: media, name: "media" },
|
|
367
|
-
];
|
|
368
|
-
|
|
369
|
-
contexts.forEach(({ parent, name }) => {
|
|
370
|
-
const context = createDeclarationsCallbackContext(parent, root);
|
|
371
|
-
const initialVariablesCount = parent.variables.length;
|
|
372
|
-
|
|
373
|
-
context.variable(`${name}-var`, "test");
|
|
374
|
-
|
|
375
|
-
expect(parent.variables).toHaveLength(initialVariablesCount + 1);
|
|
376
|
-
|
|
377
|
-
const lastVar = parent.variables[
|
|
378
|
-
parent.variables.length - 1
|
|
379
|
-
] as Variable;
|
|
380
|
-
expect(lastVar.type).toBe("variable");
|
|
381
|
-
});
|
|
382
|
-
});
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
describe("root parameter handling", () => {
|
|
386
|
-
it("should pass root parameter correctly to all created functions", () => {
|
|
387
|
-
const customRoot = createRoot();
|
|
388
|
-
const context = createDeclarationsCallbackContext(selector, customRoot);
|
|
389
|
-
|
|
390
|
-
// The functions should use the passed root, but this is primarily internal
|
|
391
|
-
// We can verify they work correctly by checking the functions execute
|
|
392
|
-
const testVar = context.variable("test", "value");
|
|
393
|
-
const testSelector = context.selector(".test", {});
|
|
394
|
-
const testMedia = context.media("(test)", {});
|
|
395
|
-
|
|
396
|
-
expect(testVar).toBeDefined();
|
|
397
|
-
expect(testSelector).toBeDefined();
|
|
398
|
-
expect(testMedia.type).toBe("at-rule");
|
|
399
|
-
});
|
|
400
|
-
|
|
401
|
-
it("should handle when parent and root are the same", () => {
|
|
402
|
-
const context = createDeclarationsCallbackContext(root, root);
|
|
403
|
-
|
|
404
|
-
const testVar = context.variable("root-var", "value");
|
|
405
|
-
|
|
406
|
-
expect(testVar.name).toBe("root-var");
|
|
407
|
-
expect(root.variables).toContainEqual(testVar);
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
it("should handle when parent and root are different", () => {
|
|
411
|
-
const differentRoot = createRoot();
|
|
412
|
-
const context = createDeclarationsCallbackContext(
|
|
413
|
-
selector,
|
|
414
|
-
differentRoot,
|
|
415
|
-
);
|
|
416
|
-
|
|
417
|
-
const testVar = context.variable("different-root-var", "value");
|
|
418
|
-
|
|
419
|
-
expect(testVar.name).toBe("different-root-var");
|
|
420
|
-
expect(selector.variables).toContainEqual(testVar);
|
|
421
|
-
expect(differentRoot.variables).not.toContainEqual(testVar);
|
|
422
|
-
});
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
describe("context object properties", () => {
|
|
426
|
-
it("should return object with exactly four properties", () => {
|
|
427
|
-
const context = createDeclarationsCallbackContext(root, root);
|
|
428
|
-
const keys = Object.keys(context);
|
|
429
|
-
|
|
430
|
-
expect(keys).toHaveLength(7);
|
|
431
|
-
expect(keys).toContain("atRule");
|
|
432
|
-
expect(keys).toContain("variable");
|
|
433
|
-
expect(keys).toContain("selector");
|
|
434
|
-
expect(keys).toContain("keyframes");
|
|
435
|
-
expect(keys).toContain("media");
|
|
436
|
-
expect(keys).toContain("css");
|
|
437
|
-
expect(keys).toContain("ref");
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
it("should not have enumerable prototype properties", () => {
|
|
441
|
-
const context = createDeclarationsCallbackContext(root, root);
|
|
442
|
-
|
|
443
|
-
expect(Object.hasOwn(context, "variable")).toBe(true);
|
|
444
|
-
expect(Object.hasOwn(context, "selector")).toBe(true);
|
|
445
|
-
expect(Object.hasOwn(context, "keyframes")).toBe(true);
|
|
446
|
-
expect(Object.hasOwn(context, "media")).toBe(true);
|
|
447
|
-
});
|
|
448
|
-
|
|
449
|
-
it("should return functions with correct return types", () => {
|
|
450
|
-
const context = createDeclarationsCallbackContext(root, root);
|
|
451
|
-
|
|
452
|
-
const varResult = context.variable("test-var", "value");
|
|
453
|
-
const selectorResult = context.selector(".test", {});
|
|
454
|
-
const keyframesResult = context.keyframes("test-animation", {
|
|
455
|
-
"0%": { opacity: 0 },
|
|
456
|
-
"100%": { opacity: 1 },
|
|
457
|
-
});
|
|
458
|
-
const mediaResult = context.media("(test)", {});
|
|
459
|
-
|
|
460
|
-
expect(varResult.type).toBe("variable");
|
|
461
|
-
expect(selectorResult.type).toBe("selector");
|
|
462
|
-
expect(keyframesResult.type).toBe("at-rule");
|
|
463
|
-
expect(mediaResult.type).toBe("at-rule");
|
|
464
|
-
});
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
describe("real-world usage patterns", () => {
|
|
468
|
-
it("should support chaining multiple function calls", () => {
|
|
469
|
-
const context = createDeclarationsCallbackContext(root, root);
|
|
470
|
-
|
|
471
|
-
const primaryColor = context.variable("primary-color", "#006cff");
|
|
472
|
-
context.selector(".button", {
|
|
473
|
-
backgroundColor: primaryColor.value as string,
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
context.media("(min-width: 768px)", ({ selector }) => {
|
|
477
|
-
selector(".button", {
|
|
478
|
-
padding: "1rem 2rem",
|
|
479
|
-
});
|
|
480
|
-
});
|
|
481
|
-
|
|
482
|
-
expect(root.children).toHaveLength(2); // selector + media
|
|
483
|
-
expect(root.variables).toHaveLength(1); // variable
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
it("should work in complex nested scenarios", () => {
|
|
487
|
-
const context = createDeclarationsCallbackContext(root, root);
|
|
488
|
-
|
|
489
|
-
context.selector(".component", ({ variable, media }) => {
|
|
490
|
-
const componentSpacing = variable("component-spacing", "1rem");
|
|
491
|
-
|
|
492
|
-
media("(min-width: 768px)", {
|
|
493
|
-
padding: componentSpacing.value as string,
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
return {
|
|
497
|
-
display: "block",
|
|
498
|
-
};
|
|
499
|
-
});
|
|
500
|
-
|
|
501
|
-
expect(root.children).toHaveLength(1); // main selector
|
|
502
|
-
const mainSelector = root.children[0] as Selector;
|
|
503
|
-
expect(mainSelector.children).toHaveLength(1); // media
|
|
504
|
-
expect(mainSelector.variables).toHaveLength(1); // variable
|
|
505
|
-
});
|
|
506
|
-
|
|
507
|
-
it("should support design system patterns", () => {
|
|
508
|
-
const context = createDeclarationsCallbackContext(root, root);
|
|
509
|
-
|
|
510
|
-
// Define tokens
|
|
511
|
-
const spacing = context.variable("spacing-md", "1rem");
|
|
512
|
-
const color = context.variable("color-primary", "#006cff");
|
|
513
|
-
|
|
514
|
-
// Create component with responsive behavior
|
|
515
|
-
context.selector(".card", ({ media }) => {
|
|
516
|
-
media("(min-width: 768px)", {
|
|
517
|
-
padding: "1.5rem",
|
|
518
|
-
});
|
|
519
|
-
|
|
520
|
-
return {
|
|
521
|
-
padding: spacing.value as string,
|
|
522
|
-
borderColor: color.value as string,
|
|
523
|
-
};
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
expect(root.children).toHaveLength(1); // 1 selector
|
|
527
|
-
expect(root.variables).toHaveLength(2); // 2 variables
|
|
528
|
-
});
|
|
529
|
-
});
|
|
530
|
-
|
|
531
|
-
describe("error handling and edge cases", () => {
|
|
532
|
-
it("should handle empty containers", () => {
|
|
533
|
-
const emptySelector: Selector = {
|
|
534
|
-
type: "selector",
|
|
535
|
-
query: "",
|
|
536
|
-
variables: [],
|
|
537
|
-
declarations: {},
|
|
538
|
-
children: [],
|
|
539
|
-
};
|
|
540
|
-
|
|
541
|
-
const context = createDeclarationsCallbackContext(emptySelector, root);
|
|
542
|
-
|
|
543
|
-
expect(() => context.variable("test", "value")).not.toThrow();
|
|
544
|
-
expect(() => context.selector(".test", {})).not.toThrow();
|
|
545
|
-
expect(() => context.media("(test)", {})).not.toThrow();
|
|
546
|
-
});
|
|
547
|
-
|
|
548
|
-
it("should handle containers with existing children", () => {
|
|
549
|
-
const existingVar = {
|
|
550
|
-
type: "variable" as const,
|
|
551
|
-
name: "existing",
|
|
552
|
-
value: "value",
|
|
553
|
-
};
|
|
554
|
-
|
|
555
|
-
selector.variables.push(existingVar);
|
|
556
|
-
const initialLength = selector.variables.length;
|
|
557
|
-
|
|
558
|
-
const context = createDeclarationsCallbackContext(selector, root);
|
|
559
|
-
context.variable("new-var", "new-value");
|
|
560
|
-
|
|
561
|
-
expect(selector.variables).toHaveLength(initialLength + 1);
|
|
562
|
-
expect(selector.variables).toContain(existingVar);
|
|
563
|
-
});
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
describe("type consistency", () => {
|
|
567
|
-
it("should maintain type consistency across function calls", () => {
|
|
568
|
-
const context = createDeclarationsCallbackContext(root, root);
|
|
569
|
-
|
|
570
|
-
// All functions should work with the same interfaces they would individually
|
|
571
|
-
const variableFunction = createVariableFunction(root, root);
|
|
572
|
-
const _selectorFunction = createSelectorFunction(root, root);
|
|
573
|
-
const _mediaFunction = createAtRuleFunction(root, root);
|
|
574
|
-
|
|
575
|
-
// Context functions should behave identically
|
|
576
|
-
const contextVar = context.variable("test1", "value1");
|
|
577
|
-
const directVar = variableFunction("test2", "value2");
|
|
578
|
-
|
|
579
|
-
expect(contextVar.type).toBe(directVar.type);
|
|
580
|
-
expect(typeof contextVar.name).toBe(typeof directVar.name);
|
|
581
|
-
expect(typeof contextVar.value).toBe(typeof directVar.value);
|
|
582
|
-
});
|
|
583
|
-
});
|
|
584
|
-
});
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/** biome-ignore-all lint/suspicious/noConfusingVoidType: Returning declarations in callback is optional */
|
|
2
|
-
import { isTokenValue } from "../typeGuards";
|
|
3
|
-
import type {
|
|
4
|
-
Container,
|
|
5
|
-
DeclarationsBlock,
|
|
6
|
-
DeclarationsCallbackContext,
|
|
7
|
-
Root,
|
|
8
|
-
} from "../types";
|
|
9
|
-
import {
|
|
10
|
-
createAtRuleFunction,
|
|
11
|
-
createKeyframesFunction,
|
|
12
|
-
createMediaFunction,
|
|
13
|
-
} from "./atRule";
|
|
14
|
-
import { createCssFunction } from "./css";
|
|
15
|
-
import { createRefFunction } from "./ref";
|
|
16
|
-
import { createSelectorFunction } from "./selector";
|
|
17
|
-
import { createVariableFunction } from "./variable";
|
|
18
|
-
|
|
19
|
-
export function createDeclarationsCallbackContext(
|
|
20
|
-
parent: Container,
|
|
21
|
-
root: Root,
|
|
22
|
-
): DeclarationsCallbackContext {
|
|
23
|
-
const variable = createVariableFunction(parent, root);
|
|
24
|
-
const selector = createSelectorFunction(parent, root);
|
|
25
|
-
const atRule = createAtRuleFunction(parent, root);
|
|
26
|
-
const keyframes = createKeyframesFunction(root, root);
|
|
27
|
-
const media = createMediaFunction(parent, root);
|
|
28
|
-
const ref = createRefFunction(root, root);
|
|
29
|
-
const css = createCssFunction(root, root);
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
variable,
|
|
33
|
-
selector,
|
|
34
|
-
keyframes,
|
|
35
|
-
atRule,
|
|
36
|
-
media,
|
|
37
|
-
ref,
|
|
38
|
-
css,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function parseDeclarationsBlock(
|
|
43
|
-
declarations: DeclarationsBlock,
|
|
44
|
-
context: DeclarationsCallbackContext,
|
|
45
|
-
) {
|
|
46
|
-
for (const key in declarations) {
|
|
47
|
-
// If the key represents a selector or media query, remove it and add it as a separate declaration
|
|
48
|
-
if (key.startsWith("@")) {
|
|
49
|
-
const atRuleDeclarations = declarations[key];
|
|
50
|
-
if (
|
|
51
|
-
typeof atRuleDeclarations === "object" &&
|
|
52
|
-
atRuleDeclarations !== null &&
|
|
53
|
-
!isTokenValue(atRuleDeclarations)
|
|
54
|
-
) {
|
|
55
|
-
const identifier = key.replace(/^@(\w+).*/, "$1");
|
|
56
|
-
const rule = key.replace(`@${identifier}`, "").trim();
|
|
57
|
-
context.atRule(identifier, rule, atRuleDeclarations);
|
|
58
|
-
delete declarations[key];
|
|
59
|
-
}
|
|
60
|
-
} else if (/^[.&:]/.test(key)) {
|
|
61
|
-
// If the key starts with a special character, treat it as a nested selector
|
|
62
|
-
const nested = declarations[key] as DeclarationsBlock;
|
|
63
|
-
if (typeof nested === "object") {
|
|
64
|
-
context.selector(key, nested);
|
|
65
|
-
delete declarations[key];
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return declarations;
|
|
71
|
-
}
|
package/src/tokens/index.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export * from "./css";
|
|
2
|
-
export * from "./declarations";
|
|
3
|
-
export * from "./atRule";
|
|
4
|
-
export * from "./modifier";
|
|
5
|
-
export * from "./ref";
|
|
6
|
-
export * from "./root";
|
|
7
|
-
export * from "./selector";
|
|
8
|
-
export * from "./utility";
|
|
9
|
-
export * from "./variable";
|
|
10
|
-
export * from "./theme";
|
|
11
|
-
export * from "./recipe";
|