domflax 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +159 -0
- package/dist/{chunk-4HHISSMR.js → chunk-DNHOGPYV.js} +2675 -1503
- package/dist/chunk-DNHOGPYV.js.map +1 -0
- package/dist/{chunk-ZJ2S36GY.js → chunk-DOQEBGWB.js} +33 -20
- package/dist/chunk-DOQEBGWB.js.map +1 -0
- package/dist/{chunk-77SLHRN6.js → chunk-DWLB7FRR.js} +341 -176
- package/dist/chunk-DWLB7FRR.js.map +1 -0
- package/dist/cli.cjs +2209 -774
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +234 -116
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +3021 -1699
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +477 -54
- package/dist/index.d.ts +477 -54
- package/dist/index.js +49 -3
- package/dist/pattern-CV607P87.d.ts +547 -0
- package/dist/pattern-F5xBtIE-.d.cts +547 -0
- package/dist/pattern-kit.cjs +60 -39
- package/dist/pattern-kit.cjs.map +1 -1
- package/dist/pattern-kit.d.cts +3 -18
- package/dist/pattern-kit.d.ts +3 -18
- package/dist/pattern-kit.js +3 -1
- package/dist/pattern-kit.js.map +1 -1
- package/dist/{types-BQ7l6dVe.d.ts → resolve-ops-DIwEelH-.d.cts} +26 -251
- package/dist/{types-BQ7l6dVe.d.cts → resolve-ops-DIwEelH-.d.ts} +26 -251
- package/dist/verify.d.cts +1 -1
- package/dist/verify.d.ts +1 -1
- package/dist/webpack-loader.cjs +2975 -1699
- package/dist/webpack-loader.cjs.map +1 -1
- package/dist/webpack-loader.d.cts +2 -2
- package/dist/webpack-loader.d.ts +2 -2
- package/dist/webpack-loader.js +3 -3
- package/package.json +3 -6
- package/dist/chunk-4HHISSMR.js.map +0 -1
- package/dist/chunk-77SLHRN6.js.map +0 -1
- package/dist/chunk-ZJ2S36GY.js.map +0 -1
- package/dist/pattern-CX6iBzTD.d.ts +0 -237
- package/dist/pattern-P4FIKAUB.d.cts +0 -237
|
@@ -10,46 +10,108 @@ import {
|
|
|
10
10
|
createFragment,
|
|
11
11
|
createText,
|
|
12
12
|
defaultMeta,
|
|
13
|
+
definePattern,
|
|
13
14
|
emptyClassList,
|
|
14
15
|
emptyStyleMap,
|
|
15
|
-
hasDynamicChildren,
|
|
16
16
|
hasDynamicClasses,
|
|
17
|
-
hasEventHandlers,
|
|
18
|
-
hasRef,
|
|
19
17
|
isElement,
|
|
20
18
|
normalizer,
|
|
21
19
|
not,
|
|
22
|
-
pattern,
|
|
23
20
|
targetedByCombinator
|
|
24
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-DWLB7FRR.js";
|
|
25
22
|
import {
|
|
26
23
|
__filename,
|
|
27
24
|
init_esm_shims
|
|
28
25
|
} from "./chunk-6WVVF6AD.js";
|
|
29
26
|
|
|
30
|
-
// ../patterns/src/flatten/
|
|
27
|
+
// ../patterns/src/library/flatten/display-contents-wrapper.pattern.ts
|
|
31
28
|
init_esm_shims();
|
|
32
29
|
function asEl(node) {
|
|
33
30
|
const n = node;
|
|
34
31
|
return n.kind === "element" ? n : null;
|
|
35
32
|
}
|
|
33
|
+
function metaOf(node) {
|
|
34
|
+
return asEl(node)?.meta ?? null;
|
|
35
|
+
}
|
|
36
|
+
var declaresCustomProperties = (node) => metaOf(node)?.declaresCustomProperties ?? false;
|
|
37
|
+
var hasSpreadAttrs = (node) => metaOf(node)?.hasSpreadAttrs ?? false;
|
|
38
|
+
var isComponentNode = (node) => metaOf(node)?.isComponent ?? false;
|
|
39
|
+
var hasOwnAttrs = (node) => {
|
|
40
|
+
const el = asEl(node);
|
|
41
|
+
if (!el) return false;
|
|
42
|
+
return el.attrs.entries.size > 0 || el.attrs.spreads.length > 0;
|
|
43
|
+
};
|
|
44
|
+
var targetedByStructuralPseudo = (node, ctx) => {
|
|
45
|
+
const el = asEl(node);
|
|
46
|
+
if (!el) return false;
|
|
47
|
+
if (el.meta.targetedByStructuralPseudo) return true;
|
|
48
|
+
return ctx.selectors.targetedByStructuralPseudo(el.id);
|
|
49
|
+
};
|
|
50
|
+
var displayContentsWrapper = definePattern({
|
|
51
|
+
name: "display-contents-wrapper",
|
|
52
|
+
category: "flatten/display-contents-wrapper",
|
|
53
|
+
safety: 2,
|
|
54
|
+
doc: {
|
|
55
|
+
title: "Flatten display:contents wrapper",
|
|
56
|
+
summary: "A div with display:contents (which generates no box) wrapping a single element child, with no own visual style, no attributes beyond an inert class, and no opacity barriers, is removed; its sole child is hoisted in its place.",
|
|
57
|
+
before: '<div style="display:contents"><Child/></div>',
|
|
58
|
+
after: "<Child/>",
|
|
59
|
+
safetyRationale: "A display:contents element generates no box at all, so its children already render as direct children of its parent; removing it is layout-identical. It paints nothing, establishes no formatting/stacking/box context, is no containing block, carries no ref/handlers/dynamic-children/html/spread/component identity, owns no targetable attrs / custom-property coupling, and is not a combinator/structural-pseudo subject; inheritable styles are folded onto the child before removal."
|
|
60
|
+
},
|
|
61
|
+
match: {
|
|
62
|
+
tag: "div",
|
|
63
|
+
style: { display: "contents" },
|
|
64
|
+
onlyChild: "element",
|
|
65
|
+
paintsNothing: true,
|
|
66
|
+
where: [
|
|
67
|
+
not(declaresCustomProperties),
|
|
68
|
+
not(hasOwnAttrs),
|
|
69
|
+
not(hasDynamicClasses),
|
|
70
|
+
not(hasSpreadAttrs),
|
|
71
|
+
not(isComponentNode),
|
|
72
|
+
not(targetedByStructuralPseudo)
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
rewrite: { flattenInto: "child" },
|
|
76
|
+
test: {
|
|
77
|
+
cases: [
|
|
78
|
+
{
|
|
79
|
+
// `display:contents` generates no box, so removing it is provably layout-identical → the
|
|
80
|
+
// wrapper is flattened even under the conservative gate; the child is hoisted.
|
|
81
|
+
before: '<div className="contents"><a className="text-blue-500">Link</a></div>',
|
|
82
|
+
after: '<a className="text-blue-500">Link</a>'
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
noMatch: [
|
|
86
|
+
// A ref pins the wrapper's element identity (a hard opacity barrier) → not a passthrough.
|
|
87
|
+
'<div className="contents" ref={rootRef}><a className="text-blue-500">Link</a></div>'
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// ../patterns/src/library/flatten/empty-style-div.pattern.ts
|
|
93
|
+
init_esm_shims();
|
|
94
|
+
function asEl2(node) {
|
|
95
|
+
const n = node;
|
|
96
|
+
return n.kind === "element" ? n : null;
|
|
97
|
+
}
|
|
36
98
|
function metaFlag(flag) {
|
|
37
|
-
return (node) => Boolean(
|
|
99
|
+
return (node) => Boolean(asEl2(node)?.meta[flag]);
|
|
38
100
|
}
|
|
39
101
|
var establishesBox = metaFlag("establishesBox");
|
|
40
102
|
var establishesFormattingContext = metaFlag("establishesFormattingContext");
|
|
41
103
|
var establishesStackingContext = metaFlag("establishesStackingContext");
|
|
42
104
|
var isContainingBlock = metaFlag("isContainingBlock");
|
|
43
|
-
var
|
|
44
|
-
var
|
|
45
|
-
const el =
|
|
105
|
+
var declaresCustomProperties2 = metaFlag("declaresCustomProperties");
|
|
106
|
+
var targetedByStructuralPseudo2 = (node, ctx) => {
|
|
107
|
+
const el = asEl2(node);
|
|
46
108
|
if (!el) return false;
|
|
47
109
|
if (el.meta.targetedByStructuralPseudo) return true;
|
|
48
110
|
return ctx.selectors.targetedByStructuralPseudo(el.id);
|
|
49
111
|
};
|
|
50
112
|
var DISPLAY = "display";
|
|
51
113
|
var hasNonBlockDisplay = (node, ctx) => {
|
|
52
|
-
const el =
|
|
114
|
+
const el = asEl2(node);
|
|
53
115
|
if (!el) return false;
|
|
54
116
|
const sm = ctx.computedOf(el) ?? el.computed;
|
|
55
117
|
for (const block of sm.blocks.values()) {
|
|
@@ -58,7 +120,7 @@ var hasNonBlockDisplay = (node, ctx) => {
|
|
|
58
120
|
}
|
|
59
121
|
return false;
|
|
60
122
|
};
|
|
61
|
-
var emptyStyleDiv =
|
|
123
|
+
var emptyStyleDiv = definePattern({
|
|
62
124
|
name: "empty-style-div",
|
|
63
125
|
category: "flatten/empty-style-div",
|
|
64
126
|
safety: 1,
|
|
@@ -79,26 +141,29 @@ var emptyStyleDiv = pattern({
|
|
|
79
141
|
not(establishesFormattingContext),
|
|
80
142
|
not(establishesStackingContext),
|
|
81
143
|
not(isContainingBlock),
|
|
82
|
-
not(
|
|
83
|
-
not(
|
|
144
|
+
not(declaresCustomProperties2),
|
|
145
|
+
not(targetedByStructuralPseudo2)
|
|
84
146
|
]
|
|
85
147
|
},
|
|
86
148
|
rewrite: { flattenInto: "child" },
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
149
|
+
test: {
|
|
150
|
+
cases: [
|
|
151
|
+
{
|
|
152
|
+
// A layout-neutral, style-free block div is a provably-safe flatten → removed, child hoisted.
|
|
153
|
+
before: '<div><span className="bg-red-200">Hi</span></div>',
|
|
154
|
+
after: '<span className="bg-red-200">Hi</span>'
|
|
155
|
+
}
|
|
156
|
+
],
|
|
157
|
+
noMatch: [
|
|
93
158
|
// The wrapper paints its own background (own visual style) → not layout-neutral, kept.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
159
|
+
'<div className="bg-blue-500"><span className="bg-red-200">Hi</span></div>'
|
|
160
|
+
]
|
|
161
|
+
}
|
|
97
162
|
});
|
|
98
163
|
|
|
99
|
-
// ../patterns/src/flatten/flex-center-wrapper.pattern.ts
|
|
164
|
+
// ../patterns/src/library/flatten/flex-center-wrapper.pattern.ts
|
|
100
165
|
init_esm_shims();
|
|
101
|
-
var flexCenterWrapper =
|
|
166
|
+
var flexCenterWrapper = definePattern({
|
|
102
167
|
name: "flex-center-wrapper",
|
|
103
168
|
category: "flatten/flex-center-wrapper",
|
|
104
169
|
safety: 2,
|
|
@@ -119,21 +184,65 @@ var flexCenterWrapper = pattern({
|
|
|
119
184
|
flattenInto: "child",
|
|
120
185
|
childGains: { placeSelf: "center" }
|
|
121
186
|
},
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
//
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
187
|
+
// Collapsing a flex-centering wrapper to `place-self:center` on the child only stays centered when
|
|
188
|
+
// the child's NEW parent is flex/grid; moreover the wrapper's own `display:flex` establishes a
|
|
189
|
+
// formatting context. Both make this a `needs-verification` flatten, which the conservative
|
|
190
|
+
// production gate (`'provably-safe'`, used by the harness) intentionally REVERTS — so every case
|
|
191
|
+
// here is a no-match: the wrapper is preserved. Op-level rewrite correctness (purity, id-preserving
|
|
192
|
+
// unwrap, opacity-barrier safety) is still asserted by the invariant suite over every pattern.
|
|
193
|
+
test: {
|
|
194
|
+
noMatch: [
|
|
195
|
+
// Even under a static flex/grid parent the centering flatten is not provably layout-neutral
|
|
196
|
+
// (the wrapper itself establishes a flex formatting context) → left unchanged.
|
|
197
|
+
'<div className="grid"><div className="flex items-center justify-center"><span className="bg-red-200">x</span></div></div>',
|
|
198
|
+
// Non-flex/grid parent (document root): place-self centering would not hold → left unchanged.
|
|
199
|
+
'<div className="flex justify-center items-center"><div className="bg-red-200">Hello</div></div>',
|
|
200
|
+
// onClick is a hard opacity barrier → the wrapper is load-bearing regardless of the gate.
|
|
201
|
+
'<div className="flex justify-center items-center" onClick={handleClick}><div className="bg-red-200">Hello</div></div>'
|
|
202
|
+
]
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// ../patterns/src/library/flatten/inline-flex-center-wrapper.pattern.ts
|
|
207
|
+
init_esm_shims();
|
|
208
|
+
var inlineFlexCenterWrapper = definePattern({
|
|
209
|
+
name: "inline-flex-center-wrapper",
|
|
210
|
+
category: "flatten/inline-flex-center-wrapper",
|
|
211
|
+
safety: 2,
|
|
212
|
+
doc: {
|
|
213
|
+
title: "Flatten inline-flex-centering wrapper",
|
|
214
|
+
summary: "A div that only centers a single child (display:inline-flex; align-items:center; justify-content:center) is removed; the child gains place-self:center.",
|
|
215
|
+
before: '<div style="display:inline-flex;align-items:center;justify-content:center"><Child/></div>',
|
|
216
|
+
after: '<Child style="place-self:center"/>',
|
|
217
|
+
safetyRationale: "Wrapper paints nothing, carries no ref/handlers/dynamic children, and is not a combinator subject; inheritable styles are folded onto the child before removal."
|
|
218
|
+
},
|
|
219
|
+
match: {
|
|
220
|
+
tag: "div",
|
|
221
|
+
style: { display: "inline-flex", alignItems: "center", justifyContent: "center" },
|
|
222
|
+
onlyChild: "element",
|
|
223
|
+
paintsNothing: true
|
|
224
|
+
},
|
|
225
|
+
rewrite: {
|
|
226
|
+
flattenInto: "child",
|
|
227
|
+
childGains: { placeSelf: "center" }
|
|
228
|
+
},
|
|
229
|
+
// Like its block-level sibling, this centering flatten is `needs-verification` (the wrapper's own
|
|
230
|
+
// `display:inline-flex` establishes a formatting context, and place-self centering only holds under
|
|
231
|
+
// a flex/grid parent), so the conservative production gate (`'provably-safe'`) REVERTS it — every
|
|
232
|
+
// case here is a no-match. Op-level correctness is covered by the invariant suite.
|
|
233
|
+
test: {
|
|
234
|
+
noMatch: [
|
|
235
|
+
// Even under a static flex/grid parent the centering flatten is not provably layout-neutral.
|
|
236
|
+
'<div className="grid"><div className="inline-flex items-center justify-center"><span className="bg-red-200">x</span></div></div>',
|
|
237
|
+
// Non-flex/grid parent (document root) → left unchanged.
|
|
238
|
+
'<div className="inline-flex justify-center items-center"><div className="bg-red-200">Hello</div></div>',
|
|
239
|
+
// onClick is a hard opacity barrier → the wrapper is load-bearing regardless of the gate.
|
|
240
|
+
'<div className="inline-flex justify-center items-center" onClick={handleClick}><div className="bg-red-200">Hello</div></div>'
|
|
241
|
+
]
|
|
242
|
+
}
|
|
134
243
|
});
|
|
135
244
|
|
|
136
|
-
// ../patterns/src/flatten/nested-flex-merge.pattern.ts
|
|
245
|
+
// ../patterns/src/library/flatten/nested-flex-merge.pattern.ts
|
|
137
246
|
init_esm_shims();
|
|
138
247
|
function baseConditionStyleMap(decls) {
|
|
139
248
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -200,7 +309,7 @@ var isInnerFlex = and(
|
|
|
200
309
|
computed(DISPLAY_FLEX),
|
|
201
310
|
not(targetedByCombinator)
|
|
202
311
|
);
|
|
203
|
-
var nestedFlexMerge =
|
|
312
|
+
var nestedFlexMerge = definePattern({
|
|
204
313
|
name: "nested-flex-merge",
|
|
205
314
|
category: "flatten/nested-flex-merge",
|
|
206
315
|
safety: 2,
|
|
@@ -236,24 +345,148 @@ var nestedFlexMerge = pattern({
|
|
|
236
345
|
rw.unwrap(outer)
|
|
237
346
|
];
|
|
238
347
|
},
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
348
|
+
// Merging the outer flex container into the inner removes the outer's box, but a `display:flex`
|
|
349
|
+
// wrapper establishes a formatting context, so this is a `needs-verification` flatten that the
|
|
350
|
+
// conservative production gate (`'provably-safe'`) REVERTS — every case here is a no-match. The
|
|
351
|
+
// merge's op-level correctness (purity, id-preserving unwrap, opacity-barrier safety) is asserted
|
|
352
|
+
// by the invariant suite over every pattern.
|
|
353
|
+
test: {
|
|
354
|
+
noMatch: [
|
|
355
|
+
// The merge is real but not provably layout-neutral (the wrapper establishes a flex context),
|
|
356
|
+
// so under the conservative gate the nested containers are left in place.
|
|
357
|
+
'<div className="flex items-center gap-2" data-x="1"><div className="flex flex-col">X</div></div>',
|
|
358
|
+
// A non-flex wrapper does not match the flex-container signature → left unchanged anyway.
|
|
359
|
+
'<div className="block bg-blue-500"><div className="flex flex-col">X</div></div>'
|
|
360
|
+
]
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
// ../patterns/src/library/flatten/nested-grid-merge.pattern.ts
|
|
365
|
+
init_esm_shims();
|
|
366
|
+
function baseConditionStyleMap2(decls) {
|
|
367
|
+
const map = /* @__PURE__ */ new Map();
|
|
368
|
+
for (const [prop, value] of decls) {
|
|
369
|
+
for (const decl of normalizer.normalizeDeclaration(prop, value, false)) {
|
|
370
|
+
map.set(decl.property, decl);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
const block = { condition: BASE_CONDITION, decls: map };
|
|
374
|
+
const blocks = /* @__PURE__ */ new Map([[conditionKey(BASE_CONDITION), block]]);
|
|
375
|
+
return { blocks };
|
|
376
|
+
}
|
|
377
|
+
var DISPLAY_GRID = baseConditionStyleMap2([["display", "grid"]]);
|
|
378
|
+
var GRID_CONTAINER_PROPERTIES = /* @__PURE__ */ new Set([
|
|
379
|
+
"display",
|
|
380
|
+
"grid-template-columns",
|
|
381
|
+
"grid-template-rows",
|
|
382
|
+
"grid-template-areas",
|
|
383
|
+
"grid-auto-columns",
|
|
384
|
+
"grid-auto-rows",
|
|
385
|
+
"grid-auto-flow",
|
|
386
|
+
"justify-content",
|
|
387
|
+
"align-content",
|
|
388
|
+
"place-content",
|
|
389
|
+
"justify-items",
|
|
390
|
+
"align-items",
|
|
391
|
+
"place-items",
|
|
392
|
+
"row-gap",
|
|
393
|
+
"column-gap"
|
|
394
|
+
]);
|
|
395
|
+
function outerMergeSafe2(sm) {
|
|
396
|
+
const norm = normalizer.normalizeStyleMap(sm);
|
|
397
|
+
for (const block of norm.blocks.values()) {
|
|
398
|
+
for (const decl of block.decls.values()) {
|
|
399
|
+
if (GRID_CONTAINER_PROPERTIES.has(String(decl.property))) continue;
|
|
400
|
+
if (decl.inherited) continue;
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return true;
|
|
405
|
+
}
|
|
406
|
+
function gridConflict(outer, inner) {
|
|
407
|
+
const a = normalizer.normalizeStyleMap(outer);
|
|
408
|
+
const b = normalizer.normalizeStyleMap(inner);
|
|
409
|
+
for (const [key, blockA] of a.blocks) {
|
|
410
|
+
const blockB = b.blocks.get(key);
|
|
411
|
+
if (!blockB) continue;
|
|
412
|
+
for (const [prop, declA] of blockA.decls) {
|
|
413
|
+
if (!GRID_CONTAINER_PROPERTIES.has(String(prop))) continue;
|
|
414
|
+
const declB = blockB.decls.get(prop);
|
|
415
|
+
if (declB && declB.value !== declA.value) return true;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
function extractGridStyle(sm) {
|
|
421
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
422
|
+
for (const [key, block] of sm.blocks) {
|
|
423
|
+
const decls = /* @__PURE__ */ new Map();
|
|
424
|
+
for (const [prop, decl] of block.decls) {
|
|
425
|
+
if (GRID_CONTAINER_PROPERTIES.has(String(prop))) decls.set(prop, decl);
|
|
250
426
|
}
|
|
251
|
-
|
|
427
|
+
if (decls.size > 0) blocks.set(key, { condition: block.condition, decls });
|
|
428
|
+
}
|
|
429
|
+
return { blocks };
|
|
430
|
+
}
|
|
431
|
+
var isInnerGrid = and(
|
|
432
|
+
isElement("div"),
|
|
433
|
+
computed(DISPLAY_GRID),
|
|
434
|
+
not(targetedByCombinator)
|
|
435
|
+
);
|
|
436
|
+
var nestedGridMerge = definePattern({
|
|
437
|
+
name: "nested-grid-merge",
|
|
438
|
+
category: "flatten/nested-grid-merge",
|
|
439
|
+
safety: 2,
|
|
440
|
+
doc: {
|
|
441
|
+
title: "Merge nested grid containers",
|
|
442
|
+
summary: "A grid container whose only child is itself a grid container with non-conflicting grid properties is collapsed into one; the wrapper is removed and its grid declarations merge onto the surviving child.",
|
|
443
|
+
before: '<div style="display:grid;gap:8px"><div style="display:grid;grid-template-columns:1fr 1fr"/></div>',
|
|
444
|
+
after: '<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px"/>',
|
|
445
|
+
safetyRationale: "The wrapper paints nothing, declares only grid-container/inheritable properties, carries no ref/handlers/dynamic children, and is not a combinator subject; the two containers do not conflict on any grid property, so the union is unambiguous and lossless."
|
|
446
|
+
},
|
|
447
|
+
match: {
|
|
448
|
+
tag: "div",
|
|
449
|
+
style: { display: "grid" },
|
|
450
|
+
onlyChild: "element",
|
|
451
|
+
paintsNothing: true
|
|
452
|
+
},
|
|
453
|
+
rewrite: (ctx, rw) => {
|
|
454
|
+
const outer = ctx.node;
|
|
455
|
+
const inner = ctx.onlyElementChild();
|
|
456
|
+
if (!inner) return null;
|
|
457
|
+
if (!isInnerGrid(inner, ctx)) return null;
|
|
458
|
+
const outerStyle = ctx.computed();
|
|
459
|
+
const innerStyle = ctx.computedOf(inner);
|
|
460
|
+
if (!outerMergeSafe2(outerStyle)) return null;
|
|
461
|
+
if (gridConflict(outerStyle, innerStyle)) return null;
|
|
462
|
+
return [
|
|
463
|
+
// 1. Preserve inheritable values (color/font/…) by folding them onto the child first.
|
|
464
|
+
rw.foldInheritedStyles(outer, inner, { conditions: "all" }),
|
|
465
|
+
// 2. Transfer the wrapper's grid-container declarations onto the child (target-wins keeps the
|
|
466
|
+
// child's value for any shared property — identical anyway, we proved non-conflict).
|
|
467
|
+
rw.mergeStyle(inner, null, extractGridStyle(outerStyle), "target-wins"),
|
|
468
|
+
// 3. Remove the wrapper (structural-safe; hoists the child and preserves its IRNodeId).
|
|
469
|
+
rw.unwrap(outer)
|
|
470
|
+
];
|
|
471
|
+
},
|
|
472
|
+
// Like its flex sibling, this merge removes the outer container's box, but a `display:grid` wrapper
|
|
473
|
+
// establishes a formatting context, so it is a `needs-verification` flatten that the conservative
|
|
474
|
+
// production gate (`'provably-safe'`) REVERTS — every case here is a no-match. Op-level correctness
|
|
475
|
+
// is asserted by the invariant suite over every pattern.
|
|
476
|
+
test: {
|
|
477
|
+
noMatch: [
|
|
478
|
+
// The merge is real but not provably layout-neutral (the wrapper establishes a grid context),
|
|
479
|
+
// so under the conservative gate the nested containers are left in place.
|
|
480
|
+
'<div className="grid gap-2" data-x="1"><div className="grid grid-cols-2">X</div></div>',
|
|
481
|
+
// A non-grid wrapper does not match the grid-container signature → left unchanged anyway.
|
|
482
|
+
'<div className="block bg-blue-500"><div className="grid grid-cols-2">X</div></div>'
|
|
483
|
+
]
|
|
484
|
+
}
|
|
252
485
|
});
|
|
253
486
|
|
|
254
|
-
// ../patterns/src/flatten/passthrough-wrapper.pattern.ts
|
|
487
|
+
// ../patterns/src/library/flatten/passthrough-wrapper.pattern.ts
|
|
255
488
|
init_esm_shims();
|
|
256
|
-
function
|
|
489
|
+
function metaOf2(node) {
|
|
257
490
|
const n = node;
|
|
258
491
|
return n.kind === "element" ? n.meta : null;
|
|
259
492
|
}
|
|
@@ -262,24 +495,24 @@ function elementOf(node) {
|
|
|
262
495
|
return n.kind === "element" ? n : null;
|
|
263
496
|
}
|
|
264
497
|
var establishesContext = (node) => {
|
|
265
|
-
const m =
|
|
498
|
+
const m = metaOf2(node);
|
|
266
499
|
if (!m) return false;
|
|
267
500
|
return m.establishesBox || m.establishesFormattingContext || m.establishesStackingContext || m.isContainingBlock || m.declaresCustomProperties;
|
|
268
501
|
};
|
|
269
|
-
var
|
|
270
|
-
var
|
|
271
|
-
var
|
|
502
|
+
var hasSpreadAttrs2 = (node) => metaOf2(node)?.hasSpreadAttrs ?? false;
|
|
503
|
+
var isComponentNode2 = (node) => metaOf2(node)?.isComponent ?? false;
|
|
504
|
+
var hasOwnAttrs2 = (node) => {
|
|
272
505
|
const el = elementOf(node);
|
|
273
506
|
if (!el) return false;
|
|
274
507
|
return el.attrs.entries.size > 0 || el.attrs.spreads.length > 0;
|
|
275
508
|
};
|
|
276
|
-
var
|
|
509
|
+
var targetedByStructuralPseudo3 = (node, ctx) => {
|
|
277
510
|
const el = elementOf(node);
|
|
278
511
|
if (!el) return false;
|
|
279
512
|
if (el.meta.targetedByStructuralPseudo) return true;
|
|
280
513
|
return ctx.selectors.targetedByStructuralPseudo(el.id);
|
|
281
514
|
};
|
|
282
|
-
var passthroughWrapper =
|
|
515
|
+
var passthroughWrapper = definePattern({
|
|
283
516
|
name: "passthrough-wrapper",
|
|
284
517
|
category: "flatten/passthrough-wrapper",
|
|
285
518
|
safety: 2,
|
|
@@ -296,27 +529,34 @@ var passthroughWrapper = pattern({
|
|
|
296
529
|
paintsNothing: true,
|
|
297
530
|
where: [
|
|
298
531
|
not(establishesContext),
|
|
299
|
-
not(
|
|
532
|
+
not(hasOwnAttrs2),
|
|
300
533
|
not(hasDynamicClasses),
|
|
301
|
-
not(
|
|
302
|
-
not(
|
|
303
|
-
not(
|
|
534
|
+
not(hasSpreadAttrs2),
|
|
535
|
+
not(isComponentNode2),
|
|
536
|
+
not(targetedByStructuralPseudo3)
|
|
304
537
|
]
|
|
305
538
|
},
|
|
306
539
|
rewrite: { flattenInto: "child" },
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
540
|
+
test: {
|
|
541
|
+
cases: [
|
|
542
|
+
{
|
|
543
|
+
// A plain, style-free wrapper paints nothing and establishes no context → a provably-safe
|
|
544
|
+
// flatten under the conservative gate: the wrapper is removed and its sole child hoisted.
|
|
545
|
+
before: '<div><a className="bg-red-200">Link</a></div>',
|
|
546
|
+
after: '<a className="bg-red-200">Link</a>'
|
|
547
|
+
}
|
|
548
|
+
],
|
|
549
|
+
noMatch: [
|
|
313
550
|
// A ref pins the wrapper's element identity (a hard opacity barrier) → not a passthrough.
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
551
|
+
'<div ref={rootRef}><a className="bg-red-200">Link</a></div>',
|
|
552
|
+
// A `display:flex` wrapper establishes a formatting context, so removing its box is NOT
|
|
553
|
+
// provably layout-neutral → the conservative gate leaves it in place.
|
|
554
|
+
'<div className="flex"><a className="bg-red-200">Link</a></div>'
|
|
555
|
+
]
|
|
556
|
+
}
|
|
317
557
|
});
|
|
318
558
|
|
|
319
|
-
// ../patterns/src/flatten/redundant-fragment.pattern.ts
|
|
559
|
+
// ../patterns/src/library/flatten/redundant-fragment.pattern.ts
|
|
320
560
|
init_esm_shims();
|
|
321
561
|
function parentIsRedundantFragment(node, ctx) {
|
|
322
562
|
const el = node;
|
|
@@ -339,7 +579,7 @@ function parentIsRedundantFragment(node, ctx) {
|
|
|
339
579
|
if (ctx.selectors.reparentImpact(fid).size > 0) return false;
|
|
340
580
|
return true;
|
|
341
581
|
}
|
|
342
|
-
var redundantFragment =
|
|
582
|
+
var redundantFragment = definePattern({
|
|
343
583
|
name: "redundant-fragment",
|
|
344
584
|
category: "flatten/redundant-fragment",
|
|
345
585
|
safety: 1,
|
|
@@ -358,116 +598,434 @@ var redundantFragment = pattern({
|
|
|
358
598
|
if (!fragment || fragment.kind !== "fragment") return null;
|
|
359
599
|
return [rw.unwrap(fragment)];
|
|
360
600
|
},
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
601
|
+
test: {
|
|
602
|
+
cases: [
|
|
603
|
+
{
|
|
604
|
+
// A fragment renders no box, so unwrapping a single-child fragment is always layout-identical
|
|
605
|
+
// → a provably-safe flatten: the child is spliced up into the fragment's slot.
|
|
606
|
+
before: '<><span className="bg-red-200">Hi</span></>',
|
|
607
|
+
after: '<span className="bg-red-200">Hi</span>'
|
|
608
|
+
}
|
|
609
|
+
],
|
|
610
|
+
noMatch: [
|
|
367
611
|
// Two children ⇒ not a single-child fragment, so the fragment is load-bearing and stays.
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
612
|
+
'<><span className="bg-red-200">A</span><span className="bg-green-200">B</span></>'
|
|
613
|
+
]
|
|
614
|
+
}
|
|
371
615
|
});
|
|
372
616
|
|
|
373
|
-
// ../patterns/src/
|
|
617
|
+
// ../patterns/src/library/flatten/redundant-inline-wrapper.pattern.ts
|
|
374
618
|
init_esm_shims();
|
|
375
|
-
function
|
|
619
|
+
function asEl3(node) {
|
|
376
620
|
const n = node;
|
|
377
621
|
return n.kind === "element" ? n : null;
|
|
378
622
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
function findRedundantClasses(computed2) {
|
|
382
|
-
const winners = /* @__PURE__ */ new Set();
|
|
383
|
-
const shadowed = /* @__PURE__ */ new Set();
|
|
384
|
-
for (const block of computed2.blocks.values()) {
|
|
385
|
-
for (const decl of block.decls.values()) {
|
|
386
|
-
if (decl.origin && decl.origin.kind === "class") winners.add(decl.origin.className);
|
|
387
|
-
for (const o of decl.shadowed ?? []) {
|
|
388
|
-
if (o.kind === "class") shadowed.add(o.className);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
return { winners, shadowed };
|
|
623
|
+
function metaOf3(node) {
|
|
624
|
+
return asEl3(node)?.meta ?? null;
|
|
393
625
|
}
|
|
394
|
-
var
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
626
|
+
var establishesContext2 = (node) => {
|
|
627
|
+
const m = metaOf3(node);
|
|
628
|
+
if (!m) return false;
|
|
629
|
+
return m.establishesBox || m.establishesFormattingContext || m.establishesStackingContext || m.isContainingBlock || m.declaresCustomProperties;
|
|
630
|
+
};
|
|
631
|
+
var hasSpreadAttrs3 = (node) => metaOf3(node)?.hasSpreadAttrs ?? false;
|
|
632
|
+
var isComponentNode3 = (node) => metaOf3(node)?.isComponent ?? false;
|
|
633
|
+
var hasOwnAttrs3 = (node) => {
|
|
634
|
+
const el = asEl3(node);
|
|
635
|
+
if (!el) return false;
|
|
636
|
+
return el.attrs.entries.size > 0 || el.attrs.spreads.length > 0;
|
|
637
|
+
};
|
|
638
|
+
var targetedByStructuralPseudo4 = (node, ctx) => {
|
|
639
|
+
const el = asEl3(node);
|
|
640
|
+
if (!el) return false;
|
|
641
|
+
if (el.meta.targetedByStructuralPseudo) return true;
|
|
642
|
+
return ctx.selectors.targetedByStructuralPseudo(el.id);
|
|
643
|
+
};
|
|
644
|
+
var DISPLAY2 = "display";
|
|
645
|
+
var hasNonInlineDisplay = (node, ctx) => {
|
|
646
|
+
const el = asEl3(node);
|
|
647
|
+
if (!el) return false;
|
|
648
|
+
const sm = ctx.computedOf(el) ?? el.computed;
|
|
649
|
+
for (const block of sm.blocks.values()) {
|
|
650
|
+
const decl = block.decls.get(DISPLAY2);
|
|
651
|
+
if (decl && String(decl.value) !== "inline") return true;
|
|
652
|
+
}
|
|
653
|
+
return false;
|
|
654
|
+
};
|
|
655
|
+
var redundantInlineWrapper = definePattern({
|
|
656
|
+
name: "redundant-inline-wrapper",
|
|
657
|
+
category: "flatten/redundant-inline-wrapper",
|
|
658
|
+
safety: 2,
|
|
398
659
|
doc: {
|
|
399
|
-
title: "
|
|
400
|
-
summary: "
|
|
401
|
-
before:
|
|
402
|
-
after:
|
|
403
|
-
safetyRationale: "
|
|
660
|
+
title: "Flatten redundant inline wrapper",
|
|
661
|
+
summary: "An inline span with no own visual/box style, no attributes beyond an inert class, exactly one element child, and no opacity barriers is removed; its sole child is hoisted in its place.",
|
|
662
|
+
before: "<span><Child/></span>",
|
|
663
|
+
after: "<Child/>",
|
|
664
|
+
safetyRationale: "An empty inline box paints nothing and establishes no layout/paint/var context; with the inline default display and a single element child its removal changes no paint and no flow. The span carries no ref/handlers/dynamic-children/html/spread/component identity, owns no targetable attrs, and is not a combinator/structural-pseudo subject; inheritable styles are folded onto the child before removal."
|
|
404
665
|
},
|
|
405
666
|
match: {
|
|
667
|
+
tag: "span",
|
|
668
|
+
onlyChild: "element",
|
|
669
|
+
paintsNothing: true,
|
|
406
670
|
where: [
|
|
407
|
-
not(
|
|
408
|
-
not(
|
|
409
|
-
not(
|
|
410
|
-
not(hasDangerousHtml),
|
|
671
|
+
not(hasNonInlineDisplay),
|
|
672
|
+
not(establishesContext2),
|
|
673
|
+
not(hasOwnAttrs3),
|
|
411
674
|
not(hasDynamicClasses),
|
|
412
|
-
not(
|
|
413
|
-
not(
|
|
675
|
+
not(hasSpreadAttrs3),
|
|
676
|
+
not(isComponentNode3),
|
|
677
|
+
not(targetedByStructuralPseudo4)
|
|
414
678
|
]
|
|
415
679
|
},
|
|
416
|
-
rewrite: {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
680
|
+
rewrite: { flattenInto: "child" },
|
|
681
|
+
test: {
|
|
682
|
+
cases: [
|
|
683
|
+
{
|
|
684
|
+
// An empty inline span paints nothing and establishes no context → a provably-safe flatten:
|
|
685
|
+
// the span is removed and its sole child hoisted in place.
|
|
686
|
+
before: '<span><a className="text-blue-500">Link</a></span>',
|
|
687
|
+
after: '<a className="text-blue-500">Link</a>'
|
|
424
688
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
//
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
},
|
|
436
|
-
{
|
|
437
|
-
// Both tokens win a distinct property (no full override) → nothing to dedupe.
|
|
438
|
-
noMatch: '<p className="text-lg font-bold">Hi</p>'
|
|
439
|
-
}
|
|
440
|
-
]
|
|
689
|
+
],
|
|
690
|
+
noMatch: [
|
|
691
|
+
// A ref pins the span's element identity (a hard opacity barrier) → not a passthrough.
|
|
692
|
+
'<span ref={spanRef}><a className="text-blue-500">Link</a></span>',
|
|
693
|
+
// The span paints its own background (own visual style) → kept.
|
|
694
|
+
'<span className="bg-green-200"><a className="text-blue-500">Link</a></span>',
|
|
695
|
+
// Non-inline display (inline-block) participates in layout differently → kept.
|
|
696
|
+
'<span className="inline-block"><a className="text-blue-500">Link</a></span>'
|
|
697
|
+
]
|
|
698
|
+
}
|
|
441
699
|
});
|
|
442
700
|
|
|
443
|
-
// ../patterns/src/compress/
|
|
701
|
+
// ../patterns/src/library/compress/border-radius-shorthand.pattern.ts
|
|
444
702
|
init_esm_shims();
|
|
445
|
-
var
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
var
|
|
452
|
-
var
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
703
|
+
var CORNERS = [
|
|
704
|
+
"border-top-left-radius",
|
|
705
|
+
"border-top-right-radius",
|
|
706
|
+
"border-bottom-right-radius",
|
|
707
|
+
"border-bottom-left-radius"
|
|
708
|
+
];
|
|
709
|
+
var CORNER_SET = new Set(CORNERS);
|
|
710
|
+
var BASE_KEY = conditionKey(BASE_CONDITION);
|
|
711
|
+
var RADIUS = "border-radius";
|
|
712
|
+
var NON_COLLAPSIBLE_VALUES = /* @__PURE__ */ new Set([
|
|
713
|
+
"initial",
|
|
714
|
+
"inherit",
|
|
715
|
+
"unset",
|
|
716
|
+
"revert",
|
|
717
|
+
"revert-layer"
|
|
718
|
+
]);
|
|
719
|
+
function analyzeRadius(sm) {
|
|
720
|
+
const block = sm.blocks.get(BASE_KEY);
|
|
721
|
+
if (!block) return null;
|
|
722
|
+
const corners = [];
|
|
723
|
+
for (const corner of CORNERS) {
|
|
724
|
+
const decl = block.decls.get(corner);
|
|
725
|
+
if (!decl) return null;
|
|
726
|
+
corners.push(decl);
|
|
727
|
+
}
|
|
728
|
+
const important = corners[0].important;
|
|
729
|
+
if (!corners.every((d) => d.important === important)) return null;
|
|
730
|
+
const value = String(corners[0].value);
|
|
731
|
+
if (NON_COLLAPSIBLE_VALUES.has(value)) return null;
|
|
732
|
+
if (!corners.every((d) => String(d.value) === value)) return null;
|
|
733
|
+
const relative = corners.some((d) => d.relativeToParent);
|
|
734
|
+
return { value, important, relative };
|
|
461
735
|
}
|
|
462
|
-
function
|
|
736
|
+
function withFoldedRadius(sm, fold) {
|
|
463
737
|
const blocks = /* @__PURE__ */ new Map();
|
|
464
|
-
for (const [key, block] of
|
|
465
|
-
|
|
738
|
+
for (const [key, block] of sm.blocks) {
|
|
739
|
+
if (key !== BASE_KEY) {
|
|
740
|
+
blocks.set(key, block);
|
|
741
|
+
continue;
|
|
742
|
+
}
|
|
743
|
+
const decls = /* @__PURE__ */ new Map();
|
|
744
|
+
for (const [prop, decl] of block.decls) {
|
|
745
|
+
if (CORNER_SET.has(String(prop))) continue;
|
|
746
|
+
decls.set(prop, decl);
|
|
747
|
+
}
|
|
748
|
+
const shorthand = {
|
|
749
|
+
property: RADIUS,
|
|
750
|
+
value: fold.value,
|
|
751
|
+
important: fold.important,
|
|
752
|
+
relativeToParent: fold.relative,
|
|
753
|
+
inherited: false
|
|
754
|
+
// border-radius is never inherited
|
|
755
|
+
};
|
|
756
|
+
decls.set(shorthand.property, shorthand);
|
|
757
|
+
blocks.set(key, { condition: block.condition, decls });
|
|
758
|
+
}
|
|
759
|
+
return { blocks };
|
|
760
|
+
}
|
|
761
|
+
var borderRadiusShorthand = definePattern({
|
|
762
|
+
name: "border-radius-shorthand",
|
|
763
|
+
category: "compress/border-radius-shorthand",
|
|
764
|
+
safety: 1,
|
|
765
|
+
doc: {
|
|
766
|
+
title: "Collapse equal corner radii into border-radius",
|
|
767
|
+
summary: "An element whose four corner radii (border-*-radius longhands) are all equal is rewritten to the single Tailwind rounded-* utility (border-radius === the four equal corners).",
|
|
768
|
+
before: '<div class="rounded-tl-lg rounded-tr-lg rounded-br-lg rounded-bl-lg"/>',
|
|
769
|
+
after: '<div class="rounded-lg"/>',
|
|
770
|
+
safetyRationale: "`border-radius` is value-identical to four equal corner radii \u2014 a class-only change. It is safe even on an element with a ref, event handler, dynamic child, or dangerouslySetInnerHTML \u2014 a className rewrite touches none of them; only a dynamic/opaque class list or a combinator-subject class is excluded, so no behaviour or project selector is disturbed."
|
|
771
|
+
},
|
|
772
|
+
rewrite: {
|
|
773
|
+
rewriteClasses(computed2) {
|
|
774
|
+
const fold = analyzeRadius(computed2);
|
|
775
|
+
return fold ? withFoldedRadius(computed2, fold) : null;
|
|
776
|
+
}
|
|
777
|
+
},
|
|
778
|
+
test: {
|
|
779
|
+
cases: [
|
|
780
|
+
{
|
|
781
|
+
// The four equal corner longhands collapse to a `border-radius` decl at the IR level; the
|
|
782
|
+
// minimizing reverse-emit then picks the single shortest utility (`rounded-lg`) that reproduces
|
|
783
|
+
// it, replacing the four `rounded-{tl,tr,br,bl}-lg` tokens. `bg-red-200` is preserved.
|
|
784
|
+
before: '<div className="rounded-tl-lg rounded-tr-lg rounded-br-lg rounded-bl-lg bg-red-200">box</div>',
|
|
785
|
+
after: '<div className="bg-red-200 rounded-lg">box</div>'
|
|
786
|
+
}
|
|
787
|
+
],
|
|
788
|
+
// Corners differ (top corners vs bottom corners) → no all-equal collapse.
|
|
789
|
+
noMatch: ['<div className="rounded-t-lg rounded-b-sm bg-red-200">box</div>']
|
|
790
|
+
}
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
// ../patterns/src/library/compress/border-shorthand.pattern.ts
|
|
794
|
+
init_esm_shims();
|
|
795
|
+
var WIDTH_SIDES = [
|
|
796
|
+
"border-top-width",
|
|
797
|
+
"border-right-width",
|
|
798
|
+
"border-bottom-width",
|
|
799
|
+
"border-left-width"
|
|
800
|
+
];
|
|
801
|
+
var WIDTH_SIDE_SET = new Set(WIDTH_SIDES);
|
|
802
|
+
var BASE_KEY2 = conditionKey(BASE_CONDITION);
|
|
803
|
+
var BORDER_WIDTH = "border-width";
|
|
804
|
+
function analyzeWidth(sm) {
|
|
805
|
+
const block = sm.blocks.get(BASE_KEY2);
|
|
806
|
+
if (!block) return null;
|
|
807
|
+
const sides = [];
|
|
808
|
+
for (const side of WIDTH_SIDES) {
|
|
809
|
+
const decl = block.decls.get(side);
|
|
810
|
+
if (!decl) return null;
|
|
811
|
+
sides.push(decl);
|
|
812
|
+
}
|
|
813
|
+
const [top, right, bottom, left] = sides;
|
|
814
|
+
if (!(top.important === right.important && right.important === bottom.important && bottom.important === left.important)) {
|
|
815
|
+
return null;
|
|
816
|
+
}
|
|
817
|
+
const tv = String(top.value);
|
|
818
|
+
const rv = String(right.value);
|
|
819
|
+
const bv = String(bottom.value);
|
|
820
|
+
const lv = String(left.value);
|
|
821
|
+
if (tv !== bv || lv !== rv) return null;
|
|
822
|
+
const value = tv === lv ? tv : `${tv} ${lv}`;
|
|
823
|
+
const relative = sides.some((d) => d.relativeToParent);
|
|
824
|
+
return { value, important: top.important, relative };
|
|
825
|
+
}
|
|
826
|
+
function withFoldedWidth(sm, fold) {
|
|
827
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
828
|
+
for (const [key, block] of sm.blocks) {
|
|
829
|
+
if (key !== BASE_KEY2) {
|
|
830
|
+
blocks.set(key, block);
|
|
831
|
+
continue;
|
|
832
|
+
}
|
|
833
|
+
const decls = /* @__PURE__ */ new Map();
|
|
834
|
+
for (const [prop, decl] of block.decls) {
|
|
835
|
+
if (WIDTH_SIDE_SET.has(String(prop))) continue;
|
|
836
|
+
decls.set(prop, decl);
|
|
837
|
+
}
|
|
838
|
+
const shorthand = {
|
|
839
|
+
property: BORDER_WIDTH,
|
|
840
|
+
value: fold.value,
|
|
841
|
+
important: fold.important,
|
|
842
|
+
relativeToParent: fold.relative,
|
|
843
|
+
inherited: false
|
|
844
|
+
// border-width is never inherited
|
|
845
|
+
};
|
|
846
|
+
decls.set(shorthand.property, shorthand);
|
|
847
|
+
blocks.set(key, { condition: block.condition, decls });
|
|
848
|
+
}
|
|
849
|
+
return { blocks };
|
|
850
|
+
}
|
|
851
|
+
var borderShorthand = definePattern({
|
|
852
|
+
name: "border-shorthand",
|
|
853
|
+
category: "compress/border-shorthand",
|
|
854
|
+
safety: 1,
|
|
855
|
+
doc: {
|
|
856
|
+
title: "Collapse border-width longhands to shorthand",
|
|
857
|
+
summary: "Equal border width on all four sides (or matching x/y pairs) expressed as separate longhand declarations is collapsed to the shortest equivalent border-width shorthand (border-* / border-x-* border-y-*).",
|
|
858
|
+
before: '<div class="border-t-2 border-r-2 border-b-2 border-l-2"/>',
|
|
859
|
+
after: '<div class="border-2"/>',
|
|
860
|
+
safetyRationale: "A value-preserving re-serialization of the same computed border widths (style/color longhands untouched) \u2014 a class-only change. It is safe even on an element with a ref, event handler, dynamic child, or dangerouslySetInnerHTML \u2014 a className rewrite touches none of them; only a dynamic/opaque class list or a combinator-subject class is excluded, so no behaviour or project selector is disturbed."
|
|
861
|
+
},
|
|
862
|
+
rewrite: {
|
|
863
|
+
rewriteClasses(computed2) {
|
|
864
|
+
const fold = analyzeWidth(computed2);
|
|
865
|
+
return fold ? withFoldedWidth(computed2, fold) : null;
|
|
866
|
+
}
|
|
867
|
+
},
|
|
868
|
+
test: {
|
|
869
|
+
cases: [
|
|
870
|
+
{
|
|
871
|
+
// The four equal width longhands collapse to a `border-width` shorthand at the IR level, and the
|
|
872
|
+
// minimizing reverse-emit picks the single shortest utility (`border-2`) that reproduces it,
|
|
873
|
+
// replacing the four `border-{t,r,b,l}-2` tokens. `bg-red-200` is preserved.
|
|
874
|
+
before: '<div className="border-t-2 border-r-2 border-b-2 border-l-2 bg-red-200">box</div>',
|
|
875
|
+
after: '<div className="bg-red-200 border-2">box</div>'
|
|
876
|
+
}
|
|
877
|
+
],
|
|
878
|
+
// Asymmetric widths (top != bottom) cannot fold into a shorthand.
|
|
879
|
+
noMatch: ['<div className="border-t-2 border-r-4 border-b-8 border-l-4 bg-red-200">box</div>']
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
// ../patterns/src/library/compress/dedupe-classes.pattern.ts
|
|
884
|
+
init_esm_shims();
|
|
885
|
+
function findRedundantClasses(computed2) {
|
|
886
|
+
const winners = /* @__PURE__ */ new Set();
|
|
887
|
+
const shadowed = /* @__PURE__ */ new Set();
|
|
888
|
+
for (const block of computed2.blocks.values()) {
|
|
889
|
+
for (const decl of block.decls.values()) {
|
|
890
|
+
if (decl.origin && decl.origin.kind === "class") winners.add(decl.origin.className);
|
|
891
|
+
for (const o of decl.shadowed ?? []) {
|
|
892
|
+
if (o.kind === "class") shadowed.add(o.className);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
return { winners, shadowed };
|
|
897
|
+
}
|
|
898
|
+
var dedupeClasses = definePattern({
|
|
899
|
+
name: "dedupe-classes",
|
|
900
|
+
category: "compress/dedupe-classes",
|
|
901
|
+
safety: 1,
|
|
902
|
+
doc: {
|
|
903
|
+
title: "Dedupe fully-overridden class tokens",
|
|
904
|
+
summary: "Drops class tokens whose every declaration is overridden by a later token resolving to the same property; the surviving token set produces a byte-for-byte identical computed style.",
|
|
905
|
+
before: '<p class="text-sm text-lg" />',
|
|
906
|
+
after: '<p class="text-lg" />',
|
|
907
|
+
safetyRationale: "A fully-overridden token contributes nothing to the computed style in any condition, so removing it changes no pixels \u2014 a class-only change. It is safe even on an element with a ref, event handler, dynamic child, or dangerouslySetInnerHTML \u2014 a className rewrite touches none of them; only a dynamic/opaque class list or a combinator-subject class is excluded, so no behaviour or project selector is disturbed."
|
|
908
|
+
},
|
|
909
|
+
rewrite: {
|
|
910
|
+
dropClasses(computed2, ctx) {
|
|
911
|
+
const { winners, shadowed } = findRedundantClasses(computed2);
|
|
912
|
+
const drop = /* @__PURE__ */ new Set();
|
|
913
|
+
for (const cls of shadowed) {
|
|
914
|
+
if (winners.has(cls)) continue;
|
|
915
|
+
if (!ctx.resolver.selectorUsage(cls).droppable) continue;
|
|
916
|
+
drop.add(cls);
|
|
917
|
+
}
|
|
918
|
+
return drop;
|
|
919
|
+
}
|
|
920
|
+
},
|
|
921
|
+
test: {
|
|
922
|
+
cases: [
|
|
923
|
+
{
|
|
924
|
+
// `text-sm` is fully overridden by `text-lg` (both set font-size + line-height). The resolver
|
|
925
|
+
// records that shadowing in provenance and reports the Tailwind utility as droppable, so the
|
|
926
|
+
// pattern drops `text-sm`; the reverse-emit then re-derives the minimal set (`text-lg`).
|
|
927
|
+
before: '<p className="text-sm text-lg">Hi</p>',
|
|
928
|
+
after: '<p className="text-lg">Hi</p>'
|
|
929
|
+
}
|
|
930
|
+
],
|
|
931
|
+
// Both tokens win a distinct property (no full override) → nothing to dedupe.
|
|
932
|
+
noMatch: ['<p className="text-lg font-bold">Hi</p>']
|
|
933
|
+
}
|
|
934
|
+
});
|
|
935
|
+
|
|
936
|
+
// ../patterns/src/library/compress/gap-shorthand.pattern.ts
|
|
937
|
+
init_esm_shims();
|
|
938
|
+
var ROW_GAP = "row-gap";
|
|
939
|
+
var COLUMN_GAP = "column-gap";
|
|
940
|
+
var GAP = "gap";
|
|
941
|
+
var BASE_KEY3 = conditionKey(BASE_CONDITION);
|
|
942
|
+
function withGapShorthand(sm, gapDecl) {
|
|
943
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
944
|
+
for (const [key, block] of sm.blocks) {
|
|
945
|
+
if (key !== BASE_KEY3) {
|
|
946
|
+
blocks.set(key, block);
|
|
947
|
+
continue;
|
|
948
|
+
}
|
|
949
|
+
const decls = /* @__PURE__ */ new Map();
|
|
950
|
+
for (const [prop, decl] of block.decls) {
|
|
951
|
+
if (prop === ROW_GAP || prop === COLUMN_GAP) continue;
|
|
952
|
+
decls.set(prop, decl);
|
|
953
|
+
}
|
|
954
|
+
decls.set(gapDecl.property, gapDecl);
|
|
955
|
+
blocks.set(key, { condition: block.condition, decls });
|
|
956
|
+
}
|
|
957
|
+
return { blocks };
|
|
958
|
+
}
|
|
959
|
+
var gapShorthand = definePattern({
|
|
960
|
+
name: "gap-shorthand",
|
|
961
|
+
category: "compress/gap-shorthand",
|
|
962
|
+
safety: 1,
|
|
963
|
+
doc: {
|
|
964
|
+
title: "Collapse equal row/column gap into the `gap` shorthand",
|
|
965
|
+
summary: "An element whose computed row-gap and column-gap are equal has the two axis longhands collapsed into a single-value `gap` shorthand (Tailwind gap-x-* gap-y-* \u2192 gap-*).",
|
|
966
|
+
before: '<div style="row-gap:16px;column-gap:16px"/>',
|
|
967
|
+
after: '<div style="gap:16px"/>',
|
|
968
|
+
safetyRationale: "A single-value `gap` is value-identical to an equal row-gap+column-gap pair \u2014 a class-only change. It is safe even on an element with a ref, event handler, dynamic child, or dangerouslySetInnerHTML \u2014 a className rewrite touches none of them; only a dynamic/opaque class list or a combinator-subject class is excluded, so no behaviour or project selector is disturbed."
|
|
969
|
+
},
|
|
970
|
+
rewrite: {
|
|
971
|
+
rewriteClasses(computed2) {
|
|
972
|
+
const base = computed2.blocks.get(BASE_KEY3);
|
|
973
|
+
if (!base) return null;
|
|
974
|
+
const rowGap = base.decls.get(ROW_GAP);
|
|
975
|
+
const colGap = base.decls.get(COLUMN_GAP);
|
|
976
|
+
if (!rowGap || !colGap) return null;
|
|
977
|
+
if (rowGap.important !== colGap.important) return null;
|
|
978
|
+
if (rowGap.value !== colGap.value) return null;
|
|
979
|
+
const gapDecl = {
|
|
980
|
+
property: GAP,
|
|
981
|
+
value: rowGap.value,
|
|
982
|
+
important: rowGap.important,
|
|
983
|
+
relativeToParent: rowGap.relativeToParent || colGap.relativeToParent,
|
|
984
|
+
inherited: false
|
|
985
|
+
// gap is not an inherited property
|
|
986
|
+
};
|
|
987
|
+
return withGapShorthand(computed2, gapDecl);
|
|
988
|
+
}
|
|
989
|
+
},
|
|
990
|
+
test: {
|
|
991
|
+
cases: [
|
|
992
|
+
{
|
|
993
|
+
// Equal row/column gap collapse to a `gap` decl at the IR level; the minimizing reverse-emit
|
|
994
|
+
// re-expands `gap` to row-gap+column-gap and picks the single utility covering both (`gap-4`),
|
|
995
|
+
// replacing the `gap-x-4`+`gap-y-4` pair. `bg-red-200` is preserved.
|
|
996
|
+
before: '<div className="gap-x-4 gap-y-4 bg-red-200">box</div>',
|
|
997
|
+
after: '<div className="bg-red-200 gap-4">box</div>'
|
|
998
|
+
}
|
|
999
|
+
],
|
|
1000
|
+
// Unequal axes (row-gap != column-gap) have no single-value `gap` equivalent → not collapsed.
|
|
1001
|
+
noMatch: ['<div className="gap-x-2 gap-y-4 bg-red-200">box</div>']
|
|
1002
|
+
}
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
// ../patterns/src/library/compress/inset-shorthand.pattern.ts
|
|
1006
|
+
init_esm_shims();
|
|
1007
|
+
var TOP = "top";
|
|
1008
|
+
var RIGHT = "right";
|
|
1009
|
+
var BOTTOM = "bottom";
|
|
1010
|
+
var LEFT = "left";
|
|
1011
|
+
var INSET = "inset";
|
|
1012
|
+
var INSET_BLOCK = "inset-block";
|
|
1013
|
+
var INSET_INLINE = "inset-inline";
|
|
1014
|
+
function sameSide(a, b) {
|
|
1015
|
+
return a !== void 0 && b !== void 0 && a.value === b.value && a.important === b.important;
|
|
1016
|
+
}
|
|
1017
|
+
function asProperty(src, property) {
|
|
1018
|
+
return { ...src, property, inherited: normalizer.inherited.isInherited(property) };
|
|
1019
|
+
}
|
|
1020
|
+
function withBaseDecls(src, baseDecls) {
|
|
1021
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
1022
|
+
for (const [key, block] of src.blocks) {
|
|
1023
|
+
const decls = key === BASE_CONDITION_KEY ? baseDecls : new Map(block.decls);
|
|
466
1024
|
blocks.set(key, { condition: block.condition, decls });
|
|
467
1025
|
}
|
|
468
1026
|
return { blocks };
|
|
469
1027
|
}
|
|
470
|
-
var insetShorthand =
|
|
1028
|
+
var insetShorthand = definePattern({
|
|
471
1029
|
name: "inset-shorthand",
|
|
472
1030
|
category: "compress/inset-shorthand",
|
|
473
1031
|
safety: 2,
|
|
@@ -476,17 +1034,7 @@ var insetShorthand = pattern({
|
|
|
476
1034
|
summary: "top/right/bottom/left set to one value collapse to `inset`; a matching top/bottom or left/right pair collapses to `inset-block` / `inset-inline` (Tailwind inset-y / inset-x).",
|
|
477
1035
|
before: '<div style="top:10px;right:10px;bottom:10px;left:10px"/>',
|
|
478
1036
|
after: '<div style="inset:10px"/>',
|
|
479
|
-
safetyRationale: "Meaning-preserving shorthand compaction
|
|
480
|
-
},
|
|
481
|
-
match: {
|
|
482
|
-
where: [
|
|
483
|
-
not(hasRef),
|
|
484
|
-
not(hasEventHandlers),
|
|
485
|
-
not(hasDynamicChildren),
|
|
486
|
-
not(hasRawHtml),
|
|
487
|
-
not(hasDynamicClasses),
|
|
488
|
-
not(targetedByCombinator)
|
|
489
|
-
]
|
|
1037
|
+
safetyRationale: "Meaning-preserving inset shorthand compaction \u2014 a class-only change. It is safe even on an element with a ref, event handler, dynamic child, or dangerouslySetInnerHTML \u2014 a className rewrite touches none of them; only a dynamic/opaque class list or a combinator-subject class is excluded, so no behaviour or project selector is disturbed."
|
|
490
1038
|
},
|
|
491
1039
|
rewrite: {
|
|
492
1040
|
rewriteClasses(computed2) {
|
|
@@ -522,22 +1070,22 @@ var insetShorthand = pattern({
|
|
|
522
1070
|
return withBaseDecls(computed2, next);
|
|
523
1071
|
}
|
|
524
1072
|
},
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
1073
|
+
test: {
|
|
1074
|
+
cases: [
|
|
1075
|
+
{
|
|
1076
|
+
// The four equal inset longhands collapse to an `inset` shorthand at the IR level; the
|
|
1077
|
+
// minimizing reverse-emit expands it back to top/right/bottom/left and picks the single utility
|
|
1078
|
+
// covering all four (`inset-0`), replacing the four physical-side tokens. `bg-red-200` survives.
|
|
1079
|
+
before: '<div className="top-0 right-0 bottom-0 left-0 bg-red-200">box</div>',
|
|
1080
|
+
after: '<div className="bg-red-200 inset-0">box</div>'
|
|
1081
|
+
}
|
|
1082
|
+
],
|
|
1083
|
+
// No matching inset pair (all four distinct) → nothing collapses.
|
|
1084
|
+
noMatch: ['<div className="top-0 right-1 bottom-2 left-3 bg-red-200">box</div>']
|
|
1085
|
+
}
|
|
538
1086
|
});
|
|
539
1087
|
|
|
540
|
-
// ../patterns/src/compress/margin-shorthand.pattern.ts
|
|
1088
|
+
// ../patterns/src/library/compress/margin-shorthand.pattern.ts
|
|
541
1089
|
init_esm_shims();
|
|
542
1090
|
var MARGIN_SIDES = [
|
|
543
1091
|
"margin-top",
|
|
@@ -546,12 +1094,7 @@ var MARGIN_SIDES = [
|
|
|
546
1094
|
"margin-left"
|
|
547
1095
|
];
|
|
548
1096
|
var MARGIN_SIDE_SET = new Set(MARGIN_SIDES);
|
|
549
|
-
var
|
|
550
|
-
function asElement(node) {
|
|
551
|
-
const n = node;
|
|
552
|
-
return n.kind === "element" ? n : null;
|
|
553
|
-
}
|
|
554
|
-
var hasDangerousHtml2 = (node) => asElement(node)?.meta.hasDangerousHtml ?? false;
|
|
1097
|
+
var BASE_KEY4 = conditionKey(BASE_CONDITION);
|
|
555
1098
|
function collapseMarginValue(top, right, bottom, left) {
|
|
556
1099
|
if (right === left) {
|
|
557
1100
|
if (top === bottom) {
|
|
@@ -564,7 +1107,7 @@ function collapseMarginValue(top, right, bottom, left) {
|
|
|
564
1107
|
function withFoldedMargin(sm, marginDecl) {
|
|
565
1108
|
const blocks = /* @__PURE__ */ new Map();
|
|
566
1109
|
for (const [key, block] of sm.blocks) {
|
|
567
|
-
if (key !==
|
|
1110
|
+
if (key !== BASE_KEY4) {
|
|
568
1111
|
blocks.set(key, block);
|
|
569
1112
|
continue;
|
|
570
1113
|
}
|
|
@@ -577,7 +1120,7 @@ function withFoldedMargin(sm, marginDecl) {
|
|
|
577
1120
|
}
|
|
578
1121
|
return { blocks };
|
|
579
1122
|
}
|
|
580
|
-
var marginShorthand =
|
|
1123
|
+
var marginShorthand = definePattern({
|
|
581
1124
|
name: "margin-shorthand",
|
|
582
1125
|
category: "compress/margin-shorthand",
|
|
583
1126
|
safety: 2,
|
|
@@ -586,21 +1129,11 @@ var marginShorthand = pattern({
|
|
|
586
1129
|
summary: "An element with margin-top/right/bottom/left all set has them collapsed into the shortest legal `margin` shorthand (the m / mx / my forms); meaning is preserved, declaration count drops.",
|
|
587
1130
|
before: '<div style="margin-top:8px;margin-right:8px;margin-bottom:8px;margin-left:8px"/>',
|
|
588
1131
|
after: '<div style="margin:8px"/>',
|
|
589
|
-
safetyRationale: "
|
|
590
|
-
},
|
|
591
|
-
match: {
|
|
592
|
-
where: [
|
|
593
|
-
not(hasRef),
|
|
594
|
-
not(hasEventHandlers),
|
|
595
|
-
not(hasDynamicChildren),
|
|
596
|
-
not(hasDynamicClasses),
|
|
597
|
-
not(hasDangerousHtml2),
|
|
598
|
-
not(targetedByCombinator)
|
|
599
|
-
]
|
|
1132
|
+
safetyRationale: "A pure representation change of the same computed margins (no pixels move) \u2014 a class-only change. It is safe even on an element with a ref, event handler, dynamic child, or dangerouslySetInnerHTML \u2014 a className rewrite touches none of them; only a dynamic/opaque class list or a combinator-subject class is excluded, so no behaviour or project selector is disturbed."
|
|
600
1133
|
},
|
|
601
1134
|
rewrite: {
|
|
602
1135
|
rewriteClasses(computed2) {
|
|
603
|
-
const base = computed2.blocks.get(
|
|
1136
|
+
const base = computed2.blocks.get(BASE_KEY4);
|
|
604
1137
|
if (!base) return null;
|
|
605
1138
|
const sides = MARGIN_SIDES.map((p) => base.decls.get(p));
|
|
606
1139
|
if (sides.some((d) => d === void 0)) return null;
|
|
@@ -623,22 +1156,169 @@ var marginShorthand = pattern({
|
|
|
623
1156
|
return withFoldedMargin(computed2, marginDecl);
|
|
624
1157
|
}
|
|
625
1158
|
},
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
1159
|
+
test: {
|
|
1160
|
+
cases: [
|
|
1161
|
+
{
|
|
1162
|
+
// The four equal margin longhands collapse to a `margin` shorthand at the IR level, and the
|
|
1163
|
+
// minimizing reverse-emit picks the single shortest utility (`m-2`) reproducing it, replacing
|
|
1164
|
+
// the four `m{t,r,b,l}-2` tokens. `bg-red-200` is preserved.
|
|
1165
|
+
before: '<div className="mt-2 mr-2 mb-2 ml-2 bg-red-200">box</div>',
|
|
1166
|
+
after: '<div className="bg-red-200 m-2">box</div>'
|
|
1167
|
+
}
|
|
1168
|
+
],
|
|
1169
|
+
// Only two margin sides set → the four-longhand `margin` collapse does not apply.
|
|
1170
|
+
noMatch: ['<div className="mt-2 mb-2 bg-red-200">box</div>']
|
|
1171
|
+
}
|
|
1172
|
+
});
|
|
1173
|
+
|
|
1174
|
+
// ../patterns/src/library/compress/overflow-shorthand.pattern.ts
|
|
1175
|
+
init_esm_shims();
|
|
1176
|
+
var OVERFLOW_X = "overflow-x";
|
|
1177
|
+
var OVERFLOW_Y = "overflow-y";
|
|
1178
|
+
var OVERFLOW = "overflow";
|
|
1179
|
+
var BASE_KEY5 = conditionKey(BASE_CONDITION);
|
|
1180
|
+
function withOverflowShorthand(sm, overflowDecl) {
|
|
1181
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
1182
|
+
for (const [key, block] of sm.blocks) {
|
|
1183
|
+
if (key !== BASE_KEY5) {
|
|
1184
|
+
blocks.set(key, block);
|
|
1185
|
+
continue;
|
|
1186
|
+
}
|
|
1187
|
+
const decls = /* @__PURE__ */ new Map();
|
|
1188
|
+
for (const [prop, decl] of block.decls) {
|
|
1189
|
+
if (prop === OVERFLOW_X || prop === OVERFLOW_Y) continue;
|
|
1190
|
+
decls.set(prop, decl);
|
|
1191
|
+
}
|
|
1192
|
+
decls.set(overflowDecl.property, overflowDecl);
|
|
1193
|
+
blocks.set(key, { condition: block.condition, decls });
|
|
1194
|
+
}
|
|
1195
|
+
return { blocks };
|
|
1196
|
+
}
|
|
1197
|
+
var overflowShorthand = definePattern({
|
|
1198
|
+
name: "overflow-shorthand",
|
|
1199
|
+
category: "compress/overflow-shorthand",
|
|
1200
|
+
safety: 1,
|
|
1201
|
+
doc: {
|
|
1202
|
+
title: "Collapse equal overflow axes into the `overflow` shorthand",
|
|
1203
|
+
summary: "An element whose computed overflow-x and overflow-y are equal has the two axis longhands collapsed into a single `overflow` shorthand (Tailwind overflow-x-* overflow-y-* \u2192 overflow-*).",
|
|
1204
|
+
before: '<div style="overflow-x:auto;overflow-y:auto"/>',
|
|
1205
|
+
after: '<div style="overflow:auto"/>',
|
|
1206
|
+
safetyRationale: "A single-keyword `overflow` is value-identical to equal overflow-x+overflow-y \u2014 a class-only change. It is safe even on an element with a ref, event handler, dynamic child, or dangerouslySetInnerHTML \u2014 a className rewrite touches none of them; only a dynamic/opaque class list or a combinator-subject class is excluded, so no behaviour or project selector is disturbed."
|
|
1207
|
+
},
|
|
1208
|
+
rewrite: {
|
|
1209
|
+
rewriteClasses(computed2) {
|
|
1210
|
+
const base = computed2.blocks.get(BASE_KEY5);
|
|
1211
|
+
if (!base) return null;
|
|
1212
|
+
const overflowX = base.decls.get(OVERFLOW_X);
|
|
1213
|
+
const overflowY = base.decls.get(OVERFLOW_Y);
|
|
1214
|
+
if (!overflowX || !overflowY) return null;
|
|
1215
|
+
if (overflowX.important !== overflowY.important) return null;
|
|
1216
|
+
if (overflowX.value !== overflowY.value) return null;
|
|
1217
|
+
const overflowDecl = {
|
|
1218
|
+
property: OVERFLOW,
|
|
1219
|
+
value: overflowX.value,
|
|
1220
|
+
important: overflowX.important,
|
|
1221
|
+
relativeToParent: overflowX.relativeToParent || overflowY.relativeToParent,
|
|
1222
|
+
inherited: false
|
|
1223
|
+
// overflow is not an inherited property
|
|
1224
|
+
};
|
|
1225
|
+
return withOverflowShorthand(computed2, overflowDecl);
|
|
1226
|
+
}
|
|
1227
|
+
},
|
|
1228
|
+
test: {
|
|
1229
|
+
cases: [
|
|
1230
|
+
{
|
|
1231
|
+
// Equal overflow axes collapse to an `overflow` decl at the IR level; the minimizing
|
|
1232
|
+
// reverse-emit picks the single utility covering both (`overflow-auto`), replacing the
|
|
1233
|
+
// `overflow-x-auto`+`overflow-y-auto` pair. `bg-red-200` is preserved.
|
|
1234
|
+
before: '<div className="overflow-x-auto overflow-y-auto bg-red-200">box</div>',
|
|
1235
|
+
after: '<div className="bg-red-200 overflow-auto">box</div>'
|
|
1236
|
+
}
|
|
1237
|
+
],
|
|
1238
|
+
// Mismatched axes (overflow-x != overflow-y) have no single-keyword equivalent → not collapsed.
|
|
1239
|
+
noMatch: ['<div className="overflow-x-auto overflow-y-hidden bg-red-200">box</div>']
|
|
1240
|
+
}
|
|
1241
|
+
});
|
|
1242
|
+
|
|
1243
|
+
// ../patterns/src/library/compress/overscroll-behavior-shorthand.pattern.ts
|
|
1244
|
+
init_esm_shims();
|
|
1245
|
+
var OVERSCROLL_X = "overscroll-behavior-x";
|
|
1246
|
+
var OVERSCROLL_Y = "overscroll-behavior-y";
|
|
1247
|
+
var OVERSCROLL = "overscroll-behavior";
|
|
1248
|
+
var BASE_KEY6 = conditionKey(BASE_CONDITION);
|
|
1249
|
+
var NON_COLLAPSIBLE_VALUES2 = /* @__PURE__ */ new Set([
|
|
1250
|
+
"initial",
|
|
1251
|
+
"inherit",
|
|
1252
|
+
"unset",
|
|
1253
|
+
"revert",
|
|
1254
|
+
"revert-layer"
|
|
1255
|
+
]);
|
|
1256
|
+
function withOverscrollShorthand(sm, shorthand) {
|
|
1257
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
1258
|
+
for (const [key, block] of sm.blocks) {
|
|
1259
|
+
if (key !== BASE_KEY6) {
|
|
1260
|
+
blocks.set(key, block);
|
|
1261
|
+
continue;
|
|
1262
|
+
}
|
|
1263
|
+
const decls = /* @__PURE__ */ new Map();
|
|
1264
|
+
for (const [prop, decl] of block.decls) {
|
|
1265
|
+
if (prop === OVERSCROLL_X || prop === OVERSCROLL_Y) continue;
|
|
1266
|
+
decls.set(prop, decl);
|
|
1267
|
+
}
|
|
1268
|
+
decls.set(shorthand.property, shorthand);
|
|
1269
|
+
blocks.set(key, { condition: block.condition, decls });
|
|
1270
|
+
}
|
|
1271
|
+
return { blocks };
|
|
1272
|
+
}
|
|
1273
|
+
var overscrollBehaviorShorthand = definePattern({
|
|
1274
|
+
name: "overscroll-behavior-shorthand",
|
|
1275
|
+
category: "compress/overscroll-behavior-shorthand",
|
|
1276
|
+
safety: 1,
|
|
1277
|
+
doc: {
|
|
1278
|
+
title: "Collapse equal overscroll-behavior axes into overscroll-behavior",
|
|
1279
|
+
summary: "An element whose computed overscroll-behavior-x and overscroll-behavior-y are equal has the two axis longhands collapsed into a single `overscroll-behavior` shorthand (Tailwind overscroll-x-* overscroll-y-* \u2192 overscroll-*).",
|
|
1280
|
+
before: '<div style="overscroll-behavior-x:contain;overscroll-behavior-y:contain"/>',
|
|
1281
|
+
after: '<div class="overscroll-contain"/>',
|
|
1282
|
+
safetyRationale: "`overscroll-behavior` is value-identical to an equal x+y axis pair \u2014 a class-only change. It is safe even on an element with a ref, event handler, dynamic child, or dangerouslySetInnerHTML \u2014 a className rewrite touches none of them; only a dynamic/opaque class list or a combinator-subject class is excluded, so no behaviour or project selector is disturbed."
|
|
1283
|
+
},
|
|
1284
|
+
rewrite: {
|
|
1285
|
+
rewriteClasses(computed2) {
|
|
1286
|
+
const base = computed2.blocks.get(BASE_KEY6);
|
|
1287
|
+
if (!base) return null;
|
|
1288
|
+
const x = base.decls.get(OVERSCROLL_X);
|
|
1289
|
+
const y = base.decls.get(OVERSCROLL_Y);
|
|
1290
|
+
if (!x || !y) return null;
|
|
1291
|
+
if (x.important !== y.important) return null;
|
|
1292
|
+
const value = String(x.value);
|
|
1293
|
+
if (NON_COLLAPSIBLE_VALUES2.has(value)) return null;
|
|
1294
|
+
if (value !== String(y.value)) return null;
|
|
1295
|
+
const shorthand = {
|
|
1296
|
+
property: OVERSCROLL,
|
|
1297
|
+
value: x.value,
|
|
1298
|
+
important: x.important,
|
|
1299
|
+
relativeToParent: x.relativeToParent || y.relativeToParent,
|
|
1300
|
+
inherited: false
|
|
1301
|
+
// overscroll-behavior is not an inherited property
|
|
1302
|
+
};
|
|
1303
|
+
return withOverscrollShorthand(computed2, shorthand);
|
|
637
1304
|
}
|
|
638
|
-
|
|
1305
|
+
},
|
|
1306
|
+
test: {
|
|
1307
|
+
cases: [
|
|
1308
|
+
{
|
|
1309
|
+
// Equal x/y axes collapse to an `overscroll-behavior` decl at the IR level; the minimizing
|
|
1310
|
+
// reverse-emit picks the single utility covering both (`overscroll-contain`), replacing the
|
|
1311
|
+
// `overscroll-x-contain`+`overscroll-y-contain` pair. `bg-red-200` is preserved.
|
|
1312
|
+
before: '<div className="overscroll-x-contain overscroll-y-contain bg-red-200">box</div>',
|
|
1313
|
+
after: '<div className="bg-red-200 overscroll-contain">box</div>'
|
|
1314
|
+
}
|
|
1315
|
+
],
|
|
1316
|
+
// Axes differ (x != y) → no equal-axis collapse.
|
|
1317
|
+
noMatch: ['<div className="overscroll-x-contain overscroll-y-auto bg-red-200">box</div>']
|
|
1318
|
+
}
|
|
639
1319
|
});
|
|
640
1320
|
|
|
641
|
-
// ../patterns/src/compress/padding-shorthand.pattern.ts
|
|
1321
|
+
// ../patterns/src/library/compress/padding-shorthand.pattern.ts
|
|
642
1322
|
init_esm_shims();
|
|
643
1323
|
var PADDING_SIDES = [
|
|
644
1324
|
"padding-top",
|
|
@@ -647,9 +1327,9 @@ var PADDING_SIDES = [
|
|
|
647
1327
|
"padding-left"
|
|
648
1328
|
];
|
|
649
1329
|
var PADDING_SIDE_SET = new Set(PADDING_SIDES);
|
|
650
|
-
var
|
|
1330
|
+
var BASE_KEY7 = conditionKey(BASE_CONDITION);
|
|
651
1331
|
function analyzePadding(sm) {
|
|
652
|
-
const block = sm.blocks.get(
|
|
1332
|
+
const block = sm.blocks.get(BASE_KEY7);
|
|
653
1333
|
if (!block) return null;
|
|
654
1334
|
const sides = [];
|
|
655
1335
|
for (const side of PADDING_SIDES) {
|
|
@@ -670,16 +1350,10 @@ function analyzePadding(sm) {
|
|
|
670
1350
|
const relative = sides.some((d) => d.relativeToParent);
|
|
671
1351
|
return { value, important: top.important, relative };
|
|
672
1352
|
}
|
|
673
|
-
var isInert = (node) => {
|
|
674
|
-
const n = node;
|
|
675
|
-
if (n.kind !== "element") return false;
|
|
676
|
-
const el = n;
|
|
677
|
-
return !el.meta.hasDangerousHtml && !el.meta.hasSpreadAttrs && !el.isComponent;
|
|
678
|
-
};
|
|
679
1353
|
function withFoldedPadding(sm, fold) {
|
|
680
1354
|
const blocks = /* @__PURE__ */ new Map();
|
|
681
1355
|
for (const [key, block] of sm.blocks) {
|
|
682
|
-
if (key !==
|
|
1356
|
+
if (key !== BASE_KEY7) {
|
|
683
1357
|
blocks.set(key, block);
|
|
684
1358
|
continue;
|
|
685
1359
|
}
|
|
@@ -701,7 +1375,7 @@ function withFoldedPadding(sm, fold) {
|
|
|
701
1375
|
}
|
|
702
1376
|
return { blocks };
|
|
703
1377
|
}
|
|
704
|
-
var paddingShorthand =
|
|
1378
|
+
var paddingShorthand = definePattern({
|
|
705
1379
|
name: "padding-shorthand",
|
|
706
1380
|
category: "compress/padding-shorthand",
|
|
707
1381
|
safety: 1,
|
|
@@ -710,17 +1384,7 @@ var paddingShorthand = pattern({
|
|
|
710
1384
|
summary: "Equal padding on all four sides (or matching x/y pairs) expressed as separate longhand declarations is collapsed to the shortest equivalent padding shorthand (p-* / px-* py-*).",
|
|
711
1385
|
before: '<div class="pt-4 pr-4 pb-4 pl-4"/>',
|
|
712
1386
|
after: '<div class="p-4"/>',
|
|
713
|
-
safetyRationale: "A value-preserving re-serialization of the same computed
|
|
714
|
-
},
|
|
715
|
-
match: {
|
|
716
|
-
where: [
|
|
717
|
-
not(hasRef),
|
|
718
|
-
not(hasEventHandlers),
|
|
719
|
-
not(hasDynamicChildren),
|
|
720
|
-
not(hasDynamicClasses),
|
|
721
|
-
not(targetedByCombinator),
|
|
722
|
-
isInert
|
|
723
|
-
]
|
|
1387
|
+
safetyRationale: "A value-preserving re-serialization of the same computed padding on the same node \u2014 a class-only change. It is safe even on an element with a ref, event handler, dynamic child, or dangerouslySetInnerHTML \u2014 a className rewrite touches none of them; only a dynamic/opaque class list or a combinator-subject class is excluded, so no behaviour or project selector is disturbed."
|
|
724
1388
|
},
|
|
725
1389
|
rewrite: {
|
|
726
1390
|
rewriteClasses(computed2) {
|
|
@@ -728,35 +1392,304 @@ var paddingShorthand = pattern({
|
|
|
728
1392
|
return fold ? withFoldedPadding(computed2, fold) : null;
|
|
729
1393
|
}
|
|
730
1394
|
},
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
1395
|
+
test: {
|
|
1396
|
+
cases: [
|
|
1397
|
+
{
|
|
1398
|
+
// The four equal padding longhands collapse to a `padding` shorthand at the IR level, and the
|
|
1399
|
+
// minimizing reverse-emit picks the single shortest utility (`p-4`) that reproduces it,
|
|
1400
|
+
// replacing the four `p{t,r,b,l}-4` tokens. `bg-red-200` is preserved (its order is stable).
|
|
1401
|
+
before: '<div className="pt-4 pr-4 pb-4 pl-4 bg-red-200">box</div>',
|
|
1402
|
+
after: '<div className="bg-red-200 p-4">box</div>'
|
|
1403
|
+
},
|
|
1404
|
+
{
|
|
1405
|
+
// A dynamic `{x}` child no longer blocks compress: only the element's OWN class tokens are
|
|
1406
|
+
// rewritten (px-4 py-4 → p-4); the dynamic child is untouched by a class-only change. This is
|
|
1407
|
+
// the real-app common case (most elements have dynamic content).
|
|
1408
|
+
before: '<div className="px-4 py-4">{x}</div>',
|
|
1409
|
+
after: '<div className="p-4">{x}</div>'
|
|
1410
|
+
}
|
|
1411
|
+
],
|
|
1412
|
+
// Asymmetric padding (top != bottom) cannot fold into a shorthand → left unchanged.
|
|
1413
|
+
noMatch: ['<div className="pt-2 pr-4 pb-8 pl-4 bg-red-200">box</div>']
|
|
1414
|
+
}
|
|
1415
|
+
});
|
|
1416
|
+
|
|
1417
|
+
// ../patterns/src/library/compress/place-shorthand.pattern.ts
|
|
1418
|
+
init_esm_shims();
|
|
1419
|
+
var ALIGN_ITEMS = "align-items";
|
|
1420
|
+
var JUSTIFY_ITEMS = "justify-items";
|
|
1421
|
+
var PLACE_ITEMS = "place-items";
|
|
1422
|
+
var ALIGN_CONTENT = "align-content";
|
|
1423
|
+
var JUSTIFY_CONTENT = "justify-content";
|
|
1424
|
+
var PLACE_CONTENT = "place-content";
|
|
1425
|
+
var BASE_KEY8 = conditionKey(BASE_CONDITION);
|
|
1426
|
+
function samePair(a, b) {
|
|
1427
|
+
return a !== void 0 && b !== void 0 && a.value === b.value && a.important === b.important;
|
|
1428
|
+
}
|
|
1429
|
+
function placeDecl(property, align) {
|
|
1430
|
+
return {
|
|
1431
|
+
property,
|
|
1432
|
+
value: align.value,
|
|
1433
|
+
important: align.important,
|
|
1434
|
+
relativeToParent: false,
|
|
1435
|
+
// alignment keywords (center/start/stretch/…) are not length-relative
|
|
1436
|
+
inherited: false
|
|
1437
|
+
// none of the place-* alignment properties are inherited
|
|
1438
|
+
};
|
|
1439
|
+
}
|
|
1440
|
+
function withBaseDecls2(sm, baseDecls) {
|
|
1441
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
1442
|
+
for (const [key, block] of sm.blocks) {
|
|
1443
|
+
const decls = key === BASE_KEY8 ? new Map(baseDecls) : block.decls;
|
|
1444
|
+
blocks.set(key, { condition: block.condition, decls });
|
|
1445
|
+
}
|
|
1446
|
+
return { blocks };
|
|
1447
|
+
}
|
|
1448
|
+
var placeShorthand = definePattern({
|
|
1449
|
+
name: "place-shorthand",
|
|
1450
|
+
category: "compress/place-shorthand",
|
|
1451
|
+
safety: 1,
|
|
1452
|
+
doc: {
|
|
1453
|
+
title: "Collapse matching alignment pairs into `place-*` shorthands",
|
|
1454
|
+
summary: "When align-items equals justify-items they collapse to `place-items`; when align-content equals justify-content they collapse to `place-content`. The two collapses are independent.",
|
|
1455
|
+
before: '<div style="align-items:center;justify-items:center"/>',
|
|
1456
|
+
after: '<div style="place-items:center"/>',
|
|
1457
|
+
safetyRationale: "A `place-*` shorthand is value-identical to its equal align/justify pair \u2014 a class-only change. It is safe even on an element with a ref, event handler, dynamic child, or dangerouslySetInnerHTML \u2014 a className rewrite touches none of them; only a dynamic/opaque class list or a combinator-subject class is excluded, so no behaviour or project selector is disturbed."
|
|
1458
|
+
},
|
|
1459
|
+
rewrite: {
|
|
1460
|
+
rewriteClasses(computed2) {
|
|
1461
|
+
const base = computed2.blocks.get(BASE_KEY8);
|
|
1462
|
+
if (!base) return null;
|
|
1463
|
+
const alignItems = base.decls.get(ALIGN_ITEMS);
|
|
1464
|
+
const justifyItems = base.decls.get(JUSTIFY_ITEMS);
|
|
1465
|
+
const alignContent = base.decls.get(ALIGN_CONTENT);
|
|
1466
|
+
const justifyContent = base.decls.get(JUSTIFY_CONTENT);
|
|
1467
|
+
const next = new Map(base.decls);
|
|
1468
|
+
let collapsed = false;
|
|
1469
|
+
if (samePair(alignItems, justifyItems)) {
|
|
1470
|
+
next.delete(ALIGN_ITEMS);
|
|
1471
|
+
next.delete(JUSTIFY_ITEMS);
|
|
1472
|
+
next.set(PLACE_ITEMS, placeDecl(PLACE_ITEMS, alignItems));
|
|
1473
|
+
collapsed = true;
|
|
1474
|
+
}
|
|
1475
|
+
if (samePair(alignContent, justifyContent)) {
|
|
1476
|
+
next.delete(ALIGN_CONTENT);
|
|
1477
|
+
next.delete(JUSTIFY_CONTENT);
|
|
1478
|
+
next.set(PLACE_CONTENT, placeDecl(PLACE_CONTENT, alignContent));
|
|
1479
|
+
collapsed = true;
|
|
1480
|
+
}
|
|
1481
|
+
if (!collapsed) return null;
|
|
1482
|
+
return withBaseDecls2(computed2, next);
|
|
1483
|
+
}
|
|
1484
|
+
},
|
|
1485
|
+
test: {
|
|
1486
|
+
cases: [
|
|
1487
|
+
{
|
|
1488
|
+
// The matching items pair collapses to a `place-items` decl at the IR level; the minimizing
|
|
1489
|
+
// reverse-emit picks the single utility covering both (`place-items-center`), replacing the
|
|
1490
|
+
// `items-center`+`justify-items-center` pair. `bg-red-200` is preserved.
|
|
1491
|
+
before: '<div className="items-center justify-items-center bg-red-200">box</div>',
|
|
1492
|
+
after: '<div className="bg-red-200 place-items-center">box</div>'
|
|
1493
|
+
}
|
|
1494
|
+
],
|
|
1495
|
+
// Mismatched alignment (align-items != justify-items, no content pair) → nothing collapses.
|
|
1496
|
+
noMatch: ['<div className="items-center justify-items-start bg-red-200">box</div>']
|
|
1497
|
+
}
|
|
1498
|
+
});
|
|
1499
|
+
|
|
1500
|
+
// ../patterns/src/library/compress/scroll-margin-shorthand.pattern.ts
|
|
1501
|
+
init_esm_shims();
|
|
1502
|
+
var SCROLL_MARGIN_SIDES = [
|
|
1503
|
+
"scroll-margin-top",
|
|
1504
|
+
"scroll-margin-right",
|
|
1505
|
+
"scroll-margin-bottom",
|
|
1506
|
+
"scroll-margin-left"
|
|
1507
|
+
];
|
|
1508
|
+
var SIDE_SET = new Set(SCROLL_MARGIN_SIDES);
|
|
1509
|
+
var BASE_KEY9 = conditionKey(BASE_CONDITION);
|
|
1510
|
+
var SCROLL_MARGIN = "scroll-margin";
|
|
1511
|
+
var NON_COLLAPSIBLE_VALUES3 = /* @__PURE__ */ new Set([
|
|
1512
|
+
"initial",
|
|
1513
|
+
"inherit",
|
|
1514
|
+
"unset",
|
|
1515
|
+
"revert",
|
|
1516
|
+
"revert-layer"
|
|
1517
|
+
]);
|
|
1518
|
+
function analyzeScrollMargin(sm) {
|
|
1519
|
+
const block = sm.blocks.get(BASE_KEY9);
|
|
1520
|
+
if (!block) return null;
|
|
1521
|
+
const sides = [];
|
|
1522
|
+
for (const side of SCROLL_MARGIN_SIDES) {
|
|
1523
|
+
const decl = block.decls.get(side);
|
|
1524
|
+
if (!decl) return null;
|
|
1525
|
+
sides.push(decl);
|
|
1526
|
+
}
|
|
1527
|
+
const important = sides[0].important;
|
|
1528
|
+
if (!sides.every((d) => d.important === important)) return null;
|
|
1529
|
+
const value = String(sides[0].value);
|
|
1530
|
+
if (NON_COLLAPSIBLE_VALUES3.has(value)) return null;
|
|
1531
|
+
if (!sides.every((d) => String(d.value) === value)) return null;
|
|
1532
|
+
const relative = sides.some((d) => d.relativeToParent);
|
|
1533
|
+
return { value, important, relative };
|
|
1534
|
+
}
|
|
1535
|
+
function withFoldedScrollMargin(sm, fold) {
|
|
1536
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
1537
|
+
for (const [key, block] of sm.blocks) {
|
|
1538
|
+
if (key !== BASE_KEY9) {
|
|
1539
|
+
blocks.set(key, block);
|
|
1540
|
+
continue;
|
|
1541
|
+
}
|
|
1542
|
+
const decls = /* @__PURE__ */ new Map();
|
|
1543
|
+
for (const [prop, decl] of block.decls) {
|
|
1544
|
+
if (SIDE_SET.has(String(prop))) continue;
|
|
1545
|
+
decls.set(prop, decl);
|
|
1546
|
+
}
|
|
1547
|
+
const shorthand = {
|
|
1548
|
+
property: SCROLL_MARGIN,
|
|
1549
|
+
value: fold.value,
|
|
1550
|
+
important: fold.important,
|
|
1551
|
+
relativeToParent: fold.relative,
|
|
1552
|
+
inherited: false
|
|
1553
|
+
// scroll-margin is never inherited
|
|
1554
|
+
};
|
|
1555
|
+
decls.set(shorthand.property, shorthand);
|
|
1556
|
+
blocks.set(key, { condition: block.condition, decls });
|
|
1557
|
+
}
|
|
1558
|
+
return { blocks };
|
|
1559
|
+
}
|
|
1560
|
+
var scrollMarginShorthand = definePattern({
|
|
1561
|
+
name: "scroll-margin-shorthand",
|
|
1562
|
+
category: "compress/scroll-margin-shorthand",
|
|
1563
|
+
safety: 1,
|
|
1564
|
+
doc: {
|
|
1565
|
+
title: "Collapse equal scroll-margin sides into scroll-margin",
|
|
1566
|
+
summary: "An element whose four scroll-margin sides are all equal is rewritten to the single Tailwind scroll-m-* utility (scroll-margin === the four equal sides).",
|
|
1567
|
+
before: '<div class="scroll-mt-4 scroll-mr-4 scroll-mb-4 scroll-ml-4"/>',
|
|
1568
|
+
after: '<div class="scroll-m-4"/>',
|
|
1569
|
+
safetyRationale: "`scroll-margin` is value-identical to four equal scroll-margin sides \u2014 a class-only change. It is safe even on an element with a ref, event handler, dynamic child, or dangerouslySetInnerHTML \u2014 a className rewrite touches none of them; only a dynamic/opaque class list or a combinator-subject class is excluded, so no behaviour or project selector is disturbed."
|
|
1570
|
+
},
|
|
1571
|
+
rewrite: {
|
|
1572
|
+
rewriteClasses(computed2) {
|
|
1573
|
+
const fold = analyzeScrollMargin(computed2);
|
|
1574
|
+
return fold ? withFoldedScrollMargin(computed2, fold) : null;
|
|
1575
|
+
}
|
|
1576
|
+
},
|
|
1577
|
+
test: {
|
|
1578
|
+
cases: [
|
|
1579
|
+
{
|
|
1580
|
+
// The four equal scroll-margin longhands collapse to a `scroll-margin` decl at the IR level; the
|
|
1581
|
+
// minimizing reverse-emit then picks the single shortest utility (`scroll-m-4`) that reproduces
|
|
1582
|
+
// it, replacing the four `scroll-m{t,r,b,l}-4` tokens. `bg-red-200` is preserved.
|
|
1583
|
+
before: '<div className="scroll-mt-4 scroll-mr-4 scroll-mb-4 scroll-ml-4 bg-red-200">box</div>',
|
|
1584
|
+
after: '<div className="bg-red-200 scroll-m-4">box</div>'
|
|
1585
|
+
}
|
|
1586
|
+
],
|
|
1587
|
+
// Sides differ (top != bottom) → no all-equal collapse.
|
|
1588
|
+
noMatch: ['<div className="scroll-mt-2 scroll-mr-4 scroll-mb-8 scroll-ml-4 bg-red-200">box</div>']
|
|
1589
|
+
}
|
|
1590
|
+
});
|
|
1591
|
+
|
|
1592
|
+
// ../patterns/src/library/compress/scroll-padding-shorthand.pattern.ts
|
|
1593
|
+
init_esm_shims();
|
|
1594
|
+
var SCROLL_PADDING_SIDES = [
|
|
1595
|
+
"scroll-padding-top",
|
|
1596
|
+
"scroll-padding-right",
|
|
1597
|
+
"scroll-padding-bottom",
|
|
1598
|
+
"scroll-padding-left"
|
|
1599
|
+
];
|
|
1600
|
+
var SIDE_SET2 = new Set(SCROLL_PADDING_SIDES);
|
|
1601
|
+
var BASE_KEY10 = conditionKey(BASE_CONDITION);
|
|
1602
|
+
var SCROLL_PADDING = "scroll-padding";
|
|
1603
|
+
var NON_COLLAPSIBLE_VALUES4 = /* @__PURE__ */ new Set([
|
|
1604
|
+
"initial",
|
|
1605
|
+
"inherit",
|
|
1606
|
+
"unset",
|
|
1607
|
+
"revert",
|
|
1608
|
+
"revert-layer"
|
|
1609
|
+
]);
|
|
1610
|
+
function analyzeScrollPadding(sm) {
|
|
1611
|
+
const block = sm.blocks.get(BASE_KEY10);
|
|
1612
|
+
if (!block) return null;
|
|
1613
|
+
const sides = [];
|
|
1614
|
+
for (const side of SCROLL_PADDING_SIDES) {
|
|
1615
|
+
const decl = block.decls.get(side);
|
|
1616
|
+
if (!decl) return null;
|
|
1617
|
+
sides.push(decl);
|
|
1618
|
+
}
|
|
1619
|
+
const important = sides[0].important;
|
|
1620
|
+
if (!sides.every((d) => d.important === important)) return null;
|
|
1621
|
+
const value = String(sides[0].value);
|
|
1622
|
+
if (NON_COLLAPSIBLE_VALUES4.has(value)) return null;
|
|
1623
|
+
if (!sides.every((d) => String(d.value) === value)) return null;
|
|
1624
|
+
const relative = sides.some((d) => d.relativeToParent);
|
|
1625
|
+
return { value, important, relative };
|
|
1626
|
+
}
|
|
1627
|
+
function withFoldedScrollPadding(sm, fold) {
|
|
1628
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
1629
|
+
for (const [key, block] of sm.blocks) {
|
|
1630
|
+
if (key !== BASE_KEY10) {
|
|
1631
|
+
blocks.set(key, block);
|
|
1632
|
+
continue;
|
|
1633
|
+
}
|
|
1634
|
+
const decls = /* @__PURE__ */ new Map();
|
|
1635
|
+
for (const [prop, decl] of block.decls) {
|
|
1636
|
+
if (SIDE_SET2.has(String(prop))) continue;
|
|
1637
|
+
decls.set(prop, decl);
|
|
1638
|
+
}
|
|
1639
|
+
const shorthand = {
|
|
1640
|
+
property: SCROLL_PADDING,
|
|
1641
|
+
value: fold.value,
|
|
1642
|
+
important: fold.important,
|
|
1643
|
+
relativeToParent: fold.relative,
|
|
1644
|
+
inherited: false
|
|
1645
|
+
// scroll-padding is never inherited
|
|
1646
|
+
};
|
|
1647
|
+
decls.set(shorthand.property, shorthand);
|
|
1648
|
+
blocks.set(key, { condition: block.condition, decls });
|
|
1649
|
+
}
|
|
1650
|
+
return { blocks };
|
|
1651
|
+
}
|
|
1652
|
+
var scrollPaddingShorthand = definePattern({
|
|
1653
|
+
name: "scroll-padding-shorthand",
|
|
1654
|
+
category: "compress/scroll-padding-shorthand",
|
|
1655
|
+
safety: 1,
|
|
1656
|
+
doc: {
|
|
1657
|
+
title: "Collapse equal scroll-padding sides into scroll-padding",
|
|
1658
|
+
summary: "An element whose four scroll-padding sides are all equal is rewritten to the single Tailwind scroll-p-* utility (scroll-padding === the four equal sides).",
|
|
1659
|
+
before: '<div class="scroll-pt-4 scroll-pr-4 scroll-pb-4 scroll-pl-4"/>',
|
|
1660
|
+
after: '<div class="scroll-p-4"/>',
|
|
1661
|
+
safetyRationale: "`scroll-padding` is value-identical to four equal scroll-padding sides \u2014 a class-only change. It is safe even on an element with a ref, event handler, dynamic child, or dangerouslySetInnerHTML \u2014 a className rewrite touches none of them; only a dynamic/opaque class list or a combinator-subject class is excluded, so no behaviour or project selector is disturbed."
|
|
1662
|
+
},
|
|
1663
|
+
rewrite: {
|
|
1664
|
+
rewriteClasses(computed2) {
|
|
1665
|
+
const fold = analyzeScrollPadding(computed2);
|
|
1666
|
+
return fold ? withFoldedScrollPadding(computed2, fold) : null;
|
|
742
1667
|
}
|
|
743
|
-
|
|
1668
|
+
},
|
|
1669
|
+
test: {
|
|
1670
|
+
cases: [
|
|
1671
|
+
{
|
|
1672
|
+
// The four equal scroll-padding longhands collapse to a `scroll-padding` decl at the IR level;
|
|
1673
|
+
// the minimizing reverse-emit then picks the single shortest utility (`scroll-p-4`) that
|
|
1674
|
+
// reproduces it, replacing the four `scroll-p{t,r,b,l}-4` tokens. `bg-red-200` is preserved.
|
|
1675
|
+
before: '<div className="scroll-pt-4 scroll-pr-4 scroll-pb-4 scroll-pl-4 bg-red-200">box</div>',
|
|
1676
|
+
after: '<div className="bg-red-200 scroll-p-4">box</div>'
|
|
1677
|
+
}
|
|
1678
|
+
],
|
|
1679
|
+
// Sides differ (top != bottom) → no all-equal collapse.
|
|
1680
|
+
noMatch: ['<div className="scroll-pt-2 scroll-pr-4 scroll-pb-8 scroll-pl-4 bg-red-200">box</div>']
|
|
1681
|
+
}
|
|
744
1682
|
});
|
|
745
1683
|
|
|
746
|
-
// ../patterns/src/compress/size-shorthand.pattern.ts
|
|
1684
|
+
// ../patterns/src/library/compress/size-shorthand.pattern.ts
|
|
747
1685
|
init_esm_shims();
|
|
748
1686
|
var WIDTH = "width";
|
|
749
1687
|
var HEIGHT = "height";
|
|
750
1688
|
var SIZE = "size";
|
|
751
|
-
var
|
|
752
|
-
function asElement2(node) {
|
|
753
|
-
const n = node;
|
|
754
|
-
return n.kind === "element" ? n : null;
|
|
755
|
-
}
|
|
1689
|
+
var NON_COLLAPSIBLE_VALUES5 = /* @__PURE__ */ new Set(["auto", "initial", "unset"]);
|
|
756
1690
|
function baseBlock(sm) {
|
|
757
1691
|
return sm.blocks.get(conditionKey(BASE_CONDITION));
|
|
758
1692
|
}
|
|
759
|
-
var hasDangerousHtml3 = (node) => asElement2(node)?.meta.hasDangerousHtml ?? false;
|
|
760
1693
|
function withSizeShorthand(sm, value, important) {
|
|
761
1694
|
const baseKey = conditionKey(BASE_CONDITION);
|
|
762
1695
|
const blocks = /* @__PURE__ */ new Map();
|
|
@@ -775,7 +1708,7 @@ function withSizeShorthand(sm, value, important) {
|
|
|
775
1708
|
}
|
|
776
1709
|
return { blocks };
|
|
777
1710
|
}
|
|
778
|
-
var sizeShorthand =
|
|
1711
|
+
var sizeShorthand = definePattern({
|
|
779
1712
|
name: "size-shorthand",
|
|
780
1713
|
category: "compress/size-shorthand",
|
|
781
1714
|
safety: 2,
|
|
@@ -784,17 +1717,7 @@ var sizeShorthand = pattern({
|
|
|
784
1717
|
summary: "An element whose computed width and height are equal is rewritten to the single Tailwind size-* utility (size-* === width + height at the same value).",
|
|
785
1718
|
before: '<div style="width:1rem;height:1rem"/>',
|
|
786
1719
|
after: '<div class="size-4"/>',
|
|
787
|
-
safetyRationale: "size
|
|
788
|
-
},
|
|
789
|
-
match: {
|
|
790
|
-
where: [
|
|
791
|
-
not(hasRef),
|
|
792
|
-
not(hasEventHandlers),
|
|
793
|
-
not(hasDynamicChildren),
|
|
794
|
-
not(hasDangerousHtml3),
|
|
795
|
-
not(hasDynamicClasses),
|
|
796
|
-
not(targetedByCombinator)
|
|
797
|
-
]
|
|
1720
|
+
safetyRationale: "`size-*` is value-identical to equal width+height \u2014 a class-only change. It is safe even on an element with a ref, event handler, dynamic child, or dangerouslySetInnerHTML \u2014 a className rewrite touches none of them; only a dynamic/opaque class list or a combinator-subject class is excluded, so no behaviour or project selector is disturbed."
|
|
798
1721
|
},
|
|
799
1722
|
rewrite: {
|
|
800
1723
|
rewriteClasses(computed2) {
|
|
@@ -803,1425 +1726,1674 @@ var sizeShorthand = pattern({
|
|
|
803
1726
|
const h = base?.decls.get(HEIGHT);
|
|
804
1727
|
if (!w || !h) return null;
|
|
805
1728
|
if (w.important !== h.important) return null;
|
|
806
|
-
if (
|
|
1729
|
+
if (NON_COLLAPSIBLE_VALUES5.has(String(w.value))) return null;
|
|
807
1730
|
if (w.value !== h.value) return null;
|
|
808
1731
|
return withSizeShorthand(computed2, String(w.value), w.important);
|
|
809
1732
|
}
|
|
810
1733
|
},
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
1734
|
+
test: {
|
|
1735
|
+
cases: [
|
|
1736
|
+
{
|
|
1737
|
+
// Equal width/height collapse to a `size` decl at the IR level; the minimizing reverse-emit
|
|
1738
|
+
// expands `size` back to width+height, finds the single utility covering both (`size-10`), and
|
|
1739
|
+
// replaces the `h-10`+`w-10` pair with it. `bg-red-200` is preserved.
|
|
1740
|
+
before: '<div className="h-10 w-10 bg-red-200">box</div>',
|
|
1741
|
+
after: '<div className="bg-red-200 size-10">box</div>'
|
|
1742
|
+
}
|
|
1743
|
+
],
|
|
1744
|
+
// Width and height differ → no equal-axis collapse.
|
|
1745
|
+
noMatch: ['<div className="h-10 w-20 bg-red-200">box</div>']
|
|
1746
|
+
}
|
|
824
1747
|
});
|
|
825
1748
|
|
|
826
1749
|
// ../patterns/src/_registry.generated.ts
|
|
827
1750
|
init_esm_shims();
|
|
828
1751
|
var builtinPatterns = [
|
|
1752
|
+
displayContentsWrapper,
|
|
829
1753
|
emptyStyleDiv,
|
|
830
1754
|
flexCenterWrapper,
|
|
1755
|
+
inlineFlexCenterWrapper,
|
|
831
1756
|
nestedFlexMerge,
|
|
1757
|
+
nestedGridMerge,
|
|
832
1758
|
passthroughWrapper,
|
|
833
1759
|
redundantFragment,
|
|
1760
|
+
redundantInlineWrapper,
|
|
1761
|
+
borderRadiusShorthand,
|
|
1762
|
+
borderShorthand,
|
|
834
1763
|
dedupeClasses,
|
|
1764
|
+
gapShorthand,
|
|
835
1765
|
insetShorthand,
|
|
836
1766
|
marginShorthand,
|
|
1767
|
+
overflowShorthand,
|
|
1768
|
+
overscrollBehaviorShorthand,
|
|
837
1769
|
paddingShorthand,
|
|
1770
|
+
placeShorthand,
|
|
1771
|
+
scrollMarginShorthand,
|
|
1772
|
+
scrollPaddingShorthand,
|
|
838
1773
|
sizeShorthand
|
|
839
1774
|
];
|
|
840
1775
|
|
|
841
1776
|
// ../patterns/src/index.ts
|
|
842
1777
|
init_esm_shims();
|
|
843
1778
|
|
|
844
|
-
// ../
|
|
1779
|
+
// ../resolver-tailwind/src/tailwind/resolver.ts
|
|
845
1780
|
init_esm_shims();
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
case "JSXNamespacedName":
|
|
858
|
-
return `${node.namespace.name}:${node.name.name}`;
|
|
1781
|
+
|
|
1782
|
+
// ../resolver-tailwind/src/tailwind/emit.ts
|
|
1783
|
+
init_esm_shims();
|
|
1784
|
+
|
|
1785
|
+
// ../resolver-tailwind/src/tailwind/fingerprint.ts
|
|
1786
|
+
init_esm_shims();
|
|
1787
|
+
function fnv1a(input) {
|
|
1788
|
+
let h = 2166136261;
|
|
1789
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
1790
|
+
h ^= input.charCodeAt(i);
|
|
1791
|
+
h = Math.imul(h, 16777619);
|
|
859
1792
|
}
|
|
1793
|
+
return (h >>> 0).toString(16).padStart(8, "0");
|
|
860
1794
|
}
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
case "MemberExpression":
|
|
875
|
-
case "OptionalMemberExpression":
|
|
876
|
-
return "member";
|
|
877
|
-
case "ConditionalExpression":
|
|
878
|
-
case "LogicalExpression":
|
|
879
|
-
return "conditional";
|
|
880
|
-
case "TemplateLiteral":
|
|
881
|
-
case "TaggedTemplateExpression":
|
|
882
|
-
return "template";
|
|
883
|
-
case "Identifier":
|
|
884
|
-
return "identifier";
|
|
885
|
-
case "SpreadElement":
|
|
886
|
-
return "spread";
|
|
887
|
-
default:
|
|
888
|
-
return "other";
|
|
1795
|
+
|
|
1796
|
+
// ../resolver-tailwind/src/tailwind/emit.ts
|
|
1797
|
+
function expandForEmit(norm, prop, value, important) {
|
|
1798
|
+
const pairsFor = (p, v) => norm.normalizeDeclaration(p, v, important).map((d) => [d.property, String(d.value)]);
|
|
1799
|
+
if (prop === "size") {
|
|
1800
|
+
return [...pairsFor("width", value), ...pairsFor("height", value)];
|
|
1801
|
+
}
|
|
1802
|
+
if (prop === "inset-block" || prop === "inset-inline") {
|
|
1803
|
+
const parts = value.split(/\s+/).filter((s) => s.length > 0);
|
|
1804
|
+
const a = parts[0] ?? value;
|
|
1805
|
+
const b = parts[1] ?? a;
|
|
1806
|
+
const sides = prop === "inset-block" ? ["top", "bottom"] : ["left", "right"];
|
|
1807
|
+
return [...pairsFor(sides[0], a), ...pairsFor(sides[1], b)];
|
|
889
1808
|
}
|
|
1809
|
+
return pairsFor(prop, value);
|
|
890
1810
|
}
|
|
891
|
-
function
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
1811
|
+
function synthesizeResidual(remaining, ctx) {
|
|
1812
|
+
if (remaining.size === 0) return void 0;
|
|
1813
|
+
const norm = ctx.normalizer ?? normalizer;
|
|
1814
|
+
const decls = /* @__PURE__ */ new Map();
|
|
1815
|
+
for (const [prop, value] of remaining) {
|
|
1816
|
+
for (const decl of norm.normalizeDeclaration(String(prop), value, false)) {
|
|
1817
|
+
decls.set(decl.property, decl);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
const block = { condition: BASE_CONDITION, decls };
|
|
1821
|
+
const styleMap = { blocks: /* @__PURE__ */ new Map([[conditionKey(BASE_CONDITION), block]]) };
|
|
1822
|
+
const css = [...remaining].map(([p, v]) => `${p}:${v}`).join(";");
|
|
1823
|
+
const className = `df-${fnv1a(css)}`;
|
|
1824
|
+
const synthetic = { className, decls: styleMap, css: `.${className}{${css}}` };
|
|
1825
|
+
try {
|
|
1826
|
+
ctx.sink.register(synthetic);
|
|
1827
|
+
} catch {
|
|
908
1828
|
}
|
|
1829
|
+
return synthetic;
|
|
909
1830
|
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
1831
|
+
|
|
1832
|
+
// ../resolver-tailwind/src/tailwind/engine.ts
|
|
1833
|
+
init_esm_shims();
|
|
1834
|
+
import { createRequire } from "module";
|
|
1835
|
+
import * as path from "path";
|
|
1836
|
+
function moduleBase() {
|
|
1837
|
+
return typeof __filename === "string" ? __filename : import.meta.url;
|
|
913
1838
|
}
|
|
914
|
-
function
|
|
915
|
-
const
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
path: ctx.id,
|
|
926
|
-
text: code,
|
|
927
|
-
frontend: "jsx",
|
|
928
|
-
eol,
|
|
929
|
-
indentUnit: " ",
|
|
930
|
-
native: ast
|
|
931
|
-
};
|
|
932
|
-
doc.sources.set(FILE_ID, sourceFile);
|
|
933
|
-
const spanOf = (node) => {
|
|
934
|
-
if (node.start == null || node.end == null) return null;
|
|
935
|
-
const span = {
|
|
936
|
-
file: FILE_ID,
|
|
937
|
-
start: node.start,
|
|
938
|
-
end: node.end,
|
|
939
|
-
startLoc: node.loc ? { line: node.loc.start.line, column: node.loc.start.column } : void 0,
|
|
940
|
-
endLoc: node.loc ? { line: node.loc.end.line, column: node.loc.end.column } : void 0
|
|
941
|
-
};
|
|
942
|
-
return span;
|
|
943
|
-
};
|
|
944
|
-
const sliceOf = (node) => node.start == null || node.end == null ? "" : code.slice(node.start, node.end);
|
|
945
|
-
const internExpr = (node, spread) => {
|
|
946
|
-
const payload = { text: sliceOf(node), spread };
|
|
947
|
-
return doc.exprs.intern({
|
|
948
|
-
span: spanOf(node) ?? { file: FILE_ID, start: 0, end: 0 },
|
|
949
|
-
kind: exprKind(node),
|
|
950
|
-
payload
|
|
951
|
-
});
|
|
952
|
-
};
|
|
953
|
-
const splitTokens = (raw) => raw.split(/\s+/).filter((t) => t.length > 0).map((value) => ({ value }));
|
|
954
|
-
const buildClassList = (attr) => {
|
|
955
|
-
const attrSpan = spanOf(attr) ?? void 0;
|
|
956
|
-
const v = attr.value;
|
|
957
|
-
const staticList = (tokens, valueSpan) => {
|
|
958
|
-
const seg = { kind: "static", span: valueSpan ?? void 0, tokens };
|
|
959
|
-
return {
|
|
960
|
-
form: "string-literal",
|
|
961
|
-
segments: [seg],
|
|
962
|
-
valueSpan,
|
|
963
|
-
attrSpan,
|
|
964
|
-
hasDynamic: false,
|
|
965
|
-
opaque: false,
|
|
966
|
-
rewritable: true
|
|
967
|
-
};
|
|
968
|
-
};
|
|
969
|
-
if (v == null) return staticList([], null);
|
|
970
|
-
if (v.type === "StringLiteral") {
|
|
971
|
-
return staticList(splitTokens(v.value), spanOf(v));
|
|
972
|
-
}
|
|
973
|
-
if (v.type === "JSXExpressionContainer") {
|
|
974
|
-
const expr = v.expression;
|
|
975
|
-
if (expr.type === "StringLiteral") {
|
|
976
|
-
return staticList(splitTokens(expr.value), spanOf(expr));
|
|
977
|
-
}
|
|
978
|
-
if (expr.type === "JSXEmptyExpression") return staticList([], null);
|
|
979
|
-
const ref = internExpr(expr, false);
|
|
980
|
-
const valueSpan = spanOf(expr);
|
|
981
|
-
const seg = { kind: "dynamic", span: valueSpan ?? void 0, expr: ref };
|
|
982
|
-
return {
|
|
983
|
-
form: classFormOf(expr),
|
|
984
|
-
segments: [seg],
|
|
985
|
-
valueSpan,
|
|
986
|
-
attrSpan,
|
|
987
|
-
hasDynamic: true,
|
|
988
|
-
opaque: true,
|
|
989
|
-
rewritable: false
|
|
990
|
-
};
|
|
991
|
-
}
|
|
992
|
-
return emptyClassList();
|
|
993
|
-
};
|
|
994
|
-
const staticTokensOf2 = (classes) => {
|
|
995
|
-
const out = [];
|
|
996
|
-
for (const seg of classes.segments) {
|
|
997
|
-
if (seg.kind === "static") for (const t of seg.tokens) out.push(t.value);
|
|
1839
|
+
function projectRequire(projectRoot) {
|
|
1840
|
+
const bases = [];
|
|
1841
|
+
if (projectRoot) bases.push(path.join(projectRoot, "__domflax__.js"));
|
|
1842
|
+
bases.push(path.join(process.cwd(), "__domflax__.js"));
|
|
1843
|
+
bases.push(moduleBase());
|
|
1844
|
+
for (const base of bases) {
|
|
1845
|
+
try {
|
|
1846
|
+
const candidate = createRequire(base);
|
|
1847
|
+
candidate.resolve("tailwindcss/package.json");
|
|
1848
|
+
return candidate;
|
|
1849
|
+
} catch {
|
|
998
1850
|
}
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1851
|
+
}
|
|
1852
|
+
return null;
|
|
1853
|
+
}
|
|
1854
|
+
function loadEngine(options) {
|
|
1855
|
+
const req = projectRequire(options.projectRoot);
|
|
1856
|
+
if (!req) return null;
|
|
1857
|
+
try {
|
|
1858
|
+
const resolveConfig = req("tailwindcss/resolveConfig.js");
|
|
1859
|
+
const { createContext } = req("tailwindcss/lib/lib/setupContextUtils.js");
|
|
1860
|
+
const { generateRules } = req("tailwindcss/lib/lib/generateRules.js");
|
|
1861
|
+
const pkg = req("tailwindcss/package.json");
|
|
1862
|
+
let userConfig = options.config ?? { content: [{ raw: "" }] };
|
|
1863
|
+
if (options.configPath !== void 0) {
|
|
1864
|
+
const loadConfig = req("tailwindcss/loadConfig.js");
|
|
1865
|
+
userConfig = loadConfig(options.configPath);
|
|
1006
1866
|
}
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1867
|
+
const resolved = resolveConfig(userConfig);
|
|
1868
|
+
const context = createContext(resolved);
|
|
1869
|
+
return {
|
|
1870
|
+
version: pkg.version,
|
|
1871
|
+
context,
|
|
1872
|
+
generate(candidates) {
|
|
1873
|
+
const rules = generateRules(new Set(candidates), context);
|
|
1874
|
+
return rules.map(([, node]) => node);
|
|
1010
1875
|
}
|
|
1011
|
-
|
|
1876
|
+
};
|
|
1877
|
+
} catch {
|
|
1878
|
+
return null;
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
// ../resolver-tailwind/src/tailwind/extract.ts
|
|
1883
|
+
init_esm_shims();
|
|
1884
|
+
|
|
1885
|
+
// ../resolver-tailwind/src/tailwind/selector.ts
|
|
1886
|
+
init_esm_shims();
|
|
1887
|
+
var LEGACY_PSEUDO_ELEMENTS = /* @__PURE__ */ new Set([
|
|
1888
|
+
":before",
|
|
1889
|
+
":after",
|
|
1890
|
+
":first-line",
|
|
1891
|
+
":first-letter"
|
|
1892
|
+
]);
|
|
1893
|
+
function parseSelector(selector) {
|
|
1894
|
+
const sel = selector.trim();
|
|
1895
|
+
if (sel.length === 0 || sel[0] !== ".") return { kind: "complex" };
|
|
1896
|
+
let i = 1;
|
|
1897
|
+
for (; i < sel.length; i += 1) {
|
|
1898
|
+
const c = sel[i];
|
|
1899
|
+
if (c === "\\") {
|
|
1900
|
+
i += 1;
|
|
1901
|
+
continue;
|
|
1012
1902
|
}
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
const buildChild = (node, parentId) => {
|
|
1016
|
-
switch (node.type) {
|
|
1017
|
-
case "JSXText": {
|
|
1018
|
-
const id = doc.alloc.next();
|
|
1019
|
-
doc.nodes.set(
|
|
1020
|
-
id,
|
|
1021
|
-
createText(id, node.value, {
|
|
1022
|
-
parent: parentId,
|
|
1023
|
-
span: spanOf(node),
|
|
1024
|
-
collapsible: /^\s*$/.test(node.value)
|
|
1025
|
-
})
|
|
1026
|
-
);
|
|
1027
|
-
return id;
|
|
1028
|
-
}
|
|
1029
|
-
case "JSXExpressionContainer": {
|
|
1030
|
-
if (node.expression.type === "JSXEmptyExpression") return null;
|
|
1031
|
-
const id = doc.alloc.next();
|
|
1032
|
-
const ref = internExpr(node.expression, false);
|
|
1033
|
-
doc.nodes.set(id, createExpr(id, ref, { parent: parentId, span: spanOf(node) }));
|
|
1034
|
-
return id;
|
|
1035
|
-
}
|
|
1036
|
-
case "JSXSpreadChild": {
|
|
1037
|
-
const id = doc.alloc.next();
|
|
1038
|
-
const ref = internExpr(node.expression, true);
|
|
1039
|
-
doc.nodes.set(id, createExpr(id, ref, { parent: parentId, span: spanOf(node) }));
|
|
1040
|
-
return id;
|
|
1041
|
-
}
|
|
1042
|
-
case "JSXElement":
|
|
1043
|
-
return buildElement(node, parentId);
|
|
1044
|
-
case "JSXFragment":
|
|
1045
|
-
return buildFragment(node, parentId);
|
|
1046
|
-
default:
|
|
1047
|
-
return null;
|
|
1903
|
+
if (c === ":" || c === "." || c === "[" || c === " " || c === ">" || c === "+" || c === "~" || c === ",") {
|
|
1904
|
+
break;
|
|
1048
1905
|
}
|
|
1049
|
-
}
|
|
1050
|
-
const
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1906
|
+
}
|
|
1907
|
+
const remainder = sel.slice(i);
|
|
1908
|
+
if (remainder.length === 0) {
|
|
1909
|
+
return { kind: "simple", states: [], pseudoElement: "" };
|
|
1910
|
+
}
|
|
1911
|
+
if (!/^(?:::?[-a-z]+(?:\([^()]*\))?)+$/i.test(remainder)) {
|
|
1912
|
+
return { kind: "complex" };
|
|
1913
|
+
}
|
|
1914
|
+
const parts = remainder.match(/::?[-a-z]+(?:\([^()]*\))?/gi) ?? [];
|
|
1915
|
+
const states = [];
|
|
1916
|
+
let pseudoElement = "";
|
|
1917
|
+
for (const part of parts) {
|
|
1918
|
+
if (part.startsWith("::") || LEGACY_PSEUDO_ELEMENTS.has(part)) {
|
|
1919
|
+
pseudoElement = part.startsWith("::") ? part : `:${part}`;
|
|
1920
|
+
} else {
|
|
1921
|
+
states.push(part);
|
|
1056
1922
|
}
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
});
|
|
1066
|
-
return id;
|
|
1923
|
+
}
|
|
1924
|
+
return { kind: "simple", states, pseudoElement };
|
|
1925
|
+
}
|
|
1926
|
+
function makeCondition(media, states, pseudoElement) {
|
|
1927
|
+
return {
|
|
1928
|
+
media,
|
|
1929
|
+
states: [...new Set(states)].sort(),
|
|
1930
|
+
pseudoElement
|
|
1067
1931
|
};
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
for (const attr of opening.attributes) {
|
|
1080
|
-
if (attr.type === "JSXSpreadAttribute") {
|
|
1081
|
-
spreads.push(internExpr(attr.argument, true));
|
|
1082
|
-
meta.hasSpreadAttrs = true;
|
|
1083
|
-
continue;
|
|
1084
|
-
}
|
|
1085
|
-
const name = attrName(attr.name);
|
|
1086
|
-
if (name === "className" || name === "class") {
|
|
1087
|
-
classes = buildClassList(attr);
|
|
1088
|
-
continue;
|
|
1089
|
-
}
|
|
1090
|
-
if (name === "ref") meta.hasRef = true;
|
|
1091
|
-
else if (name === "key") meta.hasKey = true;
|
|
1092
|
-
else if (name === "dangerouslySetInnerHTML") meta.hasDangerousHtml = true;
|
|
1093
|
-
else if (/^on[A-Z]/.test(name)) meta.hasEventHandlers = true;
|
|
1094
|
-
entries.set(name, buildAttrValue(attr));
|
|
1095
|
-
order.push(name);
|
|
1096
|
-
}
|
|
1097
|
-
const attrs = { entries, spreads, order };
|
|
1098
|
-
const children = [];
|
|
1099
|
-
for (const c of node.children) {
|
|
1100
|
-
const cid = buildChild(c, id);
|
|
1101
|
-
if (cid != null) children.push(cid);
|
|
1102
|
-
}
|
|
1103
|
-
for (const cid of children) {
|
|
1104
|
-
const cn = doc.nodes.get(cid);
|
|
1105
|
-
if (cn && cn.kind === "expr") {
|
|
1106
|
-
meta.hasDynamicChildren = true;
|
|
1107
|
-
break;
|
|
1108
|
-
}
|
|
1932
|
+
}
|
|
1933
|
+
function unescapeClass(selector) {
|
|
1934
|
+
const sel = selector.trim();
|
|
1935
|
+
if (sel[0] !== ".") return null;
|
|
1936
|
+
let out = "";
|
|
1937
|
+
for (let i = 1; i < sel.length; i += 1) {
|
|
1938
|
+
const c = sel[i];
|
|
1939
|
+
if (c === "\\") {
|
|
1940
|
+
i += 1;
|
|
1941
|
+
if (i < sel.length) out += sel[i];
|
|
1942
|
+
continue;
|
|
1109
1943
|
}
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
const tokens = staticTokensOf2(classes);
|
|
1113
|
-
if (tokens.length > 0) {
|
|
1114
|
-
const res = ctx.resolver.resolve({
|
|
1115
|
-
classes: tokens,
|
|
1116
|
-
element: { tagName: tag, namespace: component ? void 0 : "html" }
|
|
1117
|
-
});
|
|
1118
|
-
computed2 = ctx.normalizer.normalizeStyleMap(res.styles);
|
|
1119
|
-
for (const w of res.warnings) {
|
|
1120
|
-
diagnostics.push({
|
|
1121
|
-
code: "DF_STYLE_CONFLICT_UNRESOLVED",
|
|
1122
|
-
severity: w.severity,
|
|
1123
|
-
message: w.message,
|
|
1124
|
-
nodeId: id
|
|
1125
|
-
});
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1944
|
+
if (c === ":" || c === "." || c === "[" || c === " " || c === ">" || c === "+" || c === "~" || c === ",") {
|
|
1945
|
+
return null;
|
|
1128
1946
|
}
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
} : null : null;
|
|
1149
|
-
backref.set(id, {
|
|
1150
|
-
nodeId: id,
|
|
1151
|
-
span: spanOf(node) ?? { file: FILE_ID, start: 0, end: 0 },
|
|
1152
|
-
openTagSpan: spanOf(opening),
|
|
1153
|
-
closeTagSpan: node.closingElement ? spanOf(node.closingElement) : null,
|
|
1154
|
-
innerSpan: inner,
|
|
1155
|
-
selfClosing: opening.selfClosing
|
|
1156
|
-
});
|
|
1157
|
-
return id;
|
|
1158
|
-
};
|
|
1159
|
-
const roots = [];
|
|
1160
|
-
traverse(ast, {
|
|
1161
|
-
JSXElement(path3) {
|
|
1162
|
-
roots.push(path3.node);
|
|
1163
|
-
path3.skip();
|
|
1164
|
-
},
|
|
1165
|
-
JSXFragment(path3) {
|
|
1166
|
-
roots.push(path3.node);
|
|
1167
|
-
path3.skip();
|
|
1947
|
+
out += c;
|
|
1948
|
+
}
|
|
1949
|
+
return out.length > 0 ? out : null;
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
// ../resolver-tailwind/src/tailwind/extract.ts
|
|
1953
|
+
function collectRules(node, mediaStack, inUnsupportedAtRule, out) {
|
|
1954
|
+
if (node.type === "rule") {
|
|
1955
|
+
out.push({ rule: node, media: mediaStack, unsupported: inUnsupportedAtRule });
|
|
1956
|
+
return;
|
|
1957
|
+
}
|
|
1958
|
+
if (node.type === "atrule") {
|
|
1959
|
+
const at = node;
|
|
1960
|
+
const children = at.nodes ?? [];
|
|
1961
|
+
if (at.name === "media") {
|
|
1962
|
+
const nextStack = at.params ? [...mediaStack, at.params] : mediaStack;
|
|
1963
|
+
for (const child of children) collectRules(child, nextStack, inUnsupportedAtRule, out);
|
|
1964
|
+
} else {
|
|
1965
|
+
for (const child of children) collectRules(child, mediaStack, true, out);
|
|
1168
1966
|
}
|
|
1169
|
-
});
|
|
1170
|
-
const rootFrag = doc.nodes.get(doc.root);
|
|
1171
|
-
for (const r of roots) {
|
|
1172
|
-
const id = r.type === "JSXFragment" ? buildFragment(r, doc.root) : buildElement(r, doc.root);
|
|
1173
|
-
rootFrag.children.push(id);
|
|
1174
1967
|
}
|
|
1175
|
-
return { doc, diagnostics };
|
|
1176
1968
|
}
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1969
|
+
function extractToken(token, nodes) {
|
|
1970
|
+
if (nodes.length === 0) return { blocks: [], produced: false };
|
|
1971
|
+
const leaves = [];
|
|
1972
|
+
for (const node of nodes) collectRules(node, [], false, leaves);
|
|
1973
|
+
const blocks = [];
|
|
1974
|
+
let sawComplex = false;
|
|
1975
|
+
for (const { rule, media, unsupported } of leaves) {
|
|
1976
|
+
const parsed = parseSelector(rule.selector);
|
|
1977
|
+
if (parsed.kind === "complex" || unsupported) {
|
|
1978
|
+
sawComplex = true;
|
|
1979
|
+
continue;
|
|
1980
|
+
}
|
|
1981
|
+
const decls = [];
|
|
1982
|
+
for (const child of rule.nodes ?? []) {
|
|
1983
|
+
if (child.type !== "decl") continue;
|
|
1984
|
+
const d = child;
|
|
1985
|
+
if (typeof d.value !== "string") continue;
|
|
1986
|
+
decls.push([d.prop, d.value, d.important === true]);
|
|
1987
|
+
}
|
|
1988
|
+
if (decls.length === 0) continue;
|
|
1989
|
+
const mediaQuery = media.join(" and ");
|
|
1990
|
+
blocks.push({ condition: makeCondition(mediaQuery, parsed.states, parsed.pseudoElement), decls });
|
|
1185
1991
|
}
|
|
1186
|
-
};
|
|
1187
|
-
|
|
1188
|
-
return jsxFrontend;
|
|
1992
|
+
const opaque = sawComplex && blocks.length === 0 ? { token, reason: "combinator-variant", detail: "utility targets descendants/siblings, not its own box" } : void 0;
|
|
1993
|
+
return { blocks, produced: true, opaque };
|
|
1189
1994
|
}
|
|
1190
1995
|
|
|
1191
|
-
// ../
|
|
1996
|
+
// ../resolver-tailwind/src/tailwind/serialize.ts
|
|
1192
1997
|
init_esm_shims();
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
if (payload && typeof payload.text === "string") {
|
|
1199
|
-
return { text: payload.text, spread: payload.spread === true };
|
|
1998
|
+
function serializeCssNode(node) {
|
|
1999
|
+
if (node.type === "decl") {
|
|
2000
|
+
const d = node;
|
|
2001
|
+
if (typeof d.value !== "string") return "";
|
|
2002
|
+
return `${d.prop}:${d.value}${d.important === true ? " !important" : ""}`;
|
|
1200
2003
|
}
|
|
1201
|
-
if (
|
|
1202
|
-
const
|
|
1203
|
-
|
|
2004
|
+
if (node.type === "rule") {
|
|
2005
|
+
const r = node;
|
|
2006
|
+
const body = (r.nodes ?? []).map((c) => serializeCssNode(c)).filter((s) => s.length > 0).join(";");
|
|
2007
|
+
return `${r.selector}{${body}}`;
|
|
1204
2008
|
}
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
for (const seg of classes.segments) {
|
|
1210
|
-
if (seg.kind === "static") for (const t of seg.tokens) out.push(t.value);
|
|
2009
|
+
if (node.type === "atrule") {
|
|
2010
|
+
const a = node;
|
|
2011
|
+
const body = (a.nodes ?? []).map((c) => serializeCssNode(c)).filter((s) => s.length > 0).join("");
|
|
2012
|
+
return `@${a.name} ${a.params}{${body}}`;
|
|
1211
2013
|
}
|
|
1212
|
-
return
|
|
2014
|
+
return "";
|
|
1213
2015
|
}
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
2016
|
+
|
|
2017
|
+
// ../resolver-tailwind/src/tailwind/stylemap.ts
|
|
2018
|
+
init_esm_shims();
|
|
2019
|
+
function buildStyleMap(blockMaps) {
|
|
2020
|
+
if (blockMaps.size === 0) return emptyStyleMap();
|
|
2021
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
2022
|
+
for (const { condition, decls } of blockMaps.values()) {
|
|
2023
|
+
if (decls.size === 0) continue;
|
|
2024
|
+
blocks.set(conditionKey(condition), { condition, decls });
|
|
1217
2025
|
}
|
|
1218
|
-
return
|
|
2026
|
+
if (blocks.size === 0) return emptyStyleMap();
|
|
2027
|
+
return normalizer.normalizeStyleMap({ blocks });
|
|
1219
2028
|
}
|
|
1220
|
-
function
|
|
2029
|
+
function shadowedBy(prev) {
|
|
1221
2030
|
const out = [];
|
|
1222
2031
|
const seen = /* @__PURE__ */ new Set();
|
|
1223
|
-
const
|
|
1224
|
-
if (seen.has(
|
|
1225
|
-
seen.add(
|
|
1226
|
-
|
|
1227
|
-
if (!n) return;
|
|
1228
|
-
out.push(n);
|
|
1229
|
-
if (n.kind === "element" || n.kind === "fragment") for (const c of n.children) visit(c);
|
|
2032
|
+
const add = (o) => {
|
|
2033
|
+
if (!o || o.kind !== "class" || seen.has(o.className)) return;
|
|
2034
|
+
seen.add(o.className);
|
|
2035
|
+
out.push(o);
|
|
1230
2036
|
};
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
function strictlyContains(a, b) {
|
|
1235
|
-
if (a.file !== b.file) return false;
|
|
1236
|
-
if (a.start <= b.start && b.end <= a.end) return !(a.start === b.start && a.end === b.end);
|
|
1237
|
-
return false;
|
|
2037
|
+
for (const o of prev.shadowed ?? []) add(o);
|
|
2038
|
+
add(prev.origin);
|
|
2039
|
+
return out.length > 0 ? out : void 0;
|
|
1238
2040
|
}
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
2041
|
+
|
|
2042
|
+
// ../resolver-tailwind/src/tailwind/usage.ts
|
|
2043
|
+
init_esm_shims();
|
|
2044
|
+
var OPAQUE_USAGE = {
|
|
2045
|
+
asSubject: true,
|
|
2046
|
+
asAncestor: true,
|
|
2047
|
+
asCompound: true,
|
|
2048
|
+
asSibling: true,
|
|
2049
|
+
asHasArgument: true,
|
|
2050
|
+
asStructural: true,
|
|
2051
|
+
droppable: false
|
|
2052
|
+
};
|
|
2053
|
+
var DROPPABLE_USAGE = {
|
|
2054
|
+
asSubject: true,
|
|
2055
|
+
asAncestor: false,
|
|
2056
|
+
asCompound: false,
|
|
2057
|
+
asSibling: false,
|
|
2058
|
+
asHasArgument: false,
|
|
2059
|
+
asStructural: false,
|
|
2060
|
+
droppable: true
|
|
2061
|
+
};
|
|
2062
|
+
|
|
2063
|
+
// ../resolver-tailwind/src/tailwind/resolver.ts
|
|
2064
|
+
var TailwindResolver = class {
|
|
2065
|
+
id = "tailwind";
|
|
2066
|
+
provider;
|
|
2067
|
+
fingerprint;
|
|
2068
|
+
#engine;
|
|
2069
|
+
/** Per-token extraction cache (engine output is pure for a fixed config). */
|
|
2070
|
+
#tokenCache = /* @__PURE__ */ new Map();
|
|
2071
|
+
/** Per-class-set forward-resolution cache. */
|
|
2072
|
+
#resolveCache = /* @__PURE__ */ new Map();
|
|
2073
|
+
/** Lazily built reverse index for {@link emit}. */
|
|
2074
|
+
#reverseIndex = null;
|
|
2075
|
+
constructor(config = {}) {
|
|
2076
|
+
this.#engine = loadEngine(config);
|
|
2077
|
+
this.provider = config.provider ?? (this.#engine ? `tailwindcss@${this.#engine.version}` : "tailwindcss");
|
|
2078
|
+
const seed = JSON.stringify(config.config ?? {}) + (config.configPath ?? "");
|
|
2079
|
+
this.fingerprint = config.fingerprint ?? `${this.provider}/${fnv1a(seed)}`;
|
|
2080
|
+
}
|
|
2081
|
+
/** Engine-backed, cached single-token extraction. */
|
|
2082
|
+
#extract(token) {
|
|
2083
|
+
const cached = this.#tokenCache.get(token);
|
|
2084
|
+
if (cached) return cached;
|
|
2085
|
+
let result;
|
|
2086
|
+
if (!this.#engine) {
|
|
2087
|
+
result = { blocks: [], produced: false };
|
|
2088
|
+
} else {
|
|
2089
|
+
try {
|
|
2090
|
+
result = extractToken(token, this.#engine.generate([token]));
|
|
2091
|
+
} catch {
|
|
2092
|
+
result = { blocks: [], produced: false };
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
this.#tokenCache.set(token, result);
|
|
2096
|
+
return result;
|
|
2097
|
+
}
|
|
2098
|
+
owns(token) {
|
|
2099
|
+
if (token.length === 0) return false;
|
|
2100
|
+
return this.#extract(token).produced;
|
|
2101
|
+
}
|
|
2102
|
+
resolve(input) {
|
|
2103
|
+
const key = JSON.stringify(input.classes);
|
|
2104
|
+
const cached = this.#resolveCache.get(key);
|
|
2105
|
+
if (cached) return cached;
|
|
2106
|
+
const blockMaps = /* @__PURE__ */ new Map();
|
|
2107
|
+
const resolved = [];
|
|
2108
|
+
const unknown = [];
|
|
2109
|
+
const opaque = [];
|
|
2110
|
+
input.classes.forEach((token, tokenIndex) => {
|
|
2111
|
+
const extracted = this.#extract(token);
|
|
2112
|
+
if (!extracted.produced) {
|
|
2113
|
+
unknown.push(token);
|
|
2114
|
+
return;
|
|
2115
|
+
}
|
|
2116
|
+
if (extracted.opaque) opaque.push(extracted.opaque);
|
|
2117
|
+
if (extracted.blocks.length === 0) return;
|
|
2118
|
+
const origin = { kind: "class", tokenIndex, className: token };
|
|
2119
|
+
let contributed = false;
|
|
2120
|
+
for (const block of extracted.blocks) {
|
|
2121
|
+
const ck = conditionKey(block.condition);
|
|
2122
|
+
let bucket = blockMaps.get(ck);
|
|
2123
|
+
if (!bucket) {
|
|
2124
|
+
bucket = { condition: block.condition, decls: /* @__PURE__ */ new Map() };
|
|
2125
|
+
blockMaps.set(ck, bucket);
|
|
2126
|
+
}
|
|
2127
|
+
for (const [prop, value, important] of block.decls) {
|
|
2128
|
+
for (const decl of normalizer.normalizeDeclaration(prop, value, important)) {
|
|
2129
|
+
const prev = bucket.decls.get(decl.property);
|
|
2130
|
+
const shadowed = prev ? shadowedBy(prev) : void 0;
|
|
2131
|
+
bucket.decls.set(decl.property, shadowed ? { ...decl, origin, shadowed } : { ...decl, origin });
|
|
2132
|
+
contributed = true;
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
if (contributed) resolved.push(token);
|
|
2137
|
+
});
|
|
2138
|
+
const result = {
|
|
2139
|
+
styles: buildStyleMap(blockMaps),
|
|
2140
|
+
resolved,
|
|
2141
|
+
unknown,
|
|
2142
|
+
opaque,
|
|
2143
|
+
warnings: []
|
|
2144
|
+
};
|
|
2145
|
+
this.#resolveCache.set(key, result);
|
|
2146
|
+
return result;
|
|
2147
|
+
}
|
|
2148
|
+
/**
|
|
2149
|
+
* Lazily build the reverse index from the engine's own enumerable class list. Each indexable
|
|
2150
|
+
* utility maps to its NORMALIZED BASE longhand declarations (property → canonical value). Utilities
|
|
2151
|
+
* with variant conditions, combinator selectors, or no BASE declarations are skipped. Sorted by
|
|
2152
|
+
* declaration count (desc) so greedier (shorthand-like) utilities are tried first.
|
|
2153
|
+
*/
|
|
2154
|
+
#buildReverseIndex() {
|
|
2155
|
+
if (this.#reverseIndex) return this.#reverseIndex;
|
|
2156
|
+
const index = [];
|
|
2157
|
+
if (this.#engine) {
|
|
2158
|
+
try {
|
|
2159
|
+
const classes = this.#engine.context.getClassList().filter((c) => typeof c === "string");
|
|
2160
|
+
const nodes = this.#engine.generate(classes);
|
|
2161
|
+
for (const node of nodes) {
|
|
2162
|
+
if (node.type !== "rule") continue;
|
|
2163
|
+
const rule = node;
|
|
2164
|
+
const parsed = parseSelector(rule.selector);
|
|
2165
|
+
if (parsed.kind !== "simple" || parsed.states.length > 0 || parsed.pseudoElement !== "") {
|
|
2166
|
+
continue;
|
|
2167
|
+
}
|
|
2168
|
+
const className = unescapeClass(rule.selector);
|
|
2169
|
+
if (className === null) continue;
|
|
2170
|
+
const decls = /* @__PURE__ */ new Map();
|
|
2171
|
+
for (const child of rule.nodes ?? []) {
|
|
2172
|
+
if (child.type !== "decl") continue;
|
|
2173
|
+
const d = child;
|
|
2174
|
+
if (typeof d.value !== "string") continue;
|
|
2175
|
+
for (const decl of normalizer.normalizeDeclaration(d.prop, d.value, d.important === true)) {
|
|
2176
|
+
decls.set(decl.property, String(decl.value));
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
if (decls.size > 0) index.push([className, decls]);
|
|
2180
|
+
}
|
|
2181
|
+
} catch {
|
|
2182
|
+
}
|
|
1251
2183
|
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
if (el.isComponent) return false;
|
|
1256
|
-
const back = doc.backref.get(el.id);
|
|
1257
|
-
const openTag = back?.openTagSpan;
|
|
1258
|
-
if (!openTag || openTag.file !== sf.id) return false;
|
|
1259
|
-
const insertAt = openTag.start + 1 + el.tag.length;
|
|
1260
|
-
ms.appendLeft(insertAt, ` className="${tokens.join(" ")}"`);
|
|
1261
|
-
return true;
|
|
1262
|
-
}
|
|
1263
|
-
function surgicalPrint(doc) {
|
|
1264
|
-
const sf = primarySource(doc);
|
|
1265
|
-
if (!sf) return null;
|
|
1266
|
-
const ms = new MagicString(sf.text);
|
|
1267
|
-
const kept = collectKept(doc);
|
|
1268
|
-
const keptSpans = [];
|
|
1269
|
-
for (const n of kept) if (n.span && n.span.file === sf.id) keptSpans.push(n.span);
|
|
1270
|
-
const removed = [];
|
|
1271
|
-
for (const id of backrefIds(doc)) {
|
|
1272
|
-
if (doc.nodes.has(id)) continue;
|
|
1273
|
-
const back = doc.backref.get(id);
|
|
1274
|
-
if (!back || back.span.file !== sf.id) continue;
|
|
1275
|
-
const unwrapped = keptSpans.some((k) => strictlyContains(back.span, k));
|
|
1276
|
-
removed.push({ backref: back, unwrapped });
|
|
2184
|
+
index.sort((a, b) => b[1].size - a[1].size || (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
|
|
2185
|
+
this.#reverseIndex = index;
|
|
2186
|
+
return index;
|
|
1277
2187
|
}
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
const
|
|
1281
|
-
const
|
|
1282
|
-
if (
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
ms.remove(close.start, close.end);
|
|
2188
|
+
emit(styles, ctx) {
|
|
2189
|
+
const norm = ctx.normalizer ?? normalizer;
|
|
2190
|
+
const normalized = norm.normalizeStyleMap(styles);
|
|
2191
|
+
const base = normalized.blocks.get(conditionKey(BASE_CONDITION));
|
|
2192
|
+
if (!base || base.decls.size === 0) return { classes: [], exact: true, warnings: [] };
|
|
2193
|
+
const hasNonBase = normalized.blocks.size > 1;
|
|
2194
|
+
const target = /* @__PURE__ */ new Map();
|
|
2195
|
+
for (const [prop, decl] of base.decls) {
|
|
2196
|
+
for (const [lp, lv] of expandForEmit(norm, String(prop), String(decl.value), decl.important)) {
|
|
2197
|
+
target.set(lp, lv);
|
|
1289
2198
|
}
|
|
1290
|
-
} else {
|
|
1291
|
-
ms.remove(span.start, span.end);
|
|
1292
2199
|
}
|
|
2200
|
+
const candidates = [];
|
|
2201
|
+
for (const entry of this.#buildReverseIndex()) {
|
|
2202
|
+
const [, declMap] = entry;
|
|
2203
|
+
if (declMap.size === 0 || declMap.size > target.size) continue;
|
|
2204
|
+
let fits = true;
|
|
2205
|
+
for (const [prop, value] of declMap) {
|
|
2206
|
+
if (target.get(prop) !== value) {
|
|
2207
|
+
fits = false;
|
|
2208
|
+
break;
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
if (fits) candidates.push(entry);
|
|
2212
|
+
}
|
|
2213
|
+
const remaining = new Map(target);
|
|
2214
|
+
const classes = [];
|
|
2215
|
+
while (remaining.size > 0) {
|
|
2216
|
+
let best = null;
|
|
2217
|
+
let bestCover = 0;
|
|
2218
|
+
for (const entry of candidates) {
|
|
2219
|
+
const [token, declMap] = entry;
|
|
2220
|
+
let cover = 0;
|
|
2221
|
+
for (const prop of declMap.keys()) if (remaining.has(prop)) cover += 1;
|
|
2222
|
+
if (cover === 0) continue;
|
|
2223
|
+
const better = best === null || cover > bestCover || cover === bestCover && declMap.size < best[1].size || cover === bestCover && declMap.size === best[1].size && token < best[0];
|
|
2224
|
+
if (better) {
|
|
2225
|
+
best = entry;
|
|
2226
|
+
bestCover = cover;
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
if (!best) break;
|
|
2230
|
+
classes.push(best[0]);
|
|
2231
|
+
for (const prop of best[1].keys()) remaining.delete(prop);
|
|
2232
|
+
}
|
|
2233
|
+
const exact = remaining.size === 0 && !hasNonBase;
|
|
2234
|
+
if (remaining.size === 0) return { classes, exact, warnings: [] };
|
|
2235
|
+
const residual = synthesizeResidual(remaining, ctx);
|
|
2236
|
+
return residual ? { classes, residual, exact, warnings: [] } : { classes, exact, warnings: [] };
|
|
1293
2237
|
}
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
}
|
|
1308
|
-
function classText(doc, classes) {
|
|
1309
|
-
if (classes.form === "absent" || classes.segments.length === 0) return null;
|
|
1310
|
-
const dynamic = classes.segments.find((s) => s.kind === "dynamic");
|
|
1311
|
-
if (dynamic && dynamic.kind === "dynamic") {
|
|
1312
|
-
return `className={${exprText(doc, dynamic.expr).text}}`;
|
|
1313
|
-
}
|
|
1314
|
-
const tokens = staticTokensOf(classes);
|
|
1315
|
-
return `className="${tokens.join(" ")}"`;
|
|
1316
|
-
}
|
|
1317
|
-
function attrText(doc, name, value) {
|
|
1318
|
-
if (value.kind === "static") {
|
|
1319
|
-
if (value.value === true) return name;
|
|
1320
|
-
if (value.value === false) return "";
|
|
1321
|
-
return `${name}="${String(value.value)}"`;
|
|
1322
|
-
}
|
|
1323
|
-
return `${name}={${exprText(doc, value.expr).text}}`;
|
|
1324
|
-
}
|
|
1325
|
-
function printElement(doc, el) {
|
|
1326
|
-
const parts = [];
|
|
1327
|
-
const cls = classText(doc, el.classes);
|
|
1328
|
-
if (cls !== null) parts.push(cls);
|
|
1329
|
-
for (const name of el.attrs.order) {
|
|
1330
|
-
const v = el.attrs.entries.get(name);
|
|
1331
|
-
if (!v) continue;
|
|
1332
|
-
const text = attrText(doc, name, v);
|
|
1333
|
-
if (text.length > 0) parts.push(text);
|
|
1334
|
-
}
|
|
1335
|
-
for (const ref of el.attrs.spreads) parts.push(`{...${exprText(doc, ref).text}}`);
|
|
1336
|
-
const attrStr = parts.length > 0 ? ` ${parts.join(" ")}` : "";
|
|
1337
|
-
const tag = el.tag;
|
|
1338
|
-
if (el.children.length === 0) {
|
|
1339
|
-
return el.selfClosing ? `<${tag}${attrStr} />` : `<${tag}${attrStr}></${tag}>`;
|
|
1340
|
-
}
|
|
1341
|
-
const inner = el.children.map((c) => printNode(doc, c)).join("");
|
|
1342
|
-
return `<${tag}${attrStr}>${inner}</${tag}>`;
|
|
1343
|
-
}
|
|
1344
|
-
function printNode(doc, id) {
|
|
1345
|
-
const node = doc.nodes.get(id);
|
|
1346
|
-
if (!node) return "";
|
|
1347
|
-
switch (node.kind) {
|
|
1348
|
-
case "text":
|
|
1349
|
-
return node.value;
|
|
1350
|
-
case "comment":
|
|
1351
|
-
return `{/*${node.value}*/}`;
|
|
1352
|
-
case "expr": {
|
|
1353
|
-
const { text, spread } = exprText(doc, node.expr);
|
|
1354
|
-
return spread ? `{...${text}}` : `{${text}}`;
|
|
2238
|
+
/**
|
|
2239
|
+
* Generate a CSS stylesheet that defines `classes`, so a verifier can render a subtree with the
|
|
2240
|
+
* real Tailwind styling applied. Backed by the same engine `resolve` uses (`generate(candidates)`),
|
|
2241
|
+
* serialized to plain CSS. Returns `''` when the engine is unavailable or generates nothing.
|
|
2242
|
+
*/
|
|
2243
|
+
cssFor(classes) {
|
|
2244
|
+
if (!this.#engine) return "";
|
|
2245
|
+
const tokens = [...new Set(classes)].filter((c) => c.length > 0);
|
|
2246
|
+
if (tokens.length === 0) return "";
|
|
2247
|
+
try {
|
|
2248
|
+
return this.#engine.generate(tokens).map((n) => serializeCssNode(n)).filter((s) => s.length > 0).join("\n");
|
|
2249
|
+
} catch {
|
|
2250
|
+
return "";
|
|
1355
2251
|
}
|
|
1356
|
-
case "fragment":
|
|
1357
|
-
return `<>${node.children.map((c) => printNode(doc, c)).join("")}</>`;
|
|
1358
|
-
case "element":
|
|
1359
|
-
return printElement(doc, node);
|
|
1360
2252
|
}
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
function doPrint(doc) {
|
|
1368
|
-
const surgical = surgicalPrint(doc);
|
|
1369
|
-
return surgical ?? rePrint(doc);
|
|
1370
|
-
}
|
|
1371
|
-
var jsxBackend = {
|
|
1372
|
-
name: "babel-jsx",
|
|
1373
|
-
langs: JSX_LANGS2,
|
|
1374
|
-
print(doc, _plan, _ctx) {
|
|
1375
|
-
const code = doPrint(doc);
|
|
1376
|
-
return { code, map: null, edits: [], diagnostics: [] };
|
|
2253
|
+
selectorUsage(token) {
|
|
2254
|
+
const ex = this.#extract(token);
|
|
2255
|
+
if (!ex.produced || ex.opaque) return OPAQUE_USAGE;
|
|
2256
|
+
const baseOnly = ex.blocks.length > 0 && ex.blocks.every((b) => conditionKey(b.condition) === conditionKey(BASE_CONDITION));
|
|
2257
|
+
if (!baseOnly) return OPAQUE_USAGE;
|
|
2258
|
+
return DROPPABLE_USAGE;
|
|
1377
2259
|
}
|
|
1378
2260
|
};
|
|
1379
|
-
function
|
|
1380
|
-
return
|
|
2261
|
+
function createTailwindResolver(config) {
|
|
2262
|
+
return new TailwindResolver(config);
|
|
1381
2263
|
}
|
|
1382
2264
|
|
|
1383
|
-
// ../
|
|
2265
|
+
// ../resolver-tailwind/src/index.ts
|
|
1384
2266
|
init_esm_shims();
|
|
1385
2267
|
|
|
1386
|
-
// ../resolver-
|
|
2268
|
+
// ../resolver-css/src/resolver.ts
|
|
1387
2269
|
init_esm_shims();
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
const { generateRules } = req("tailwindcss/lib/lib/generateRules.js");
|
|
1415
|
-
const pkg = req("tailwindcss/package.json");
|
|
1416
|
-
let userConfig = options.config ?? { content: [{ raw: "" }] };
|
|
1417
|
-
if (options.configPath !== void 0) {
|
|
1418
|
-
const loadConfig = req("tailwindcss/loadConfig.js");
|
|
1419
|
-
userConfig = loadConfig(options.configPath);
|
|
1420
|
-
}
|
|
1421
|
-
const resolved = resolveConfig(userConfig);
|
|
1422
|
-
const context = createContext(resolved);
|
|
1423
|
-
return {
|
|
1424
|
-
version: pkg.version,
|
|
1425
|
-
context,
|
|
1426
|
-
generate(candidates) {
|
|
1427
|
-
const rules = generateRules(new Set(candidates), context);
|
|
1428
|
-
return rules.map(([, node]) => node);
|
|
1429
|
-
}
|
|
1430
|
-
};
|
|
1431
|
-
} catch {
|
|
1432
|
-
return null;
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
|
-
var LEGACY_PSEUDO_ELEMENTS = /* @__PURE__ */ new Set([
|
|
2270
|
+
|
|
2271
|
+
// ../resolver-css/src/constants.ts
|
|
2272
|
+
init_esm_shims();
|
|
2273
|
+
var CSS_RESOLVER_ID = "css";
|
|
2274
|
+
var CSS_RESOLVER_PROVIDER = "custom-css";
|
|
2275
|
+
var ENGINE_VERSION = "css-index@1";
|
|
2276
|
+
var STRUCTURAL_PSEUDOS = /* @__PURE__ */ new Set([
|
|
2277
|
+
":nth-child",
|
|
2278
|
+
":nth-last-child",
|
|
2279
|
+
":first-child",
|
|
2280
|
+
":last-child",
|
|
2281
|
+
":only-child",
|
|
2282
|
+
":nth-of-type",
|
|
2283
|
+
":nth-last-of-type",
|
|
2284
|
+
":first-of-type",
|
|
2285
|
+
":last-of-type",
|
|
2286
|
+
":only-of-type"
|
|
2287
|
+
]);
|
|
2288
|
+
var FUNCTIONAL_PSEUDOS = /* @__PURE__ */ new Set([
|
|
2289
|
+
":not",
|
|
2290
|
+
":is",
|
|
2291
|
+
":where",
|
|
2292
|
+
":has",
|
|
2293
|
+
":matches"
|
|
2294
|
+
]);
|
|
2295
|
+
var LEGACY_PSEUDO_ELEMENTS2 = /* @__PURE__ */ new Set([
|
|
1436
2296
|
":before",
|
|
1437
2297
|
":after",
|
|
1438
2298
|
":first-line",
|
|
1439
2299
|
":first-letter"
|
|
1440
2300
|
]);
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
2301
|
+
|
|
2302
|
+
// ../resolver-css/src/engine.ts
|
|
2303
|
+
init_esm_shims();
|
|
2304
|
+
import { createRequire as createRequire2 } from "module";
|
|
2305
|
+
import * as path2 from "path";
|
|
2306
|
+
function moduleBase2() {
|
|
2307
|
+
return typeof __filename === "string" ? __filename : import.meta.url;
|
|
2308
|
+
}
|
|
2309
|
+
function loadPostcssEngine(projectRoot) {
|
|
2310
|
+
const bases = [];
|
|
2311
|
+
if (projectRoot) bases.push(path2.join(projectRoot, "__domflax__.js"));
|
|
2312
|
+
bases.push(path2.join(process.cwd(), "__domflax__.js"));
|
|
2313
|
+
bases.push(moduleBase2());
|
|
2314
|
+
for (const base of bases) {
|
|
2315
|
+
try {
|
|
2316
|
+
const req = createRequire2(base);
|
|
2317
|
+
req.resolve("postcss");
|
|
2318
|
+
req.resolve("postcss-selector-parser");
|
|
2319
|
+
const postcss = req("postcss");
|
|
2320
|
+
const raw = req("postcss-selector-parser");
|
|
2321
|
+
const selector = raw.default ?? raw;
|
|
2322
|
+
return { parse: postcss.parse, selectorParser: selector };
|
|
2323
|
+
} catch {
|
|
1453
2324
|
}
|
|
1454
2325
|
}
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
2326
|
+
return null;
|
|
2327
|
+
}
|
|
2328
|
+
var pc = null;
|
|
2329
|
+
var sp = null;
|
|
2330
|
+
function ensurePostcss(projectRoot) {
|
|
2331
|
+
if (pc && sp) return;
|
|
2332
|
+
const engine = loadPostcssEngine(projectRoot);
|
|
2333
|
+
if (!engine) {
|
|
2334
|
+
throw new Error(
|
|
2335
|
+
'@domflax/resolver-css requires "postcss" and "postcss-selector-parser" to be installed in your project (they are optional peer dependencies of domflax, loaded only when the custom-CSS provider is used). Install them with: npm install postcss postcss-selector-parser'
|
|
2336
|
+
);
|
|
1458
2337
|
}
|
|
1459
|
-
|
|
1460
|
-
|
|
2338
|
+
pc = engine.parse;
|
|
2339
|
+
sp = engine.selectorParser;
|
|
2340
|
+
}
|
|
2341
|
+
|
|
2342
|
+
// ../resolver-css/src/postcss-helpers.ts
|
|
2343
|
+
init_esm_shims();
|
|
2344
|
+
function mediaContext(rule) {
|
|
2345
|
+
const parts = [];
|
|
2346
|
+
let skip = false;
|
|
2347
|
+
let parent = rule.parent;
|
|
2348
|
+
while (parent && parent.type === "atrule") {
|
|
2349
|
+
const at = parent;
|
|
2350
|
+
const name = at.name.toLowerCase();
|
|
2351
|
+
if (name === "media") parts.unshift(at.params.trim().replace(/\s+/g, " "));
|
|
2352
|
+
else if (name === "keyframes" || name.endsWith("keyframes") || name === "font-face") skip = true;
|
|
2353
|
+
parent = parent.parent;
|
|
1461
2354
|
}
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
} else {
|
|
1469
|
-
states.push(part);
|
|
1470
|
-
}
|
|
2355
|
+
return { media: parts.join(" and "), skip };
|
|
2356
|
+
}
|
|
2357
|
+
function collectDecls(rule) {
|
|
2358
|
+
const out = [];
|
|
2359
|
+
for (const node of rule.nodes) {
|
|
2360
|
+
if (node.type === "decl") out.push([node.prop, node.value, node.important === true]);
|
|
1471
2361
|
}
|
|
1472
|
-
return
|
|
2362
|
+
return out;
|
|
1473
2363
|
}
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
2364
|
+
|
|
2365
|
+
// ../resolver-css/src/misc-helpers.ts
|
|
2366
|
+
init_esm_shims();
|
|
2367
|
+
import { readFileSync } from "fs";
|
|
2368
|
+
function isPlainClassToken(token) {
|
|
2369
|
+
return token.length > 0 && !/[\s.#>+~:[\]()]/.test(token);
|
|
1480
2370
|
}
|
|
1481
|
-
function
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
2371
|
+
function readCssPath(path3) {
|
|
2372
|
+
try {
|
|
2373
|
+
return { id: path3, css: readFileSync(path3, "utf8") };
|
|
2374
|
+
} catch (cause) {
|
|
2375
|
+
throw new Error(`resolver-css: cannot read CSS file "${path3}"`, { cause });
|
|
1485
2376
|
}
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
2377
|
+
}
|
|
2378
|
+
function deriveFingerprint(provider, files) {
|
|
2379
|
+
const parts = files.map((f) => `${f.id}:${f.css.length}`).sort();
|
|
2380
|
+
return `${provider}/${ENGINE_VERSION}::${parts.join("|")}`;
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
// ../resolver-css/src/selector-helpers.ts
|
|
2384
|
+
init_esm_shims();
|
|
2385
|
+
function splitCompounds(selector) {
|
|
2386
|
+
const compounds = [];
|
|
2387
|
+
let current = [];
|
|
2388
|
+
let leftCombinator = null;
|
|
2389
|
+
for (const node of selector.nodes) {
|
|
2390
|
+
if (sp.isCombinator(node)) {
|
|
2391
|
+
compounds.push({ leftCombinator, nodes: current });
|
|
2392
|
+
current = [];
|
|
2393
|
+
leftCombinator = combinatorValue(node);
|
|
1492
2394
|
} else {
|
|
1493
|
-
|
|
2395
|
+
current.push(node);
|
|
1494
2396
|
}
|
|
1495
2397
|
}
|
|
2398
|
+
compounds.push({ leftCombinator, nodes: current });
|
|
2399
|
+
return compounds;
|
|
1496
2400
|
}
|
|
1497
|
-
function
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
for (const node of nodes) collectRules(node, [], false, leaves);
|
|
1501
|
-
const blocks = [];
|
|
1502
|
-
let sawComplex = false;
|
|
1503
|
-
for (const { rule, media, unsupported } of leaves) {
|
|
1504
|
-
const parsed = parseSelector(rule.selector);
|
|
1505
|
-
if (parsed.kind === "complex" || unsupported) {
|
|
1506
|
-
sawComplex = true;
|
|
1507
|
-
continue;
|
|
1508
|
-
}
|
|
1509
|
-
const decls = [];
|
|
1510
|
-
for (const child of rule.nodes ?? []) {
|
|
1511
|
-
if (child.type !== "decl") continue;
|
|
1512
|
-
const d = child;
|
|
1513
|
-
if (typeof d.value !== "string") continue;
|
|
1514
|
-
decls.push([d.prop, d.value, d.important === true]);
|
|
1515
|
-
}
|
|
1516
|
-
if (decls.length === 0) continue;
|
|
1517
|
-
const mediaQuery = media.join(" and ");
|
|
1518
|
-
blocks.push({ condition: makeCondition(mediaQuery, parsed.states, parsed.pseudoElement), decls });
|
|
1519
|
-
}
|
|
1520
|
-
const opaque = sawComplex && blocks.length === 0 ? { token, reason: "combinator-variant", detail: "utility targets descendants/siblings, not its own box" } : void 0;
|
|
1521
|
-
return { blocks, produced: true, opaque };
|
|
2401
|
+
function combinatorValue(node) {
|
|
2402
|
+
const v = node.value;
|
|
2403
|
+
return v.trim() === "" ? " " : v.trim();
|
|
1522
2404
|
}
|
|
1523
|
-
function
|
|
1524
|
-
|
|
1525
|
-
const blocks = /* @__PURE__ */ new Map();
|
|
1526
|
-
for (const { condition, decls } of blockMaps.values()) {
|
|
1527
|
-
if (decls.size === 0) continue;
|
|
1528
|
-
blocks.set(conditionKey(condition), { condition, decls });
|
|
1529
|
-
}
|
|
1530
|
-
if (blocks.size === 0) return emptyStyleMap();
|
|
1531
|
-
return normalizer.normalizeStyleMap({ blocks });
|
|
2405
|
+
function pseudoName(node) {
|
|
2406
|
+
return node.value.toLowerCase();
|
|
1532
2407
|
}
|
|
1533
|
-
function
|
|
1534
|
-
|
|
1535
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1536
|
-
const add = (o) => {
|
|
1537
|
-
if (!o || o.kind !== "class" || seen.has(o.className)) return;
|
|
1538
|
-
seen.add(o.className);
|
|
1539
|
-
out.push(o);
|
|
1540
|
-
};
|
|
1541
|
-
for (const o of prev.shadowed ?? []) add(o);
|
|
1542
|
-
add(prev.origin);
|
|
1543
|
-
return out.length > 0 ? out : void 0;
|
|
2408
|
+
function isPseudoElement(node) {
|
|
2409
|
+
return sp.isPseudoElement(node) || LEGACY_PSEUDO_ELEMENTS2.has(pseudoName(node));
|
|
1544
2410
|
}
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
asCompound: true,
|
|
1549
|
-
asSibling: true,
|
|
1550
|
-
asHasArgument: true,
|
|
1551
|
-
asStructural: true,
|
|
1552
|
-
droppable: false
|
|
1553
|
-
};
|
|
1554
|
-
var DROPPABLE_USAGE = {
|
|
1555
|
-
asSubject: true,
|
|
1556
|
-
asAncestor: false,
|
|
1557
|
-
asCompound: false,
|
|
1558
|
-
asSibling: false,
|
|
1559
|
-
asHasArgument: false,
|
|
1560
|
-
asStructural: false,
|
|
1561
|
-
droppable: true
|
|
1562
|
-
};
|
|
1563
|
-
function fnv1a(input) {
|
|
1564
|
-
let h = 2166136261;
|
|
1565
|
-
for (let i = 0; i < input.length; i += 1) {
|
|
1566
|
-
h ^= input.charCodeAt(i);
|
|
1567
|
-
h = Math.imul(h, 16777619);
|
|
1568
|
-
}
|
|
1569
|
-
return (h >>> 0).toString(16).padStart(8, "0");
|
|
2411
|
+
function normalizePseudoElement(node) {
|
|
2412
|
+
const name = pseudoName(node);
|
|
2413
|
+
return name.startsWith("::") ? name : `::${name.replace(/^:/, "")}`;
|
|
1570
2414
|
}
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
2415
|
+
|
|
2416
|
+
// ../resolver-css/src/resolver.ts
|
|
2417
|
+
var CustomCSSResolver = class {
|
|
2418
|
+
id = CSS_RESOLVER_ID;
|
|
2419
|
+
provider = CSS_RESOLVER_PROVIDER;
|
|
1574
2420
|
fingerprint;
|
|
1575
|
-
#
|
|
1576
|
-
/**
|
|
1577
|
-
#
|
|
1578
|
-
/**
|
|
1579
|
-
#
|
|
1580
|
-
/**
|
|
1581
|
-
#
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
const
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
result = { blocks: [], produced: false };
|
|
1595
|
-
} else {
|
|
1596
|
-
try {
|
|
1597
|
-
result = extractToken(token, this.#engine.generate([token]));
|
|
1598
|
-
} catch {
|
|
1599
|
-
result = { blocks: [], produced: false };
|
|
1600
|
-
}
|
|
2421
|
+
#files;
|
|
2422
|
+
/** Forward map: class token → simple-`.class` rule contributions (source order). */
|
|
2423
|
+
#classIndex = /* @__PURE__ */ new Map();
|
|
2424
|
+
/** Selector-participation facts per class token. */
|
|
2425
|
+
#usage = /* @__PURE__ */ new Map();
|
|
2426
|
+
/** Every class referenced anywhere in the stylesheets (forward-resolvable or not). */
|
|
2427
|
+
#known = /* @__PURE__ */ new Set();
|
|
2428
|
+
/** Distinct COMPLEX selectors (combinator or structural pseudo), sorted. */
|
|
2429
|
+
#complex;
|
|
2430
|
+
#reverse = null;
|
|
2431
|
+
constructor(cssFiles = [], options = {}) {
|
|
2432
|
+
ensurePostcss(options.projectRoot);
|
|
2433
|
+
const fromDisk = (options.files ?? []).map(readCssPath);
|
|
2434
|
+
this.#files = [...cssFiles, ...fromDisk];
|
|
2435
|
+
this.fingerprint = options.fingerprint ?? deriveFingerprint(this.provider, this.#files);
|
|
2436
|
+
const complex = /* @__PURE__ */ new Set();
|
|
2437
|
+
let order = 0;
|
|
2438
|
+
for (const file of this.#files) {
|
|
2439
|
+
order = this.#indexFile(file, order, complex);
|
|
1601
2440
|
}
|
|
1602
|
-
this.#
|
|
1603
|
-
|
|
2441
|
+
this.#complex = [...complex].sort();
|
|
2442
|
+
}
|
|
2443
|
+
/** The stylesheets this resolver was constructed with (raw sources + any read from disk). */
|
|
2444
|
+
get files() {
|
|
2445
|
+
return this.#files;
|
|
1604
2446
|
}
|
|
2447
|
+
/** Owns any plain class token referenced by one of {@link files}. */
|
|
1605
2448
|
owns(token) {
|
|
1606
|
-
|
|
1607
|
-
return this.#extract(token).produced;
|
|
2449
|
+
return isPlainClassToken(token) && this.#known.has(token);
|
|
1608
2450
|
}
|
|
1609
2451
|
resolve(input) {
|
|
1610
|
-
const
|
|
1611
|
-
const cached = this.#resolveCache.get(key);
|
|
1612
|
-
if (cached) return cached;
|
|
1613
|
-
const blockMaps = /* @__PURE__ */ new Map();
|
|
2452
|
+
const styles = this.#resolveTokens(input.classes, input.classes);
|
|
1614
2453
|
const resolved = [];
|
|
1615
2454
|
const unknown = [];
|
|
1616
|
-
const
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
2455
|
+
for (const token of input.classes) {
|
|
2456
|
+
if (this.#classIndex.has(token)) resolved.push(token);
|
|
2457
|
+
else unknown.push(token);
|
|
2458
|
+
}
|
|
2459
|
+
return { styles, resolved, unknown, opaque: [], warnings: [] };
|
|
2460
|
+
}
|
|
2461
|
+
emit(styles, ctx) {
|
|
2462
|
+
const norm = ctx.normalizer ?? normalizer;
|
|
2463
|
+
const remaining = /* @__PURE__ */ new Map();
|
|
2464
|
+
for (const [ck, block] of norm.normalizeStyleMap(styles).blocks) {
|
|
2465
|
+
for (const [prop, decl] of block.decls) {
|
|
2466
|
+
remaining.set(`${ck} ${prop}`, String(decl.value));
|
|
1622
2467
|
}
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
blockMaps.set(ck, bucket);
|
|
1633
|
-
}
|
|
1634
|
-
for (const [prop, value, important] of block.decls) {
|
|
1635
|
-
for (const decl of normalizer.normalizeDeclaration(prop, value, important)) {
|
|
1636
|
-
const prev = bucket.decls.get(decl.property);
|
|
1637
|
-
const shadowed = prev ? shadowedBy(prev) : void 0;
|
|
1638
|
-
bucket.decls.set(decl.property, shadowed ? { ...decl, origin, shadowed } : { ...decl, origin });
|
|
1639
|
-
contributed = true;
|
|
1640
|
-
}
|
|
2468
|
+
}
|
|
2469
|
+
if (remaining.size === 0) return { classes: [], exact: true, warnings: [] };
|
|
2470
|
+
const classes = [];
|
|
2471
|
+
for (const { token, keyed } of this.#reverseIndex()) {
|
|
2472
|
+
let matches = true;
|
|
2473
|
+
for (const [key, value] of keyed) {
|
|
2474
|
+
if (remaining.get(key) !== value) {
|
|
2475
|
+
matches = false;
|
|
2476
|
+
break;
|
|
1641
2477
|
}
|
|
1642
2478
|
}
|
|
1643
|
-
if (
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
2479
|
+
if (!matches) continue;
|
|
2480
|
+
classes.push(token);
|
|
2481
|
+
for (const key of keyed.keys()) remaining.delete(key);
|
|
2482
|
+
if (remaining.size === 0) break;
|
|
2483
|
+
}
|
|
2484
|
+
return { classes, exact: remaining.size === 0, warnings: [] };
|
|
2485
|
+
}
|
|
2486
|
+
/**
|
|
2487
|
+
* Return a CSS stylesheet defining the given class tokens, so a verifier can render a subtree with
|
|
2488
|
+
* the project's real styling applied. The source stylesheets ARE the definition, so we hand back
|
|
2489
|
+
* their concatenation verbatim (every relevant rule — including combinator/structural selectors —
|
|
2490
|
+
* is preserved). `classes` is accepted for interface parity but the full source is always returned.
|
|
2491
|
+
*/
|
|
2492
|
+
cssFor(_classes) {
|
|
2493
|
+
return this.#files.map((f) => f.css).join("\n");
|
|
2494
|
+
}
|
|
2495
|
+
selectorUsage(token) {
|
|
2496
|
+
const u = this.#usage.get(token);
|
|
2497
|
+
if (!u) {
|
|
2498
|
+
return {
|
|
2499
|
+
asSubject: false,
|
|
2500
|
+
asAncestor: false,
|
|
2501
|
+
asCompound: false,
|
|
2502
|
+
asSibling: false,
|
|
2503
|
+
asHasArgument: false,
|
|
2504
|
+
asStructural: false,
|
|
2505
|
+
droppable: true
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2508
|
+
return {
|
|
2509
|
+
asSubject: u.asSubject,
|
|
2510
|
+
asAncestor: u.asAncestor,
|
|
2511
|
+
asCompound: u.asCompound,
|
|
2512
|
+
asSibling: u.asSibling,
|
|
2513
|
+
asHasArgument: u.asHasArgument,
|
|
2514
|
+
asStructural: u.asStructural,
|
|
2515
|
+
// Safe to drop/rename only when every reference is the lone subject of a bare `.x {}`.
|
|
2516
|
+
droppable: u.referenced && !u.loadBearing
|
|
1651
2517
|
};
|
|
1652
|
-
this.#resolveCache.set(key, result);
|
|
1653
|
-
return result;
|
|
1654
2518
|
}
|
|
1655
2519
|
/**
|
|
1656
|
-
*
|
|
1657
|
-
*
|
|
1658
|
-
*
|
|
1659
|
-
* declaration count (desc) so greedier (shorthand-like) utilities are tried first.
|
|
2520
|
+
* The distinct COMPLEX selectors found across all stylesheets — anything containing a combinator
|
|
2521
|
+
* (descendant / `>` / `+` / `~`) or a structural pseudo (`:nth-child`, `:first-child`, …). Feeds
|
|
2522
|
+
* domflax's CSS-selector-safety guard.
|
|
1660
2523
|
*/
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
2524
|
+
complexSelectors() {
|
|
2525
|
+
return this.#complex;
|
|
2526
|
+
}
|
|
2527
|
+
/* ─────────────────────────── internals ─────────────────────────── */
|
|
2528
|
+
/** Parse one stylesheet and fold its rules into the indexes. Returns the advanced order counter. */
|
|
2529
|
+
#indexFile(file, startOrder, complex) {
|
|
2530
|
+
let order = startOrder;
|
|
2531
|
+
let root;
|
|
2532
|
+
try {
|
|
2533
|
+
root = pc(file.css, { from: file.id });
|
|
2534
|
+
} catch {
|
|
2535
|
+
return order;
|
|
2536
|
+
}
|
|
2537
|
+
root.walkRules((rule) => {
|
|
2538
|
+
const media = mediaContext(rule);
|
|
2539
|
+
if (media.skip) return;
|
|
2540
|
+
const decls = collectDecls(rule);
|
|
2541
|
+
let ast;
|
|
1665
2542
|
try {
|
|
1666
|
-
|
|
1667
|
-
const nodes = this.#engine.generate(classes);
|
|
1668
|
-
for (const node of nodes) {
|
|
1669
|
-
if (node.type !== "rule") continue;
|
|
1670
|
-
const rule = node;
|
|
1671
|
-
const parsed = parseSelector(rule.selector);
|
|
1672
|
-
if (parsed.kind !== "simple" || parsed.states.length > 0 || parsed.pseudoElement !== "") {
|
|
1673
|
-
continue;
|
|
1674
|
-
}
|
|
1675
|
-
const className = unescapeClass(rule.selector);
|
|
1676
|
-
if (className === null) continue;
|
|
1677
|
-
const decls = /* @__PURE__ */ new Map();
|
|
1678
|
-
for (const child of rule.nodes ?? []) {
|
|
1679
|
-
if (child.type !== "decl") continue;
|
|
1680
|
-
const d = child;
|
|
1681
|
-
if (typeof d.value !== "string") continue;
|
|
1682
|
-
for (const decl of normalizer.normalizeDeclaration(d.prop, d.value, d.important === true)) {
|
|
1683
|
-
decls.set(decl.property, String(decl.value));
|
|
1684
|
-
}
|
|
1685
|
-
}
|
|
1686
|
-
if (decls.size > 0) index.push([className, decls]);
|
|
1687
|
-
}
|
|
2543
|
+
ast = sp().astSync(rule.selector);
|
|
1688
2544
|
} catch {
|
|
2545
|
+
return;
|
|
1689
2546
|
}
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
return index;
|
|
1694
|
-
}
|
|
1695
|
-
emit(styles, ctx) {
|
|
1696
|
-
const norm = ctx.normalizer ?? normalizer;
|
|
1697
|
-
const normalized = norm.normalizeStyleMap(styles);
|
|
1698
|
-
const base = normalized.blocks.get(conditionKey(BASE_CONDITION));
|
|
1699
|
-
if (!base || base.decls.size === 0) return { classes: [], exact: true, warnings: [] };
|
|
1700
|
-
const hasNonBase = normalized.blocks.size > 1;
|
|
1701
|
-
const target = /* @__PURE__ */ new Map();
|
|
1702
|
-
for (const [prop, decl] of base.decls) {
|
|
1703
|
-
for (const [lp, lv] of expandForEmit(norm, String(prop), String(decl.value), decl.important)) {
|
|
1704
|
-
target.set(lp, lv);
|
|
2547
|
+
for (const sel of ast.nodes) {
|
|
2548
|
+
const thisOrder = order;
|
|
2549
|
+
this.#analyzeSelector(sel, media.media, decls, thisOrder, complex);
|
|
1705
2550
|
}
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
2551
|
+
order += 1;
|
|
2552
|
+
});
|
|
2553
|
+
return order;
|
|
2554
|
+
}
|
|
2555
|
+
/** Analyze one comma-segment selector: forward indexing, usage facts, complex detection. */
|
|
2556
|
+
#analyzeSelector(selector, media, decls, order, complex) {
|
|
2557
|
+
const compounds = splitCompounds(selector);
|
|
2558
|
+
let hasCombinator = false;
|
|
2559
|
+
let hasStructural = false;
|
|
2560
|
+
compounds.forEach((compound, index) => {
|
|
2561
|
+
const isSubject = index === compounds.length - 1;
|
|
2562
|
+
const rightCombinator = index < compounds.length - 1 ? compounds[index + 1].leftCombinator : null;
|
|
2563
|
+
if (rightCombinator) hasCombinator = true;
|
|
2564
|
+
const classes = compound.nodes.filter((n) => sp.isClassName(n));
|
|
2565
|
+
const otherSimple = compound.nodes.some(
|
|
2566
|
+
(n) => sp.isTag(n) || sp.isIdentifier(n) || sp.isAttribute(n) || sp.isUniversal(n) || sp.isNesting(n)
|
|
2567
|
+
);
|
|
2568
|
+
const pseudos = compound.nodes.filter((n) => sp.isPseudo(n));
|
|
2569
|
+
const structuralPseudo = pseudos.some((p) => STRUCTURAL_PSEUDOS.has(pseudoName(p)));
|
|
2570
|
+
const functionalPseudo = pseudos.some((p) => FUNCTIONAL_PSEUDOS.has(pseudoName(p)));
|
|
2571
|
+
const statePseudos = pseudos.filter(
|
|
2572
|
+
(p) => sp.isPseudoClass(p) && !STRUCTURAL_PSEUDOS.has(pseudoName(p)) && !FUNCTIONAL_PSEUDOS.has(pseudoName(p))
|
|
2573
|
+
);
|
|
2574
|
+
const elementPseudos = pseudos.filter((p) => isPseudoElement(p));
|
|
2575
|
+
const qualified = classes.length > 1 || otherSimple || functionalPseudo || statePseudos.length > 0;
|
|
2576
|
+
if (structuralPseudo) hasStructural = true;
|
|
2577
|
+
for (const cls of classes) {
|
|
2578
|
+
const token = cls.value;
|
|
2579
|
+
this.#known.add(token);
|
|
2580
|
+
const u = this.#getUsage(token);
|
|
2581
|
+
u.referenced = true;
|
|
2582
|
+
if (isSubject) u.asSubject = true;
|
|
2583
|
+
if (rightCombinator === " " || rightCombinator === ">") u.asAncestor = true;
|
|
2584
|
+
if (rightCombinator === "+" || rightCombinator === "~") u.asSibling = true;
|
|
2585
|
+
if (qualified) u.asCompound = true;
|
|
2586
|
+
if (structuralPseudo) u.asStructural = true;
|
|
2587
|
+
if (rightCombinator !== null || qualified || structuralPseudo || elementPseudos.length > 0) {
|
|
2588
|
+
u.loadBearing = true;
|
|
1716
2589
|
}
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
for (const entry of candidates) {
|
|
1726
|
-
const [token, declMap] = entry;
|
|
1727
|
-
let cover = 0;
|
|
1728
|
-
for (const prop of declMap.keys()) if (remaining.has(prop)) cover += 1;
|
|
1729
|
-
if (cover === 0) continue;
|
|
1730
|
-
const better = best === null || cover > bestCover || cover === bestCover && declMap.size < best[1].size || cover === bestCover && declMap.size === best[1].size && token < best[0];
|
|
1731
|
-
if (better) {
|
|
1732
|
-
best = entry;
|
|
1733
|
-
bestCover = cover;
|
|
2590
|
+
const forwardEligible = compounds.length === 1 && classes.length === 1 && !otherSimple && !structuralPseudo && !functionalPseudo && elementPseudos.length <= 1;
|
|
2591
|
+
if (forwardEligible && decls.length > 0) {
|
|
2592
|
+
const condition = {
|
|
2593
|
+
media,
|
|
2594
|
+
states: statePseudos.map(pseudoName).sort(),
|
|
2595
|
+
pseudoElement: elementPseudos.length === 1 ? normalizePseudoElement(elementPseudos[0]) : ""
|
|
2596
|
+
};
|
|
2597
|
+
this.#addRuleEntry(token, { order, token, condition, decls });
|
|
1734
2598
|
}
|
|
1735
2599
|
}
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
2600
|
+
for (const p of pseudos) {
|
|
2601
|
+
const isHas = pseudoName(p) === ":has";
|
|
2602
|
+
p.walkClasses((inner) => {
|
|
2603
|
+
const token = inner.value;
|
|
2604
|
+
this.#known.add(token);
|
|
2605
|
+
const u = this.#getUsage(token);
|
|
2606
|
+
u.referenced = true;
|
|
2607
|
+
u.loadBearing = true;
|
|
2608
|
+
if (isHas) u.asHasArgument = true;
|
|
2609
|
+
});
|
|
2610
|
+
}
|
|
2611
|
+
});
|
|
2612
|
+
if (hasCombinator || hasStructural) {
|
|
2613
|
+
complex.add(selector.toString().trim());
|
|
1739
2614
|
}
|
|
1740
|
-
const exact = remaining.size === 0 && !hasNonBase;
|
|
1741
|
-
if (remaining.size === 0) return { classes, exact, warnings: [] };
|
|
1742
|
-
const residual = synthesizeResidual(remaining, ctx);
|
|
1743
|
-
return residual ? { classes, residual, exact, warnings: [] } : { classes, exact, warnings: [] };
|
|
1744
|
-
}
|
|
1745
|
-
selectorUsage(token) {
|
|
1746
|
-
const ex = this.#extract(token);
|
|
1747
|
-
if (!ex.produced || ex.opaque) return OPAQUE_USAGE;
|
|
1748
|
-
const baseOnly = ex.blocks.length > 0 && ex.blocks.every((b) => conditionKey(b.condition) === conditionKey(BASE_CONDITION));
|
|
1749
|
-
if (!baseOnly) return OPAQUE_USAGE;
|
|
1750
|
-
return DROPPABLE_USAGE;
|
|
1751
|
-
}
|
|
1752
|
-
};
|
|
1753
|
-
function expandForEmit(norm, prop, value, important) {
|
|
1754
|
-
const pairsFor = (p, v) => norm.normalizeDeclaration(p, v, important).map((d) => [d.property, String(d.value)]);
|
|
1755
|
-
if (prop === "size") {
|
|
1756
|
-
return [...pairsFor("width", value), ...pairsFor("height", value)];
|
|
1757
2615
|
}
|
|
1758
|
-
|
|
1759
|
-
const
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
const sides = prop === "inset-block" ? ["top", "bottom"] : ["left", "right"];
|
|
1763
|
-
return [...pairsFor(sides[0], a), ...pairsFor(sides[1], b)];
|
|
2616
|
+
#addRuleEntry(token, entry) {
|
|
2617
|
+
const list = this.#classIndex.get(token);
|
|
2618
|
+
if (list) list.push(entry);
|
|
2619
|
+
else this.#classIndex.set(token, [entry]);
|
|
1764
2620
|
}
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
2621
|
+
#getUsage(token) {
|
|
2622
|
+
let u = this.#usage.get(token);
|
|
2623
|
+
if (!u) {
|
|
2624
|
+
u = {
|
|
2625
|
+
referenced: false,
|
|
2626
|
+
asSubject: false,
|
|
2627
|
+
asAncestor: false,
|
|
2628
|
+
asCompound: false,
|
|
2629
|
+
asSibling: false,
|
|
2630
|
+
asHasArgument: false,
|
|
2631
|
+
asStructural: false,
|
|
2632
|
+
loadBearing: false
|
|
2633
|
+
};
|
|
2634
|
+
this.#usage.set(token, u);
|
|
1774
2635
|
}
|
|
2636
|
+
return u;
|
|
1775
2637
|
}
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
return synthetic;
|
|
1786
|
-
}
|
|
1787
|
-
function unescapeClass(selector) {
|
|
1788
|
-
const sel = selector.trim();
|
|
1789
|
-
if (sel[0] !== ".") return null;
|
|
1790
|
-
let out = "";
|
|
1791
|
-
for (let i = 1; i < sel.length; i += 1) {
|
|
1792
|
-
const c = sel[i];
|
|
1793
|
-
if (c === "\\") {
|
|
1794
|
-
i += 1;
|
|
1795
|
-
if (i < sel.length) out += sel[i];
|
|
1796
|
-
continue;
|
|
2638
|
+
/**
|
|
2639
|
+
* Resolve a set of tokens into a normalized condition-keyed StyleMap. `tokenList` is the original
|
|
2640
|
+
* class list (for per-declaration `tokenIndex` provenance); `request` is the set being resolved.
|
|
2641
|
+
*/
|
|
2642
|
+
#resolveTokens(request, tokenList) {
|
|
2643
|
+
const entries = [];
|
|
2644
|
+
for (const token of new Set(request)) {
|
|
2645
|
+
const list = this.#classIndex.get(token);
|
|
2646
|
+
if (list) entries.push(...list);
|
|
1797
2647
|
}
|
|
1798
|
-
if (
|
|
1799
|
-
|
|
2648
|
+
if (entries.length === 0) return emptyStyleMap();
|
|
2649
|
+
entries.sort((a, b) => a.order - b.order);
|
|
2650
|
+
const acc = /* @__PURE__ */ new Map();
|
|
2651
|
+
for (const entry of entries) {
|
|
2652
|
+
const key = conditionKey(entry.condition);
|
|
2653
|
+
let block = acc.get(key);
|
|
2654
|
+
if (!block) {
|
|
2655
|
+
block = { condition: entry.condition, decls: /* @__PURE__ */ new Map() };
|
|
2656
|
+
acc.set(key, block);
|
|
2657
|
+
}
|
|
2658
|
+
const tokenIndex = tokenList.indexOf(entry.token);
|
|
2659
|
+
const origin = { kind: "class", tokenIndex, className: entry.token };
|
|
2660
|
+
for (const [prop, value, important] of entry.decls) {
|
|
2661
|
+
for (const decl of normalizer.normalizeDeclaration(prop, value, important)) {
|
|
2662
|
+
block.decls.set(decl.property, { ...decl, origin });
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
1800
2665
|
}
|
|
1801
|
-
|
|
2666
|
+
const rawBlocks = /* @__PURE__ */ new Map();
|
|
2667
|
+
for (const [key, block] of acc) {
|
|
2668
|
+
if (block.decls.size === 0) continue;
|
|
2669
|
+
rawBlocks.set(key, { condition: block.condition, decls: block.decls });
|
|
2670
|
+
}
|
|
2671
|
+
if (rawBlocks.size === 0) return emptyStyleMap();
|
|
2672
|
+
return normalizer.normalizeStyleMap({ blocks: rawBlocks });
|
|
1802
2673
|
}
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
2674
|
+
/** Build (once) the reverse index used by {@link emit}. */
|
|
2675
|
+
#reverseIndex() {
|
|
2676
|
+
if (this.#reverse) return this.#reverse;
|
|
2677
|
+
const out = [];
|
|
2678
|
+
for (const token of this.#classIndex.keys()) {
|
|
2679
|
+
const styles = this.#resolveTokens([token], [token]);
|
|
2680
|
+
const keyed = /* @__PURE__ */ new Map();
|
|
2681
|
+
for (const [ck, block] of styles.blocks) {
|
|
2682
|
+
for (const [prop, decl] of block.decls) keyed.set(`${ck} ${prop}`, String(decl.value));
|
|
2683
|
+
}
|
|
2684
|
+
if (keyed.size > 0) out.push({ token, keyed });
|
|
2685
|
+
}
|
|
2686
|
+
out.sort((a, b) => b.keyed.size - a.keyed.size);
|
|
2687
|
+
this.#reverse = out;
|
|
2688
|
+
return out;
|
|
2689
|
+
}
|
|
2690
|
+
};
|
|
2691
|
+
function createCssResolver(cssFiles = [], options) {
|
|
2692
|
+
return new CustomCSSResolver(cssFiles, options);
|
|
1807
2693
|
}
|
|
1808
2694
|
|
|
1809
2695
|
// ../resolver-css/src/index.ts
|
|
1810
2696
|
init_esm_shims();
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
const selector = raw.default ?? raw;
|
|
1830
|
-
return { parse: postcss.parse, selectorParser: selector };
|
|
1831
|
-
} catch {
|
|
1832
|
-
}
|
|
1833
|
-
}
|
|
1834
|
-
return null;
|
|
1835
|
-
}
|
|
1836
|
-
var pc = null;
|
|
1837
|
-
var sp = null;
|
|
1838
|
-
function ensurePostcss(projectRoot) {
|
|
1839
|
-
if (pc && sp) return;
|
|
1840
|
-
const engine = loadPostcssEngine(projectRoot);
|
|
1841
|
-
if (!engine) {
|
|
1842
|
-
throw new Error(
|
|
1843
|
-
'@domflax/resolver-css requires "postcss" and "postcss-selector-parser" to be installed in your project (they are optional peer dependencies of domflax, loaded only when the custom-CSS provider is used). Install them with: npm install postcss postcss-selector-parser'
|
|
1844
|
-
);
|
|
2697
|
+
|
|
2698
|
+
// ../frontend-jsx/src/frontend.ts
|
|
2699
|
+
init_esm_shims();
|
|
2700
|
+
|
|
2701
|
+
// ../frontend-jsx/src/frontend-ast.ts
|
|
2702
|
+
init_esm_shims();
|
|
2703
|
+
import babelTraverse from "@babel/traverse";
|
|
2704
|
+
var traverse = typeof babelTraverse === "function" ? babelTraverse : babelTraverse.default;
|
|
2705
|
+
var JSX_LANGS = ["jsx", "tsx"];
|
|
2706
|
+
var FILE_ID = 1;
|
|
2707
|
+
function jsxName(node) {
|
|
2708
|
+
switch (node.type) {
|
|
2709
|
+
case "JSXIdentifier":
|
|
2710
|
+
return node.name;
|
|
2711
|
+
case "JSXMemberExpression":
|
|
2712
|
+
return `${jsxName(node.object)}.${node.property.name}`;
|
|
2713
|
+
case "JSXNamespacedName":
|
|
2714
|
+
return `${node.namespace.name}:${node.name.name}`;
|
|
1845
2715
|
}
|
|
1846
|
-
pc = engine.parse;
|
|
1847
|
-
sp = engine.selectorParser;
|
|
1848
2716
|
}
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
":
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
var CustomCSSResolver = class {
|
|
1878
|
-
id = CSS_RESOLVER_ID;
|
|
1879
|
-
provider = CSS_RESOLVER_PROVIDER;
|
|
1880
|
-
fingerprint;
|
|
1881
|
-
#files;
|
|
1882
|
-
/** Forward map: class token → simple-`.class` rule contributions (source order). */
|
|
1883
|
-
#classIndex = /* @__PURE__ */ new Map();
|
|
1884
|
-
/** Selector-participation facts per class token. */
|
|
1885
|
-
#usage = /* @__PURE__ */ new Map();
|
|
1886
|
-
/** Every class referenced anywhere in the stylesheets (forward-resolvable or not). */
|
|
1887
|
-
#known = /* @__PURE__ */ new Set();
|
|
1888
|
-
/** Distinct COMPLEX selectors (combinator or structural pseudo), sorted. */
|
|
1889
|
-
#complex;
|
|
1890
|
-
#reverse = null;
|
|
1891
|
-
constructor(cssFiles = [], options = {}) {
|
|
1892
|
-
ensurePostcss(options.projectRoot);
|
|
1893
|
-
const fromDisk = (options.files ?? []).map(readCssPath);
|
|
1894
|
-
this.#files = [...cssFiles, ...fromDisk];
|
|
1895
|
-
this.fingerprint = options.fingerprint ?? deriveFingerprint(this.provider, this.#files);
|
|
1896
|
-
const complex = /* @__PURE__ */ new Set();
|
|
1897
|
-
let order = 0;
|
|
1898
|
-
for (const file of this.#files) {
|
|
1899
|
-
order = this.#indexFile(file, order, complex);
|
|
1900
|
-
}
|
|
1901
|
-
this.#complex = [...complex].sort();
|
|
1902
|
-
}
|
|
1903
|
-
/** The stylesheets this resolver was constructed with (raw sources + any read from disk). */
|
|
1904
|
-
get files() {
|
|
1905
|
-
return this.#files;
|
|
2717
|
+
function isComponentName(node) {
|
|
2718
|
+
if (node.type === "JSXMemberExpression") return true;
|
|
2719
|
+
if (node.type === "JSXNamespacedName") return false;
|
|
2720
|
+
return /^[A-Z]/.test(node.name);
|
|
2721
|
+
}
|
|
2722
|
+
function attrName(name) {
|
|
2723
|
+
return name.type === "JSXNamespacedName" ? `${name.namespace.name}:${name.name.name}` : name.name;
|
|
2724
|
+
}
|
|
2725
|
+
function exprKind(node) {
|
|
2726
|
+
switch (node.type) {
|
|
2727
|
+
case "CallExpression":
|
|
2728
|
+
case "OptionalCallExpression":
|
|
2729
|
+
return "call";
|
|
2730
|
+
case "MemberExpression":
|
|
2731
|
+
case "OptionalMemberExpression":
|
|
2732
|
+
return "member";
|
|
2733
|
+
case "ConditionalExpression":
|
|
2734
|
+
case "LogicalExpression":
|
|
2735
|
+
return "conditional";
|
|
2736
|
+
case "TemplateLiteral":
|
|
2737
|
+
case "TaggedTemplateExpression":
|
|
2738
|
+
return "template";
|
|
2739
|
+
case "Identifier":
|
|
2740
|
+
return "identifier";
|
|
2741
|
+
case "SpreadElement":
|
|
2742
|
+
return "spread";
|
|
2743
|
+
default:
|
|
2744
|
+
return "other";
|
|
1906
2745
|
}
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
2746
|
+
}
|
|
2747
|
+
function classFormOf(node) {
|
|
2748
|
+
switch (node.type) {
|
|
2749
|
+
case "TemplateLiteral":
|
|
2750
|
+
case "TaggedTemplateExpression":
|
|
2751
|
+
return "template-literal";
|
|
2752
|
+
case "CallExpression":
|
|
2753
|
+
case "OptionalCallExpression":
|
|
2754
|
+
return "call";
|
|
2755
|
+
case "ConditionalExpression":
|
|
2756
|
+
case "LogicalExpression":
|
|
2757
|
+
return "conditional";
|
|
2758
|
+
case "MemberExpression":
|
|
2759
|
+
case "OptionalMemberExpression":
|
|
2760
|
+
case "Identifier":
|
|
2761
|
+
return "member";
|
|
2762
|
+
default:
|
|
2763
|
+
return "call";
|
|
1910
2764
|
}
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
2765
|
+
}
|
|
2766
|
+
function findNestedJsxRoots(root) {
|
|
2767
|
+
const out = [];
|
|
2768
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2769
|
+
const visit = (n) => {
|
|
2770
|
+
if (!n || seen.has(n)) return;
|
|
2771
|
+
seen.add(n);
|
|
2772
|
+
switch (n.type) {
|
|
2773
|
+
case "JSXElement":
|
|
2774
|
+
case "JSXFragment":
|
|
2775
|
+
out.push(n);
|
|
2776
|
+
return;
|
|
2777
|
+
case "ParenthesizedExpression":
|
|
2778
|
+
case "TSNonNullExpression":
|
|
2779
|
+
case "TSAsExpression":
|
|
2780
|
+
case "TSSatisfiesExpression":
|
|
2781
|
+
case "TSTypeAssertion":
|
|
2782
|
+
visit(n.expression);
|
|
2783
|
+
return;
|
|
2784
|
+
case "LogicalExpression":
|
|
2785
|
+
visit(n.left);
|
|
2786
|
+
visit(n.right);
|
|
2787
|
+
return;
|
|
2788
|
+
case "ConditionalExpression":
|
|
2789
|
+
visit(n.consequent);
|
|
2790
|
+
visit(n.alternate);
|
|
2791
|
+
return;
|
|
2792
|
+
case "SequenceExpression":
|
|
2793
|
+
for (const e of n.expressions) visit(e);
|
|
2794
|
+
return;
|
|
2795
|
+
case "CallExpression":
|
|
2796
|
+
case "OptionalCallExpression":
|
|
2797
|
+
for (const a of n.arguments) visit(a);
|
|
2798
|
+
return;
|
|
2799
|
+
case "ArrowFunctionExpression":
|
|
2800
|
+
case "FunctionExpression":
|
|
2801
|
+
visit(n.body);
|
|
2802
|
+
return;
|
|
2803
|
+
case "BlockStatement":
|
|
2804
|
+
for (const s of n.body) visit(s);
|
|
2805
|
+
return;
|
|
2806
|
+
case "ReturnStatement":
|
|
2807
|
+
visit(n.argument);
|
|
2808
|
+
return;
|
|
2809
|
+
case "IfStatement":
|
|
2810
|
+
visit(n.consequent);
|
|
2811
|
+
visit(n.alternate);
|
|
2812
|
+
return;
|
|
2813
|
+
case "ArrayExpression":
|
|
2814
|
+
for (const el of n.elements) visit(el);
|
|
2815
|
+
return;
|
|
2816
|
+
default:
|
|
2817
|
+
return;
|
|
1918
2818
|
}
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
2819
|
+
};
|
|
2820
|
+
visit(root);
|
|
2821
|
+
return out;
|
|
2822
|
+
}
|
|
2823
|
+
function looksLikeJsx(id, code) {
|
|
2824
|
+
if (/\.[jt]sx$/i.test(id)) return true;
|
|
2825
|
+
return /<\/?[A-Za-z][\w.-]*|<>/.test(code);
|
|
2826
|
+
}
|
|
2827
|
+
|
|
2828
|
+
// ../frontend-jsx/src/frontend-parse.ts
|
|
2829
|
+
init_esm_shims();
|
|
2830
|
+
import { parse as babelParse } from "@babel/parser";
|
|
2831
|
+
function doParse(code, ctx) {
|
|
2832
|
+
const diagnostics = [];
|
|
2833
|
+
const doc = createDocument("jsx");
|
|
2834
|
+
const backref = doc.backref;
|
|
2835
|
+
const ast = babelParse(code, {
|
|
2836
|
+
sourceType: "module",
|
|
2837
|
+
plugins: ["jsx", "typescript"]
|
|
2838
|
+
});
|
|
2839
|
+
const eol = code.includes("\r\n") ? "\r\n" : "\n";
|
|
2840
|
+
const sourceFile = {
|
|
2841
|
+
id: FILE_ID,
|
|
2842
|
+
path: ctx.id,
|
|
2843
|
+
text: code,
|
|
2844
|
+
frontend: "jsx",
|
|
2845
|
+
eol,
|
|
2846
|
+
indentUnit: " ",
|
|
2847
|
+
native: ast
|
|
2848
|
+
};
|
|
2849
|
+
doc.sources.set(FILE_ID, sourceFile);
|
|
2850
|
+
const spanOf = (node) => {
|
|
2851
|
+
if (node.start == null || node.end == null) return null;
|
|
2852
|
+
const span = {
|
|
2853
|
+
file: FILE_ID,
|
|
2854
|
+
start: node.start,
|
|
2855
|
+
end: node.end,
|
|
2856
|
+
startLoc: node.loc ? { line: node.loc.start.line, column: node.loc.start.column } : void 0,
|
|
2857
|
+
endLoc: node.loc ? { line: node.loc.end.line, column: node.loc.end.column } : void 0
|
|
2858
|
+
};
|
|
2859
|
+
return span;
|
|
2860
|
+
};
|
|
2861
|
+
const sliceOf = (node) => node.start == null || node.end == null ? "" : code.slice(node.start, node.end);
|
|
2862
|
+
const internExpr = (node, spread) => {
|
|
2863
|
+
const payload = { text: sliceOf(node), spread };
|
|
2864
|
+
return doc.exprs.intern({
|
|
2865
|
+
span: spanOf(node) ?? { file: FILE_ID, start: 0, end: 0 },
|
|
2866
|
+
kind: exprKind(node),
|
|
2867
|
+
payload
|
|
2868
|
+
});
|
|
2869
|
+
};
|
|
2870
|
+
const splitTokens = (raw) => raw.split(/\s+/).filter((t) => t.length > 0).map((value) => ({ value }));
|
|
2871
|
+
const buildClassList = (attr) => {
|
|
2872
|
+
const attrSpan = spanOf(attr) ?? void 0;
|
|
2873
|
+
const v = attr.value;
|
|
2874
|
+
const staticList = (tokens, valueSpan) => {
|
|
2875
|
+
const seg = { kind: "static", span: valueSpan ?? void 0, tokens };
|
|
2876
|
+
return {
|
|
2877
|
+
form: "string-literal",
|
|
2878
|
+
segments: [seg],
|
|
2879
|
+
valueSpan,
|
|
2880
|
+
attrSpan,
|
|
2881
|
+
hasDynamic: false,
|
|
2882
|
+
opaque: false,
|
|
2883
|
+
rewritable: true
|
|
2884
|
+
};
|
|
2885
|
+
};
|
|
2886
|
+
if (v == null) return staticList([], null);
|
|
2887
|
+
if (v.type === "StringLiteral") {
|
|
2888
|
+
return staticList(splitTokens(v.value), spanOf(v));
|
|
1928
2889
|
}
|
|
1929
|
-
if (
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
for (const [key, value] of keyed) {
|
|
1934
|
-
if (remaining.get(key) !== value) {
|
|
1935
|
-
matches = false;
|
|
1936
|
-
break;
|
|
1937
|
-
}
|
|
2890
|
+
if (v.type === "JSXExpressionContainer") {
|
|
2891
|
+
const expr = v.expression;
|
|
2892
|
+
if (expr.type === "StringLiteral") {
|
|
2893
|
+
return staticList(splitTokens(expr.value), spanOf(expr));
|
|
1938
2894
|
}
|
|
1939
|
-
if (
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
}
|
|
1944
|
-
return { classes, exact: remaining.size === 0, warnings: [] };
|
|
1945
|
-
}
|
|
1946
|
-
selectorUsage(token) {
|
|
1947
|
-
const u = this.#usage.get(token);
|
|
1948
|
-
if (!u) {
|
|
2895
|
+
if (expr.type === "JSXEmptyExpression") return staticList([], null);
|
|
2896
|
+
const ref = internExpr(expr, false);
|
|
2897
|
+
const valueSpan = spanOf(expr);
|
|
2898
|
+
const seg = { kind: "dynamic", span: valueSpan ?? void 0, expr: ref };
|
|
1949
2899
|
return {
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
2900
|
+
form: classFormOf(expr),
|
|
2901
|
+
segments: [seg],
|
|
2902
|
+
valueSpan,
|
|
2903
|
+
attrSpan,
|
|
2904
|
+
hasDynamic: true,
|
|
2905
|
+
opaque: true,
|
|
2906
|
+
rewritable: false
|
|
1957
2907
|
};
|
|
1958
2908
|
}
|
|
1959
|
-
return
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
asStructural: u.asStructural,
|
|
1966
|
-
// Safe to drop/rename only when every reference is the lone subject of a bare `.x {}`.
|
|
1967
|
-
droppable: u.referenced && !u.loadBearing
|
|
1968
|
-
};
|
|
1969
|
-
}
|
|
1970
|
-
/**
|
|
1971
|
-
* The distinct COMPLEX selectors found across all stylesheets — anything containing a combinator
|
|
1972
|
-
* (descendant / `>` / `+` / `~`) or a structural pseudo (`:nth-child`, `:first-child`, …). Feeds
|
|
1973
|
-
* domflax's CSS-selector-safety guard.
|
|
1974
|
-
*/
|
|
1975
|
-
complexSelectors() {
|
|
1976
|
-
return this.#complex;
|
|
1977
|
-
}
|
|
1978
|
-
/* ─────────────────────────── internals ─────────────────────────── */
|
|
1979
|
-
/** Parse one stylesheet and fold its rules into the indexes. Returns the advanced order counter. */
|
|
1980
|
-
#indexFile(file, startOrder, complex) {
|
|
1981
|
-
let order = startOrder;
|
|
1982
|
-
let root;
|
|
1983
|
-
try {
|
|
1984
|
-
root = pc(file.css, { from: file.id });
|
|
1985
|
-
} catch {
|
|
1986
|
-
return order;
|
|
2909
|
+
return emptyClassList();
|
|
2910
|
+
};
|
|
2911
|
+
const staticTokensOf2 = (classes) => {
|
|
2912
|
+
const out = [];
|
|
2913
|
+
for (const seg of classes.segments) {
|
|
2914
|
+
if (seg.kind === "static") for (const t of seg.tokens) out.push(t.value);
|
|
1987
2915
|
}
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
2916
|
+
return out;
|
|
2917
|
+
};
|
|
2918
|
+
const buildAttrValue = (attr) => {
|
|
2919
|
+
const v = attr.value;
|
|
2920
|
+
if (v == null) return { kind: "static", value: true, span: spanOf(attr) ?? void 0 };
|
|
2921
|
+
if (v.type === "StringLiteral") {
|
|
2922
|
+
return { kind: "static", value: v.value, span: spanOf(v) ?? void 0 };
|
|
2923
|
+
}
|
|
2924
|
+
if (v.type === "JSXExpressionContainer") {
|
|
2925
|
+
if (v.expression.type === "JSXEmptyExpression") {
|
|
2926
|
+
return { kind: "static", value: true, span: spanOf(v) ?? void 0 };
|
|
2927
|
+
}
|
|
2928
|
+
return { kind: "dynamic", expr: internExpr(v.expression, false), span: spanOf(v) ?? void 0 };
|
|
2929
|
+
}
|
|
2930
|
+
return { kind: "dynamic", expr: internExpr(v, false), span: spanOf(v) ?? void 0 };
|
|
2931
|
+
};
|
|
2932
|
+
const buildNestedRoot = (jsx, parentId) => jsx.type === "JSXFragment" ? buildFragment(jsx, parentId) : buildElement(jsx, parentId);
|
|
2933
|
+
const appendChild = (node, parentId, out) => {
|
|
2934
|
+
switch (node.type) {
|
|
2935
|
+
case "JSXText": {
|
|
2936
|
+
const id = doc.alloc.next();
|
|
2937
|
+
doc.nodes.set(
|
|
2938
|
+
id,
|
|
2939
|
+
createText(id, node.value, {
|
|
2940
|
+
parent: parentId,
|
|
2941
|
+
span: spanOf(node),
|
|
2942
|
+
collapsible: /^\s*$/.test(node.value)
|
|
2943
|
+
})
|
|
2944
|
+
);
|
|
2945
|
+
out.push(id);
|
|
1996
2946
|
return;
|
|
1997
2947
|
}
|
|
1998
|
-
|
|
1999
|
-
const
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
return order;
|
|
2005
|
-
}
|
|
2006
|
-
/** Analyze one comma-segment selector: forward indexing, usage facts, complex detection. */
|
|
2007
|
-
#analyzeSelector(selector, media, decls, order, complex) {
|
|
2008
|
-
const compounds = splitCompounds(selector);
|
|
2009
|
-
let hasCombinator = false;
|
|
2010
|
-
let hasStructural = false;
|
|
2011
|
-
compounds.forEach((compound, index) => {
|
|
2012
|
-
const isSubject = index === compounds.length - 1;
|
|
2013
|
-
const rightCombinator = index < compounds.length - 1 ? compounds[index + 1].leftCombinator : null;
|
|
2014
|
-
if (rightCombinator) hasCombinator = true;
|
|
2015
|
-
const classes = compound.nodes.filter((n) => sp.isClassName(n));
|
|
2016
|
-
const otherSimple = compound.nodes.some(
|
|
2017
|
-
(n) => sp.isTag(n) || sp.isIdentifier(n) || sp.isAttribute(n) || sp.isUniversal(n) || sp.isNesting(n)
|
|
2018
|
-
);
|
|
2019
|
-
const pseudos = compound.nodes.filter((n) => sp.isPseudo(n));
|
|
2020
|
-
const structuralPseudo = pseudos.some((p) => STRUCTURAL_PSEUDOS.has(pseudoName(p)));
|
|
2021
|
-
const functionalPseudo = pseudos.some((p) => FUNCTIONAL_PSEUDOS.has(pseudoName(p)));
|
|
2022
|
-
const statePseudos = pseudos.filter(
|
|
2023
|
-
(p) => sp.isPseudoClass(p) && !STRUCTURAL_PSEUDOS.has(pseudoName(p)) && !FUNCTIONAL_PSEUDOS.has(pseudoName(p))
|
|
2024
|
-
);
|
|
2025
|
-
const elementPseudos = pseudos.filter((p) => isPseudoElement(p));
|
|
2026
|
-
const qualified = classes.length > 1 || otherSimple || functionalPseudo || statePseudos.length > 0;
|
|
2027
|
-
if (structuralPseudo) hasStructural = true;
|
|
2028
|
-
for (const cls of classes) {
|
|
2029
|
-
const token = cls.value;
|
|
2030
|
-
this.#known.add(token);
|
|
2031
|
-
const u = this.#getUsage(token);
|
|
2032
|
-
u.referenced = true;
|
|
2033
|
-
if (isSubject) u.asSubject = true;
|
|
2034
|
-
if (rightCombinator === " " || rightCombinator === ">") u.asAncestor = true;
|
|
2035
|
-
if (rightCombinator === "+" || rightCombinator === "~") u.asSibling = true;
|
|
2036
|
-
if (qualified) u.asCompound = true;
|
|
2037
|
-
if (structuralPseudo) u.asStructural = true;
|
|
2038
|
-
if (rightCombinator !== null || qualified || structuralPseudo || elementPseudos.length > 0) {
|
|
2039
|
-
u.loadBearing = true;
|
|
2948
|
+
case "JSXExpressionContainer": {
|
|
2949
|
+
const expr = node.expression;
|
|
2950
|
+
if (expr.type === "JSXEmptyExpression") return;
|
|
2951
|
+
if (expr.type === "JSXElement" || expr.type === "JSXFragment") {
|
|
2952
|
+
out.push(buildNestedRoot(expr, parentId));
|
|
2953
|
+
return;
|
|
2040
2954
|
}
|
|
2041
|
-
const
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2955
|
+
const id = doc.alloc.next();
|
|
2956
|
+
const ref = internExpr(expr, false);
|
|
2957
|
+
doc.nodes.set(id, createExpr(id, ref, { parent: parentId, span: spanOf(node) }));
|
|
2958
|
+
out.push(id);
|
|
2959
|
+
for (const jsx of findNestedJsxRoots(expr)) out.push(buildNestedRoot(jsx, parentId));
|
|
2960
|
+
return;
|
|
2961
|
+
}
|
|
2962
|
+
case "JSXSpreadChild": {
|
|
2963
|
+
const id = doc.alloc.next();
|
|
2964
|
+
const ref = internExpr(node.expression, true);
|
|
2965
|
+
doc.nodes.set(id, createExpr(id, ref, { parent: parentId, span: spanOf(node) }));
|
|
2966
|
+
out.push(id);
|
|
2967
|
+
for (const jsx of findNestedJsxRoots(node.expression)) {
|
|
2968
|
+
out.push(buildNestedRoot(jsx, parentId));
|
|
2049
2969
|
}
|
|
2970
|
+
return;
|
|
2050
2971
|
}
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2972
|
+
case "JSXElement":
|
|
2973
|
+
out.push(buildElement(node, parentId));
|
|
2974
|
+
return;
|
|
2975
|
+
case "JSXFragment":
|
|
2976
|
+
out.push(buildFragment(node, parentId));
|
|
2977
|
+
return;
|
|
2978
|
+
default:
|
|
2979
|
+
return;
|
|
2980
|
+
}
|
|
2981
|
+
};
|
|
2982
|
+
const buildFragment = (node, parentId) => {
|
|
2983
|
+
const id = doc.alloc.next();
|
|
2984
|
+
const children = [];
|
|
2985
|
+
for (const c of node.children) appendChild(c, id, children);
|
|
2986
|
+
doc.nodes.set(id, createFragment(id, { children, parent: parentId, span: spanOf(node) }));
|
|
2987
|
+
backref.set(id, {
|
|
2988
|
+
nodeId: id,
|
|
2989
|
+
span: spanOf(node) ?? { file: FILE_ID, start: 0, end: 0 },
|
|
2990
|
+
openTagSpan: spanOf(node.openingFragment),
|
|
2991
|
+
closeTagSpan: spanOf(node.closingFragment),
|
|
2992
|
+
innerSpan: null,
|
|
2993
|
+
selfClosing: false
|
|
2994
|
+
});
|
|
2995
|
+
return id;
|
|
2996
|
+
};
|
|
2997
|
+
const buildElement = (node, parentId) => {
|
|
2998
|
+
const id = doc.alloc.next();
|
|
2999
|
+
const opening = node.openingElement;
|
|
3000
|
+
const tag = jsxName(opening.name);
|
|
3001
|
+
const component = isComponentName(opening.name);
|
|
3002
|
+
const meta = defaultMeta();
|
|
3003
|
+
meta.isComponent = component;
|
|
3004
|
+
let classes = emptyClassList();
|
|
3005
|
+
const entries = /* @__PURE__ */ new Map();
|
|
3006
|
+
const order = [];
|
|
3007
|
+
const spreads = [];
|
|
3008
|
+
for (const attr of opening.attributes) {
|
|
3009
|
+
if (attr.type === "JSXSpreadAttribute") {
|
|
3010
|
+
spreads.push(internExpr(attr.argument, true));
|
|
3011
|
+
meta.hasSpreadAttrs = true;
|
|
3012
|
+
continue;
|
|
3013
|
+
}
|
|
3014
|
+
const name = attrName(attr.name);
|
|
3015
|
+
if (name === "className" || name === "class") {
|
|
3016
|
+
classes = buildClassList(attr);
|
|
3017
|
+
continue;
|
|
3018
|
+
}
|
|
3019
|
+
if (name === "ref") meta.hasRef = true;
|
|
3020
|
+
else if (name === "key") meta.hasKey = true;
|
|
3021
|
+
else if (name === "dangerouslySetInnerHTML") meta.hasDangerousHtml = true;
|
|
3022
|
+
else if (/^on[A-Z]/.test(name)) meta.hasEventHandlers = true;
|
|
3023
|
+
entries.set(name, buildAttrValue(attr));
|
|
3024
|
+
order.push(name);
|
|
3025
|
+
}
|
|
3026
|
+
const attrs = { entries, spreads, order };
|
|
3027
|
+
const children = [];
|
|
3028
|
+
for (const c of node.children) appendChild(c, id, children);
|
|
3029
|
+
for (const cid of children) {
|
|
3030
|
+
const cn = doc.nodes.get(cid);
|
|
3031
|
+
if (cn && cn.kind === "expr") {
|
|
3032
|
+
meta.hasDynamicChildren = true;
|
|
3033
|
+
break;
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
let computed2 = emptyStyleMap();
|
|
3037
|
+
if (!classes.hasDynamic) {
|
|
3038
|
+
const tokens = staticTokensOf2(classes);
|
|
3039
|
+
if (tokens.length > 0) {
|
|
3040
|
+
const res = ctx.resolver.resolve({
|
|
3041
|
+
classes: tokens,
|
|
3042
|
+
element: { tagName: tag, namespace: component ? void 0 : "html" }
|
|
2060
3043
|
});
|
|
3044
|
+
computed2 = ctx.normalizer.normalizeStyleMap(res.styles);
|
|
3045
|
+
for (const w of res.warnings) {
|
|
3046
|
+
diagnostics.push({
|
|
3047
|
+
code: "DF_STYLE_CONFLICT_UNRESOLVED",
|
|
3048
|
+
severity: w.severity,
|
|
3049
|
+
message: w.message,
|
|
3050
|
+
nodeId: id
|
|
3051
|
+
});
|
|
3052
|
+
}
|
|
2061
3053
|
}
|
|
3054
|
+
}
|
|
3055
|
+
const namespace = component ? "component" : "html";
|
|
3056
|
+
const el = createElement(id, {
|
|
3057
|
+
tag,
|
|
3058
|
+
namespace,
|
|
3059
|
+
isComponent: component,
|
|
3060
|
+
selfClosing: opening.selfClosing,
|
|
3061
|
+
classes,
|
|
3062
|
+
computed: computed2,
|
|
3063
|
+
attrs,
|
|
3064
|
+
children,
|
|
3065
|
+
parent: parentId,
|
|
3066
|
+
span: spanOf(node),
|
|
3067
|
+
meta
|
|
2062
3068
|
});
|
|
2063
|
-
|
|
2064
|
-
|
|
3069
|
+
doc.nodes.set(id, el);
|
|
3070
|
+
const inner = children.length > 0 ? spanOf(node.children[0]) && spanOf(node.children.at(-1)) ? {
|
|
3071
|
+
file: FILE_ID,
|
|
3072
|
+
start: spanOf(node.children[0]).start,
|
|
3073
|
+
end: spanOf(node.children.at(-1)).end
|
|
3074
|
+
} : null : null;
|
|
3075
|
+
backref.set(id, {
|
|
3076
|
+
nodeId: id,
|
|
3077
|
+
span: spanOf(node) ?? { file: FILE_ID, start: 0, end: 0 },
|
|
3078
|
+
openTagSpan: spanOf(opening),
|
|
3079
|
+
closeTagSpan: node.closingElement ? spanOf(node.closingElement) : null,
|
|
3080
|
+
innerSpan: inner,
|
|
3081
|
+
selfClosing: opening.selfClosing
|
|
3082
|
+
});
|
|
3083
|
+
return id;
|
|
3084
|
+
};
|
|
3085
|
+
const roots = [];
|
|
3086
|
+
traverse(ast, {
|
|
3087
|
+
JSXElement(path3) {
|
|
3088
|
+
roots.push(path3.node);
|
|
3089
|
+
path3.skip();
|
|
3090
|
+
},
|
|
3091
|
+
JSXFragment(path3) {
|
|
3092
|
+
roots.push(path3.node);
|
|
3093
|
+
path3.skip();
|
|
2065
3094
|
}
|
|
3095
|
+
});
|
|
3096
|
+
const rootFrag = doc.nodes.get(doc.root);
|
|
3097
|
+
for (const r of roots) {
|
|
3098
|
+
const id = r.type === "JSXFragment" ? buildFragment(r, doc.root) : buildElement(r, doc.root);
|
|
3099
|
+
rootFrag.children.push(id);
|
|
2066
3100
|
}
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
3101
|
+
return { doc, diagnostics };
|
|
3102
|
+
}
|
|
3103
|
+
|
|
3104
|
+
// ../frontend-jsx/src/frontend.ts
|
|
3105
|
+
var jsxFrontend = {
|
|
3106
|
+
name: "babel-jsx",
|
|
3107
|
+
langs: JSX_LANGS,
|
|
3108
|
+
canParse(id, code) {
|
|
3109
|
+
return looksLikeJsx(id, code);
|
|
3110
|
+
},
|
|
3111
|
+
parse(code, ctx) {
|
|
3112
|
+
return doParse(code, ctx);
|
|
2071
3113
|
}
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
3114
|
+
};
|
|
3115
|
+
function createJsxFrontend() {
|
|
3116
|
+
return jsxFrontend;
|
|
3117
|
+
}
|
|
3118
|
+
|
|
3119
|
+
// ../frontend-jsx/src/backend.ts
|
|
3120
|
+
init_esm_shims();
|
|
3121
|
+
import MagicString from "magic-string";
|
|
3122
|
+
var JSX_LANGS2 = ["jsx", "tsx"];
|
|
3123
|
+
function exprText(doc, ref) {
|
|
3124
|
+
const rec = doc.exprs.get(ref);
|
|
3125
|
+
const payload = rec?.payload;
|
|
3126
|
+
if (payload && typeof payload.text === "string") {
|
|
3127
|
+
return { text: payload.text, spread: payload.spread === true };
|
|
3128
|
+
}
|
|
3129
|
+
if (rec) {
|
|
3130
|
+
const sf = doc.sources.get(rec.span.file);
|
|
3131
|
+
if (sf) return { text: sf.text.slice(rec.span.start, rec.span.end), spread: false };
|
|
3132
|
+
}
|
|
3133
|
+
return { text: "", spread: false };
|
|
3134
|
+
}
|
|
3135
|
+
function staticTokensOf(classes) {
|
|
3136
|
+
const out = [];
|
|
3137
|
+
for (const seg of classes.segments) {
|
|
3138
|
+
if (seg.kind === "static") for (const t of seg.tokens) out.push(t.value);
|
|
3139
|
+
}
|
|
3140
|
+
return out;
|
|
3141
|
+
}
|
|
3142
|
+
function primarySource(doc) {
|
|
3143
|
+
for (const sf of doc.sources.values()) {
|
|
3144
|
+
if (typeof sf.text === "string" && sf.text.length > 0) return sf;
|
|
2088
3145
|
}
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
3146
|
+
return null;
|
|
3147
|
+
}
|
|
3148
|
+
function collectKept(doc) {
|
|
3149
|
+
const out = [];
|
|
3150
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3151
|
+
const visit = (id) => {
|
|
3152
|
+
if (seen.has(id)) return;
|
|
3153
|
+
seen.add(id);
|
|
3154
|
+
const n = doc.nodes.get(id);
|
|
3155
|
+
if (!n) return;
|
|
3156
|
+
out.push(n);
|
|
3157
|
+
if (n.kind === "element" || n.kind === "fragment") for (const c of n.children) visit(c);
|
|
3158
|
+
};
|
|
3159
|
+
visit(doc.root);
|
|
3160
|
+
return out;
|
|
3161
|
+
}
|
|
3162
|
+
function strictlyContains(a, b) {
|
|
3163
|
+
if (a.file !== b.file) return false;
|
|
3164
|
+
if (a.start <= b.start && b.end <= a.end) return !(a.start === b.start && a.end === b.end);
|
|
3165
|
+
return false;
|
|
3166
|
+
}
|
|
3167
|
+
function editClasses(ms, doc, sf, el) {
|
|
3168
|
+
const classes = el.classes;
|
|
3169
|
+
if (classes.hasDynamic || classes.opaque) return false;
|
|
3170
|
+
const tokens = staticTokensOf(classes);
|
|
3171
|
+
const valueSpan = classes.valueSpan;
|
|
3172
|
+
if (valueSpan && valueSpan.file === sf.id) {
|
|
3173
|
+
const current = sf.text.slice(valueSpan.start, valueSpan.end);
|
|
3174
|
+
const quote = current.startsWith("'") ? "'" : '"';
|
|
3175
|
+
const next = `${quote}${tokens.join(" ")}${quote}`;
|
|
3176
|
+
if (current !== next) {
|
|
3177
|
+
ms.overwrite(valueSpan.start, valueSpan.end, next);
|
|
3178
|
+
return true;
|
|
2098
3179
|
}
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
3180
|
+
return false;
|
|
3181
|
+
}
|
|
3182
|
+
if (tokens.length === 0) return false;
|
|
3183
|
+
if (el.isComponent) return false;
|
|
3184
|
+
const back = doc.backref.get(el.id);
|
|
3185
|
+
const openTag = back?.openTagSpan;
|
|
3186
|
+
if (!openTag || openTag.file !== sf.id) return false;
|
|
3187
|
+
const insertAt = openTag.start + 1 + el.tag.length;
|
|
3188
|
+
ms.appendLeft(insertAt, ` className="${tokens.join(" ")}"`);
|
|
3189
|
+
return true;
|
|
3190
|
+
}
|
|
3191
|
+
function extractKeyAttr(openTag) {
|
|
3192
|
+
const m = /(^|\s)key\s*=\s*/.exec(openTag);
|
|
3193
|
+
if (!m) return null;
|
|
3194
|
+
const keyStart = m.index + m[1].length;
|
|
3195
|
+
let i = m.index + m[0].length;
|
|
3196
|
+
const ch = openTag[i];
|
|
3197
|
+
if (ch === "{") {
|
|
3198
|
+
let depth = 0;
|
|
3199
|
+
for (; i < openTag.length; i += 1) {
|
|
3200
|
+
const c = openTag[i];
|
|
3201
|
+
if (c === "{") depth += 1;
|
|
3202
|
+
else if (c === "}") {
|
|
3203
|
+
depth -= 1;
|
|
3204
|
+
if (depth === 0) {
|
|
3205
|
+
i += 1;
|
|
3206
|
+
break;
|
|
2114
3207
|
}
|
|
2115
3208
|
}
|
|
2116
3209
|
}
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
}
|
|
2125
|
-
/** Build (once) the reverse index used by {@link emit}. */
|
|
2126
|
-
#reverseIndex() {
|
|
2127
|
-
if (this.#reverse) return this.#reverse;
|
|
2128
|
-
const out = [];
|
|
2129
|
-
for (const token of this.#classIndex.keys()) {
|
|
2130
|
-
const styles = this.#resolveTokens([token], [token]);
|
|
2131
|
-
const keyed = /* @__PURE__ */ new Map();
|
|
2132
|
-
for (const [ck, block] of styles.blocks) {
|
|
2133
|
-
for (const [prop, decl] of block.decls) keyed.set(`${ck}\0${prop}`, String(decl.value));
|
|
3210
|
+
} else if (ch === '"' || ch === "'") {
|
|
3211
|
+
const q = ch;
|
|
3212
|
+
i += 1;
|
|
3213
|
+
for (; i < openTag.length; i += 1) {
|
|
3214
|
+
if (openTag[i] === q) {
|
|
3215
|
+
i += 1;
|
|
3216
|
+
break;
|
|
2134
3217
|
}
|
|
2135
|
-
if (keyed.size > 0) out.push({ token, keyed });
|
|
2136
3218
|
}
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
3219
|
+
} else {
|
|
3220
|
+
for (; i < openTag.length; i += 1) {
|
|
3221
|
+
if (/[\s>/]/.test(openTag[i])) break;
|
|
3222
|
+
}
|
|
2140
3223
|
}
|
|
2141
|
-
|
|
2142
|
-
function createCssResolver(cssFiles = [], options) {
|
|
2143
|
-
return new CustomCSSResolver(cssFiles, options);
|
|
3224
|
+
return openTag.slice(keyStart, i);
|
|
2144
3225
|
}
|
|
2145
|
-
function
|
|
2146
|
-
const
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
3226
|
+
function transferKeyOnUnwrap(ms, doc, sf, region, kept) {
|
|
3227
|
+
const open = region.openTagSpan;
|
|
3228
|
+
if (!open || open.file !== sf.id) return;
|
|
3229
|
+
const keyAttr = extractKeyAttr(sf.text.slice(open.start, open.end));
|
|
3230
|
+
if (!keyAttr) return;
|
|
3231
|
+
const inside = [];
|
|
3232
|
+
for (const n of kept) {
|
|
3233
|
+
if (n.kind !== "element" || !n.span || n.span.file !== sf.id) continue;
|
|
3234
|
+
if (strictlyContains(region.span, n.span)) inside.push(n);
|
|
3235
|
+
}
|
|
3236
|
+
const maximal = inside.filter(
|
|
3237
|
+
(n) => !inside.some((o) => o !== n && o.span && n.span && strictlyContains(o.span, n.span))
|
|
3238
|
+
);
|
|
3239
|
+
if (maximal.length !== 1) return;
|
|
3240
|
+
const child = maximal[0];
|
|
3241
|
+
const childOpen = doc.backref.get(child.id)?.openTagSpan;
|
|
3242
|
+
if (!childOpen || childOpen.file !== sf.id) return;
|
|
3243
|
+
if (extractKeyAttr(sf.text.slice(childOpen.start, childOpen.end))) return;
|
|
3244
|
+
ms.appendLeft(childOpen.start + 1 + child.tag.length, ` ${keyAttr}`);
|
|
3245
|
+
}
|
|
3246
|
+
function surgicalPrint(doc) {
|
|
3247
|
+
const sf = primarySource(doc);
|
|
3248
|
+
if (!sf) return null;
|
|
3249
|
+
const ms = new MagicString(sf.text);
|
|
3250
|
+
const kept = collectKept(doc);
|
|
3251
|
+
const keptSpans = [];
|
|
3252
|
+
for (const n of kept) if (n.span && n.span.file === sf.id) keptSpans.push(n.span);
|
|
3253
|
+
const removed = [];
|
|
3254
|
+
for (const id of backrefIds(doc)) {
|
|
3255
|
+
if (doc.nodes.has(id)) continue;
|
|
3256
|
+
const back = doc.backref.get(id);
|
|
3257
|
+
if (!back || back.span.file !== sf.id) continue;
|
|
3258
|
+
const unwrapped = keptSpans.some((k) => strictlyContains(back.span, k));
|
|
3259
|
+
removed.push({ backref: back, unwrapped });
|
|
3260
|
+
}
|
|
3261
|
+
const fullRemovals = removed.filter((r) => !r.unwrapped).map((r) => r.backref.span);
|
|
3262
|
+
for (const r of removed) {
|
|
3263
|
+
const span = r.backref.span;
|
|
3264
|
+
const coveredByFull = fullRemovals.some((f) => f !== span && strictlyContains(f, span));
|
|
3265
|
+
if (coveredByFull) continue;
|
|
3266
|
+
if (r.unwrapped) {
|
|
3267
|
+
transferKeyOnUnwrap(ms, doc, sf, r.backref, kept);
|
|
3268
|
+
const open = r.backref.openTagSpan;
|
|
3269
|
+
const close = r.backref.closeTagSpan;
|
|
3270
|
+
if (open && open.file === sf.id && open.end > open.start) ms.remove(open.start, open.end);
|
|
3271
|
+
if (close && close.file === sf.id && close.end > close.start) {
|
|
3272
|
+
ms.remove(close.start, close.end);
|
|
3273
|
+
}
|
|
2154
3274
|
} else {
|
|
2155
|
-
|
|
3275
|
+
ms.remove(span.start, span.end);
|
|
2156
3276
|
}
|
|
2157
3277
|
}
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
}
|
|
2161
|
-
|
|
2162
|
-
const v = node.value;
|
|
2163
|
-
return v.trim() === "" ? " " : v.trim();
|
|
3278
|
+
for (const n of kept) {
|
|
3279
|
+
if (n.kind === "element") editClasses(ms, doc, sf, n);
|
|
3280
|
+
}
|
|
3281
|
+
return ms.toString();
|
|
2164
3282
|
}
|
|
2165
|
-
function
|
|
2166
|
-
|
|
3283
|
+
function backrefIds(doc) {
|
|
3284
|
+
const out = [];
|
|
3285
|
+
const max = doc.alloc.peek;
|
|
3286
|
+
for (let i = 1; i < max; i += 1) {
|
|
3287
|
+
const id = i;
|
|
3288
|
+
if (doc.backref.get(id)) out.push(id);
|
|
3289
|
+
}
|
|
3290
|
+
return out;
|
|
2167
3291
|
}
|
|
2168
|
-
function
|
|
2169
|
-
|
|
3292
|
+
function classText(doc, classes) {
|
|
3293
|
+
if (classes.form === "absent" || classes.segments.length === 0) return null;
|
|
3294
|
+
const dynamic = classes.segments.find((s) => s.kind === "dynamic");
|
|
3295
|
+
if (dynamic && dynamic.kind === "dynamic") {
|
|
3296
|
+
return `className={${exprText(doc, dynamic.expr).text}}`;
|
|
3297
|
+
}
|
|
3298
|
+
const tokens = staticTokensOf(classes);
|
|
3299
|
+
return `className="${tokens.join(" ")}"`;
|
|
2170
3300
|
}
|
|
2171
|
-
function
|
|
2172
|
-
|
|
2173
|
-
|
|
3301
|
+
function attrText(doc, name, value) {
|
|
3302
|
+
if (value.kind === "static") {
|
|
3303
|
+
if (value.value === true) return name;
|
|
3304
|
+
if (value.value === false) return "";
|
|
3305
|
+
return `${name}="${String(value.value)}"`;
|
|
3306
|
+
}
|
|
3307
|
+
return `${name}={${exprText(doc, value.expr).text}}`;
|
|
2174
3308
|
}
|
|
2175
|
-
function
|
|
3309
|
+
function printElement(doc, el) {
|
|
2176
3310
|
const parts = [];
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
const
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
parent = parent.parent;
|
|
3311
|
+
const cls = classText(doc, el.classes);
|
|
3312
|
+
if (cls !== null) parts.push(cls);
|
|
3313
|
+
for (const name of el.attrs.order) {
|
|
3314
|
+
const v = el.attrs.entries.get(name);
|
|
3315
|
+
if (!v) continue;
|
|
3316
|
+
const text = attrText(doc, name, v);
|
|
3317
|
+
if (text.length > 0) parts.push(text);
|
|
2185
3318
|
}
|
|
2186
|
-
|
|
3319
|
+
for (const ref of el.attrs.spreads) parts.push(`{...${exprText(doc, ref).text}}`);
|
|
3320
|
+
const attrStr = parts.length > 0 ? ` ${parts.join(" ")}` : "";
|
|
3321
|
+
const tag = el.tag;
|
|
3322
|
+
if (el.children.length === 0) {
|
|
3323
|
+
return el.selfClosing ? `<${tag}${attrStr} />` : `<${tag}${attrStr}></${tag}>`;
|
|
3324
|
+
}
|
|
3325
|
+
const inner = el.children.map((c) => printNode(doc, c)).join("");
|
|
3326
|
+
return `<${tag}${attrStr}>${inner}</${tag}>`;
|
|
2187
3327
|
}
|
|
2188
|
-
function
|
|
2189
|
-
const
|
|
2190
|
-
|
|
2191
|
-
|
|
3328
|
+
function printNode(doc, id) {
|
|
3329
|
+
const node = doc.nodes.get(id);
|
|
3330
|
+
if (!node) return "";
|
|
3331
|
+
switch (node.kind) {
|
|
3332
|
+
case "text":
|
|
3333
|
+
return node.value;
|
|
3334
|
+
case "comment":
|
|
3335
|
+
return `{/*${node.value}*/}`;
|
|
3336
|
+
case "expr": {
|
|
3337
|
+
const { text, spread } = exprText(doc, node.expr);
|
|
3338
|
+
return spread ? `{...${text}}` : `{${text}}`;
|
|
3339
|
+
}
|
|
3340
|
+
case "fragment":
|
|
3341
|
+
return `<>${node.children.map((c) => printNode(doc, c)).join("")}</>`;
|
|
3342
|
+
case "element":
|
|
3343
|
+
return printElement(doc, node);
|
|
2192
3344
|
}
|
|
2193
|
-
return out;
|
|
2194
3345
|
}
|
|
2195
|
-
function
|
|
2196
|
-
|
|
3346
|
+
function rePrint(doc) {
|
|
3347
|
+
const root = doc.nodes.get(doc.root);
|
|
3348
|
+
if (!root || root.kind !== "fragment") return printNode(doc, doc.root);
|
|
3349
|
+
return root.children.map((c) => printNode(doc, c)).join("");
|
|
2197
3350
|
}
|
|
2198
|
-
function
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
} catch (cause) {
|
|
2202
|
-
throw new Error(`resolver-css: cannot read CSS file "${path3}"`, { cause });
|
|
2203
|
-
}
|
|
3351
|
+
function doPrint(doc) {
|
|
3352
|
+
const surgical = surgicalPrint(doc);
|
|
3353
|
+
return surgical ?? rePrint(doc);
|
|
2204
3354
|
}
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
3355
|
+
var jsxBackend = {
|
|
3356
|
+
name: "babel-jsx",
|
|
3357
|
+
langs: JSX_LANGS2,
|
|
3358
|
+
print(doc, _plan, _ctx) {
|
|
3359
|
+
const code = doPrint(doc);
|
|
3360
|
+
return { code, map: null, edits: [], diagnostics: [] };
|
|
3361
|
+
}
|
|
3362
|
+
};
|
|
3363
|
+
function createJsxBackend() {
|
|
3364
|
+
return jsxBackend;
|
|
2208
3365
|
}
|
|
2209
3366
|
|
|
3367
|
+
// ../frontend-jsx/src/index.ts
|
|
3368
|
+
init_esm_shims();
|
|
3369
|
+
|
|
2210
3370
|
export {
|
|
3371
|
+
displayContentsWrapper,
|
|
2211
3372
|
emptyStyleDiv,
|
|
2212
3373
|
flexCenterWrapper,
|
|
3374
|
+
inlineFlexCenterWrapper,
|
|
2213
3375
|
nestedFlexMerge,
|
|
3376
|
+
nestedGridMerge,
|
|
2214
3377
|
passthroughWrapper,
|
|
2215
3378
|
redundantFragment,
|
|
3379
|
+
redundantInlineWrapper,
|
|
3380
|
+
borderRadiusShorthand,
|
|
3381
|
+
borderShorthand,
|
|
2216
3382
|
dedupeClasses,
|
|
3383
|
+
gapShorthand,
|
|
2217
3384
|
insetShorthand,
|
|
2218
3385
|
marginShorthand,
|
|
3386
|
+
overflowShorthand,
|
|
3387
|
+
overscrollBehaviorShorthand,
|
|
2219
3388
|
paddingShorthand,
|
|
3389
|
+
placeShorthand,
|
|
3390
|
+
scrollMarginShorthand,
|
|
3391
|
+
scrollPaddingShorthand,
|
|
2220
3392
|
sizeShorthand,
|
|
2221
3393
|
builtinPatterns,
|
|
2222
|
-
createJsxFrontend,
|
|
2223
|
-
createJsxBackend,
|
|
2224
3394
|
createTailwindResolver,
|
|
2225
|
-
createCssResolver
|
|
3395
|
+
createCssResolver,
|
|
3396
|
+
createJsxFrontend,
|
|
3397
|
+
createJsxBackend
|
|
2226
3398
|
};
|
|
2227
|
-
//# sourceMappingURL=chunk-
|
|
3399
|
+
//# sourceMappingURL=chunk-DNHOGPYV.js.map
|