@pikacss/core 0.0.46 → 0.0.48

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 (4) hide show
  1. package/README.md +30 -0
  2. package/dist/index.d.mts +3565 -1817
  3. package/dist/index.mjs +2541 -208
  4. package/package.json +11 -5
package/dist/index.mjs CHANGED
@@ -1,9 +1,891 @@
1
- //#region src/internal/constants.ts
2
- const ATOMIC_STYLE_ID_PLACEHOLDER = "%";
3
- const ATOMIC_STYLE_ID_PLACEHOLDER_RE_GLOBAL = /%/g;
4
-
1
+ //#region src/internal/generated-shorthand-map.ts
2
+ const PROPERTY_EFFECTS = {
3
+ "accent-color": ["accent-color"],
4
+ "align-content": ["align-content"],
5
+ "align-items": ["align-items"],
6
+ "align-self": ["align-self"],
7
+ "align-tracks": ["align-tracks"],
8
+ "alignment-baseline": ["alignment-baseline"],
9
+ "all": ["all"],
10
+ "anchor-name": ["anchor-name"],
11
+ "anchor-scope": ["anchor-scope"],
12
+ "animation": [
13
+ "animation-delay",
14
+ "animation-direction",
15
+ "animation-duration",
16
+ "animation-fill-mode",
17
+ "animation-iteration-count",
18
+ "animation-name",
19
+ "animation-play-state",
20
+ "animation-timeline",
21
+ "animation-timing-function"
22
+ ],
23
+ "animation-composition": ["animation-composition"],
24
+ "animation-delay": ["animation-delay"],
25
+ "animation-direction": ["animation-direction"],
26
+ "animation-duration": ["animation-duration"],
27
+ "animation-fill-mode": ["animation-fill-mode"],
28
+ "animation-iteration-count": ["animation-iteration-count"],
29
+ "animation-name": ["animation-name"],
30
+ "animation-play-state": ["animation-play-state"],
31
+ "animation-range": ["animation-range-end", "animation-range-start"],
32
+ "animation-range-end": ["animation-range-end"],
33
+ "animation-range-start": ["animation-range-start"],
34
+ "animation-timeline": ["animation-timeline"],
35
+ "animation-timing-function": ["animation-timing-function"],
36
+ "animation-trigger": ["animation-trigger"],
37
+ "appearance": ["appearance"],
38
+ "aspect-ratio": ["aspect-ratio"],
39
+ "backdrop-filter": ["backdrop-filter"],
40
+ "backface-visibility": ["backface-visibility"],
41
+ "background": [
42
+ "background-attachment",
43
+ "background-clip",
44
+ "background-color",
45
+ "background-image",
46
+ "background-origin",
47
+ "background-position-x",
48
+ "background-position-y",
49
+ "background-repeat",
50
+ "background-size"
51
+ ],
52
+ "background-attachment": ["background-attachment"],
53
+ "background-blend-mode": ["background-blend-mode"],
54
+ "background-clip": ["background-clip"],
55
+ "background-color": ["background-color"],
56
+ "background-image": ["background-image"],
57
+ "background-origin": ["background-origin"],
58
+ "background-position": ["background-position-x", "background-position-y"],
59
+ "background-position-x": ["background-position-x"],
60
+ "background-position-y": ["background-position-y"],
61
+ "background-repeat": ["background-repeat"],
62
+ "background-size": ["background-size"],
63
+ "baseline-shift": ["baseline-shift"],
64
+ "baseline-source": ["baseline-source"],
65
+ "block-size": ["block-size"],
66
+ "border": [
67
+ "border-bottom-color",
68
+ "border-left-color",
69
+ "border-right-color",
70
+ "border-top-color",
71
+ "border-bottom-style",
72
+ "border-left-style",
73
+ "border-right-style",
74
+ "border-top-style",
75
+ "border-bottom-width",
76
+ "border-left-width",
77
+ "border-right-width",
78
+ "border-top-width"
79
+ ],
80
+ "border-block": [
81
+ "border-block-end-color",
82
+ "border-block-start-color",
83
+ "border-block-end-style",
84
+ "border-block-start-style",
85
+ "border-block-end-width",
86
+ "border-block-start-width"
87
+ ],
88
+ "border-block-color": ["border-block-end-color", "border-block-start-color"],
89
+ "border-block-end": [
90
+ "border-block-end-color",
91
+ "border-block-end-style",
92
+ "border-block-end-width"
93
+ ],
94
+ "border-block-end-color": ["border-block-end-color"],
95
+ "border-block-end-style": ["border-block-end-style"],
96
+ "border-block-end-width": ["border-block-end-width"],
97
+ "border-block-start": [
98
+ "border-block-start-color",
99
+ "border-block-start-style",
100
+ "border-block-start-width"
101
+ ],
102
+ "border-block-start-color": ["border-block-start-color"],
103
+ "border-block-start-style": ["border-block-start-style"],
104
+ "border-block-start-width": ["border-block-start-width"],
105
+ "border-block-style": ["border-block-end-style", "border-block-start-style"],
106
+ "border-block-width": ["border-block-end-width", "border-block-start-width"],
107
+ "border-bottom": [
108
+ "border-bottom-color",
109
+ "border-bottom-style",
110
+ "border-bottom-width"
111
+ ],
112
+ "border-bottom-color": ["border-bottom-color"],
113
+ "border-bottom-left-radius": ["border-bottom-left-radius"],
114
+ "border-bottom-right-radius": ["border-bottom-right-radius"],
115
+ "border-bottom-style": ["border-bottom-style"],
116
+ "border-bottom-width": ["border-bottom-width"],
117
+ "border-collapse": ["border-collapse"],
118
+ "border-color": [
119
+ "border-bottom-color",
120
+ "border-left-color",
121
+ "border-right-color",
122
+ "border-top-color"
123
+ ],
124
+ "border-end-end-radius": ["border-end-end-radius"],
125
+ "border-end-start-radius": ["border-end-start-radius"],
126
+ "border-image": [
127
+ "border-image-outset",
128
+ "border-image-repeat",
129
+ "border-image-slice",
130
+ "border-image-source",
131
+ "border-image-width"
132
+ ],
133
+ "border-image-outset": ["border-image-outset"],
134
+ "border-image-repeat": ["border-image-repeat"],
135
+ "border-image-slice": ["border-image-slice"],
136
+ "border-image-source": ["border-image-source"],
137
+ "border-image-width": ["border-image-width"],
138
+ "border-inline": [
139
+ "border-inline-end-color",
140
+ "border-inline-start-color",
141
+ "border-inline-end-style",
142
+ "border-inline-start-style",
143
+ "border-inline-end-width",
144
+ "border-inline-start-width"
145
+ ],
146
+ "border-inline-color": ["border-inline-end-color", "border-inline-start-color"],
147
+ "border-inline-end": [
148
+ "border-inline-end-color",
149
+ "border-inline-end-style",
150
+ "border-inline-end-width"
151
+ ],
152
+ "border-inline-end-color": ["border-inline-end-color"],
153
+ "border-inline-end-style": ["border-inline-end-style"],
154
+ "border-inline-end-width": ["border-inline-end-width"],
155
+ "border-inline-start": [
156
+ "border-inline-start-color",
157
+ "border-inline-start-style",
158
+ "border-inline-start-width"
159
+ ],
160
+ "border-inline-start-color": ["border-inline-start-color"],
161
+ "border-inline-start-style": ["border-inline-start-style"],
162
+ "border-inline-start-width": ["border-inline-start-width"],
163
+ "border-inline-style": ["border-inline-end-style", "border-inline-start-style"],
164
+ "border-inline-width": ["border-inline-end-width", "border-inline-start-width"],
165
+ "border-left": [
166
+ "border-left-color",
167
+ "border-left-style",
168
+ "border-left-width"
169
+ ],
170
+ "border-left-color": ["border-left-color"],
171
+ "border-left-style": ["border-left-style"],
172
+ "border-left-width": ["border-left-width"],
173
+ "border-radius": [
174
+ "border-bottom-left-radius",
175
+ "border-bottom-right-radius",
176
+ "border-top-left-radius",
177
+ "border-top-right-radius"
178
+ ],
179
+ "border-right": [
180
+ "border-right-color",
181
+ "border-right-style",
182
+ "border-right-width"
183
+ ],
184
+ "border-right-color": ["border-right-color"],
185
+ "border-right-style": ["border-right-style"],
186
+ "border-right-width": ["border-right-width"],
187
+ "border-spacing": ["border-spacing"],
188
+ "border-start-end-radius": ["border-start-end-radius"],
189
+ "border-start-start-radius": ["border-start-start-radius"],
190
+ "border-style": [
191
+ "border-bottom-style",
192
+ "border-left-style",
193
+ "border-right-style",
194
+ "border-top-style"
195
+ ],
196
+ "border-top": [
197
+ "border-top-color",
198
+ "border-top-style",
199
+ "border-top-width"
200
+ ],
201
+ "border-top-color": ["border-top-color"],
202
+ "border-top-left-radius": ["border-top-left-radius"],
203
+ "border-top-right-radius": ["border-top-right-radius"],
204
+ "border-top-style": ["border-top-style"],
205
+ "border-top-width": ["border-top-width"],
206
+ "border-width": [
207
+ "border-bottom-width",
208
+ "border-left-width",
209
+ "border-right-width",
210
+ "border-top-width"
211
+ ],
212
+ "bottom": ["bottom"],
213
+ "box-align": ["box-align"],
214
+ "box-decoration-break": ["box-decoration-break"],
215
+ "box-direction": ["box-direction"],
216
+ "box-flex": ["box-flex"],
217
+ "box-flex-group": ["box-flex-group"],
218
+ "box-lines": ["box-lines"],
219
+ "box-ordinal-group": ["box-ordinal-group"],
220
+ "box-orient": ["box-orient"],
221
+ "box-pack": ["box-pack"],
222
+ "box-shadow": ["box-shadow"],
223
+ "box-sizing": ["box-sizing"],
224
+ "break-after": ["break-after"],
225
+ "break-before": ["break-before"],
226
+ "break-inside": ["break-inside"],
227
+ "caption-side": ["caption-side"],
228
+ "caret": [
229
+ "caret-animation",
230
+ "caret-color",
231
+ "caret-shape"
232
+ ],
233
+ "caret-animation": ["caret-animation"],
234
+ "caret-color": ["caret-color"],
235
+ "caret-shape": ["caret-shape"],
236
+ "clear": ["clear"],
237
+ "clip": ["clip"],
238
+ "clip-path": ["clip-path"],
239
+ "clip-rule": ["clip-rule"],
240
+ "color": ["color"],
241
+ "color-interpolation-filters": ["color-interpolation-filters"],
242
+ "color-scheme": ["color-scheme"],
243
+ "column-count": ["column-count"],
244
+ "column-fill": ["column-fill"],
245
+ "column-gap": ["column-gap"],
246
+ "column-height": ["column-height"],
247
+ "column-rule": [
248
+ "column-rule-color",
249
+ "column-rule-style",
250
+ "column-rule-width"
251
+ ],
252
+ "column-rule-color": ["column-rule-color"],
253
+ "column-rule-style": ["column-rule-style"],
254
+ "column-rule-width": ["column-rule-width"],
255
+ "column-span": ["column-span"],
256
+ "column-width": ["column-width"],
257
+ "column-wrap": ["column-wrap"],
258
+ "columns": [
259
+ "column-count",
260
+ "column-height",
261
+ "column-width"
262
+ ],
263
+ "contain": ["contain"],
264
+ "contain-intrinsic-block-size": ["contain-intrinsic-block-size"],
265
+ "contain-intrinsic-height": ["contain-intrinsic-height"],
266
+ "contain-intrinsic-inline-size": ["contain-intrinsic-inline-size"],
267
+ "contain-intrinsic-size": ["contain-intrinsic-height", "contain-intrinsic-width"],
268
+ "contain-intrinsic-width": ["contain-intrinsic-width"],
269
+ "container": ["container-name", "container-type"],
270
+ "container-name": ["container-name"],
271
+ "container-type": ["container-type"],
272
+ "content": ["content"],
273
+ "content-visibility": ["content-visibility"],
274
+ "corner-block-end-shape": ["corner-end-end-shape", "corner-end-start-shape"],
275
+ "corner-block-start-shape": ["corner-start-end-shape", "corner-start-start-shape"],
276
+ "corner-bottom-left-shape": ["corner-bottom-left-shape"],
277
+ "corner-bottom-right-shape": ["corner-bottom-right-shape"],
278
+ "corner-bottom-shape": ["corner-bottom-left-shape", "corner-bottom-right-shape"],
279
+ "corner-end-end-shape": ["corner-end-end-shape"],
280
+ "corner-end-start-shape": ["corner-end-start-shape"],
281
+ "corner-inline-end-shape": ["corner-end-end-shape", "corner-start-end-shape"],
282
+ "corner-inline-start-shape": ["corner-start-end-shape", "corner-start-start-shape"],
283
+ "corner-left-shape": ["corner-bottom-left-shape", "corner-top-left-shape"],
284
+ "corner-right-shape": ["corner-bottom-right-shape", "corner-top-right-shape"],
285
+ "corner-shape": [
286
+ "corner-bottom-left-shape",
287
+ "corner-bottom-right-shape",
288
+ "corner-top-left-shape",
289
+ "corner-top-right-shape"
290
+ ],
291
+ "corner-start-end-shape": ["corner-start-end-shape"],
292
+ "corner-start-start-shape": ["corner-start-start-shape"],
293
+ "corner-top-left-shape": ["corner-top-left-shape"],
294
+ "corner-top-right-shape": ["corner-top-right-shape"],
295
+ "corner-top-shape": ["corner-top-left-shape", "corner-top-right-shape"],
296
+ "counter-increment": ["counter-increment"],
297
+ "counter-reset": ["counter-reset"],
298
+ "counter-set": ["counter-set"],
299
+ "cursor": ["cursor"],
300
+ "cx": ["cx"],
301
+ "cy": ["cy"],
302
+ "d": ["d"],
303
+ "direction": ["direction"],
304
+ "display": ["display"],
305
+ "dominant-baseline": ["dominant-baseline"],
306
+ "dynamic-range-limit": ["dynamic-range-limit"],
307
+ "empty-cells": ["empty-cells"],
308
+ "field-sizing": ["field-sizing"],
309
+ "fill": ["fill"],
310
+ "fill-opacity": ["fill-opacity"],
311
+ "fill-rule": ["fill-rule"],
312
+ "filter": ["filter"],
313
+ "flex": [
314
+ "flex-basis",
315
+ "flex-grow",
316
+ "flex-shrink"
317
+ ],
318
+ "flex-basis": ["flex-basis"],
319
+ "flex-direction": ["flex-direction"],
320
+ "flex-flow": ["flex-direction", "flex-wrap"],
321
+ "flex-grow": ["flex-grow"],
322
+ "flex-shrink": ["flex-shrink"],
323
+ "flex-wrap": ["flex-wrap"],
324
+ "float": ["float"],
325
+ "flood-color": ["flood-color"],
326
+ "flood-opacity": ["flood-opacity"],
327
+ "font": [
328
+ "font-family",
329
+ "font-size",
330
+ "font-stretch",
331
+ "font-style",
332
+ "font-variant-alternates",
333
+ "font-variant-caps",
334
+ "font-variant-east-asian",
335
+ "font-variant-emoji",
336
+ "font-variant-ligatures",
337
+ "font-variant-numeric",
338
+ "font-variant-position",
339
+ "font-weight",
340
+ "line-height"
341
+ ],
342
+ "font-family": ["font-family"],
343
+ "font-feature-settings": ["font-feature-settings"],
344
+ "font-kerning": ["font-kerning"],
345
+ "font-language-override": ["font-language-override"],
346
+ "font-optical-sizing": ["font-optical-sizing"],
347
+ "font-palette": ["font-palette"],
348
+ "font-size": ["font-size"],
349
+ "font-size-adjust": ["font-size-adjust"],
350
+ "font-smooth": ["font-smooth"],
351
+ "font-stretch": ["font-stretch"],
352
+ "font-style": ["font-style"],
353
+ "font-synthesis": [
354
+ "font-synthesis-position",
355
+ "font-synthesis-small-caps",
356
+ "font-synthesis-style",
357
+ "font-synthesis-weight"
358
+ ],
359
+ "font-synthesis-position": ["font-synthesis-position"],
360
+ "font-synthesis-small-caps": ["font-synthesis-small-caps"],
361
+ "font-synthesis-style": ["font-synthesis-style"],
362
+ "font-synthesis-weight": ["font-synthesis-weight"],
363
+ "font-variant": [
364
+ "font-variant-alternates",
365
+ "font-variant-caps",
366
+ "font-variant-east-asian",
367
+ "font-variant-emoji",
368
+ "font-variant-ligatures",
369
+ "font-variant-numeric",
370
+ "font-variant-position"
371
+ ],
372
+ "font-variant-alternates": ["font-variant-alternates"],
373
+ "font-variant-caps": ["font-variant-caps"],
374
+ "font-variant-east-asian": ["font-variant-east-asian"],
375
+ "font-variant-emoji": ["font-variant-emoji"],
376
+ "font-variant-ligatures": ["font-variant-ligatures"],
377
+ "font-variant-numeric": ["font-variant-numeric"],
378
+ "font-variant-position": ["font-variant-position"],
379
+ "font-variation-settings": ["font-variation-settings"],
380
+ "font-weight": ["font-weight"],
381
+ "font-width": ["font-width"],
382
+ "forced-color-adjust": ["forced-color-adjust"],
383
+ "gap": ["column-gap", "row-gap"],
384
+ "grid": [
385
+ "column-gap",
386
+ "grid-auto-columns",
387
+ "grid-auto-flow",
388
+ "grid-auto-rows",
389
+ "grid-column-gap",
390
+ "grid-row-gap",
391
+ "grid-template-areas",
392
+ "grid-template-columns",
393
+ "grid-template-rows",
394
+ "row-gap"
395
+ ],
396
+ "grid-area": [
397
+ "grid-column-end",
398
+ "grid-column-start",
399
+ "grid-row-end",
400
+ "grid-row-start"
401
+ ],
402
+ "grid-auto-columns": ["grid-auto-columns"],
403
+ "grid-auto-flow": ["grid-auto-flow"],
404
+ "grid-auto-rows": ["grid-auto-rows"],
405
+ "grid-column": ["grid-column-end", "grid-column-start"],
406
+ "grid-column-end": ["grid-column-end"],
407
+ "grid-column-gap": ["grid-column-gap"],
408
+ "grid-column-start": ["grid-column-start"],
409
+ "grid-gap": ["grid-column-gap", "grid-row-gap"],
410
+ "grid-row": ["grid-row-end", "grid-row-start"],
411
+ "grid-row-end": ["grid-row-end"],
412
+ "grid-row-gap": ["grid-row-gap"],
413
+ "grid-row-start": ["grid-row-start"],
414
+ "grid-template": [
415
+ "grid-template-areas",
416
+ "grid-template-columns",
417
+ "grid-template-rows"
418
+ ],
419
+ "grid-template-areas": ["grid-template-areas"],
420
+ "grid-template-columns": ["grid-template-columns"],
421
+ "grid-template-rows": ["grid-template-rows"],
422
+ "hanging-punctuation": ["hanging-punctuation"],
423
+ "height": ["height"],
424
+ "hyphenate-character": ["hyphenate-character"],
425
+ "hyphenate-limit-chars": ["hyphenate-limit-chars"],
426
+ "hyphens": ["hyphens"],
427
+ "image-orientation": ["image-orientation"],
428
+ "image-rendering": ["image-rendering"],
429
+ "image-resolution": ["image-resolution"],
430
+ "ime-mode": ["ime-mode"],
431
+ "initial-letter": ["initial-letter"],
432
+ "initial-letter-align": ["initial-letter-align"],
433
+ "inline-size": ["inline-size"],
434
+ "inset": [
435
+ "bottom",
436
+ "left",
437
+ "right",
438
+ "top"
439
+ ],
440
+ "inset-block": ["inset-block-end", "inset-block-start"],
441
+ "inset-block-end": ["inset-block-end"],
442
+ "inset-block-start": ["inset-block-start"],
443
+ "inset-inline": ["inset-inline-end", "inset-inline-start"],
444
+ "inset-inline-end": ["inset-inline-end"],
445
+ "inset-inline-start": ["inset-inline-start"],
446
+ "interactivity": ["interactivity"],
447
+ "interest-delay": ["interest-delay-end", "interest-delay-start"],
448
+ "interest-delay-end": ["interest-delay-end"],
449
+ "interest-delay-start": ["interest-delay-start"],
450
+ "interpolate-size": ["interpolate-size"],
451
+ "isolation": ["isolation"],
452
+ "justify-content": ["justify-content"],
453
+ "justify-items": ["justify-items"],
454
+ "justify-self": ["justify-self"],
455
+ "justify-tracks": ["justify-tracks"],
456
+ "left": ["left"],
457
+ "letter-spacing": ["letter-spacing"],
458
+ "lighting-color": ["lighting-color"],
459
+ "line-break": ["line-break"],
460
+ "line-clamp": ["line-clamp"],
461
+ "line-height": ["line-height"],
462
+ "line-height-step": ["line-height-step"],
463
+ "list-style": [
464
+ "list-style-image",
465
+ "list-style-position",
466
+ "list-style-type"
467
+ ],
468
+ "list-style-image": ["list-style-image"],
469
+ "list-style-position": ["list-style-position"],
470
+ "list-style-type": ["list-style-type"],
471
+ "margin": [
472
+ "margin-bottom",
473
+ "margin-left",
474
+ "margin-right",
475
+ "margin-top"
476
+ ],
477
+ "margin-block": ["margin-block-end", "margin-block-start"],
478
+ "margin-block-end": ["margin-block-end"],
479
+ "margin-block-start": ["margin-block-start"],
480
+ "margin-bottom": ["margin-bottom"],
481
+ "margin-inline": ["margin-inline-end", "margin-inline-start"],
482
+ "margin-inline-end": ["margin-inline-end"],
483
+ "margin-inline-start": ["margin-inline-start"],
484
+ "margin-left": ["margin-left"],
485
+ "margin-right": ["margin-right"],
486
+ "margin-top": ["margin-top"],
487
+ "margin-trim": ["margin-trim"],
488
+ "marker": [
489
+ "marker-end",
490
+ "marker-mid",
491
+ "marker-start"
492
+ ],
493
+ "marker-end": ["marker-end"],
494
+ "marker-mid": ["marker-mid"],
495
+ "marker-start": ["marker-start"],
496
+ "mask": [
497
+ "mask-clip",
498
+ "mask-composite",
499
+ "mask-image",
500
+ "mask-mode",
501
+ "mask-origin",
502
+ "mask-position",
503
+ "mask-repeat",
504
+ "mask-size"
505
+ ],
506
+ "mask-border": [
507
+ "mask-border-mode",
508
+ "mask-border-outset",
509
+ "mask-border-repeat",
510
+ "mask-border-slice",
511
+ "mask-border-source",
512
+ "mask-border-width"
513
+ ],
514
+ "mask-border-mode": ["mask-border-mode"],
515
+ "mask-border-outset": ["mask-border-outset"],
516
+ "mask-border-repeat": ["mask-border-repeat"],
517
+ "mask-border-slice": ["mask-border-slice"],
518
+ "mask-border-source": ["mask-border-source"],
519
+ "mask-border-width": ["mask-border-width"],
520
+ "mask-clip": ["mask-clip"],
521
+ "mask-composite": ["mask-composite"],
522
+ "mask-image": ["mask-image"],
523
+ "mask-mode": ["mask-mode"],
524
+ "mask-origin": ["mask-origin"],
525
+ "mask-position": ["mask-position"],
526
+ "mask-repeat": ["mask-repeat"],
527
+ "mask-size": ["mask-size"],
528
+ "mask-type": ["mask-type"],
529
+ "masonry-auto-flow": ["masonry-auto-flow"],
530
+ "math-depth": ["math-depth"],
531
+ "math-shift": ["math-shift"],
532
+ "math-style": ["math-style"],
533
+ "max-block-size": ["max-block-size"],
534
+ "max-height": ["max-height"],
535
+ "max-inline-size": ["max-inline-size"],
536
+ "max-lines": ["max-lines"],
537
+ "max-width": ["max-width"],
538
+ "min-block-size": ["min-block-size"],
539
+ "min-height": ["min-height"],
540
+ "min-inline-size": ["min-inline-size"],
541
+ "min-width": ["min-width"],
542
+ "mix-blend-mode": ["mix-blend-mode"],
543
+ "object-fit": ["object-fit"],
544
+ "object-position": ["object-position"],
545
+ "object-view-box": ["object-view-box"],
546
+ "offset": [
547
+ "offset-anchor",
548
+ "offset-distance",
549
+ "offset-path",
550
+ "offset-position",
551
+ "offset-rotate"
552
+ ],
553
+ "offset-anchor": ["offset-anchor"],
554
+ "offset-distance": ["offset-distance"],
555
+ "offset-path": ["offset-path"],
556
+ "offset-position": ["offset-position"],
557
+ "offset-rotate": ["offset-rotate"],
558
+ "opacity": ["opacity"],
559
+ "order": ["order"],
560
+ "orphans": ["orphans"],
561
+ "outline": [
562
+ "outline-color",
563
+ "outline-style",
564
+ "outline-width"
565
+ ],
566
+ "outline-color": ["outline-color"],
567
+ "outline-offset": ["outline-offset"],
568
+ "outline-style": ["outline-style"],
569
+ "outline-width": ["outline-width"],
570
+ "overflow": ["overflow-x", "overflow-y"],
571
+ "overflow-anchor": ["overflow-anchor"],
572
+ "overflow-block": ["overflow-block"],
573
+ "overflow-clip-box": ["overflow-clip-box"],
574
+ "overflow-clip-margin": ["overflow-clip-margin"],
575
+ "overflow-inline": ["overflow-inline"],
576
+ "overflow-wrap": ["overflow-wrap"],
577
+ "overflow-x": ["overflow-x"],
578
+ "overflow-y": ["overflow-y"],
579
+ "overlay": ["overlay"],
580
+ "overscroll-behavior": ["overscroll-behavior-x", "overscroll-behavior-y"],
581
+ "overscroll-behavior-block": ["overscroll-behavior-block"],
582
+ "overscroll-behavior-inline": ["overscroll-behavior-inline"],
583
+ "overscroll-behavior-x": ["overscroll-behavior-x"],
584
+ "overscroll-behavior-y": ["overscroll-behavior-y"],
585
+ "padding": [
586
+ "padding-bottom",
587
+ "padding-left",
588
+ "padding-right",
589
+ "padding-top"
590
+ ],
591
+ "padding-block": ["padding-block-end", "padding-block-start"],
592
+ "padding-block-end": ["padding-block-end"],
593
+ "padding-block-start": ["padding-block-start"],
594
+ "padding-bottom": ["padding-bottom"],
595
+ "padding-inline": ["padding-inline-end", "padding-inline-start"],
596
+ "padding-inline-end": ["padding-inline-end"],
597
+ "padding-inline-start": ["padding-inline-start"],
598
+ "padding-left": ["padding-left"],
599
+ "padding-right": ["padding-right"],
600
+ "padding-top": ["padding-top"],
601
+ "page": ["page"],
602
+ "page-break-after": ["page-break-after"],
603
+ "page-break-before": ["page-break-before"],
604
+ "page-break-inside": ["page-break-inside"],
605
+ "paint-order": ["paint-order"],
606
+ "perspective": ["perspective"],
607
+ "perspective-origin": ["perspective-origin"],
608
+ "place-content": ["align-content", "justify-content"],
609
+ "place-items": ["align-items", "justify-items"],
610
+ "place-self": ["align-self", "justify-self"],
611
+ "pointer-events": ["pointer-events"],
612
+ "position": ["position"],
613
+ "position-anchor": ["position-anchor"],
614
+ "position-area": ["position-area"],
615
+ "position-try": ["position-try-fallbacks", "position-try-order"],
616
+ "position-try-fallbacks": ["position-try-fallbacks"],
617
+ "position-try-order": ["position-try-order"],
618
+ "position-visibility": ["position-visibility"],
619
+ "print-color-adjust": ["print-color-adjust"],
620
+ "quotes": ["quotes"],
621
+ "r": ["r"],
622
+ "reading-flow": ["reading-flow"],
623
+ "reading-order": ["reading-order"],
624
+ "resize": ["resize"],
625
+ "right": ["right"],
626
+ "rotate": ["rotate"],
627
+ "row-gap": ["row-gap"],
628
+ "ruby-align": ["ruby-align"],
629
+ "ruby-merge": ["ruby-merge"],
630
+ "ruby-overhang": ["ruby-overhang"],
631
+ "ruby-position": ["ruby-position"],
632
+ "rx": ["rx"],
633
+ "ry": ["ry"],
634
+ "scale": ["scale"],
635
+ "scroll-behavior": ["scroll-behavior"],
636
+ "scroll-initial-target": ["scroll-initial-target"],
637
+ "scroll-margin": [
638
+ "scroll-margin-bottom",
639
+ "scroll-margin-left",
640
+ "scroll-margin-right",
641
+ "scroll-margin-top"
642
+ ],
643
+ "scroll-margin-block": ["scroll-margin-block-end", "scroll-margin-block-start"],
644
+ "scroll-margin-block-end": ["scroll-margin-block-end"],
645
+ "scroll-margin-block-start": ["scroll-margin-block-start"],
646
+ "scroll-margin-bottom": ["scroll-margin-bottom"],
647
+ "scroll-margin-inline": ["scroll-margin-inline-end", "scroll-margin-inline-start"],
648
+ "scroll-margin-inline-end": ["scroll-margin-inline-end"],
649
+ "scroll-margin-inline-start": ["scroll-margin-inline-start"],
650
+ "scroll-margin-left": ["scroll-margin-left"],
651
+ "scroll-margin-right": ["scroll-margin-right"],
652
+ "scroll-margin-top": ["scroll-margin-top"],
653
+ "scroll-marker-group": ["scroll-marker-group"],
654
+ "scroll-padding": [
655
+ "scroll-padding-bottom",
656
+ "scroll-padding-left",
657
+ "scroll-padding-right",
658
+ "scroll-padding-top"
659
+ ],
660
+ "scroll-padding-block": ["scroll-padding-block-end", "scroll-padding-block-start"],
661
+ "scroll-padding-block-end": ["scroll-padding-block-end"],
662
+ "scroll-padding-block-start": ["scroll-padding-block-start"],
663
+ "scroll-padding-bottom": ["scroll-padding-bottom"],
664
+ "scroll-padding-inline": ["scroll-padding-inline-end", "scroll-padding-inline-start"],
665
+ "scroll-padding-inline-end": ["scroll-padding-inline-end"],
666
+ "scroll-padding-inline-start": ["scroll-padding-inline-start"],
667
+ "scroll-padding-left": ["scroll-padding-left"],
668
+ "scroll-padding-right": ["scroll-padding-right"],
669
+ "scroll-padding-top": ["scroll-padding-top"],
670
+ "scroll-snap-align": ["scroll-snap-align"],
671
+ "scroll-snap-coordinate": ["scroll-snap-coordinate"],
672
+ "scroll-snap-destination": ["scroll-snap-destination"],
673
+ "scroll-snap-points-x": ["scroll-snap-points-x"],
674
+ "scroll-snap-points-y": ["scroll-snap-points-y"],
675
+ "scroll-snap-stop": ["scroll-snap-stop"],
676
+ "scroll-snap-type": ["scroll-snap-type"],
677
+ "scroll-snap-type-x": ["scroll-snap-type-x"],
678
+ "scroll-snap-type-y": ["scroll-snap-type-y"],
679
+ "scroll-target-group": ["scroll-target-group"],
680
+ "scroll-timeline": ["scroll-timeline-axis", "scroll-timeline-name"],
681
+ "scroll-timeline-axis": ["scroll-timeline-axis"],
682
+ "scroll-timeline-name": ["scroll-timeline-name"],
683
+ "scrollbar-color": ["scrollbar-color"],
684
+ "scrollbar-gutter": ["scrollbar-gutter"],
685
+ "scrollbar-width": ["scrollbar-width"],
686
+ "shape-image-threshold": ["shape-image-threshold"],
687
+ "shape-margin": ["shape-margin"],
688
+ "shape-outside": ["shape-outside"],
689
+ "shape-rendering": ["shape-rendering"],
690
+ "speak-as": ["speak-as"],
691
+ "stop-color": ["stop-color"],
692
+ "stop-opacity": ["stop-opacity"],
693
+ "stroke": [
694
+ "stroke-dasharray",
695
+ "stroke-dashoffset",
696
+ "stroke-linecap",
697
+ "stroke-linejoin",
698
+ "stroke-miterlimit",
699
+ "stroke-opacity",
700
+ "stroke-width"
701
+ ],
702
+ "stroke-color": ["stroke-color"],
703
+ "stroke-dasharray": ["stroke-dasharray"],
704
+ "stroke-dashoffset": ["stroke-dashoffset"],
705
+ "stroke-linecap": ["stroke-linecap"],
706
+ "stroke-linejoin": ["stroke-linejoin"],
707
+ "stroke-miterlimit": ["stroke-miterlimit"],
708
+ "stroke-opacity": ["stroke-opacity"],
709
+ "stroke-width": ["stroke-width"],
710
+ "tab-size": ["tab-size"],
711
+ "table-layout": ["table-layout"],
712
+ "text-align": ["text-align"],
713
+ "text-align-last": ["text-align-last"],
714
+ "text-anchor": ["text-anchor"],
715
+ "text-autospace": ["text-autospace"],
716
+ "text-box": ["text-box-edge", "text-box-trim"],
717
+ "text-box-edge": ["text-box-edge"],
718
+ "text-box-trim": ["text-box-trim"],
719
+ "text-combine-upright": ["text-combine-upright"],
720
+ "text-decoration": [
721
+ "text-decoration-color",
722
+ "text-decoration-line",
723
+ "text-decoration-style",
724
+ "text-decoration-thickness"
725
+ ],
726
+ "text-decoration-color": ["text-decoration-color"],
727
+ "text-decoration-inset": ["text-decoration-inset"],
728
+ "text-decoration-line": ["text-decoration-line"],
729
+ "text-decoration-skip": ["text-decoration-skip"],
730
+ "text-decoration-skip-ink": ["text-decoration-skip-ink"],
731
+ "text-decoration-style": ["text-decoration-style"],
732
+ "text-decoration-thickness": ["text-decoration-thickness"],
733
+ "text-emphasis": ["text-emphasis-color", "text-emphasis-style"],
734
+ "text-emphasis-color": ["text-emphasis-color"],
735
+ "text-emphasis-position": ["text-emphasis-position"],
736
+ "text-emphasis-style": ["text-emphasis-style"],
737
+ "text-indent": ["text-indent"],
738
+ "text-justify": ["text-justify"],
739
+ "text-orientation": ["text-orientation"],
740
+ "text-overflow": ["text-overflow"],
741
+ "text-rendering": ["text-rendering"],
742
+ "text-shadow": ["text-shadow"],
743
+ "text-size-adjust": ["text-size-adjust"],
744
+ "text-spacing-trim": ["text-spacing-trim"],
745
+ "text-transform": ["text-transform"],
746
+ "text-underline-offset": ["text-underline-offset"],
747
+ "text-underline-position": ["text-underline-position"],
748
+ "text-wrap": ["text-wrap-mode", "text-wrap-style"],
749
+ "text-wrap-mode": ["text-wrap-mode"],
750
+ "text-wrap-style": ["text-wrap-style"],
751
+ "timeline-scope": ["timeline-scope"],
752
+ "timeline-trigger": [
753
+ "timeline-trigger-exit-range-end",
754
+ "timeline-trigger-exit-range-start",
755
+ "timeline-trigger-name",
756
+ "timeline-trigger-range-end",
757
+ "timeline-trigger-range-start",
758
+ "timeline-trigger-source"
759
+ ],
760
+ "timeline-trigger-exit-range": ["timeline-trigger-exit-range-end", "timeline-trigger-exit-range-start"],
761
+ "timeline-trigger-exit-range-end": ["timeline-trigger-exit-range-end"],
762
+ "timeline-trigger-exit-range-start": ["timeline-trigger-exit-range-start"],
763
+ "timeline-trigger-name": ["timeline-trigger-name"],
764
+ "timeline-trigger-range": ["timeline-trigger-range-end", "timeline-trigger-range-start"],
765
+ "timeline-trigger-range-end": ["timeline-trigger-range-end"],
766
+ "timeline-trigger-range-start": ["timeline-trigger-range-start"],
767
+ "timeline-trigger-source": ["timeline-trigger-source"],
768
+ "top": ["top"],
769
+ "touch-action": ["touch-action"],
770
+ "transform": ["transform"],
771
+ "transform-box": ["transform-box"],
772
+ "transform-origin": ["transform-origin"],
773
+ "transform-style": ["transform-style"],
774
+ "transition": [
775
+ "transition-behavior",
776
+ "transition-delay",
777
+ "transition-duration",
778
+ "transition-property",
779
+ "transition-timing-function"
780
+ ],
781
+ "transition-behavior": ["transition-behavior"],
782
+ "transition-delay": ["transition-delay"],
783
+ "transition-duration": ["transition-duration"],
784
+ "transition-property": ["transition-property"],
785
+ "transition-timing-function": ["transition-timing-function"],
786
+ "translate": ["translate"],
787
+ "trigger-scope": ["trigger-scope"],
788
+ "unicode-bidi": ["unicode-bidi"],
789
+ "user-select": ["user-select"],
790
+ "vector-effect": ["vector-effect"],
791
+ "vertical-align": ["vertical-align"],
792
+ "view-timeline": ["view-timeline-axis", "view-timeline-name"],
793
+ "view-timeline-axis": ["view-timeline-axis"],
794
+ "view-timeline-inset": ["view-timeline-inset"],
795
+ "view-timeline-name": ["view-timeline-name"],
796
+ "view-transition-class": ["view-transition-class"],
797
+ "view-transition-name": ["view-transition-name"],
798
+ "visibility": ["visibility"],
799
+ "white-space": ["text-wrap-mode", "white-space-collapse"],
800
+ "white-space-collapse": ["white-space-collapse"],
801
+ "widows": ["widows"],
802
+ "width": ["width"],
803
+ "will-change": ["will-change"],
804
+ "word-break": ["word-break"],
805
+ "word-spacing": ["word-spacing"],
806
+ "word-wrap": ["word-wrap"],
807
+ "writing-mode": ["writing-mode"],
808
+ "x": ["x"],
809
+ "y": ["y"],
810
+ "z-index": ["z-index"],
811
+ "zoom": ["zoom"]
812
+ };
813
+ //#endregion
814
+ //#region src/internal/property-effects.ts
815
+ const UNIVERSAL_EFFECT = "*";
816
+ const propertyEffectsLookup = PROPERTY_EFFECTS;
817
+ function isCustomProperty(property) {
818
+ return property.startsWith("--");
819
+ }
820
+ function isVendorPrefixedProperty(property) {
821
+ return property.startsWith("-") && property.startsWith("--") === false;
822
+ }
823
+ /**
824
+ * Returns the list of CSS properties that a given property can affect, accounting for shorthand expansion.
825
+ * @internal
826
+ *
827
+ * @param property - A CSS property name in kebab-case.
828
+ * @returns An array of affected property names. For the `all` shorthand, returns `['*']`. For custom or vendor-prefixed properties, returns the property itself. For standard shorthands, returns the set of longhand properties they expand to.
829
+ *
830
+ * @remarks Used by the order-sensitivity detection logic to determine whether two properties in the same scope share overlapping effects (e.g. `margin` and `margin-top`). The lookup map is generated at build time from the CSS shorthand specification.
831
+ *
832
+ * @example
833
+ * ```ts
834
+ * getPropertyEffects('margin') // ['margin-top', 'margin-right', 'margin-bottom', 'margin-left']
835
+ * getPropertyEffects('color') // ['color']
836
+ * getPropertyEffects('--my-var') // ['--my-var']
837
+ * getPropertyEffects('all') // ['*']
838
+ * ```
839
+ */
840
+ function getPropertyEffects(property) {
841
+ if (property === "all") return [UNIVERSAL_EFFECT];
842
+ if (isCustomProperty(property) || isVendorPrefixedProperty(property)) return [property];
843
+ return propertyEffectsLookup[property] || [property];
844
+ }
845
+ /**
846
+ * Determines whether two CSS properties have overlapping effects, meaning they can interfere with each other when both are present in the same selector scope.
847
+ * @internal
848
+ *
849
+ * @param left - First CSS property name in kebab-case.
850
+ * @param right - Second CSS property name in kebab-case.
851
+ * @returns `true` if the two properties share at least one common affected property or if either is the universal `all` shorthand.
852
+ *
853
+ * @remarks Custom properties never overlap with other properties. Identical properties always overlap. This check drives the order-sensitivity detection in `optimizeAtomicStyleContents`, ensuring that shorthand/longhand conflicts like `margin` + `margin-top` are correctly handled.
854
+ *
855
+ * @example
856
+ * ```ts
857
+ * hasPropertyEffectOverlap('margin', 'margin-top') // true
858
+ * hasPropertyEffectOverlap('color', 'font-size') // false
859
+ * hasPropertyEffectOverlap('all', 'color') // true
860
+ * ```
861
+ */
862
+ function hasPropertyEffectOverlap(left, right) {
863
+ if (left === right) return true;
864
+ if (isCustomProperty(left) || isCustomProperty(right)) return false;
865
+ const leftEffects = getPropertyEffects(left);
866
+ const rightEffects = getPropertyEffects(right);
867
+ if (leftEffects.includes(UNIVERSAL_EFFECT) || rightEffects.includes(UNIVERSAL_EFFECT)) return true;
868
+ const rightEffectSet = new Set(rightEffects);
869
+ return leftEffects.some((effect) => rightEffectSet.has(effect));
870
+ }
5
871
  //#endregion
6
872
  //#region src/internal/utils.ts
873
+ /**
874
+ * Creates a scoped logger with configurable log-level functions and a toggleable debug mode.
875
+ *
876
+ * @param prefix - Label prepended to every log message (e.g. `'[PikaCSS]'`).
877
+ * @returns A logger object with `debug`, `info`, `warn`, `error` methods and configuration setters.
878
+ *
879
+ * @remarks Debug messages are suppressed by default. Call `log.toggleDebug()` to enable them. Each log level can be replaced with a custom implementation via the `set*Fn` methods, which is useful for redirecting output in non-browser environments.
880
+ *
881
+ * @example
882
+ * ```ts
883
+ * const log = createLogger('[MyPlugin]')
884
+ * log.info('initialized') // '[MyPlugin][INFO] initialized'
885
+ * log.toggleDebug()
886
+ * log.debug('verbose info') // '[MyPlugin][DEBUG] verbose info'
887
+ * ```
888
+ */
7
889
  function createLogger(prefix) {
8
890
  let currentPrefix = prefix;
9
891
  let enabledDebug = false;
@@ -45,9 +927,36 @@ function createLogger(prefix) {
45
927
  }
46
928
  };
47
929
  }
930
+ /**
931
+ * Default logger instance used throughout the PikaCSS core engine, prefixed with `[PikaCSS]`.
932
+ *
933
+ * @remarks Shared across all internal modules. Plugins and integration code can call `log.toggleDebug()` to enable verbose output during development.
934
+ *
935
+ * @example
936
+ * ```ts
937
+ * log.info('Engine created')
938
+ * log.warn('Unknown layer detected')
939
+ * ```
940
+ */
48
941
  const log = createLogger("[PikaCSS]");
49
942
  const chars = [..."abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"];
50
943
  const numOfChars = chars.length;
944
+ /**
945
+ * Converts a non-negative integer to a compact alphabetic string using a bijective base-52 encoding (a-z, A-Z).
946
+ * @internal
947
+ *
948
+ * @param num - The non-negative integer to encode.
949
+ * @returns A short alphabetic string unique to the given integer.
950
+ *
951
+ * @remarks Used to generate compact, human-readable atomic style class IDs. The encoding is deterministic: the same number always produces the same string.
952
+ *
953
+ * @example
954
+ * ```ts
955
+ * numberToChars(0) // 'a'
956
+ * numberToChars(51) // 'Z'
957
+ * numberToChars(52) // 'ba'
958
+ * ```
959
+ */
51
960
  function numberToChars(num) {
52
961
  if (num < numOfChars) return chars[num];
53
962
  let result = "";
@@ -59,48 +968,230 @@ function numberToChars(num) {
59
968
  return result;
60
969
  }
61
970
  const UPPER_CASE = /[A-Z]/g;
971
+ /**
972
+ * Converts a camelCase string to kebab-case at runtime. CSS custom properties (`--*`) are returned unchanged.
973
+ * @internal
974
+ *
975
+ * @param str - The camelCase string to convert.
976
+ * @returns The kebab-case equivalent of the input string.
977
+ *
978
+ * @remarks Runtime counterpart of the `ToKebab` type utility. Used during style extraction to normalize JavaScript-style property names to CSS property names.
979
+ *
980
+ * @example
981
+ * ```ts
982
+ * toKebab('backgroundColor') // 'background-color'
983
+ * toKebab('--my-var') // '--my-var'
984
+ * ```
985
+ */
62
986
  function toKebab(str) {
63
987
  if (str.startsWith("--")) return str;
64
988
  return str.replace(UPPER_CASE, (c) => `-${c.toLowerCase()}`);
65
989
  }
990
+ /**
991
+ * Type-narrowing guard that returns `true` when the value is neither `null` nor `undefined`.
992
+ * @internal
993
+ *
994
+ * @typeParam T - The type of the input value.
995
+ * @param value - The value to test.
996
+ * @returns `true` if the value is non-nullish, narrowing the type to `NonNullable<T>`.
997
+ *
998
+ * @remarks Commonly used as a `.filter()` predicate to strip nullish entries from arrays while preserving the narrowed type.
999
+ *
1000
+ * @example
1001
+ * ```ts
1002
+ * [1, null, 2, undefined].filter(isNotNullish) // [1, 2] typed as number[]
1003
+ * ```
1004
+ */
66
1005
  function isNotNullish(value) {
67
1006
  return value != null;
68
1007
  }
1008
+ /**
1009
+ * Type-narrowing guard that returns `true` when the value is not a string, narrowing the type to `Exclude<V, string>`.
1010
+ * @internal
1011
+ *
1012
+ * @typeParam V - The union type of the input value.
1013
+ * @param value - The value to test.
1014
+ * @returns `true` if the value is not a `string`.
1015
+ *
1016
+ * @remarks Useful for filtering processed style items to separate resolved definition objects from unresolved string references.
1017
+ *
1018
+ * @example
1019
+ * ```ts
1020
+ * const items: (string | object)[] = ['btn', { color: 'red' }]
1021
+ * const objects = items.filter(isNotString) // [{ color: 'red' }]
1022
+ * ```
1023
+ */
69
1024
  function isNotString(value) {
70
1025
  return typeof value !== "string";
71
1026
  }
1027
+ /**
1028
+ * Tests whether a value conforms to the `InternalPropertyValue` shape: a string, a `[value, fallback[]]` tuple, or nullish.
1029
+ * @internal
1030
+ *
1031
+ * @param v - The value to inspect.
1032
+ * @returns `true` if the value is a valid property value.
1033
+ *
1034
+ * @remarks During extraction, the engine uses this guard to distinguish CSS property values from nested selector objects or style item arrays.
1035
+ *
1036
+ * @example
1037
+ * ```ts
1038
+ * isPropertyValue('red') // true
1039
+ * isPropertyValue(['red', ['blue']]) // true
1040
+ * isPropertyValue(null) // true
1041
+ * isPropertyValue({ color: 'red' }) // false
1042
+ * ```
1043
+ */
72
1044
  function isPropertyValue(v) {
73
1045
  if (Array.isArray(v)) return v.length === 2 && typeof v[0] === "string" && Array.isArray(v[1]) && v[1].every((i) => typeof i === "string");
74
1046
  if (v == null) return true;
75
1047
  if (typeof v === "string") return true;
76
1048
  return false;
77
1049
  }
1050
+ /**
1051
+ * Serializes a value to a JSON string for use as a deterministic cache key.
1052
+ * @internal
1053
+ *
1054
+ * @param value - The value to serialize.
1055
+ * @returns The JSON string representation.
1056
+ *
1057
+ * @remarks Used to produce stable keys for selector chains and property content when building deduplication maps in the optimization pipeline.
1058
+ *
1059
+ * @example
1060
+ * ```ts
1061
+ * serialize(['.pk-%', 'color']) // '[[".pk-%"],"color"]'
1062
+ * ```
1063
+ */
78
1064
  function serialize(value) {
79
1065
  return JSON.stringify(value);
80
1066
  }
1067
+ /**
1068
+ * Adds one or more values to a `Set` and returns whether the set's size increased.
1069
+ * @internal
1070
+ *
1071
+ * @typeParam T - The element type of the set.
1072
+ * @param set - The target set to append to.
1073
+ * @param values - Values to add.
1074
+ * @returns `true` if at least one new element was added (the set grew).
1075
+ *
1076
+ * @remarks The boolean return is used by `appendAutocomplete` to determine whether the autocomplete config actually changed, avoiding unnecessary notification callbacks.
1077
+ *
1078
+ * @example
1079
+ * ```ts
1080
+ * const s = new Set(['a'])
1081
+ * addToSet(s, 'a', 'b') // true (added 'b')
1082
+ * addToSet(s, 'a') // false (no change)
1083
+ * ```
1084
+ */
81
1085
  function addToSet(set, ...values) {
1086
+ const before = set.size;
82
1087
  values.forEach((value) => set.add(value));
1088
+ return set.size !== before;
83
1089
  }
84
- function appendAutocompleteSelectors(config, ...selectors) {
85
- addToSet(config.autocomplete.selectors, ...selectors);
86
- }
87
- function appendAutocompleteStyleItemStrings(config, ...styleItemStrings) {
88
- addToSet(config.autocomplete.styleItemStrings, ...styleItemStrings);
89
- }
90
- function appendAutocompleteExtraProperties(config, ...properties) {
91
- addToSet(config.autocomplete.extraProperties, ...properties);
1090
+ /**
1091
+ * Flattens an `Arrayable<string>` value and adds all entries to a `Set`, returning whether the set grew.
1092
+ * @internal
1093
+ *
1094
+ * @param set - The target set to append to.
1095
+ * @param values - A single string or array of strings to add, or `undefined`/`null` to skip.
1096
+ * @returns `true` if at least one new entry was added; `false` if the input was nullish or all entries already existed.
1097
+ *
1098
+ * @remarks Short-circuits on nullish input for convenience, since many autocomplete contribution fields are optional.
1099
+ *
1100
+ * @example
1101
+ * ```ts
1102
+ * const s = new Set<string>()
1103
+ * appendAutocompleteEntries(s, 'hover') // true
1104
+ * appendAutocompleteEntries(s, ['hover']) // false (already present)
1105
+ * appendAutocompleteEntries(s, undefined) // false
1106
+ * ```
1107
+ */
1108
+ function appendAutocompleteEntries(set, values) {
1109
+ if (values == null) return false;
1110
+ return addToSet(set, ...[values].flat());
92
1111
  }
93
- function appendAutocompleteExtraCssProperties(config, ...properties) {
94
- addToSet(config.autocomplete.extraCssProperties, ...properties);
1112
+ /**
1113
+ * Merges a record of `Arrayable<string>` values into a `Map<string, string[]>`, returning whether any entry was added.
1114
+ * @internal
1115
+ *
1116
+ * @param map - The target map to append entries to.
1117
+ * @param entries - A record mapping keys to single or arrayed string values, or `undefined` to skip.
1118
+ * @returns `true` if at least one entry was added or extended; `false` if the input was nullish or empty.
1119
+ *
1120
+ * @remarks Existing map entries are extended (not replaced) with the new values, maintaining all previously registered suggestions for a given key. This accumulative behavior allows multiple plugins to contribute value suggestions for the same property.
1121
+ *
1122
+ * @example
1123
+ * ```ts
1124
+ * const map = new Map<string, string[]>()
1125
+ * appendAutocompleteRecordEntries(map, { color: ['red', 'blue'] }) // true
1126
+ * appendAutocompleteRecordEntries(map, { color: 'green' }) // true (now ['red','blue','green'])
1127
+ * ```
1128
+ */
1129
+ function appendAutocompleteRecordEntries(map, entries) {
1130
+ if (entries == null) return false;
1131
+ let changed = false;
1132
+ for (const [key, value] of Object.entries(entries)) {
1133
+ const nextValues = [value].flat();
1134
+ if (nextValues.length === 0) continue;
1135
+ const current = map.get(key) || [];
1136
+ map.set(key, [...current, ...nextValues]);
1137
+ changed = true;
1138
+ }
1139
+ return changed;
95
1140
  }
96
- function appendAutocompletePropertyValues(config, property, ...tsTypes) {
97
- const current = config.autocomplete.properties.get(property) || [];
98
- config.autocomplete.properties.set(property, [...current, ...tsTypes]);
1141
+ function normalizeAutocompleteRecordEntries(entries) {
1142
+ if (entries == null) return void 0;
1143
+ return Array.isArray(entries) ? Object.fromEntries(entries) : entries;
99
1144
  }
100
- function appendAutocompleteCssPropertyValues(config, property, ...values) {
101
- const current = config.autocomplete.cssProperties.get(property) || [];
102
- config.autocomplete.cssProperties.set(property, [...current, ...values]);
1145
+ /**
1146
+ * Merges an `AutocompleteContribution` or `AutocompleteConfig` into the resolved autocomplete state, returning whether any entry changed.
1147
+ *
1148
+ * @param config - The resolved engine config (or a subset with the `autocomplete` field) to mutate.
1149
+ * @param contribution - The autocomplete entries to merge in.
1150
+ * @returns `true` if any selector, shortcut, property, CSS property, or pattern entry was added or extended.
1151
+ *
1152
+ * @remarks Called by `engine.appendAutocomplete()` and during initial config resolution. Each sub-field (selectors, shortcuts, etc.) is independently merged and the function returns `true` if any of them changed, which triggers an `autocompleteConfigUpdated` notification.
1153
+ *
1154
+ * @example
1155
+ * ```ts
1156
+ * const changed = appendAutocomplete(resolvedConfig, {
1157
+ * selectors: 'dark',
1158
+ * cssProperties: { color: 'primary' },
1159
+ * })
1160
+ * ```
1161
+ */
1162
+ function appendAutocomplete(config, contribution) {
1163
+ const { patterns, properties, cssProperties, ...literals } = contribution;
1164
+ return [
1165
+ appendAutocompleteEntries(config.autocomplete.selectors, literals.selectors),
1166
+ appendAutocompleteEntries(config.autocomplete.shortcuts, literals.shortcuts),
1167
+ appendAutocompleteEntries(config.autocomplete.extraProperties, literals.extraProperties),
1168
+ appendAutocompleteEntries(config.autocomplete.extraCssProperties, literals.extraCssProperties),
1169
+ appendAutocompleteRecordEntries(config.autocomplete.properties, normalizeAutocompleteRecordEntries(properties)),
1170
+ appendAutocompleteRecordEntries(config.autocomplete.cssProperties, normalizeAutocompleteRecordEntries(cssProperties)),
1171
+ appendAutocompleteEntries(config.autocomplete.patterns.selectors, patterns?.selectors),
1172
+ appendAutocompleteEntries(config.autocomplete.patterns.shortcuts, patterns?.shortcuts),
1173
+ appendAutocompleteRecordEntries(config.autocomplete.patterns.properties, patterns?.properties),
1174
+ appendAutocompleteRecordEntries(config.autocomplete.patterns.cssProperties, patterns?.cssProperties)
1175
+ ].some(Boolean);
103
1176
  }
1177
+ /**
1178
+ * Serializes a `CSSStyleBlocks` tree into a CSS string, optionally formatted with indentation and newlines.
1179
+ *
1180
+ * @param blocks - The CSS block tree to render.
1181
+ * @param isFormatted - When `true`, output includes indentation and newlines for readability; when `false`, output is minified.
1182
+ * @param depth - Current nesting depth for indentation (defaults to `0`).
1183
+ * @returns The rendered CSS string.
1184
+ *
1185
+ * @remarks Recursively renders nested blocks (e.g. media queries wrapping selectors). Empty blocks (no properties and no children) are omitted from the output.
1186
+ *
1187
+ * @example
1188
+ * ```ts
1189
+ * const blocks: CSSStyleBlocks = new Map()
1190
+ * blocks.set('.pk-a', { properties: [{ property: 'color', value: 'red' }] })
1191
+ * renderCSSStyleBlocks(blocks, true)
1192
+ * // '.pk-a {\n color: red;\n}'
1193
+ * ```
1194
+ */
104
1195
  function renderCSSStyleBlocks(blocks, isFormatted, depth = 0) {
105
1196
  const blockIndent = isFormatted ? " ".repeat(depth) : "";
106
1197
  const blockBodyIndent = isFormatted ? " ".repeat(depth + 1) : "";
@@ -119,7 +1210,235 @@ function renderCSSStyleBlocks(blocks, isFormatted, depth = 0) {
119
1210
  });
120
1211
  return lines.join(lineEnd);
121
1212
  }
122
-
1213
+ //#endregion
1214
+ //#region src/internal/atomic-style.ts
1215
+ /**
1216
+ * Creates a fresh, empty `EngineStore` with all maps initialized.
1217
+ * @internal
1218
+ *
1219
+ * @returns A new `EngineStore` instance with empty maps.
1220
+ *
1221
+ * @remarks Called once during engine construction. Each engine instance owns a single store.
1222
+ *
1223
+ * @example
1224
+ * ```ts
1225
+ * const store = createEngineStore()
1226
+ * store.atomicStyles.size // 0
1227
+ * ```
1228
+ */
1229
+ function createEngineStore() {
1230
+ return {
1231
+ atomicStyleIds: /* @__PURE__ */ new Map(),
1232
+ atomicStyles: /* @__PURE__ */ new Map(),
1233
+ atomicStyleIdsByBaseKey: /* @__PURE__ */ new Map(),
1234
+ atomicStyleOrder: /* @__PURE__ */ new Map()
1235
+ };
1236
+ }
1237
+ /**
1238
+ * Assigns or retrieves a compact atomic style ID for the given resolved style content.
1239
+ * @internal
1240
+ *
1241
+ * @param options - Object containing the style `content`, the engine `prefix`, and the `stored` ID map.
1242
+ * @param options.content - The resolved style content to hash and identify.
1243
+ * @param options.prefix - The class-name prefix used when constructing a new atomic style ID.
1244
+ * @param options.stored - The map that caches assigned IDs by serialized key.
1245
+ * @returns The short alphabetic ID string (e.g. `'pk-a'`, `'pk-bA'`).
1246
+ *
1247
+ * @remarks For non-order-sensitive content, returns a cached ID if one already exists for the same base key. For order-sensitive content (where `orderSensitiveTo` is set), always generates a new ID to prevent incorrect reuse across different call-site orderings.
1248
+ *
1249
+ * @example
1250
+ * ```ts
1251
+ * const id = getAtomicStyleId({ content, prefix: 'pk-', stored: store.atomicStyleIds })
1252
+ * // 'pk-a'
1253
+ * ```
1254
+ */
1255
+ function getAtomicStyleId({ content, prefix, stored }) {
1256
+ const baseKey = getAtomicStyleBaseKey(content);
1257
+ if (isOrderSensitiveContent(content) === false) {
1258
+ const cached = stored.get(baseKey);
1259
+ if (cached != null) {
1260
+ log.debug(`Atomic style cached: ${cached}`);
1261
+ return cached;
1262
+ }
1263
+ }
1264
+ const num = stored.size;
1265
+ const id = `${prefix}${numberToChars(num)}`;
1266
+ const key = getAtomicStyleStoredKey({
1267
+ content,
1268
+ baseKey,
1269
+ num
1270
+ });
1271
+ stored.set(key, id);
1272
+ log.debug(`Generated new atomic style ID: ${id}`);
1273
+ return id;
1274
+ }
1275
+ /**
1276
+ * Resolves a `StyleContent` into an atomic style: either reusing an existing ID or creating a new `AtomicStyle` entry in the store.
1277
+ * @internal
1278
+ *
1279
+ * @param options - Object containing the style `content`, `prefix`, `store`, and the per-use-call `resolvedIdsByBaseKey` map for order-sensitive reuse tracking.
1280
+ * @param options.content - The style content to resolve into a cached or newly registered atomic style.
1281
+ * @param options.prefix - The atomic style ID prefix for any newly created IDs.
1282
+ * @param options.store - The engine store holding existing atomic styles and lookup maps.
1283
+ * @param options.resolvedIdsByBaseKey - Per-call memoization map for reusing order-sensitive IDs within one `engine.use()` execution.
1284
+ * @returns An `AtomicStyleResolution` with the assigned `id` and optionally the newly created `atomicStyle` (absent when the ID was already registered).
1285
+ *
1286
+ * @remarks First checks for reusable order-sensitive IDs within the current `engine.use()` call, then falls back to `getAtomicStyleId` for general ID assignment. When a new atomic style is created, it is registered in all store indices.
1287
+ *
1288
+ * @example
1289
+ * ```ts
1290
+ * const { id, atomicStyle } = resolveAtomicStyle({
1291
+ * content, prefix: 'pk-', store, resolvedIdsByBaseKey,
1292
+ * })
1293
+ * ```
1294
+ */
1295
+ function resolveAtomicStyle({ content, prefix, store, resolvedIdsByBaseKey }) {
1296
+ const reusableId = findReusableOrderSensitiveAtomicStyleId({
1297
+ content,
1298
+ store,
1299
+ resolvedIdsByBaseKey
1300
+ });
1301
+ if (reusableId != null) {
1302
+ log.debug(`Order-sensitive atomic style reused: ${reusableId}`);
1303
+ return { id: reusableId };
1304
+ }
1305
+ const id = getAtomicStyleId({
1306
+ content,
1307
+ prefix,
1308
+ stored: store.atomicStyleIds
1309
+ });
1310
+ if (store.atomicStyles.has(id)) return { id };
1311
+ const atomicStyle = {
1312
+ id,
1313
+ content
1314
+ };
1315
+ registerAtomicStyle(store, atomicStyle);
1316
+ return {
1317
+ id,
1318
+ atomicStyle
1319
+ };
1320
+ }
1321
+ /**
1322
+ * Deduplicates and optimizes a list of extracted style contents by merging duplicate selector-property pairs and detecting order-sensitive shorthand overlaps.
1323
+ * @internal
1324
+ * @param list - The raw extracted style contents to optimize.
1325
+ * @returns An optimized array of `StyleContent` entries with nullish-value removals applied and `orderSensitiveTo` metadata attached where needed.
1326
+ *
1327
+ * @remarks Later definitions of the same selector-property pair cancel earlier ones. When two properties in the same scope share overlapping CSS effects (e.g. `margin` and `margin-top`), the later one is marked as order-sensitive to prevent incorrect ID reuse.
1328
+ *
1329
+ * @example
1330
+ * ```ts
1331
+ * const optimized = optimizeAtomicStyleContents(extractedList)
1332
+ * ```
1333
+ */
1334
+ function optimizeAtomicStyleContents(list) {
1335
+ const map = /* @__PURE__ */ new Map();
1336
+ const scopedEntries = /* @__PURE__ */ new Map();
1337
+ list.forEach((content) => {
1338
+ const scopeKey = serialize(content.selector);
1339
+ const key = serialize([content.selector, content.property]);
1340
+ const scoped = scopedEntries.get(scopeKey) || /* @__PURE__ */ new Map();
1341
+ scopedEntries.set(scopeKey, scoped);
1342
+ map.delete(key);
1343
+ scoped.delete(key);
1344
+ if (content.value == null) return;
1345
+ const { selector, property, value } = content;
1346
+ const nextContent = {
1347
+ selector,
1348
+ property,
1349
+ value
1350
+ };
1351
+ const dependencyKeys = getOrderSensitiveDependencyKeys(scoped, property);
1352
+ if (dependencyKeys.length > 0) nextContent.orderSensitiveTo = dependencyKeys;
1353
+ map.set(key, nextContent);
1354
+ scoped.set(key, nextContent);
1355
+ });
1356
+ return [...map.values()];
1357
+ }
1358
+ /**
1359
+ * Computes the base cache key for an atomic style from its selector, property, and value.
1360
+ * @internal
1361
+ *
1362
+ * @param content - An object with `selector`, `property`, and `value` fields.
1363
+ * @returns A deterministic serialized string key.
1364
+ *
1365
+ * @remarks Used for deduplication: two atomic styles with the same base key are considered equivalent (unless order-sensitive). The key is derived by serializing the triple `[selector, property, value]`.
1366
+ *
1367
+ * @example
1368
+ * ```ts
1369
+ * const key = getAtomicStyleBaseKey({ selector: ['.pk-__ID__'], property: 'color', value: ['red'] })
1370
+ * ```
1371
+ */
1372
+ function getAtomicStyleBaseKey(content) {
1373
+ return serialize([
1374
+ content.selector,
1375
+ content.property,
1376
+ content.value
1377
+ ]);
1378
+ }
1379
+ function getAtomicStyleStoredKey({ content, baseKey, num }) {
1380
+ return isOrderSensitiveContent(content) ? serialize([
1381
+ baseKey,
1382
+ "order-sensitive",
1383
+ num
1384
+ ]) : baseKey;
1385
+ }
1386
+ function isOrderSensitiveContent(content) {
1387
+ return (content.orderSensitiveTo?.length ?? 0) > 0;
1388
+ }
1389
+ function registerAtomicStyle(store, atomicStyle) {
1390
+ const { id, content } = atomicStyle;
1391
+ const baseKey = getAtomicStyleBaseKey(content);
1392
+ store.atomicStyleOrder.set(id, store.atomicStyles.size);
1393
+ store.atomicStyles.set(id, atomicStyle);
1394
+ const ids = store.atomicStyleIdsByBaseKey.get(baseKey);
1395
+ if (ids == null) store.atomicStyleIdsByBaseKey.set(baseKey, [id]);
1396
+ else ids.push(id);
1397
+ }
1398
+ function getRequiredAtomicStyleOrder({ dependencyKeys, store, resolvedIdsByBaseKey }) {
1399
+ let requiredOrder = -1;
1400
+ for (const dependencyKey of dependencyKeys) {
1401
+ const dependencyId = resolvedIdsByBaseKey.get(dependencyKey) ?? store.atomicStyleIds.get(dependencyKey);
1402
+ if (dependencyId == null) continue;
1403
+ const dependencyOrder = store.atomicStyleOrder.get(dependencyId);
1404
+ if (dependencyOrder != null) requiredOrder = Math.max(requiredOrder, dependencyOrder);
1405
+ }
1406
+ return requiredOrder;
1407
+ }
1408
+ function findReusableOrderSensitiveAtomicStyleId({ content, store, resolvedIdsByBaseKey }) {
1409
+ if (isOrderSensitiveContent(content) === false) return void 0;
1410
+ const baseKey = getAtomicStyleBaseKey(content);
1411
+ const requiredOrder = getRequiredAtomicStyleOrder({
1412
+ dependencyKeys: content.orderSensitiveTo,
1413
+ store,
1414
+ resolvedIdsByBaseKey
1415
+ });
1416
+ return (store.atomicStyleIdsByBaseKey.get(baseKey) ?? []).find((candidateId) => {
1417
+ const candidateOrder = store.atomicStyleOrder.get(candidateId);
1418
+ return candidateOrder != null && candidateOrder > requiredOrder;
1419
+ });
1420
+ }
1421
+ function getOrderSensitiveDependencyKeys(scoped, property) {
1422
+ const dependencyKeys = [];
1423
+ for (const existing of scoped.values()) if (hasPropertyEffectOverlap(existing.property, property)) dependencyKeys.push(getAtomicStyleBaseKey(existing));
1424
+ return dependencyKeys;
1425
+ }
1426
+ //#endregion
1427
+ //#region src/internal/constants.ts
1428
+ /**
1429
+ * CSS `@layer` at-rule prefix used when constructing layer-scoped selectors
1430
+ * in generated stylesheet output.
1431
+ *
1432
+ * @internal
1433
+ */
1434
+ const LAYER_SELECTOR_PREFIX = "@layer ";
1435
+ /**
1436
+ * Global regex matching all occurrences of {@link ATOMIC_STYLE_ID_PLACEHOLDER}
1437
+ * for batch replacement in selector templates.
1438
+ *
1439
+ * @internal
1440
+ */
1441
+ const ATOMIC_STYLE_ID_PLACEHOLDER_RE_GLOBAL = /%/g;
123
1442
  //#endregion
124
1443
  //#region src/internal/extractor.ts
125
1444
  function replaceBySplitAndJoin(str, split, mapFn, join) {
@@ -131,9 +1450,42 @@ const RE_SPLIT = /\s*,\s*/g;
131
1450
  const DEFAULT_SELECTOR_PLACEHOLDER_RE_GLOBAL = /\$/g;
132
1451
  const ATTRIBUTE_SUFFIX_MATCH = "$=";
133
1452
  const ATTRIBUTE_SUFFIX_MATCH_RE_GLOBAL = /\$=/g;
1453
+ /**
1454
+ * Normalizes selector strings by replacing placeholders (`$` → `defaultSelector`, `%` → atomic style ID placeholder) and splitting comma-separated selectors.
1455
+ * @internal
1456
+ *
1457
+ * @param options - Object containing the raw `selectors` array and the `defaultSelector` template.
1458
+ * @param options.selectors - The raw selector strings to normalize.
1459
+ * @param options.defaultSelector - The selector template that replaces `$` placeholders.
1460
+ * @returns An array of normalized selector strings with all placeholders resolved.
1461
+ *
1462
+ * @remarks The `$` character in a selector is replaced with the engine's `defaultSelector`. The `%` character is the atomic style ID placeholder, preserved for later substitution. Attribute suffix matches (`$=`) are protected from the `$` replacement.
1463
+ *
1464
+ * @example
1465
+ * ```ts
1466
+ * normalizeSelectors({ selectors: ['$hover $'], defaultSelector: '.%' })
1467
+ * // ['.%:hover .%'] (conceptually)
1468
+ * ```
1469
+ */
134
1470
  function normalizeSelectors({ selectors, defaultSelector }) {
135
- return selectors.map((s) => replaceBySplitAndJoin(s.replace(RE_SPLIT, ","), ATOMIC_STYLE_ID_PLACEHOLDER_RE_GLOBAL, (a) => replaceBySplitAndJoin(a, ATTRIBUTE_SUFFIX_MATCH_RE_GLOBAL, (b) => replaceBySplitAndJoin(b, DEFAULT_SELECTOR_PLACEHOLDER_RE_GLOBAL, null, defaultSelector), ATTRIBUTE_SUFFIX_MATCH), ATOMIC_STYLE_ID_PLACEHOLDER));
1471
+ return selectors.map((s) => replaceBySplitAndJoin(s.replace(RE_SPLIT, ","), ATOMIC_STYLE_ID_PLACEHOLDER_RE_GLOBAL, (a) => replaceBySplitAndJoin(a, ATTRIBUTE_SUFFIX_MATCH_RE_GLOBAL, (b) => replaceBySplitAndJoin(b, DEFAULT_SELECTOR_PLACEHOLDER_RE_GLOBAL, null, defaultSelector), ATTRIBUTE_SUFFIX_MATCH), "%"));
136
1472
  }
1473
+ /**
1474
+ * Normalizes a raw `InternalPropertyValue` into the extraction output format: an array of trimmed, deduplicated CSS value strings with fallbacks ordered before the primary value, or `null`/`undefined` to signal removal.
1475
+ * @internal
1476
+ *
1477
+ * @param value - The raw property value to normalize.
1478
+ * @returns An array of CSS value strings (fallbacks first, primary last), or `null`/`undefined` for removal.
1479
+ *
1480
+ * @remarks For tuple values `[primary, fallbacks]`, duplicates among fallbacks are removed and the primary value is appended last so CSS cascade uses it as the effective value while older browsers fall back to earlier entries.
1481
+ *
1482
+ * @example
1483
+ * ```ts
1484
+ * normalizeValue('red') // ['red']
1485
+ * normalizeValue(['red', ['blue']]) // ['blue', 'red']
1486
+ * normalizeValue(null) // null
1487
+ * ```
1488
+ */
137
1489
  function normalizeValue(value) {
138
1490
  if (value == null) return value;
139
1491
  if (Array.isArray(value)) {
@@ -153,13 +1505,40 @@ function normalizeValue(value) {
153
1505
  }
154
1506
  return [value.trim()];
155
1507
  }
1508
+ /**
1509
+ * Recursively walks a style definition tree, extracting each CSS property-value pair into a flat list of `ExtractedStyleContent` entries with their full selector chain.
1510
+ * @internal
1511
+ *
1512
+ * @param options - Extraction context: the `styleDefinition` to walk, current nesting `levels`, accumulated `result`, `defaultSelector`, and plugin transform hooks for selectors, style items, and style definitions.
1513
+ * @param options.styleDefinition - The style definition subtree currently being traversed.
1514
+ * @param options.levels - The accumulated nested selector levels leading to this subtree.
1515
+ * @param options.result - The mutable extraction result array being appended to.
1516
+ * @param options.defaultSelector - The selector substituted when extracted selectors omit the atomic-style placeholder.
1517
+ * @param options.transformSelectors - Hook that rewrites selector levels before normalization.
1518
+ * @param options.transformStyleItems - Hook that expands array style items before recursive extraction.
1519
+ * @param options.transformStyleDefinitions - Hook that rewrites style definition objects before traversal.
1520
+ * @returns The accumulated array of `ExtractedStyleContent` entries.
1521
+ *
1522
+ * @remarks Property values are identified using `isPropertyValue`. Array values are treated as style item lists (resolved via `transformStyleItems`). Object values are treated as nested style definitions and recursed into. The transform hooks allow plugins (shortcuts, selectors) to intercept and expand values during extraction.
1523
+ *
1524
+ * @example
1525
+ * ```ts
1526
+ * const contents = await extract({
1527
+ * styleDefinition: { color: 'red', '$hover': { color: 'blue' } },
1528
+ * defaultSelector: '.%',
1529
+ * transformSelectors: async s => s,
1530
+ * transformStyleItems: async i => i,
1531
+ * transformStyleDefinitions: async d => d,
1532
+ * })
1533
+ * ```
1534
+ */
156
1535
  async function extract({ styleDefinition, levels = [], result = [], defaultSelector, transformSelectors, transformStyleItems, transformStyleDefinitions }) {
157
1536
  for (const definition of await transformStyleDefinitions([styleDefinition])) for (const [k, v] of Object.entries(definition)) if (isPropertyValue(v)) {
158
1537
  const selector = normalizeSelectors({
159
1538
  selectors: await transformSelectors(levels),
160
1539
  defaultSelector
161
1540
  });
162
- if (selector.length === 0 || selector.every((s) => s.includes(ATOMIC_STYLE_ID_PLACEHOLDER) === false)) selector.push(defaultSelector);
1541
+ if (selector.length === 0 || selector.every((s) => s.includes("%") === false)) selector.push(defaultSelector);
163
1542
  result.push({
164
1543
  selector,
165
1544
  property: toKebab(k),
@@ -188,53 +1567,139 @@ async function extract({ styleDefinition, levels = [], result = [], defaultSelec
188
1567
  });
189
1568
  return result;
190
1569
  }
1570
+ /**
1571
+ * Creates a bound extraction function that closes over the default selector and plugin transform hooks.
1572
+ * @internal
1573
+ *
1574
+ * @param options - The extraction options: `defaultSelector`, `transformSelectors`, `transformStyleItems`, and `transformStyleDefinitions`.
1575
+ * @param options.defaultSelector - The selector used when no explicit atomic placeholder selector remains.
1576
+ * @param options.transformSelectors - Hook that rewrites selector arrays before normalization.
1577
+ * @param options.transformStyleItems - Hook that rewrites or expands style item arrays.
1578
+ * @param options.transformStyleDefinitions - Hook that rewrites style definition objects before extraction.
1579
+ * @returns An `ExtractFn` that accepts a style definition and returns extracted contents.
1580
+ *
1581
+ * @remarks Called once during engine construction. The returned function is stored as `engine.extract` and used for all subsequent `engine.use()` calls.
1582
+ *
1583
+ * @example
1584
+ * ```ts
1585
+ * const extractFn = createExtractFn({
1586
+ * defaultSelector: '.%',
1587
+ * transformSelectors: async s => s,
1588
+ * transformStyleItems: async i => i,
1589
+ * transformStyleDefinitions: async d => d,
1590
+ * })
1591
+ * ```
1592
+ */
191
1593
  function createExtractFn(options) {
192
1594
  return (styleDefinition) => extract({
193
1595
  styleDefinition,
194
1596
  ...options
195
1597
  });
196
1598
  }
197
-
198
1599
  //#endregion
199
1600
  //#region src/internal/plugin.ts
1601
+ function getPluginHook(plugin, hook) {
1602
+ const hookFn = plugin[hook];
1603
+ return typeof hookFn === "function" ? hookFn : null;
1604
+ }
1605
+ function applyHookPayload(current, next) {
1606
+ return next ?? current;
1607
+ }
1608
+ function logHookStart(kind, hook) {
1609
+ log.debug(`Executing ${kind.toLowerCase()} hook: ${hook}`);
1610
+ }
1611
+ function logHookEnd(kind, hook) {
1612
+ log.debug(`${kind} hook "${hook}" completed`);
1613
+ }
1614
+ function logPluginHookStart(plugin, hook) {
1615
+ log.debug(` - Plugin "${plugin.name}" executing ${hook}`);
1616
+ }
1617
+ function logPluginHookEnd(plugin, hook) {
1618
+ log.debug(` - Plugin "${plugin.name}" completed ${hook}`);
1619
+ }
1620
+ function logPluginHookError(plugin, hook, error) {
1621
+ log.error(`Plugin "${plugin.name}" failed to execute hook "${hook}": ${error instanceof Error ? error.message : error}`, error);
1622
+ }
1623
+ /**
1624
+ * Executes an async hook across all plugins in order, piping the payload through each plugin's handler.
1625
+ * @internal
1626
+ *
1627
+ * @typeParam P - The payload/return type flowing through the hook pipeline.
1628
+ * @param plugins - The ordered list of engine plugins to execute.
1629
+ * @param hook - The name of the async hook to invoke.
1630
+ * @param payload - The initial payload to pass into the first plugin.
1631
+ * @returns The final payload after all plugins have processed it.
1632
+ *
1633
+ * @remarks Each plugin's hook receives the current payload and may return a replacement. If a plugin's hook throws, the error is logged and the current payload is preserved for subsequent plugins.
1634
+ *
1635
+ * @example
1636
+ * ```ts
1637
+ * const config = await execAsyncHook(plugins, 'configureRawConfig', rawConfig)
1638
+ * ```
1639
+ */
200
1640
  async function execAsyncHook(plugins, hook, payload) {
201
- log.debug(`Executing async hook: ${hook}`);
1641
+ logHookStart("Async", hook);
202
1642
  let current = payload;
203
1643
  for (const plugin of plugins) {
204
- const pluginRecord = plugin;
205
- if (pluginRecord[hook] == null) continue;
1644
+ const hookFn = getPluginHook(plugin, hook);
1645
+ if (hookFn == null) continue;
206
1646
  try {
207
- log.debug(` - Plugin "${plugin.name}" executing ${hook}`);
208
- const hookFn = pluginRecord[hook];
209
- const newPayload = await hookFn(current);
210
- if (newPayload != null) current = newPayload;
211
- log.debug(` - Plugin "${plugin.name}" completed ${hook}`);
1647
+ logPluginHookStart(plugin, hook);
1648
+ current = applyHookPayload(current, await hookFn(current));
1649
+ logPluginHookEnd(plugin, hook);
212
1650
  } catch (error) {
213
- log.error(`Plugin "${plugin.name}" failed to execute hook "${hook}": ${error instanceof Error ? error.message : error}`, error);
1651
+ logPluginHookError(plugin, hook, error);
214
1652
  }
215
1653
  }
216
- log.debug(`Async hook "${hook}" completed`);
1654
+ logHookEnd("Async", hook);
217
1655
  return current;
218
1656
  }
1657
+ /**
1658
+ * Executes a synchronous hook across all plugins in order, piping the payload through each plugin's handler.
1659
+ * @internal
1660
+ *
1661
+ * @typeParam P - The payload/return type flowing through the hook pipeline.
1662
+ * @param plugins - The ordered list of engine plugins to execute.
1663
+ * @param hook - The name of the sync hook to invoke.
1664
+ * @param payload - The initial payload to pass into the first plugin.
1665
+ * @returns The final payload after all plugins have processed it.
1666
+ *
1667
+ * @remarks Functions identically to `execAsyncHook` but without awaiting. Used for notification-style hooks like `preflightUpdated` or `atomicStyleAdded`.
1668
+ *
1669
+ * @example
1670
+ * ```ts
1671
+ * execSyncHook(plugins, 'atomicStyleAdded', atomicStyle)
1672
+ * ```
1673
+ */
219
1674
  function execSyncHook(plugins, hook, payload) {
220
- log.debug(`Executing sync hook: ${hook}`);
1675
+ logHookStart("Sync", hook);
221
1676
  let current = payload;
222
1677
  for (const plugin of plugins) {
223
- const pluginRecord = plugin;
224
- if (pluginRecord[hook] == null) continue;
1678
+ const hookFn = getPluginHook(plugin, hook);
1679
+ if (hookFn == null) continue;
225
1680
  try {
226
- log.debug(` - Plugin "${plugin.name}" executing ${hook}`);
227
- const hookFn = pluginRecord[hook];
228
- const newPayload = hookFn(current);
229
- if (newPayload != null) current = newPayload;
230
- log.debug(` - Plugin "${plugin.name}" completed ${hook}`);
1681
+ logPluginHookStart(plugin, hook);
1682
+ current = applyHookPayload(current, hookFn(current));
1683
+ logPluginHookEnd(plugin, hook);
231
1684
  } catch (error) {
232
- log.error(`Plugin "${plugin.name}" failed to execute hook "${hook}": ${error instanceof Error ? error.message : error}`, error);
1685
+ logPluginHookError(plugin, hook, error);
233
1686
  }
234
1687
  }
235
- log.debug(`Sync hook "${hook}" completed`);
1688
+ logHookEnd("Sync", hook);
236
1689
  return current;
237
1690
  }
1691
+ /**
1692
+ * Pre-built hook dispatcher object mapping each hook name to a function that delegates to `execAsyncHook` or `execSyncHook`.
1693
+ * @internal
1694
+ *
1695
+ * @remarks Provides a convenient, type-safe interface for calling any engine hook by name without manually selecting between `execAsyncHook` and `execSyncHook`. Used throughout the `Engine` class.
1696
+ *
1697
+ * @example
1698
+ * ```ts
1699
+ * const config = await hooks.configureRawConfig(plugins, rawConfig)
1700
+ * hooks.preflightUpdated(plugins)
1701
+ * ```
1702
+ */
238
1703
  const hooks = {
239
1704
  configureRawConfig: (plugins, config) => execAsyncHook(plugins, "configureRawConfig", config),
240
1705
  rawConfigConfigured: (plugins, config) => execSyncHook(plugins, "rawConfigConfigured", config),
@@ -252,15 +1717,45 @@ const orderMap = new Map([
252
1717
  ["pre", 0],
253
1718
  ["post", 2]
254
1719
  ]);
1720
+ /**
1721
+ * Sorts an array of plugins by their `order` property: `'pre'` first, default in the middle, `'post'` last.
1722
+ * @internal
1723
+ *
1724
+ * @param plugins - The unordered array of engine plugins.
1725
+ * @returns A new array sorted by execution order.
1726
+ *
1727
+ * @remarks The original array is not mutated. Plugins with the same order retain their relative insertion order (stable sort).
1728
+ *
1729
+ * @example
1730
+ * ```ts
1731
+ * const ordered = resolvePlugins([postPlugin, prePlugin, normalPlugin])
1732
+ * // [prePlugin, normalPlugin, postPlugin]
1733
+ * ```
1734
+ */
255
1735
  function resolvePlugins(plugins) {
256
1736
  return [...plugins].sort((a, b) => orderMap.get(a.order) - orderMap.get(b.order));
257
1737
  }
258
1738
  /* c8 ignore start */
1739
+ /**
1740
+ * Identity helper that returns the plugin object as-is, providing TypeScript type inference for plugin definitions.
1741
+ *
1742
+ * @param plugin - The engine plugin definition.
1743
+ * @returns The same plugin object, unchanged.
1744
+ *
1745
+ * @remarks This is a compile-time-only helper; it has no runtime effect. Using it ensures type checking and IDE autocompletion for hook names and payloads.
1746
+ *
1747
+ * @example
1748
+ * ```ts
1749
+ * export default defineEnginePlugin({
1750
+ * name: 'my-plugin',
1751
+ * configureRawConfig: (config) => ({ ...config, important: true }),
1752
+ * })
1753
+ * ```
1754
+ */
259
1755
  function defineEnginePlugin(plugin) {
260
1756
  return plugin;
261
1757
  }
262
1758
  /* c8 ignore end */
263
-
264
1759
  //#endregion
265
1760
  //#region src/internal/plugins/important.ts
266
1761
  function appendImportant(v) {
@@ -271,6 +1766,18 @@ function modifyPropertyValue(value) {
271
1766
  if (Array.isArray(value)) return [appendImportant(value[0]), value[1].map((i) => appendImportant(i))];
272
1767
  return appendImportant(value);
273
1768
  }
1769
+ /**
1770
+ * Built-in engine plugin that appends `!important` to generated CSS declarations.
1771
+ *
1772
+ * @returns An `EnginePlugin` that intercepts `transformStyleDefinitions` to conditionally append `!important` to every property value.
1773
+ *
1774
+ * @remarks When `EngineConfig.important.default` is `true`, all property values receive `!important` unless the style definition explicitly sets `__important: false`. Individual style definitions can also opt-in with `__important: true` regardless of the default.
1775
+ *
1776
+ * @example
1777
+ * ```ts
1778
+ * createEngine({ plugins: [important()] })
1779
+ * ```
1780
+ */
274
1781
  function important() {
275
1782
  let defaultValue;
276
1783
  return defineEnginePlugin({
@@ -279,14 +1786,15 @@ function important() {
279
1786
  defaultValue = config.important?.default ?? false;
280
1787
  },
281
1788
  configureEngine(engine) {
282
- engine.appendAutocompleteExtraProperties("__important");
283
- engine.appendAutocompletePropertyValues("__important", "boolean");
1789
+ engine.appendAutocomplete({
1790
+ extraProperties: "__important",
1791
+ properties: { __important: "boolean" }
1792
+ });
284
1793
  },
285
1794
  transformStyleDefinitions(styleDefinitions) {
286
1795
  return styleDefinitions.map((styleDefinition) => {
287
1796
  const { __important, ...rest } = styleDefinition;
288
- const value = __important;
289
- if ((value == null ? defaultValue : value) === false) return rest;
1797
+ if ((__important ?? defaultValue) === false) return rest;
290
1798
  return Object.fromEntries(Object.entries(rest).map(([k, v]) => {
291
1799
  if (isPropertyValue(v)) return [k, modifyPropertyValue(v)];
292
1800
  return [k, v];
@@ -295,9 +1803,20 @@ function important() {
295
1803
  }
296
1804
  });
297
1805
  }
298
-
299
1806
  //#endregion
300
1807
  //#region src/internal/plugins/keyframes.ts
1808
+ /**
1809
+ * Built-in engine plugin that provides CSS `@keyframes` registration, autocomplete integration, and smart pruning.
1810
+ *
1811
+ * @returns An `EnginePlugin` that registers keyframes definitions, wires up `animationName`/`animation` autocomplete entries, and emits a preflight containing only the `@keyframes` rules actually referenced by atomic styles.
1812
+ *
1813
+ * @remarks Reads `EngineConfig.keyframes` during `rawConfigConfigured` and attaches the `engine.keyframes` management interface during `configureEngine`. Unused keyframes are pruned from the output unless `pruneUnused: false` is set on the individual definition or globally.
1814
+ *
1815
+ * @example
1816
+ * ```ts
1817
+ * createEngine({ plugins: [keyframes()] })
1818
+ * ```
1819
+ */
301
1820
  function keyframes() {
302
1821
  let resolveKeyframesConfig;
303
1822
  let configList;
@@ -315,9 +1834,10 @@ function keyframes() {
315
1834
  const resolved = resolveKeyframesConfig(config);
316
1835
  const { name, frames, autocomplete: autocompleteAnimation } = resolved;
317
1836
  if (frames != null) engine.keyframes.store.set(name, resolved);
318
- engine.appendAutocompleteCssPropertyValues("animationName", name);
319
- engine.appendAutocompleteCssPropertyValues("animation", `${name} `);
320
- if (autocompleteAnimation != null) engine.appendAutocompleteCssPropertyValues("animation", ...autocompleteAnimation);
1837
+ engine.appendAutocomplete({ cssProperties: {
1838
+ animationName: name,
1839
+ animation: autocompleteAnimation.length > 0 ? [`${name} `, ...autocompleteAnimation] : `${name} `
1840
+ } });
321
1841
  });
322
1842
  engine.notifyPreflightUpdated();
323
1843
  }
@@ -372,17 +1892,35 @@ function createResolveConfigFn({ pruneUnused: defaultPruneUnused = true } = {})
372
1892
  };
373
1893
  };
374
1894
  }
375
-
376
1895
  //#endregion
377
1896
  //#region src/internal/resolver.ts
378
1897
  function stripGlobalFlag(re) {
379
1898
  if (!re.global) return re;
380
1899
  return new RegExp(re.source, re.flags.replace("g", ""));
381
1900
  }
1901
+ /**
1902
+ * Base resolver class that manages static and dynamic rules and caches resolution results.
1903
+ * @internal
1904
+ *
1905
+ * @typeParam T - The type of resolved values.
1906
+ *
1907
+ * @remarks Subclasses override resolution behavior (e.g. `RecursiveResolver` adds recursive expansion). The base class handles rule storage, cache lookup, and the static-then-dynamic matching order. Results are cached in `_resolvedResultsMap` for subsequent lookups.
1908
+ *
1909
+ * @example
1910
+ * ```ts
1911
+ * class MyResolver extends AbstractResolver<string> { }
1912
+ * const r = new MyResolver()
1913
+ * r.addStaticRule({ key: 'x', string: 'x', resolved: 'X' })
1914
+ * ```
1915
+ */
382
1916
  var AbstractResolver = class {
1917
+ /** Cache of previously resolved input-string → result pairs. */
383
1918
  _resolvedResultsMap = /* @__PURE__ */ new Map();
1919
+ /** Registry of static rules keyed by their unique key. */
384
1920
  staticRulesMap = /* @__PURE__ */ new Map();
1921
+ /** Registry of dynamic rules keyed by their unique key. */
385
1922
  dynamicRulesMap = /* @__PURE__ */ new Map();
1923
+ /** Callback invoked after a successful resolution, receiving the input string, rule type, and result. */
386
1924
  onResolved = () => {};
387
1925
  get staticRules() {
388
1926
  return [...this.staticRulesMap.values()];
@@ -390,11 +1928,37 @@ var AbstractResolver = class {
390
1928
  get dynamicRules() {
391
1929
  return [...this.dynamicRulesMap.values()];
392
1930
  }
1931
+ /**
1932
+ * Registers a static rule in the resolver.
1933
+ *
1934
+ * @param rule - The static rule to register.
1935
+ * @returns `this` for chaining.
1936
+ *
1937
+ * @remarks Overwrites any existing static rule with the same key.
1938
+ *
1939
+ * @example
1940
+ * ```ts
1941
+ * resolver.addStaticRule({ key: 'dark', string: 'dark', resolved: ['.dark &'] })
1942
+ * ```
1943
+ */
393
1944
  addStaticRule(rule) {
394
1945
  log.debug(`Adding static rule: ${rule.key}`);
395
1946
  this.staticRulesMap.set(rule.key, rule);
396
1947
  return this;
397
1948
  }
1949
+ /**
1950
+ * Removes a static rule and its cached resolution result.
1951
+ *
1952
+ * @param key - The key of the static rule to remove.
1953
+ * @returns `this` for chaining.
1954
+ *
1955
+ * @remarks Logs a warning if the key does not exist. Also evicts the cached result for the rule's input string.
1956
+ *
1957
+ * @example
1958
+ * ```ts
1959
+ * resolver.removeStaticRule('dark')
1960
+ * ```
1961
+ */
398
1962
  removeStaticRule(key) {
399
1963
  const rule = this.staticRulesMap.get(key);
400
1964
  if (rule == null) {
@@ -406,11 +1970,37 @@ var AbstractResolver = class {
406
1970
  this._resolvedResultsMap.delete(rule.string);
407
1971
  return this;
408
1972
  }
1973
+ /**
1974
+ * Registers a dynamic rule in the resolver.
1975
+ *
1976
+ * @param rule - The dynamic rule to register.
1977
+ * @returns `this` for chaining.
1978
+ *
1979
+ * @remarks Overwrites any existing dynamic rule with the same key.
1980
+ *
1981
+ * @example
1982
+ * ```ts
1983
+ * resolver.addDynamicRule({ key: 'bp', stringPattern: /^bp-(\d+)$/, createResolved: m => [`@media (min-width: ${m[1]}px)`] })
1984
+ * ```
1985
+ */
409
1986
  addDynamicRule(rule) {
410
1987
  log.debug(`Adding dynamic rule: ${rule.key}`);
411
1988
  this.dynamicRulesMap.set(rule.key, rule);
412
1989
  return this;
413
1990
  }
1991
+ /**
1992
+ * Removes a dynamic rule and evicts all cached results that its pattern matched.
1993
+ *
1994
+ * @param key - The key of the dynamic rule to remove.
1995
+ * @returns `this` for chaining.
1996
+ *
1997
+ * @remarks Iterates through all cached results and deletes any whose input string matches the removed rule's pattern. Logs a warning if the key does not exist.
1998
+ *
1999
+ * @example
2000
+ * ```ts
2001
+ * resolver.removeDynamicRule('bp')
2002
+ * ```
2003
+ */
414
2004
  removeDynamicRule(key) {
415
2005
  const rule = this.dynamicRulesMap.get(key);
416
2006
  if (rule == null) {
@@ -427,6 +2017,19 @@ var AbstractResolver = class {
427
2017
  log.debug(` - Cleared ${matchedResolvedStringList.length} cached results`);
428
2018
  return this;
429
2019
  }
2020
+ /**
2021
+ * Attempts to resolve an input string by checking cached results, then static rules, then dynamic rules in order.
2022
+ *
2023
+ * @param string - The input string to resolve.
2024
+ * @returns The resolved result wrapper, or `null`/`undefined` if no rule matches.
2025
+ *
2026
+ * @remarks Results are cached for subsequent calls. Invokes `onResolved` after a successful match. Dynamic rule matching is async because `createResolved` may return a `Promise`.
2027
+ *
2028
+ * @example
2029
+ * ```ts
2030
+ * const result = await resolver._resolve('hover')
2031
+ * ```
2032
+ */
430
2033
  async _resolve(string) {
431
2034
  const existedResult = this._resolvedResultsMap.get(string);
432
2035
  if (existedResult != null) {
@@ -460,6 +2063,19 @@ var AbstractResolver = class {
460
2063
  }
461
2064
  log.debug(`Resolution failed for: ${string}`);
462
2065
  }
2066
+ /**
2067
+ * Updates or creates the cached resolved result for a given input string.
2068
+ *
2069
+ * @param string - The input string whose cached result should be updated.
2070
+ * @param resolved - The new resolved value to store.
2071
+ *
2072
+ * @remarks If a cached `ResolvedResult` already exists for `string`, its `value` property is mutated in place. Otherwise a new entry is created. This allows `RecursiveResolver` to retroactively update partially resolved values without allocating a new wrapper.
2073
+ *
2074
+ * @example
2075
+ * ```ts
2076
+ * resolver._setResolvedResult('hover', ['&:hover'])
2077
+ * ```
2078
+ */
463
2079
  _setResolvedResult(string, resolved) {
464
2080
  const resolvedResult = this._resolvedResultsMap.get(string);
465
2081
  if (resolvedResult) {
@@ -469,7 +2085,36 @@ var AbstractResolver = class {
469
2085
  this._resolvedResultsMap.set(string, { value: resolved });
470
2086
  }
471
2087
  };
2088
+ /**
2089
+ * Resolver subclass that recursively expands resolved values until all string references are fully resolved.
2090
+ * @internal
2091
+ *
2092
+ * @typeParam T - The element type of the final resolved array.
2093
+ *
2094
+ * @remarks Each resolution step may return a mix of final values and string references. The `resolve` method recurses into string values, flattening nested references while detecting circular dependencies via a visited set.
2095
+ *
2096
+ * @example
2097
+ * ```ts
2098
+ * class SelectorResolver extends RecursiveResolver<string> { }
2099
+ * const result = await resolver.resolve('hover-focus')
2100
+ * // ['&:hover', '&:focus'] after recursive expansion
2101
+ * ```
2102
+ */
472
2103
  var RecursiveResolver = class extends AbstractResolver {
2104
+ /**
2105
+ * Recursively resolves an input string into a flat array of final values.
2106
+ *
2107
+ * @param string - The input string to resolve.
2108
+ * @param _visited - Accumulator set for cycle detection; callers should omit this.
2109
+ * @returns A flat array of resolved values. If no rule matches, returns `[string]` cast to `T`.
2110
+ *
2111
+ * @remarks Detects circular references and short-circuits by returning the unresolved string. After full expansion, the cache is updated with the final flat result via `_setResolvedResult`.
2112
+ *
2113
+ * @example
2114
+ * ```ts
2115
+ * const selectors = await resolver.resolve('hover')
2116
+ * ```
2117
+ */
473
2118
  async resolve(string, _visited) {
474
2119
  const visited = _visited ?? /* @__PURE__ */ new Set();
475
2120
  if (visited.has(string)) {
@@ -488,6 +2133,26 @@ var RecursiveResolver = class extends AbstractResolver {
488
2133
  return result;
489
2134
  }
490
2135
  };
2136
+ /**
2137
+ * Normalizes a user-supplied rule shorthand into a `ResolvedRuleConfig`, a plain redirect string, or `undefined`.
2138
+ * @internal
2139
+ *
2140
+ * @typeParam T - The element type of the rule's resolved value array.
2141
+ * @param config - The raw rule configuration: a string redirect, a tuple (`[string, value]` or `[RegExp, fn, autocomplete?]`), or an object with `keyName` and `value` properties.
2142
+ * @param keyName - The property name on an object-form config that holds the match key or pattern.
2143
+ * @returns A `ResolvedRuleConfig<T>` for valid static/dynamic configs, the original string for redirect configs, or `undefined` if the config shape is unrecognized.
2144
+ *
2145
+ * @remarks Handles three config shapes:
2146
+ * - **String**: returned as-is for the caller to treat as a redirect to another rule.
2147
+ * - **Tuple**: `[string, T | T[]]` for static rules, `[RegExp, fn, autocomplete?]` for dynamic rules.
2148
+ * - **Object**: `{ [keyName]: string | RegExp, value: T | fn, autocomplete?: string[] }`.
2149
+ *
2150
+ * @example
2151
+ * ```ts
2152
+ * resolveRuleConfig(['hover', '&:hover'], 'selector')
2153
+ * // { type: 'static', rule: { key: 'hover', ... }, autocomplete: ['hover'] }
2154
+ * ```
2155
+ */
491
2156
  function resolveRuleConfig(config, keyName) {
492
2157
  if (typeof config === "string") return config;
493
2158
  if (Array.isArray(config)) {
@@ -538,9 +2203,20 @@ function resolveRuleConfig(config, keyName) {
538
2203
  };
539
2204
  }
540
2205
  }
541
-
542
2206
  //#endregion
543
2207
  //#region src/internal/plugins/selectors.ts
2208
+ /**
2209
+ * Built-in engine plugin that provides the selector resolution system.
2210
+ *
2211
+ * @returns An `EnginePlugin` that registers the `selectors` resolver on the engine and hooks into `transformSelectors` to expand selector names into resolved CSS selectors.
2212
+ *
2213
+ * @remarks Reads `EngineConfig.selectors` during `rawConfigConfigured`, attaches a `RecursiveResolver` to `engine.selectors` during `configureEngine`, and resolves all selector strings in the `transformSelectors` hook.
2214
+ *
2215
+ * @example
2216
+ * ```ts
2217
+ * createEngine({ plugins: [selectors()] })
2218
+ * ```
2219
+ */
544
2220
  function selectors() {
545
2221
  let engine;
546
2222
  let configList;
@@ -558,18 +2234,21 @@ function selectors() {
558
2234
  const resolved = resolveSelectorConfig(config);
559
2235
  if (resolved == null) return;
560
2236
  if (typeof resolved === "string") {
561
- engine.appendAutocompleteSelectors(resolved);
2237
+ engine.appendAutocomplete({ selectors: resolved });
562
2238
  return;
563
2239
  }
564
- if (resolved.type === "static") engine.selectors.resolver.addStaticRule(resolved.rule);
565
- else if (resolved.type === "dynamic") engine.selectors.resolver.addDynamicRule(resolved.rule);
566
- engine.appendAutocompleteSelectors(...resolved.autocomplete);
2240
+ const addRule = {
2241
+ static: () => engine.selectors.resolver.addStaticRule(resolved.rule),
2242
+ dynamic: () => engine.selectors.resolver.addDynamicRule(resolved.rule)
2243
+ }[resolved.type];
2244
+ addRule?.();
2245
+ engine.appendAutocomplete({ selectors: resolved.autocomplete });
567
2246
  });
568
2247
  }
569
2248
  };
570
2249
  engine.selectors.add(...configList);
571
2250
  engine.selectors.resolver.onResolved = (string, type) => {
572
- if (type === "dynamic") engine.appendAutocompleteSelectors(string);
2251
+ if (type === "dynamic") engine.appendAutocomplete({ selectors: string });
573
2252
  };
574
2253
  },
575
2254
  async transformSelectors(selectors) {
@@ -580,12 +2259,36 @@ function selectors() {
580
2259
  });
581
2260
  }
582
2261
  var SelectorResolver = class extends RecursiveResolver {};
2262
+ /**
2263
+ * Normalizes a `Selector` configuration into a `ResolvedRuleConfig`, a redirect string, or `undefined`.
2264
+ *
2265
+ * @param config - The selector rule configuration to resolve.
2266
+ * @returns A resolved static/dynamic rule config, a redirect string, or `undefined` if the shape is unrecognized.
2267
+ *
2268
+ * @remarks Delegates to the generic `resolveRuleConfig` with `'selector'` as the key name.
2269
+ *
2270
+ * @example
2271
+ * ```ts
2272
+ * const resolved = resolveSelectorConfig(['hover', '&:hover'])
2273
+ * ```
2274
+ */
583
2275
  function resolveSelectorConfig(config) {
584
2276
  return resolveRuleConfig(config, "selector");
585
2277
  }
586
-
587
2278
  //#endregion
588
2279
  //#region src/internal/plugins/shortcuts.ts
2280
+ /**
2281
+ * Built-in engine plugin that provides the shortcut resolution system.
2282
+ *
2283
+ * @returns An `EnginePlugin` that registers the `shortcuts` resolver on the engine and hooks into `transformStyleItems` and `transformStyleDefinitions` to expand shortcut names into style items.
2284
+ *
2285
+ * @remarks Reads `EngineConfig.shortcuts` during `rawConfigConfigured`, attaches a `RecursiveResolver` to `engine.shortcuts` during `configureEngine`, and expands shortcut references in both `transformStyleItems` (string style items) and `transformStyleDefinitions` (the `__shortcut` pseudo-property).
2286
+ *
2287
+ * @example
2288
+ * ```ts
2289
+ * createEngine({ plugins: [shortcuts()] })
2290
+ * ```
2291
+ */
589
2292
  function shortcuts() {
590
2293
  let engine;
591
2294
  let configList;
@@ -603,22 +2306,27 @@ function shortcuts() {
603
2306
  const resolved = resolveShortcutConfig(config);
604
2307
  if (resolved == null) return;
605
2308
  if (typeof resolved === "string") {
606
- engine.appendAutocompleteStyleItemStrings(resolved);
2309
+ engine.appendAutocomplete({ shortcuts: resolved });
607
2310
  return;
608
2311
  }
609
- if (resolved.type === "static") engine.shortcuts.resolver.addStaticRule(resolved.rule);
610
- else if (resolved.type === "dynamic") engine.shortcuts.resolver.addDynamicRule(resolved.rule);
611
- engine.appendAutocompleteStyleItemStrings(...resolved.autocomplete);
2312
+ const addRule = {
2313
+ static: () => engine.shortcuts.resolver.addStaticRule(resolved.rule),
2314
+ dynamic: () => engine.shortcuts.resolver.addDynamicRule(resolved.rule)
2315
+ }[resolved.type];
2316
+ addRule?.();
2317
+ engine.appendAutocomplete({ shortcuts: resolved.autocomplete });
612
2318
  });
613
2319
  }
614
2320
  };
615
2321
  engine.shortcuts.add(...configList);
616
2322
  engine.shortcuts.resolver.onResolved = (string, type) => {
617
- if (type === "dynamic") engine.appendAutocompleteStyleItemStrings(string);
2323
+ if (type === "dynamic") engine.appendAutocomplete({ shortcuts: string });
618
2324
  };
619
- engine.appendAutocompleteExtraProperties("__shortcut");
620
- const unionType = ["(string & {})", "Autocomplete['StyleItemString']"].join(" | ");
621
- engine.appendAutocompletePropertyValues("__shortcut", unionType, `(${unionType})[]`);
2325
+ const unionType = ["(string & {})", "Autocomplete['Shortcut']"].join(" | ");
2326
+ engine.appendAutocomplete({
2327
+ extraProperties: "__shortcut",
2328
+ properties: { __shortcut: [unionType, `(${unionType})[]`] }
2329
+ });
622
2330
  },
623
2331
  async transformStyleItems(styleItems) {
624
2332
  const result = [];
@@ -650,9 +2358,99 @@ var ShortcutResolver = class extends RecursiveResolver {};
650
2358
  function resolveShortcutConfig(config) {
651
2359
  return resolveRuleConfig(config, "shortcut");
652
2360
  }
653
-
2361
+ //#endregion
2362
+ //#region src/internal/generated-property-semantics.ts
2363
+ const VARIABLE_SEMANTIC_FAMILY_PROPERTIES = {
2364
+ "angle": [],
2365
+ "color": [
2366
+ "accent-color",
2367
+ "background-color",
2368
+ "border-block-color",
2369
+ "border-block-end-color",
2370
+ "border-block-start-color",
2371
+ "border-bottom-color",
2372
+ "border-inline-color",
2373
+ "border-inline-end-color",
2374
+ "border-inline-start-color",
2375
+ "border-left-color",
2376
+ "border-right-color",
2377
+ "border-top-color",
2378
+ "caret-color",
2379
+ "color",
2380
+ "column-rule-color",
2381
+ "flood-color",
2382
+ "lighting-color",
2383
+ "outline-color",
2384
+ "stop-color",
2385
+ "stroke-color",
2386
+ "text-decoration-color",
2387
+ "text-emphasis-color"
2388
+ ],
2389
+ "easing": ["animation-timing-function", "transition-timing-function"],
2390
+ "font-family": ["font-family"],
2391
+ "image": [],
2392
+ "length": [
2393
+ "block-size",
2394
+ "border-bottom-width",
2395
+ "border-left-width",
2396
+ "border-right-width",
2397
+ "border-spacing",
2398
+ "border-top-width",
2399
+ "bottom",
2400
+ "font-size",
2401
+ "height",
2402
+ "inline-size",
2403
+ "inset-block-end",
2404
+ "inset-block-start",
2405
+ "inset-inline-end",
2406
+ "inset-inline-start",
2407
+ "left",
2408
+ "letter-spacing",
2409
+ "max-height",
2410
+ "max-width",
2411
+ "min-height",
2412
+ "min-width",
2413
+ "outline-offset",
2414
+ "right",
2415
+ "top",
2416
+ "width"
2417
+ ],
2418
+ "number": [
2419
+ "fill-opacity",
2420
+ "flex-grow",
2421
+ "flex-shrink",
2422
+ "flood-opacity",
2423
+ "opacity",
2424
+ "order",
2425
+ "orphans",
2426
+ "stop-opacity",
2427
+ "stroke-opacity",
2428
+ "widows"
2429
+ ],
2430
+ "percentage": [],
2431
+ "position": [],
2432
+ "time": [
2433
+ "animation-delay",
2434
+ "animation-duration",
2435
+ "transition-delay",
2436
+ "transition-duration"
2437
+ ],
2438
+ "url": []
2439
+ };
654
2440
  //#endregion
655
2441
  //#region src/internal/plugins/variables.ts
2442
+ /**
2443
+ * Built-in engine plugin that provides CSS custom properties (variables) with smart pruning and autocomplete integration.
2444
+ *
2445
+ * @returns An `EnginePlugin` that registers variable definitions, manages a preflight for emitting `:root` / scoped variables, and prunes unused variables from the output.
2446
+ *
2447
+ * @remarks Reads `EngineConfig.variables` during `rawConfigConfigured` and attaches the `engine.variables` management interface during `configureEngine`. A preflight is registered that collects variable references from atomic styles and other preflights, transitively expands dependencies, and emits only used (or safe-listed) variables.
2448
+ *
2449
+ * @example
2450
+ * ```ts
2451
+ * createEngine({ plugins: [variables()] })
2452
+ * ```
2453
+ */
656
2454
  function variables() {
657
2455
  let resolveVariables;
658
2456
  let rawVariables;
@@ -670,10 +2468,11 @@ function variables() {
670
2468
  add: (variables) => {
671
2469
  resolveVariables(variables).forEach((resolved) => {
672
2470
  const { name, value, autocomplete: { asValueOf, asProperty } } = resolved;
673
- asValueOf.forEach((p) => {
674
- if (p !== "-") engine.appendAutocompleteCssPropertyValues(p, `var(${name})`);
2471
+ const cssProperties = Object.fromEntries(asValueOf.filter((p) => p !== "-").map((p) => [p, `var(${name})`]));
2472
+ engine.appendAutocomplete({
2473
+ cssProperties,
2474
+ extraCssProperties: asProperty ? name : void 0
675
2475
  });
676
- if (asProperty) engine.appendAutocompleteExtraCssProperties(name);
677
2476
  if (value != null) {
678
2477
  const list = engine.variables.store.get(name) ?? [];
679
2478
  list.push(resolved);
@@ -692,7 +2491,7 @@ function variables() {
692
2491
  value.flatMap(extractUsedVarNames).forEach((name) => used.add(normalizeVariableName(name)));
693
2492
  });
694
2493
  const otherPreflights = engine.config.preflights.filter((p) => p.id !== "core:variables");
695
- (await Promise.all(otherPreflights.map(({ fn }) => Promise.resolve(fn(engine, false)).catch(() => null)))).forEach((result) => {
2494
+ (await Promise.all(otherPreflights.map(({ fn }) => Promise.resolve().then(() => fn(engine, false)).catch(() => null)))).forEach((result) => {
696
2495
  if (result == null) return;
697
2496
  extractUsedVarNamesFromPreflightResult(result).forEach((name) => used.add(name));
698
2497
  });
@@ -704,8 +2503,8 @@ function variables() {
704
2503
  const entries = varMap.get(name);
705
2504
  if (!entries) continue;
706
2505
  for (const { value } of entries) {
707
- if (value == null) continue;
708
- for (const refName of extractUsedVarNames(String(value)).map(normalizeVariableName)) if (!used.has(refName)) {
2506
+ const referencedValue = Array.isArray(value) ? value.join(" ") : String(value);
2507
+ for (const refName of extractUsedVarNames(referencedValue).map(normalizeVariableName)) if (!used.has(refName)) {
709
2508
  used.add(refName);
710
2509
  queue.push(refName);
711
2510
  }
@@ -729,57 +2528,176 @@ function variables() {
729
2528
  });
730
2529
  }
731
2530
  function createResolveVariablesFn({ pruneUnused: defaultPruneUnused = true } = {}) {
2531
+ function isVariableScopeObject(value) {
2532
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2533
+ }
732
2534
  function _resolveVariables(variables, levels, result) {
733
2535
  for (const [key, value] of Object.entries(variables)) if (key.startsWith("--")) {
734
- const { value: varValue, autocomplete = {}, pruneUnused = defaultPruneUnused } = typeof value === "object" && value !== null && !Array.isArray(value) ? value : { value };
2536
+ const { value: varValue, semanticType, autocomplete = {}, pruneUnused = defaultPruneUnused } = typeof value === "object" && value !== null && !Array.isArray(value) ? value : { value };
735
2537
  result.push({
736
2538
  name: key,
737
2539
  value: varValue,
738
2540
  selector: levels.length > 0 ? levels : [":root"],
739
2541
  autocomplete: {
740
- asValueOf: autocomplete.asValueOf ? [autocomplete.asValueOf].flat() : ["*"],
2542
+ asValueOf: resolveAutocompleteValueTargets({
2543
+ name: key,
2544
+ asValueOf: autocomplete.asValueOf,
2545
+ semanticType
2546
+ }),
741
2547
  asProperty: autocomplete.asProperty ?? true
742
2548
  },
743
2549
  pruneUnused
744
2550
  });
745
- } else _resolveVariables(value, [...levels, key], result);
2551
+ } else {
2552
+ if (!isVariableScopeObject(value)) {
2553
+ log.warn(`Invalid variables scope for selector "${key}". Expected a nested object, received ${typeof value}. Skipping.`);
2554
+ continue;
2555
+ }
2556
+ _resolveVariables(value, [...levels, key], result);
2557
+ }
746
2558
  return result;
747
2559
  }
748
2560
  return function resolveVariables(variables) {
749
2561
  return _resolveVariables(variables, [], []);
750
2562
  };
751
2563
  }
2564
+ function resolveAutocompleteValueTargets({ name, asValueOf, semanticType }) {
2565
+ const explicitTargets = asValueOf == null ? [] : [asValueOf].flat().map((value) => String(value));
2566
+ const semanticTypes = semanticType == null ? [] : [semanticType].flat().map((value) => String(value));
2567
+ if (explicitTargets.includes("-")) return [];
2568
+ const targets = /* @__PURE__ */ new Set();
2569
+ if (asValueOf == null && semanticTypes.length === 0) targets.add("*");
2570
+ explicitTargets.forEach((target) => {
2571
+ targets.add(target);
2572
+ });
2573
+ semanticTypes.forEach((family) => {
2574
+ const properties = VARIABLE_SEMANTIC_FAMILY_PROPERTIES[family];
2575
+ if (properties != null) {
2576
+ properties.forEach((property) => targets.add(property));
2577
+ return;
2578
+ }
2579
+ log.warn(`Unknown semanticType "${family}" for variable "${name}". Skipping semantic autocomplete expansion.`);
2580
+ });
2581
+ if (targets.has("*")) return ["*"];
2582
+ return [...targets];
2583
+ }
752
2584
  const VAR_NAME_RE = /var\((--[\w-]+)/g;
2585
+ /**
2586
+ * Extracts all CSS variable names referenced via `var(--*)` calls in a string.
2587
+ *
2588
+ * @param input - The CSS value string to scan.
2589
+ * @returns An array of variable names (including the `--` prefix) found in `var()` expressions.
2590
+ *
2591
+ * @remarks Uses a global regex to find all `var(--name)` occurrences. Nested `var()` calls are matched independently.
2592
+ *
2593
+ * @example
2594
+ * ```ts
2595
+ * extractUsedVarNames('color: var(--primary)') // ['--primary']
2596
+ * extractUsedVarNames('var(--a) var(--b)') // ['--a', '--b']
2597
+ * ```
2598
+ */
753
2599
  function extractUsedVarNames(input) {
754
2600
  return Array.from(input.matchAll(VAR_NAME_RE), (m) => m[1]);
755
2601
  }
2602
+ /**
2603
+ * Ensures a variable name has the `--` prefix.
2604
+ *
2605
+ * @param name - The variable name, with or without the `--` prefix.
2606
+ * @returns The name with a guaranteed `--` prefix.
2607
+ *
2608
+ * @remarks A no-op when the name already starts with `--`.
2609
+ *
2610
+ * @example
2611
+ * ```ts
2612
+ * normalizeVariableName('color') // '--color'
2613
+ * normalizeVariableName('--color') // '--color'
2614
+ * ```
2615
+ */
756
2616
  function normalizeVariableName(name) {
757
2617
  if (name.startsWith("--")) return name;
758
2618
  return `--${name}`;
759
2619
  }
760
2620
  /**
761
- * Recursively extract all CSS variable names referenced inside a preflight
762
- * result (either a plain CSS string or a `PreflightDefinition` object).
2621
+ * Recursively extracts all CSS variable names referenced in a preflight result.
2622
+ *
2623
+ * @param result - A preflight output: either a raw CSS string or a nested `PreflightDefinition` object.
2624
+ * @returns A flat array of normalized variable names found in the result.
2625
+ *
2626
+ * @remarks For string results, scans for `var(--*)` references. For object results, recursively traverses selector scopes and string/number values. All returned names are normalized with the `--` prefix.
2627
+ *
2628
+ * @example
2629
+ * ```ts
2630
+ * extractUsedVarNamesFromPreflightResult({ ':root': { color: 'var(--primary)' } })
2631
+ * // ['--primary']
2632
+ * ```
763
2633
  */
764
2634
  function extractUsedVarNamesFromPreflightResult(result) {
765
2635
  if (typeof result === "string") return extractUsedVarNames(result).map(normalizeVariableName);
766
2636
  const names = [];
767
2637
  for (const value of Object.values(result)) {
768
2638
  if (value == null) continue;
769
- if (typeof value === "string" || typeof value === "number") extractUsedVarNames(String(value)).forEach((n) => names.push(normalizeVariableName(n)));
770
- else if (typeof value === "object") extractUsedVarNamesFromPreflightResult(value).forEach((n) => names.push(n));
2639
+ if (typeof value === "string" || typeof value === "number") {
2640
+ extractUsedVarNames(String(value)).forEach((n) => names.push(normalizeVariableName(n)));
2641
+ continue;
2642
+ }
2643
+ extractUsedVarNamesFromPreflightResult(value).forEach((n) => names.push(n));
771
2644
  }
772
2645
  return names;
773
2646
  }
774
-
775
2647
  //#endregion
776
2648
  //#region src/internal/engine.ts
2649
+ /**
2650
+ * Default CSS layer name for preflight styles.
2651
+ * @internal
2652
+ *
2653
+ * @remarks Used as the layer name wrapping all unlayered preflight output when the layer exists in `config.layers`.
2654
+ *
2655
+ * @example
2656
+ * ```ts
2657
+ * // 'preflights'
2658
+ * ```
2659
+ */
777
2660
  const DEFAULT_PREFLIGHTS_LAYER = "preflights";
2661
+ /**
2662
+ * Default CSS layer name for utility (atomic) styles.
2663
+ * @internal
2664
+ *
2665
+ * @remarks Atomic styles without an explicit layer are placed into this layer when it exists in `config.layers`.
2666
+ *
2667
+ * @example
2668
+ * ```ts
2669
+ * // 'utilities'
2670
+ * ```
2671
+ */
778
2672
  const DEFAULT_UTILITIES_LAYER = "utilities";
2673
+ /**
2674
+ * Default layer ordering map: `preflights` at weight 1, `utilities` at weight 10.
2675
+ * @internal
2676
+ *
2677
+ * @remarks Merged with any user-supplied `config.layers` during engine config resolution. Numeric weights determine the `@layer` declaration order.
2678
+ *
2679
+ * @example
2680
+ * ```ts
2681
+ * // { preflights: 1, utilities: 10 }
2682
+ * ```
2683
+ */
779
2684
  const DEFAULT_LAYERS = {
780
2685
  [DEFAULT_PREFLIGHTS_LAYER]: 1,
781
2686
  [DEFAULT_UTILITIES_LAYER]: 10
782
2687
  };
2688
+ /**
2689
+ * Creates and initializes a PikaCSS engine with the given configuration.
2690
+ *
2691
+ * @param config - The engine configuration, including plugins, selectors, shortcuts, variables, keyframes, preflights, and layer settings.
2692
+ * @returns A fully initialized `Engine` instance.
2693
+ *
2694
+ * @remarks Core plugins (`important`, `variables`, `keyframes`, `selectors`, `shortcuts`) are prepended automatically. The function resolves plugins, runs all configuration hooks in sequence, and returns the ready-to-use engine.
2695
+ *
2696
+ * @example
2697
+ * ```ts
2698
+ * const engine = await createEngine({ prefix: 'pk-', plugins: [myPlugin()] })
2699
+ * ```
2700
+ */
783
2701
  async function createEngine(config = {}) {
784
2702
  log.debug("Creating engine with config:", config);
785
2703
  const corePlugins = [
@@ -797,26 +2715,53 @@ async function createEngine(config = {}) {
797
2715
  };
798
2716
  log.debug(`Total plugins resolved: ${plugins.length}`);
799
2717
  config = await hooks.configureRawConfig(config.plugins, config);
800
- hooks.rawConfigConfigured(resolvePlugins(config.plugins ?? []), config);
2718
+ hooks.rawConfigConfigured(resolvePlugins(config.plugins), config);
801
2719
  let resolvedConfig = await resolveEngineConfig(config);
802
2720
  log.debug("Engine config resolved with prefix:", resolvedConfig.prefix);
803
2721
  resolvedConfig = await hooks.configureResolvedConfig(resolvedConfig.plugins, resolvedConfig);
804
2722
  let engine = new Engine(resolvedConfig);
805
- engine.appendAutocompleteExtraProperties("__layer");
806
- engine.appendAutocompletePropertyValues("__layer", "Autocomplete['Layer']");
2723
+ engine.appendAutocomplete({
2724
+ extraProperties: "__layer",
2725
+ properties: { __layer: "Autocomplete['Layer']" }
2726
+ });
807
2727
  log.debug("Engine instance created");
808
2728
  engine = await hooks.configureEngine(engine.config.plugins, engine);
809
2729
  log.debug("Engine initialized successfully");
810
2730
  return engine;
811
2731
  }
2732
+ /**
2733
+ * The PikaCSS engine: manages atomic style resolution, rendering, preflights, and plugin hooks.
2734
+ *
2735
+ * @remarks Constructed via `createEngine()`. Holds the resolved configuration, the atomic style store, and exposes methods for processing style items (`use`), rendering CSS output (`renderPreflights`, `renderAtomicStyles`, `renderLayerOrderDeclaration`), and managing runtime extensions (`addPreflight`, `appendAutocomplete`, `appendCssImport`).
2736
+ *
2737
+ * @example
2738
+ * ```ts
2739
+ * const engine = await createEngine({ prefix: 'pk-' })
2740
+ * const ids = await engine.use({ color: 'red' })
2741
+ * const css = await engine.renderAtomicStyles(true)
2742
+ * ```
2743
+ */
812
2744
  var Engine = class {
2745
+ /** The fully resolved engine configuration. */
813
2746
  config;
2747
+ /** Reference to the plugin hook dispatcher for invoking lifecycle hooks. */
814
2748
  pluginHooks = hooks;
2749
+ /** The extraction function that decomposes style definitions into atomic style contents. */
815
2750
  extract;
816
- store = {
817
- atomicStyleIds: /* @__PURE__ */ new Map(),
818
- atomicStyles: /* @__PURE__ */ new Map()
819
- };
2751
+ /** The engine's runtime store holding registered atomic styles and their ID mappings. */
2752
+ store = createEngineStore();
2753
+ /**
2754
+ * Creates an engine instance from a resolved configuration.
2755
+ *
2756
+ * @param config - The fully resolved engine configuration.
2757
+ *
2758
+ * @remarks Initializes the `extract` function by wiring it to the plugin hook pipeline for selectors, style items, and style definitions.
2759
+ *
2760
+ * @example
2761
+ * ```ts
2762
+ * const engine = new Engine(resolvedConfig)
2763
+ * ```
2764
+ */
820
2765
  constructor(config) {
821
2766
  this.config = config;
822
2767
  this.extract = createExtractFn({
@@ -826,45 +2771,113 @@ var Engine = class {
826
2771
  transformStyleDefinitions: (styleDefinitions) => hooks.transformStyleDefinitions(this.config.plugins, styleDefinitions)
827
2772
  });
828
2773
  }
2774
+ /**
2775
+ * Fires the `preflightUpdated` hook to notify plugins that preflight content has changed.
2776
+ *
2777
+ *
2778
+ * @remarks Called automatically after `addPreflight` or when plugins modify preflight-contributing state (e.g. variables, keyframes).
2779
+ *
2780
+ * @example
2781
+ * ```ts
2782
+ * engine.notifyPreflightUpdated()
2783
+ * ```
2784
+ */
829
2785
  notifyPreflightUpdated() {
830
2786
  hooks.preflightUpdated(this.config.plugins);
831
2787
  }
2788
+ /**
2789
+ * Fires the `atomicStyleAdded` hook to notify plugins that a new atomic style was registered.
2790
+ *
2791
+ * @param atomicStyle - The atomic style that was just added to the store.
2792
+ *
2793
+ * @remarks Called automatically by `use()` when a previously unseen atomic style is resolved.
2794
+ *
2795
+ * @example
2796
+ * ```ts
2797
+ * engine.notifyAtomicStyleAdded(atomicStyle)
2798
+ * ```
2799
+ */
832
2800
  notifyAtomicStyleAdded(atomicStyle) {
833
2801
  hooks.atomicStyleAdded(this.config.plugins, atomicStyle);
834
2802
  }
2803
+ /**
2804
+ * Fires the `autocompleteConfigUpdated` hook to notify plugins that autocomplete entries changed.
2805
+ *
2806
+ *
2807
+ * @remarks Called automatically after `appendAutocomplete` when the contribution modifies the resolved autocomplete config.
2808
+ *
2809
+ * @example
2810
+ * ```ts
2811
+ * engine.notifyAutocompleteConfigUpdated()
2812
+ * ```
2813
+ */
835
2814
  notifyAutocompleteConfigUpdated() {
836
2815
  hooks.autocompleteConfigUpdated(this.config.plugins);
837
2816
  }
838
- appendAutocompleteSelectors(...selectors) {
839
- appendAutocompleteSelectors(this.config, ...selectors);
840
- this.notifyAutocompleteConfigUpdated();
841
- }
842
- appendAutocompleteStyleItemStrings(...styleItemStrings) {
843
- appendAutocompleteStyleItemStrings(this.config, ...styleItemStrings);
844
- this.notifyAutocompleteConfigUpdated();
845
- }
846
- appendAutocompleteExtraProperties(...properties) {
847
- appendAutocompleteExtraProperties(this.config, ...properties);
848
- this.notifyAutocompleteConfigUpdated();
849
- }
850
- appendAutocompleteExtraCssProperties(...properties) {
851
- appendAutocompleteExtraCssProperties(this.config, ...properties);
852
- this.notifyAutocompleteConfigUpdated();
2817
+ /**
2818
+ * Merges an autocomplete contribution into the resolved autocomplete config.
2819
+ *
2820
+ * @param contribution - The autocomplete entries to append (selectors, properties, CSS properties, etc.).
2821
+ *
2822
+ * @remarks Delegates to the `appendAutocomplete` utility and fires `autocompleteConfigUpdated` if the config was actually modified.
2823
+ *
2824
+ * @example
2825
+ * ```ts
2826
+ * engine.appendAutocomplete({ selectors: 'hover', cssProperties: { color: 'red' } })
2827
+ * ```
2828
+ */
2829
+ appendAutocomplete(contribution) {
2830
+ if (appendAutocomplete(this.config, contribution)) this.notifyAutocompleteConfigUpdated();
853
2831
  }
854
- appendAutocompletePropertyValues(property, ...tsTypes) {
855
- appendAutocompletePropertyValues(this.config, property, ...tsTypes);
856
- this.notifyAutocompleteConfigUpdated();
857
- }
858
- appendAutocompleteCssPropertyValues(property, ...values) {
859
- appendAutocompleteCssPropertyValues(this.config, property, ...values);
860
- this.notifyAutocompleteConfigUpdated();
2832
+ /**
2833
+ * Appends a CSS `@import` statement to the preflight output.
2834
+ *
2835
+ * @param cssImport - The raw `@import` string (a trailing semicolon is appended if missing).
2836
+ *
2837
+ * @remarks Deduplicates imports. Fires `preflightUpdated` when a new import is added.
2838
+ *
2839
+ * @example
2840
+ * ```ts
2841
+ * engine.appendCssImport('@import url("https://fonts.googleapis.com/css2?family=Inter")')
2842
+ * ```
2843
+ */
2844
+ appendCssImport(cssImport) {
2845
+ const normalized = normalizeCssImport(cssImport);
2846
+ if (normalized == null || this.config.cssImports.includes(normalized)) return;
2847
+ this.config.cssImports.push(normalized);
2848
+ this.notifyPreflightUpdated();
861
2849
  }
2850
+ /**
2851
+ * Registers a new preflight that will be rendered before atomic styles.
2852
+ *
2853
+ * @param preflight - A preflight definition: a function, a static string/object, or a wrapper with `layer`/`id` metadata.
2854
+ *
2855
+ * @remarks The preflight is resolved into a `ResolvedPreflight` (extracting optional `layer` and `id`) and appended to `config.preflights`. Fires `preflightUpdated` so plugins and the integration layer know to re-render.
2856
+ *
2857
+ * @example
2858
+ * ```ts
2859
+ * engine.addPreflight({ layer: 'base', preflight: '*, *::before { box-sizing: border-box; }' })
2860
+ * ```
2861
+ */
862
2862
  addPreflight(preflight) {
863
2863
  log.debug("Adding preflight");
864
2864
  this.config.preflights.push(resolvePreflight(preflight));
865
2865
  log.debug(`Total preflights: ${this.config.preflights.length}`);
866
2866
  this.notifyPreflightUpdated();
867
2867
  }
2868
+ /**
2869
+ * Processes style items through the plugin pipeline and registers the resulting atomic styles in the store.
2870
+ *
2871
+ * @param itemList - Style items to process: string references (shortcuts) and/or style definition objects.
2872
+ * @returns An array of atomic style IDs (and unresolved string references) in insertion order.
2873
+ *
2874
+ * @remarks Runs `transformStyleItems` and `extractStyleDefinition` hooks, resolves each extracted content into an atomic style, deduplicates by base key, and fires `atomicStyleAdded` for new entries.
2875
+ *
2876
+ * @example
2877
+ * ```ts
2878
+ * const ids = await engine.use({ color: 'red' }, { padding: '1rem' })
2879
+ * ```
2880
+ */
868
2881
  async use(...itemList) {
869
2882
  log.debug(`Processing ${itemList.length} style items`);
870
2883
  const { unknown, contents } = await resolveStyleItemList({
@@ -873,63 +2886,89 @@ var Engine = class {
873
2886
  extractStyleDefinition: (styleDefinition) => this.extract(styleDefinition)
874
2887
  });
875
2888
  const resolvedIds = [];
876
- contents.forEach((content) => {
877
- const id = getAtomicStyleId({
2889
+ const resolvedIdsByBaseKey = /* @__PURE__ */ new Map();
2890
+ for (const content of contents) {
2891
+ const { id, atomicStyle } = resolveAtomicStyle({
878
2892
  content,
879
2893
  prefix: this.config.prefix,
880
- stored: this.store.atomicStyleIds
2894
+ store: this.store,
2895
+ resolvedIdsByBaseKey
881
2896
  });
882
2897
  resolvedIds.push(id);
883
- if (!this.store.atomicStyles.has(id)) {
884
- const atomicStyle = {
885
- id,
886
- content
887
- };
888
- this.store.atomicStyles.set(id, atomicStyle);
2898
+ resolvedIdsByBaseKey.set(getAtomicStyleBaseKey(content), id);
2899
+ if (atomicStyle != null) {
889
2900
  log.debug(`Atomic style added: ${id}`);
890
2901
  this.notifyAtomicStyleAdded(atomicStyle);
891
2902
  }
892
- });
2903
+ }
893
2904
  log.debug(`Resolved ${resolvedIds.length} atomic styles, ${unknown.size} unknown items`);
894
2905
  return [...unknown, ...resolvedIds];
895
2906
  }
2907
+ /**
2908
+ * Renders all registered preflight definitions into a CSS string.
2909
+ *
2910
+ * @param isFormatted - Whether to produce human-readable CSS with newlines and indentation.
2911
+ * @returns The rendered preflight CSS, including `@import` statements, optional `@layer` wrappers, and all preflight content.
2912
+ *
2913
+ * @remarks Evaluates each preflight function, groups output by layer, wraps unlayered preflights in the default preflights layer (when present), and respects configured layer ordering.
2914
+ *
2915
+ * @example
2916
+ * ```ts
2917
+ * const css = await engine.renderPreflights(true)
2918
+ * ```
2919
+ */
896
2920
  async renderPreflights(isFormatted) {
897
2921
  log.debug("Rendering preflights...");
898
2922
  const lineEnd = isFormatted ? "\n" : "";
899
- const rendered = await Promise.all(this.config.preflights.map(async ({ layer, fn }) => {
2923
+ const rendered = (await Promise.all(this.config.preflights.map(async ({ layer, fn }) => {
900
2924
  const result = await fn(this, isFormatted);
901
2925
  return {
902
2926
  layer,
903
- css: typeof result === "string" ? result : await renderPreflightDefinition({
2927
+ css: (typeof result === "string" ? result : await renderPreflightDefinition({
904
2928
  engine: this,
905
2929
  preflightDefinition: result,
906
2930
  isFormatted
907
- })
2931
+ })).trim()
908
2932
  };
909
- }));
2933
+ }))).filter((r) => r.css);
910
2934
  log.debug(`Rendered ${rendered.length} preflights`);
911
- const unlayeredParts = [];
912
- const layerGroups = /* @__PURE__ */ new Map();
913
- for (const { layer, css } of rendered) if (layer == null) unlayeredParts.push(css);
914
- else {
915
- if (!layerGroups.has(layer)) layerGroups.set(layer, []);
916
- layerGroups.get(layer).push(css);
917
- }
2935
+ const { unlayeredParts, layerGroups } = groupRenderedPreflightsByLayer(rendered);
918
2936
  const outputParts = [];
2937
+ if (this.config.cssImports.length > 0) outputParts.push(...this.config.cssImports);
919
2938
  if (unlayeredParts.length > 0) {
920
- const unlayeredContent = unlayeredParts.join(lineEnd);
921
2939
  const { defaultPreflightsLayer } = this.config;
922
- if (defaultPreflightsLayer in this.config.layers) outputParts.push(`@layer ${defaultPreflightsLayer} {${lineEnd}${unlayeredContent}${lineEnd}}`);
923
- else outputParts.push(unlayeredContent);
924
- }
925
- const configLayerOrder = sortLayerNames(this.config.layers);
926
- const orderedLayerNames = [...configLayerOrder.filter((name) => layerGroups.has(name)), ...[...layerGroups.keys()].filter((name) => !configLayerOrder.includes(name))];
927
- for (const layerName of orderedLayerNames) {
928
- const content = layerGroups.get(layerName).join(lineEnd);
929
- outputParts.push(`@layer ${layerName} {${lineEnd}${content}${lineEnd}}`);
2940
+ if (defaultPreflightsLayer in this.config.layers) {
2941
+ const unlayeredContent = unlayeredParts.map((part) => part.trim().split("\n").map((line) => ` ${line}`).join(lineEnd)).join(lineEnd);
2942
+ outputParts.push(`@layer ${defaultPreflightsLayer} {${lineEnd}${unlayeredContent}${lineEnd}}`);
2943
+ } else {
2944
+ const unlayeredContent = unlayeredParts.join(lineEnd);
2945
+ outputParts.push(unlayeredContent);
2946
+ }
930
2947
  }
2948
+ outputParts.push(...renderLayerBlocks({
2949
+ layerGroups,
2950
+ layerOrder: sortLayerNames(this.config.layers),
2951
+ isFormatted,
2952
+ render: (cssList) => cssList.join(lineEnd)
2953
+ }));
931
2954
  return outputParts.join(lineEnd);
932
2955
  }
2956
+ /**
2957
+ * Renders atomic styles into a CSS string, optionally filtered by ID and grouped by layer.
2958
+ *
2959
+ * @param isFormatted - Whether to produce human-readable CSS with newlines and indentation.
2960
+ * @param options - Optional filtering: `atomicStyleIds` to render a subset, `isPreview` to use placeholder IDs.
2961
+ * @param options.atomicStyleIds - Specific atomic style IDs to render instead of the full store.
2962
+ * @param options.isPreview - Whether to keep placeholder IDs instead of substituting real class names.
2963
+ * @returns The rendered atomic-style CSS.
2964
+ *
2965
+ * @remarks Styles are sorted by rendering weight (selector specificity depth), grouped into configured `@layer` blocks, and rendered. When `isPreview` is true, atomic style IDs remain as placeholders for tooling previews.
2966
+ *
2967
+ * @example
2968
+ * ```ts
2969
+ * const css = await engine.renderAtomicStyles(true)
2970
+ * ```
2971
+ */
933
2972
  async renderAtomicStyles(isFormatted, options = {}) {
934
2973
  log.debug("Rendering atomic styles...");
935
2974
  const { atomicStyleIds = null, isPreview = false } = options;
@@ -944,19 +2983,141 @@ var Engine = class {
944
2983
  defaultUtilitiesLayer: this.config.defaultUtilitiesLayer
945
2984
  });
946
2985
  }
2986
+ /**
2987
+ * Renders the CSS `@layer` order declaration for all configured layers.
2988
+ *
2989
+ * @returns A `@layer` statement listing layer names in weight order, or an empty string if no layers are configured.
2990
+ *
2991
+ * @remarks Ensures the browser applies the intended cascade priority for `preflights`, `utilities`, and any user-defined layers.
2992
+ *
2993
+ * @example
2994
+ * ```ts
2995
+ * engine.renderLayerOrderDeclaration()
2996
+ * // '@layer preflights, utilities;'
2997
+ * ```
2998
+ */
947
2999
  renderLayerOrderDeclaration() {
948
3000
  const { layers } = this.config;
949
3001
  if (Object.keys(layers).length === 0) return "";
950
3002
  return `@layer ${sortLayerNames(layers).join(", ")};`;
951
3003
  }
952
3004
  };
3005
+ /**
3006
+ * Computes a numeric rendering weight for an atomic style based on its selector depth.
3007
+ * @internal
3008
+ *
3009
+ * @param style - The atomic style to weigh.
3010
+ * @param defaultSelector - The engine's default selector pattern.
3011
+ * @returns `0` for styles using only the default selector; otherwise the number of selector segments.
3012
+ *
3013
+ * @remarks Used to sort atomic styles so that simpler selectors appear before more specific ones in the CSS output, preserving deterministic cascade ordering.
3014
+ *
3015
+ * @example
3016
+ * ```ts
3017
+ * calcAtomicStyleRenderingWeight(style, '.pk-__PLACEHOLDER__')
3018
+ * ```
3019
+ */
953
3020
  function calcAtomicStyleRenderingWeight(style, defaultSelector) {
954
- const { selector } = style.content;
3021
+ const { selector } = splitLayerSelector(style.content.selector);
955
3022
  return selector.length === 1 && selector[0] === defaultSelector ? 0 : selector.length;
956
3023
  }
3024
+ /**
3025
+ * Sorts layer names by their numeric weight, then alphabetically for ties.
3026
+ *
3027
+ * @param layers - A record mapping layer names to numeric weights.
3028
+ * @returns An array of layer names in ascending weight order.
3029
+ *
3030
+ * @remarks Used to produce the `@layer` declaration order and to order layer group rendering.
3031
+ *
3032
+ * @example
3033
+ * ```ts
3034
+ * sortLayerNames({ utilities: 10, preflights: 1 })
3035
+ * // ['preflights', 'utilities']
3036
+ * ```
3037
+ */
957
3038
  function sortLayerNames(layers) {
958
3039
  return Object.entries(layers).sort((a, b) => a[1] - b[1] || a[0].localeCompare(b[0])).map(([name]) => name);
959
3040
  }
3041
+ function appendLayerGroupItem(layerGroups, layer, item) {
3042
+ if (!layerGroups.has(layer)) layerGroups.set(layer, []);
3043
+ layerGroups.get(layer).push(item);
3044
+ }
3045
+ function getOrderedLayerNamesForGroups(layerGroups, layerOrder) {
3046
+ return [...layerOrder.filter((name) => (layerGroups.get(name)?.length ?? 0) > 0), ...[...layerGroups.keys()].filter((name) => !layerOrder.includes(name) && layerGroups.get(name).length > 0)];
3047
+ }
3048
+ function renderLayerBlocks({ layerGroups, layerOrder, isFormatted, render }) {
3049
+ const lineEnd = isFormatted ? "\n" : "";
3050
+ return getOrderedLayerNamesForGroups(layerGroups, layerOrder).map((layerName) => {
3051
+ const items = layerGroups.get(layerName);
3052
+ return `@layer ${layerName} {${lineEnd}${isFormatted ? render(items).trim().split("\n").map((line) => ` ${line}`).join("\n") : render(items)}${lineEnd}}`;
3053
+ });
3054
+ }
3055
+ function normalizeCssImport(cssImport) {
3056
+ const normalized = cssImport.trim();
3057
+ if (normalized.length === 0) return null;
3058
+ return normalized.endsWith(";") ? normalized : `${normalized};`;
3059
+ }
3060
+ function groupRenderedPreflightsByLayer(rendered) {
3061
+ const unlayeredParts = [];
3062
+ const layerGroups = /* @__PURE__ */ new Map();
3063
+ for (const { layer, css } of rendered) {
3064
+ if (layer == null) {
3065
+ unlayeredParts.push(css);
3066
+ continue;
3067
+ }
3068
+ appendLayerGroupItem(layerGroups, layer, css);
3069
+ }
3070
+ return {
3071
+ unlayeredParts,
3072
+ layerGroups
3073
+ };
3074
+ }
3075
+ function splitLayerSelector(selector) {
3076
+ const [first, ...rest] = selector;
3077
+ if (first == null || first.startsWith("@layer ") === false) return {
3078
+ layer: void 0,
3079
+ selector
3080
+ };
3081
+ const layer = first.slice(7).trim();
3082
+ if (layer.length === 0) return {
3083
+ layer: void 0,
3084
+ selector
3085
+ };
3086
+ return {
3087
+ layer,
3088
+ selector: rest
3089
+ };
3090
+ }
3091
+ function prependLayerSelector(selector, layer) {
3092
+ return [`${LAYER_SELECTOR_PREFIX}${layer}`, ...selector];
3093
+ }
3094
+ function groupAtomicStylesByLayer({ styles, layerOrder, defaultUtilitiesLayer }) {
3095
+ const unlayeredStyles = [];
3096
+ const layerGroups = new Map(layerOrder.map((name) => [name, []]));
3097
+ const candidateDefaultLayer = defaultUtilitiesLayer ?? layerOrder.at(-1);
3098
+ const defaultLayer = candidateDefaultLayer != null && layerGroups.has(candidateDefaultLayer) ? candidateDefaultLayer : layerOrder.at(-1);
3099
+ for (const style of styles) {
3100
+ const { layer } = splitLayerSelector(style.content.selector);
3101
+ if (layer != null && layerGroups.has(layer)) {
3102
+ layerGroups.get(layer).push(style);
3103
+ continue;
3104
+ }
3105
+ if (layer != null) {
3106
+ log.warn(`Unknown layer "${layer}" encountered in atomic style; falling back to unlayered output.`);
3107
+ unlayeredStyles.push(style);
3108
+ continue;
3109
+ }
3110
+ if (defaultLayer != null) {
3111
+ layerGroups.get(defaultLayer).push(style);
3112
+ continue;
3113
+ }
3114
+ unlayeredStyles.push(style);
3115
+ }
3116
+ return {
3117
+ unlayeredStyles,
3118
+ layerGroups
3119
+ };
3120
+ }
960
3121
  function isWithLayer(p) {
961
3122
  if (typeof p !== "object" || p === null) return false;
962
3123
  const record = p;
@@ -967,6 +3128,20 @@ function isWithId(p) {
967
3128
  const record = p;
968
3129
  return typeof record.id === "string" && record.preflight !== void 0;
969
3130
  }
3131
+ /**
3132
+ * Normalizes a `Preflight` input into a `ResolvedPreflight` by extracting optional `layer` and `id` wrappers.
3133
+ * @internal
3134
+ *
3135
+ * @param preflight - A preflight value: a function, a static string/`PreflightDefinition`, or a wrapper with `layer`/`id` metadata.
3136
+ * @returns A `ResolvedPreflight` with separated `layer`, `id`, and `fn`.
3137
+ *
3138
+ * @remarks Handles nested wrappers: a `{ layer, preflight: { id, preflight: fn } }` shape is unwrapped in order.
3139
+ *
3140
+ * @example
3141
+ * ```ts
3142
+ * resolvePreflight({ layer: 'base', id: 'reset', preflight: '* { margin: 0 }' })
3143
+ * ```
3144
+ */
970
3145
  function resolvePreflight(preflight) {
971
3146
  let layer;
972
3147
  let id;
@@ -984,11 +3159,25 @@ function resolvePreflight(preflight) {
984
3159
  fn: typeof preflight === "function" ? preflight : () => preflight
985
3160
  };
986
3161
  }
3162
+ /**
3163
+ * Resolves a raw `EngineConfig` into a fully normalized `ResolvedEngineConfig`.
3164
+ * @internal
3165
+ *
3166
+ * @param config - The raw engine configuration.
3167
+ * @returns A `ResolvedEngineConfig` with defaults applied, plugins sorted, preflights resolved, and autocomplete initialized.
3168
+ *
3169
+ * @remarks Merges `DEFAULT_LAYERS`, normalizes CSS imports, resolves preflight definitions, and initializes the empty autocomplete sets/maps.
3170
+ *
3171
+ * @example
3172
+ * ```ts
3173
+ * const resolved = await resolveEngineConfig({ prefix: 'pk-' })
3174
+ * ```
3175
+ */
987
3176
  async function resolveEngineConfig(config) {
988
- const { prefix = "", defaultSelector = `.${ATOMIC_STYLE_ID_PLACEHOLDER}`, plugins = [], preflights = [] } = config;
3177
+ const { prefix = "pk-", defaultSelector = `.%`, plugins = [], cssImports = [], preflights = [] } = config;
989
3178
  const layers = Object.assign({}, DEFAULT_LAYERS, config.layers);
990
- const defaultPreflightsLayer = config.defaultPreflightsLayer ?? DEFAULT_PREFLIGHTS_LAYER;
991
- const defaultUtilitiesLayer = config.defaultUtilitiesLayer ?? DEFAULT_UTILITIES_LAYER;
3179
+ const defaultPreflightsLayer = config.defaultPreflightsLayer ?? "preflights";
3180
+ const defaultUtilitiesLayer = config.defaultUtilitiesLayer ?? "utilities";
992
3181
  log.debug(`Resolving engine config with prefix: "${prefix}", plugins: ${plugins.length}, preflights: ${preflights.length}`);
993
3182
  const resolvedConfig = {
994
3183
  rawConfig: config,
@@ -996,55 +3185,31 @@ async function resolveEngineConfig(config) {
996
3185
  prefix,
997
3186
  defaultSelector,
998
3187
  preflights: [],
3188
+ cssImports: [...new Set(cssImports.map(normalizeCssImport).filter(isNotNullish))],
999
3189
  layers,
1000
3190
  defaultPreflightsLayer,
1001
3191
  defaultUtilitiesLayer,
1002
3192
  autocomplete: {
1003
3193
  selectors: /* @__PURE__ */ new Set(),
1004
- styleItemStrings: /* @__PURE__ */ new Set(),
3194
+ shortcuts: /* @__PURE__ */ new Set(),
1005
3195
  extraProperties: /* @__PURE__ */ new Set(),
1006
3196
  extraCssProperties: /* @__PURE__ */ new Set(),
1007
3197
  properties: /* @__PURE__ */ new Map(),
1008
- cssProperties: /* @__PURE__ */ new Map()
3198
+ cssProperties: /* @__PURE__ */ new Map(),
3199
+ patterns: {
3200
+ selectors: /* @__PURE__ */ new Set(),
3201
+ shortcuts: /* @__PURE__ */ new Set(),
3202
+ properties: /* @__PURE__ */ new Map(),
3203
+ cssProperties: /* @__PURE__ */ new Map()
3204
+ }
1009
3205
  }
1010
3206
  };
3207
+ appendAutocomplete(resolvedConfig, config.autocomplete ?? {});
1011
3208
  const resolvedPreflights = preflights.map(resolvePreflight);
1012
3209
  resolvedConfig.preflights.push(...resolvedPreflights);
1013
3210
  log.debug(`Engine config resolved: ${resolvedPreflights.length} preflights processed`);
1014
3211
  return resolvedConfig;
1015
3212
  }
1016
- function getAtomicStyleId({ content, prefix, stored }) {
1017
- const key = serialize([
1018
- content.selector,
1019
- content.property,
1020
- content.value,
1021
- content.layer
1022
- ]);
1023
- const cached = stored.get(key);
1024
- if (cached != null) {
1025
- log.debug(`Atomic style cached: ${cached}`);
1026
- return cached;
1027
- }
1028
- const num = stored.size;
1029
- const id = `${prefix}${numberToChars(num)}`;
1030
- stored.set(key, id);
1031
- log.debug(`Generated new atomic style ID: ${id}`);
1032
- return id;
1033
- }
1034
- function optimizeAtomicStyleContents(list) {
1035
- const map = /* @__PURE__ */ new Map();
1036
- list.forEach((content) => {
1037
- const key = serialize([
1038
- content.selector,
1039
- content.property,
1040
- content.layer
1041
- ]);
1042
- map.delete(key);
1043
- if (content.value == null) return;
1044
- map.set(key, { ...content });
1045
- });
1046
- return [...map.values()];
1047
- }
1048
3213
  function extractLayerFromStyleItem(item) {
1049
3214
  const record = item;
1050
3215
  const layer = typeof record.__layer === "string" ? record.__layer : void 0;
@@ -1058,6 +3223,26 @@ function extractLayerFromStyleItem(item) {
1058
3223
  definition: rest
1059
3224
  };
1060
3225
  }
3226
+ /**
3227
+ * Transforms and extracts a list of style items into deduplicated atomic style contents.
3228
+ * @internal
3229
+ *
3230
+ * @param options - An object containing:
3231
+ * - `itemList` — the raw style items to process.
3232
+ * - `transformStyleItems` — the plugin hook for transforming style items.
3233
+ * - `extractStyleDefinition` — the function that decomposes a style definition into extracted contents.
3234
+ * @param options.itemList - The raw style items to process.
3235
+ * @param options.transformStyleItems - Hook that expands or rewrites style items before extraction.
3236
+ * @param options.extractStyleDefinition - Function that decomposes a style definition into extracted style contents.
3237
+ * @returns An object with `unknown` (unresolved string references) and `contents` (optimized extracted style contents).
3238
+ *
3239
+ * @remarks String items that survive the `transformStyleItems` hook are collected into the `unknown` set. Object items are extracted, optionally layer-prepended, and optimized for duplicate property merging.
3240
+ *
3241
+ * @example
3242
+ * ```ts
3243
+ * const { unknown, contents } = await resolveStyleItemList({ itemList, transformStyleItems, extractStyleDefinition })
3244
+ * ```
3245
+ */
1061
3246
  async function resolveStyleItemList({ itemList, transformStyleItems, extractStyleDefinition }) {
1062
3247
  const unknown = /* @__PURE__ */ new Set();
1063
3248
  const list = [];
@@ -1065,8 +3250,10 @@ async function resolveStyleItemList({ itemList, transformStyleItems, extractStyl
1065
3250
  else {
1066
3251
  const { layer, definition } = extractLayerFromStyleItem(styleItem);
1067
3252
  const extracted = await extractStyleDefinition(definition);
1068
- if (layer != null) extracted.forEach((c) => c.layer = layer);
1069
- list.push(...extracted);
3253
+ list.push(...layer == null ? extracted : extracted.map((content) => ({
3254
+ ...content,
3255
+ selector: prependLayerSelector(content.selector, layer)
3256
+ })));
1070
3257
  }
1071
3258
  return {
1072
3259
  unknown,
@@ -1078,8 +3265,9 @@ function sortAtomicStyles(styles, defaultSelector) {
1078
3265
  }
1079
3266
  function renderAtomicStylesCss({ atomicStyles, isPreview, isFormatted }) {
1080
3267
  const blocks = /* @__PURE__ */ new Map();
1081
- atomicStyles.forEach(({ id, content: { selector, property, value } }) => {
1082
- if (selector.some((s) => s.includes(ATOMIC_STYLE_ID_PLACEHOLDER)) === false || value == null) return;
3268
+ atomicStyles.forEach(({ id, content: { selector: rawSelector, property, value } }) => {
3269
+ const { selector } = splitLayerSelector(rawSelector);
3270
+ if (selector.some((s) => s.includes("%")) === false || value == null) return;
1083
3271
  const renderObject = {
1084
3272
  selector: isPreview ? selector : selector.map((s) => s.replace(ATOMIC_STYLE_ID_PLACEHOLDER_RE_GLOBAL, id)),
1085
3273
  properties: value.map((v) => ({
@@ -1100,6 +3288,26 @@ function renderAtomicStylesCss({ atomicStyles, isPreview, isFormatted }) {
1100
3288
  });
1101
3289
  return renderCSSStyleBlocks(blocks, isFormatted);
1102
3290
  }
3291
+ /**
3292
+ * Standalone function that renders atomic styles into CSS with layer grouping.
3293
+ * @internal
3294
+ *
3295
+ * @param payload - An object containing `atomicStyles`, `isPreview`, `isFormatted`, `defaultSelector`, and optional `layers`/`defaultUtilitiesLayer`.
3296
+ * @param payload.atomicStyles - The atomic styles to render.
3297
+ * @param payload.isPreview - Whether placeholder IDs should be preserved for preview output.
3298
+ * @param payload.isFormatted - Whether to render with indentation and line breaks.
3299
+ * @param payload.defaultSelector - The engine default selector used when computing render order.
3300
+ * @param payload.layers - Optional configured CSS layers to group atomic styles into.
3301
+ * @param payload.defaultUtilitiesLayer - Optional fallback layer for unlayered utility styles.
3302
+ * @returns The rendered CSS string.
3303
+ *
3304
+ * @remarks Sorts styles by rendering weight, groups them into `@layer` blocks when layers are configured, and renders each group. Used by both the `Engine.renderAtomicStyles` method and external consumers.
3305
+ *
3306
+ * @example
3307
+ * ```ts
3308
+ * const css = renderAtomicStyles({ atomicStyles, isPreview: false, isFormatted: true, defaultSelector: '.pk-__ID__', layers: { utilities: 10 } })
3309
+ * ```
3310
+ */
1103
3311
  function renderAtomicStyles(payload) {
1104
3312
  const { atomicStyles, isPreview, isFormatted, defaultSelector, layers, defaultUtilitiesLayer } = payload;
1105
3313
  const sortedStyles = sortAtomicStyles(atomicStyles, defaultSelector);
@@ -1110,37 +3318,49 @@ function renderAtomicStyles(payload) {
1110
3318
  });
1111
3319
  const layerOrder = sortLayerNames(layers);
1112
3320
  const lineEnd = isFormatted ? "\n" : "";
1113
- const unlayeredStyles = [];
1114
- const layerGroups = new Map(layerOrder.map((name) => [name, []]));
1115
- const candidateDefaultLayer = defaultUtilitiesLayer ?? layerOrder[layerOrder.length - 1];
1116
- const defaultLayer = candidateDefaultLayer != null && layerGroups.has(candidateDefaultLayer) ? candidateDefaultLayer : layerOrder[layerOrder.length - 1];
1117
- for (const style of sortedStyles) {
1118
- const layer = style.content.layer;
1119
- if (layer != null && layerGroups.has(layer)) layerGroups.get(layer).push(style);
1120
- else if (layer != null) {
1121
- log.warn(`Unknown layer "${layer}" encountered in atomic style; falling back to unlayered output.`);
1122
- unlayeredStyles.push(style);
1123
- } else if (defaultLayer != null) layerGroups.get(defaultLayer).push(style);
1124
- else unlayeredStyles.push(style);
1125
- }
3321
+ const { unlayeredStyles, layerGroups } = groupAtomicStylesByLayer({
3322
+ styles: sortedStyles,
3323
+ layerOrder,
3324
+ defaultUtilitiesLayer
3325
+ });
1126
3326
  const parts = [];
1127
3327
  if (unlayeredStyles.length > 0) parts.push(renderAtomicStylesCss({
1128
3328
  atomicStyles: unlayeredStyles,
1129
3329
  isPreview,
1130
3330
  isFormatted
1131
3331
  }));
1132
- for (const layerName of layerOrder) {
1133
- const styles = layerGroups.get(layerName);
1134
- if (styles.length === 0) continue;
1135
- const innerCss = renderAtomicStylesCss({
3332
+ parts.push(...renderLayerBlocks({
3333
+ layerGroups,
3334
+ layerOrder,
3335
+ isFormatted,
3336
+ render: (styles) => renderAtomicStylesCss({
1136
3337
  atomicStyles: styles,
1137
3338
  isPreview,
1138
3339
  isFormatted
1139
- });
1140
- parts.push(`@layer ${layerName} {${lineEnd}${innerCss}${lineEnd}}`);
1141
- }
3340
+ })
3341
+ }));
1142
3342
  return parts.join(lineEnd);
1143
3343
  }
3344
+ /**
3345
+ * Recursively converts a `PreflightDefinition` object tree into CSS style blocks.
3346
+ * @internal
3347
+ *
3348
+ * @param options - An object containing:
3349
+ * - `engine` — the engine instance (used for selector transformation).
3350
+ * - `preflightDefinition` — the nested object tree of selectors and CSS properties.
3351
+ * - `blocks` — accumulator map for the resulting CSS blocks.
3352
+ * @param options.engine - The engine instance used to run selector transforms.
3353
+ * @param options.preflightDefinition - The nested preflight definition object to convert into CSS blocks.
3354
+ * @param options.blocks - Optional accumulator map reused during recursive descent.
3355
+ * @returns The accumulated `CSSStyleBlocks` map.
3356
+ *
3357
+ * @remarks Each key in the definition is either a CSS property (when its value is a property value) or a nested selector scope (when its value is an object). Selector keys are expanded through `hooks.transformSelectors`. The resulting blocks map is consumable by `renderCSSStyleBlocks`.
3358
+ *
3359
+ * @example
3360
+ * ```ts
3361
+ * const blocks = await _renderPreflightDefinition({ engine, preflightDefinition: { ':root': { '--color': 'red' } } })
3362
+ * ```
3363
+ */
1144
3364
  async function _renderPreflightDefinition({ engine, preflightDefinition, blocks = /* @__PURE__ */ new Map() }) {
1145
3365
  for (const [selector, propertiesOrDefinition] of Object.entries(preflightDefinition)) {
1146
3366
  if (propertiesOrDefinition == null) continue;
@@ -1179,6 +3399,23 @@ async function _renderPreflightDefinition({ engine, preflightDefinition, blocks
1179
3399
  }
1180
3400
  return blocks;
1181
3401
  }
3402
+ /**
3403
+ * Renders a `PreflightDefinition` into a CSS string via the engine's selector pipeline.
3404
+ * @internal
3405
+ *
3406
+ * @param payload - An object with the `engine`, the `preflightDefinition` to render, and `isFormatted` flag.
3407
+ * @param payload.engine - The engine instance whose selector pipeline should be applied.
3408
+ * @param payload.preflightDefinition - The preflight definition tree to render.
3409
+ * @param payload.isFormatted - Whether the rendered CSS should include indentation and line breaks.
3410
+ * @returns The rendered CSS string.
3411
+ *
3412
+ * @remarks A convenience wrapper that calls `_renderPreflightDefinition` and pipes the result through `renderCSSStyleBlocks`.
3413
+ *
3414
+ * @example
3415
+ * ```ts
3416
+ * const css = await renderPreflightDefinition({ engine, preflightDefinition: { ':root': { color: 'red' } }, isFormatted: true })
3417
+ * ```
3418
+ */
1182
3419
  async function renderPreflightDefinition(payload) {
1183
3420
  const { engine, preflightDefinition, isFormatted } = payload;
1184
3421
  return renderCSSStyleBlocks(await _renderPreflightDefinition({
@@ -1186,31 +3423,127 @@ async function renderPreflightDefinition(payload) {
1186
3423
  preflightDefinition
1187
3424
  }), isFormatted);
1188
3425
  }
1189
-
1190
3426
  //#endregion
1191
3427
  //#region src/index.ts
3428
+ /**
3429
+ * Identity helper that returns the engine configuration as-is, providing TypeScript type inference and autocompletion.
3430
+ *
3431
+ * @typeParam T - The exact literal type of the configuration object.
3432
+ * @param config - The engine configuration object.
3433
+ * @returns The same configuration object, unchanged.
3434
+ *
3435
+ * @remarks A compile-time-only helper with no runtime effect. Useful in `pika.config.ts` files for IDE support.
3436
+ *
3437
+ * @example
3438
+ * ```ts
3439
+ * export default defineEngineConfig({ prefix: 'pk-', plugins: [myPlugin()] })
3440
+ * ```
3441
+ */
1192
3442
  function defineEngineConfig(config) {
1193
3443
  return config;
1194
3444
  }
3445
+ /**
3446
+ * Identity helper that returns the style definition as-is, providing TypeScript type inference and autocompletion.
3447
+ *
3448
+ * @typeParam T - The exact literal type of the style definition.
3449
+ * @param styleDefinition - A style definition object.
3450
+ * @returns The same style definition, unchanged.
3451
+ *
3452
+ * @remarks A compile-time-only helper with no runtime effect. Useful for extracting a reusable style definition with full type safety.
3453
+ *
3454
+ * @example
3455
+ * ```ts
3456
+ * const card = defineStyleDefinition({ padding: '1rem', borderRadius: '0.5rem' })
3457
+ * ```
3458
+ */
1195
3459
  function defineStyleDefinition(styleDefinition) {
1196
3460
  return styleDefinition;
1197
3461
  }
3462
+ /**
3463
+ * Identity helper that returns the preflight as-is, providing TypeScript type inference and autocompletion.
3464
+ *
3465
+ * @typeParam T - The exact literal type of the preflight.
3466
+ * @param preflight - A preflight definition: a function, a static string/object, or a wrapper with `layer`/`id` metadata.
3467
+ * @returns The same preflight, unchanged.
3468
+ *
3469
+ * @remarks A compile-time-only helper with no runtime effect. Useful for defining reusable preflight values with type safety.
3470
+ *
3471
+ * @example
3472
+ * ```ts
3473
+ * const reset = definePreflight('*, *::before { box-sizing: border-box; }')
3474
+ * ```
3475
+ */
1198
3476
  function definePreflight(preflight) {
1199
3477
  return preflight;
1200
3478
  }
3479
+ /**
3480
+ * Identity helper that returns the keyframes definition as-is, providing TypeScript type inference and autocompletion.
3481
+ *
3482
+ * @typeParam T - The exact literal type of the keyframes configuration.
3483
+ * @param keyframes - A keyframes definition: a name string, a tuple, or an object form.
3484
+ * @returns The same keyframes definition, unchanged.
3485
+ *
3486
+ * @remarks A compile-time-only helper with no runtime effect.
3487
+ *
3488
+ * @example
3489
+ * ```ts
3490
+ * const spin = defineKeyframes(['spin', { from: { transform: 'rotate(0deg)' }, to: { transform: 'rotate(360deg)' } }])
3491
+ * ```
3492
+ */
1201
3493
  function defineKeyframes(keyframes) {
1202
3494
  return keyframes;
1203
3495
  }
3496
+ /**
3497
+ * Identity helper that returns the selector definition as-is, providing TypeScript type inference and autocompletion.
3498
+ *
3499
+ * @typeParam T - The exact literal type of the selector configuration.
3500
+ * @param selector - A selector definition: a string redirect, tuple, or object form.
3501
+ * @returns The same selector definition, unchanged.
3502
+ *
3503
+ * @remarks A compile-time-only helper with no runtime effect.
3504
+ *
3505
+ * @example
3506
+ * ```ts
3507
+ * const hover = defineSelector(['hover', '&:hover'])
3508
+ * ```
3509
+ */
1204
3510
  function defineSelector(selector) {
1205
3511
  return selector;
1206
3512
  }
3513
+ /**
3514
+ * Identity helper that returns the shortcut definition as-is, providing TypeScript type inference and autocompletion.
3515
+ *
3516
+ * @typeParam T - The exact literal type of the shortcut configuration.
3517
+ * @param shortcut - A shortcut definition: a string redirect, tuple, or object form.
3518
+ * @returns The same shortcut definition, unchanged.
3519
+ *
3520
+ * @remarks A compile-time-only helper with no runtime effect.
3521
+ *
3522
+ * @example
3523
+ * ```ts
3524
+ * const btn = defineShortcut(['btn', { padding: '0.5rem 1rem', borderRadius: '0.25rem' }])
3525
+ * ```
3526
+ */
1207
3527
  function defineShortcut(shortcut) {
1208
3528
  return shortcut;
1209
3529
  }
3530
+ /**
3531
+ * Identity helper that returns the variables definition as-is, providing TypeScript type inference and autocompletion.
3532
+ *
3533
+ * @typeParam T - The exact literal type of the variables definition.
3534
+ * @param variables - A nested record of CSS custom property definitions.
3535
+ * @returns The same variables definition, unchanged.
3536
+ *
3537
+ * @remarks A compile-time-only helper with no runtime effect.
3538
+ *
3539
+ * @example
3540
+ * ```ts
3541
+ * const vars = defineVariables({ '--color-primary': '#3b82f6', '.dark': { '--color-primary': '#60a5fa' } })
3542
+ * ```
3543
+ */
1210
3544
  function defineVariables(variables) {
1211
3545
  return variables;
1212
3546
  }
1213
3547
  /* c8 ignore end */
1214
-
1215
3548
  //#endregion
1216
- export { appendAutocompleteCssPropertyValues, appendAutocompleteExtraCssProperties, appendAutocompleteExtraProperties, appendAutocompletePropertyValues, appendAutocompleteSelectors, appendAutocompleteStyleItemStrings, createEngine, createLogger, defineEngineConfig, defineEnginePlugin, defineKeyframes, definePreflight, defineSelector, defineShortcut, defineStyleDefinition, defineVariables, log, renderCSSStyleBlocks, sortLayerNames };
3549
+ export { appendAutocomplete, createEngine, createLogger, defineEngineConfig, defineEnginePlugin, defineKeyframes, definePreflight, defineSelector, defineShortcut, defineStyleDefinition, defineVariables, log, renderCSSStyleBlocks, sortLayerNames };