domflax 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +214 -0
- package/dist/chunk-4HHISSMR.js +2227 -0
- package/dist/chunk-4HHISSMR.js.map +1 -0
- package/dist/chunk-6WVVF6AD.js +55 -0
- package/dist/chunk-6WVVF6AD.js.map +1 -0
- package/dist/chunk-77SLHRN6.js +2047 -0
- package/dist/chunk-77SLHRN6.js.map +1 -0
- package/dist/chunk-ZJ2S36GY.js +175 -0
- package/dist/chunk-ZJ2S36GY.js.map +1 -0
- package/dist/cli.cjs +5207 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +1310 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +4383 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +539 -0
- package/dist/index.d.ts +539 -0
- package/dist/index.js +110 -0
- package/dist/index.js.map +1 -0
- package/dist/pattern-CX6iBzTD.d.ts +237 -0
- package/dist/pattern-P4FIKAUB.d.cts +237 -0
- package/dist/pattern-kit.cjs +630 -0
- package/dist/pattern-kit.cjs.map +1 -0
- package/dist/pattern-kit.d.cts +80 -0
- package/dist/pattern-kit.d.ts +80 -0
- package/dist/pattern-kit.js +55 -0
- package/dist/pattern-kit.js.map +1 -0
- package/dist/types-BQ7l6dVe.d.cts +749 -0
- package/dist/types-BQ7l6dVe.d.ts +749 -0
- package/dist/verify.cjs +2747 -0
- package/dist/verify.cjs.map +1 -0
- package/dist/verify.d.cts +245 -0
- package/dist/verify.d.ts +245 -0
- package/dist/verify.js +2700 -0
- package/dist/verify.js.map +1 -0
- package/dist/webpack-loader.cjs +4149 -0
- package/dist/webpack-loader.cjs.map +1 -0
- package/dist/webpack-loader.d.cts +21 -0
- package/dist/webpack-loader.d.ts +21 -0
- package/dist/webpack-loader.js +30 -0
- package/dist/webpack-loader.js.map +1 -0
- package/package.json +99 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,4383 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
BASE_CONDITION: () => BASE_CONDITION,
|
|
34
|
+
BASE_CONDITION_KEY: () => BASE_CONDITION_KEY,
|
|
35
|
+
DEFAULT_FIXPOINT: () => DEFAULT_FIXPOINT,
|
|
36
|
+
applyGroups: () => applyGroups,
|
|
37
|
+
applyOps: () => applyOps,
|
|
38
|
+
buildSelectorIndex: () => buildSelectorIndex,
|
|
39
|
+
builtinPatterns: () => builtinPatterns,
|
|
40
|
+
childIds: () => childIds,
|
|
41
|
+
cloneDocument: () => cloneDocument,
|
|
42
|
+
conditionKey: () => conditionKey,
|
|
43
|
+
createBackrefTable: () => createBackrefTable,
|
|
44
|
+
createComment: () => createComment,
|
|
45
|
+
createDocument: () => createDocument,
|
|
46
|
+
createDomflax: () => createDomflax,
|
|
47
|
+
createElement: () => createElement,
|
|
48
|
+
createExpr: () => createExpr,
|
|
49
|
+
createExprRegistry: () => createExprRegistry,
|
|
50
|
+
createFragment: () => createFragment,
|
|
51
|
+
createIdAllocator: () => createIdAllocator,
|
|
52
|
+
createNullResolver: () => createNullResolver,
|
|
53
|
+
createNullSelectorIndex: () => createNullSelectorIndex,
|
|
54
|
+
createPassManager: () => createPassManager,
|
|
55
|
+
createPipeline: () => createPipeline,
|
|
56
|
+
createRewriteFactory: () => createRewriteFactory,
|
|
57
|
+
createSyntheticSink: () => createSyntheticSink,
|
|
58
|
+
createText: () => createText,
|
|
59
|
+
dedupeClasses: () => dedupeClasses,
|
|
60
|
+
default: () => src_default,
|
|
61
|
+
defaultMeta: () => defaultMeta,
|
|
62
|
+
elementIds: () => elementIds,
|
|
63
|
+
emptyAttrMap: () => emptyAttrMap,
|
|
64
|
+
emptyClassList: () => emptyClassList,
|
|
65
|
+
emptyInlineStyle: () => emptyInlineStyle,
|
|
66
|
+
emptyStyleDiv: () => emptyStyleDiv,
|
|
67
|
+
emptyStyleMap: () => emptyStyleMap,
|
|
68
|
+
flexCenterWrapper: () => flexCenterWrapper,
|
|
69
|
+
getElement: () => getElement,
|
|
70
|
+
getNode: () => getNode,
|
|
71
|
+
insetShorthand: () => insetShorthand,
|
|
72
|
+
marginShorthand: () => marginShorthand,
|
|
73
|
+
nestedFlexMerge: () => nestedFlexMerge,
|
|
74
|
+
paddingShorthand: () => paddingShorthand,
|
|
75
|
+
passthroughWrapper: () => passthroughWrapper,
|
|
76
|
+
redundantFragment: () => redundantFragment,
|
|
77
|
+
runPasses: () => runPasses,
|
|
78
|
+
sizeShorthand: () => sizeShorthand,
|
|
79
|
+
syncClassesFromComputed: () => syncClassesFromComputed,
|
|
80
|
+
vite: () => vite,
|
|
81
|
+
walk: () => walk,
|
|
82
|
+
webpack: () => webpack
|
|
83
|
+
});
|
|
84
|
+
module.exports = __toCommonJS(src_exports);
|
|
85
|
+
|
|
86
|
+
// ../../node_modules/tsup/assets/cjs_shims.js
|
|
87
|
+
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
88
|
+
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
89
|
+
|
|
90
|
+
// ../core/src/builders.ts
|
|
91
|
+
function createIdAllocator(start = 1) {
|
|
92
|
+
let n = start;
|
|
93
|
+
return {
|
|
94
|
+
next() {
|
|
95
|
+
const id = n;
|
|
96
|
+
n += 1;
|
|
97
|
+
return id;
|
|
98
|
+
},
|
|
99
|
+
get peek() {
|
|
100
|
+
return n;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function createExprRegistry(start = 1) {
|
|
105
|
+
const map = /* @__PURE__ */ new Map();
|
|
106
|
+
let n = start;
|
|
107
|
+
return {
|
|
108
|
+
get(r) {
|
|
109
|
+
return map.get(r);
|
|
110
|
+
},
|
|
111
|
+
intern(rec) {
|
|
112
|
+
const ref = n;
|
|
113
|
+
n += 1;
|
|
114
|
+
map.set(ref, { ...rec, ref });
|
|
115
|
+
return ref;
|
|
116
|
+
},
|
|
117
|
+
releasePayloads() {
|
|
118
|
+
for (const [k, v] of map) map.set(k, { ...v, payload: void 0 });
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function createBackrefTable() {
|
|
123
|
+
const map = /* @__PURE__ */ new Map();
|
|
124
|
+
return {
|
|
125
|
+
get(id) {
|
|
126
|
+
return map.get(id);
|
|
127
|
+
},
|
|
128
|
+
span(id) {
|
|
129
|
+
return map.get(id)?.span ?? null;
|
|
130
|
+
},
|
|
131
|
+
childrenSpan(id) {
|
|
132
|
+
return map.get(id)?.innerSpan ?? null;
|
|
133
|
+
},
|
|
134
|
+
set(id, backref) {
|
|
135
|
+
map.set(id, backref);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function defaultMeta(safetyFloor = 0) {
|
|
140
|
+
return {
|
|
141
|
+
hasRef: false,
|
|
142
|
+
hasEventHandlers: false,
|
|
143
|
+
hasKey: false,
|
|
144
|
+
hasSpreadAttrs: false,
|
|
145
|
+
hasDynamicChildren: false,
|
|
146
|
+
isComponent: false,
|
|
147
|
+
hasDangerousHtml: false,
|
|
148
|
+
targetedByCombinator: false,
|
|
149
|
+
targetedByStructuralPseudo: false,
|
|
150
|
+
selectorDependents: 0,
|
|
151
|
+
hasOwnVisualStyle: false,
|
|
152
|
+
establishesBox: false,
|
|
153
|
+
establishesStackingContext: false,
|
|
154
|
+
isContainingBlock: false,
|
|
155
|
+
establishesFormattingContext: false,
|
|
156
|
+
declaresCustomProperties: false,
|
|
157
|
+
whitespaceSensitive: false,
|
|
158
|
+
touched: false,
|
|
159
|
+
synthetic: false,
|
|
160
|
+
safetyFloor
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
var BASE_CONDITION = { media: "", states: [], pseudoElement: "" };
|
|
164
|
+
function conditionKey(c) {
|
|
165
|
+
const states = [...c.states].sort().join(",");
|
|
166
|
+
return `${c.media}|${states}|${c.pseudoElement}`;
|
|
167
|
+
}
|
|
168
|
+
var BASE_CONDITION_KEY = conditionKey(BASE_CONDITION);
|
|
169
|
+
function emptyStyleMap() {
|
|
170
|
+
return { blocks: /* @__PURE__ */ new Map() };
|
|
171
|
+
}
|
|
172
|
+
function emptyClassList() {
|
|
173
|
+
return {
|
|
174
|
+
form: "absent",
|
|
175
|
+
segments: [],
|
|
176
|
+
valueSpan: null,
|
|
177
|
+
hasDynamic: false,
|
|
178
|
+
opaque: false,
|
|
179
|
+
rewritable: false
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function emptyAttrMap() {
|
|
183
|
+
return { entries: /* @__PURE__ */ new Map(), spreads: [], order: [] };
|
|
184
|
+
}
|
|
185
|
+
function emptyInlineStyle() {
|
|
186
|
+
return { decls: /* @__PURE__ */ new Map(), dynamic: null };
|
|
187
|
+
}
|
|
188
|
+
function createElement(id, init) {
|
|
189
|
+
return {
|
|
190
|
+
id,
|
|
191
|
+
kind: "element",
|
|
192
|
+
parent: init.parent ?? null,
|
|
193
|
+
span: init.span ?? null,
|
|
194
|
+
meta: init.meta ?? defaultMeta(),
|
|
195
|
+
tag: init.tag,
|
|
196
|
+
namespace: init.namespace ?? "html",
|
|
197
|
+
isComponent: init.isComponent ?? false,
|
|
198
|
+
selfClosing: init.selfClosing ?? false,
|
|
199
|
+
classes: init.classes ?? emptyClassList(),
|
|
200
|
+
inlineStyle: init.inlineStyle ?? emptyInlineStyle(),
|
|
201
|
+
computed: init.computed ?? emptyStyleMap(),
|
|
202
|
+
attrs: init.attrs ?? emptyAttrMap(),
|
|
203
|
+
children: init.children ?? []
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function createText(id, value, opts) {
|
|
207
|
+
return {
|
|
208
|
+
id,
|
|
209
|
+
kind: "text",
|
|
210
|
+
parent: opts?.parent ?? null,
|
|
211
|
+
span: opts?.span ?? null,
|
|
212
|
+
meta: defaultMeta(),
|
|
213
|
+
value,
|
|
214
|
+
collapsible: opts?.collapsible ?? true
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function createExpr(id, expr, opts) {
|
|
218
|
+
return {
|
|
219
|
+
id,
|
|
220
|
+
kind: "expr",
|
|
221
|
+
parent: opts?.parent ?? null,
|
|
222
|
+
span: opts?.span ?? null,
|
|
223
|
+
meta: defaultMeta(),
|
|
224
|
+
expr
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function createFragment(id, opts) {
|
|
228
|
+
return {
|
|
229
|
+
id,
|
|
230
|
+
kind: "fragment",
|
|
231
|
+
parent: opts?.parent ?? null,
|
|
232
|
+
span: opts?.span ?? null,
|
|
233
|
+
meta: defaultMeta(),
|
|
234
|
+
children: opts?.children ?? []
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
function createComment(id, value, opts) {
|
|
238
|
+
return {
|
|
239
|
+
id,
|
|
240
|
+
kind: "comment",
|
|
241
|
+
parent: opts?.parent ?? null,
|
|
242
|
+
span: opts?.span ?? null,
|
|
243
|
+
meta: defaultMeta(),
|
|
244
|
+
value
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function createDocument(frontend) {
|
|
248
|
+
const alloc = createIdAllocator();
|
|
249
|
+
const rootId = alloc.next();
|
|
250
|
+
const root = createFragment(rootId);
|
|
251
|
+
const nodes = /* @__PURE__ */ new Map([[rootId, root]]);
|
|
252
|
+
return {
|
|
253
|
+
root: rootId,
|
|
254
|
+
nodes,
|
|
255
|
+
exprs: createExprRegistry(),
|
|
256
|
+
sources: /* @__PURE__ */ new Map(),
|
|
257
|
+
backref: createBackrefTable(),
|
|
258
|
+
frontend,
|
|
259
|
+
alloc
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
function childIds(node) {
|
|
263
|
+
return node.kind === "element" || node.kind === "fragment" ? node.children : [];
|
|
264
|
+
}
|
|
265
|
+
function getNode(doc, id) {
|
|
266
|
+
return doc.nodes.get(id);
|
|
267
|
+
}
|
|
268
|
+
function getElement(doc, id) {
|
|
269
|
+
const n = doc.nodes.get(id);
|
|
270
|
+
return n && n.kind === "element" ? n : void 0;
|
|
271
|
+
}
|
|
272
|
+
function elementIds(doc) {
|
|
273
|
+
const out = [];
|
|
274
|
+
const visit = (id) => {
|
|
275
|
+
const n = doc.nodes.get(id);
|
|
276
|
+
if (!n) return;
|
|
277
|
+
if (n.kind === "element") out.push(id);
|
|
278
|
+
for (const c of childIds(n)) visit(c);
|
|
279
|
+
};
|
|
280
|
+
visit(doc.root);
|
|
281
|
+
return out;
|
|
282
|
+
}
|
|
283
|
+
function walk(doc, visitor) {
|
|
284
|
+
const roDoc = doc;
|
|
285
|
+
let stopped = false;
|
|
286
|
+
const visit = (id, depth) => {
|
|
287
|
+
if (stopped) return;
|
|
288
|
+
const node = doc.nodes.get(id);
|
|
289
|
+
if (!node) return;
|
|
290
|
+
const ctx = {
|
|
291
|
+
doc: roDoc,
|
|
292
|
+
depth,
|
|
293
|
+
parent() {
|
|
294
|
+
return node.parent == null ? null : doc.nodes.get(node.parent) ?? null;
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
const entered = visitor.enter ? visitor.enter(node, ctx) : void 0;
|
|
298
|
+
if (entered === "stop") {
|
|
299
|
+
stopped = true;
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
if (entered !== "skip") {
|
|
303
|
+
for (const child of childIds(node)) {
|
|
304
|
+
visit(child, depth + 1);
|
|
305
|
+
if (stopped) return;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
const exited = visitor.exit ? visitor.exit(node, ctx) : void 0;
|
|
309
|
+
if (exited === "stop") stopped = true;
|
|
310
|
+
};
|
|
311
|
+
visit(doc.root, 0);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// ../core/src/ops.ts
|
|
315
|
+
function cloneStyleMap(sm) {
|
|
316
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
317
|
+
for (const [key, block] of sm.blocks) {
|
|
318
|
+
blocks.set(key, {
|
|
319
|
+
condition: block.condition,
|
|
320
|
+
decls: new Map(block.decls)
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
return { blocks };
|
|
324
|
+
}
|
|
325
|
+
function cloneNode(node) {
|
|
326
|
+
const meta = { ...node.meta };
|
|
327
|
+
switch (node.kind) {
|
|
328
|
+
case "element":
|
|
329
|
+
return {
|
|
330
|
+
...node,
|
|
331
|
+
meta,
|
|
332
|
+
children: [...node.children],
|
|
333
|
+
computed: cloneStyleMap(node.computed)
|
|
334
|
+
};
|
|
335
|
+
case "fragment":
|
|
336
|
+
return { ...node, meta, children: [...node.children] };
|
|
337
|
+
default:
|
|
338
|
+
return { ...node, meta };
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
function cloneDocument(doc) {
|
|
342
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
343
|
+
for (const [id, n] of doc.nodes) nodes.set(id, cloneNode(n));
|
|
344
|
+
return {
|
|
345
|
+
root: doc.root,
|
|
346
|
+
nodes,
|
|
347
|
+
exprs: doc.exprs,
|
|
348
|
+
sources: doc.sources,
|
|
349
|
+
backref: doc.backref,
|
|
350
|
+
frontend: doc.frontend,
|
|
351
|
+
alloc: doc.alloc
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
function diag(code, message, extra) {
|
|
355
|
+
return { code, severity: "warn", message, ...extra };
|
|
356
|
+
}
|
|
357
|
+
function getParentChildren(doc, id) {
|
|
358
|
+
const node = doc.nodes.get(id);
|
|
359
|
+
if (!node || node.parent == null) return null;
|
|
360
|
+
const parent = doc.nodes.get(node.parent);
|
|
361
|
+
if (!parent) return null;
|
|
362
|
+
if (parent.kind === "element" || parent.kind === "fragment") return parent.children;
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
function markTouched(state, id) {
|
|
366
|
+
const n = state.doc.nodes.get(id);
|
|
367
|
+
if (n) {
|
|
368
|
+
n.meta.touched = true;
|
|
369
|
+
state.touched.add(id);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
function removeSubtree(state, id) {
|
|
373
|
+
const node = state.doc.nodes.get(id);
|
|
374
|
+
if (!node) return;
|
|
375
|
+
if (node.kind === "element" || node.kind === "fragment") {
|
|
376
|
+
for (const child of [...node.children]) removeSubtree(state, child);
|
|
377
|
+
}
|
|
378
|
+
state.doc.nodes.delete(id);
|
|
379
|
+
state.removed.add(id);
|
|
380
|
+
}
|
|
381
|
+
function specToAttrs(map) {
|
|
382
|
+
if (!map || map.size === 0) return { entries: /* @__PURE__ */ new Map(), spreads: [], order: [] };
|
|
383
|
+
const entries = /* @__PURE__ */ new Map();
|
|
384
|
+
const order = [];
|
|
385
|
+
for (const [k, v] of map) {
|
|
386
|
+
entries.set(k, { kind: "static", value: v });
|
|
387
|
+
order.push(k);
|
|
388
|
+
}
|
|
389
|
+
return { entries, spreads: [], order };
|
|
390
|
+
}
|
|
391
|
+
function materialize(state, spec, parent) {
|
|
392
|
+
const { doc } = state;
|
|
393
|
+
if (spec.kind === "ref") {
|
|
394
|
+
const existing = doc.nodes.get(spec.ref);
|
|
395
|
+
if (existing) existing.parent = parent;
|
|
396
|
+
return spec.ref;
|
|
397
|
+
}
|
|
398
|
+
const id = doc.alloc.next();
|
|
399
|
+
state.created.add(id);
|
|
400
|
+
switch (spec.kind) {
|
|
401
|
+
case "element": {
|
|
402
|
+
const childIds2 = [];
|
|
403
|
+
const el = createElement(id, {
|
|
404
|
+
tag: spec.tag,
|
|
405
|
+
namespace: spec.namespace,
|
|
406
|
+
selfClosing: spec.selfClosing,
|
|
407
|
+
attrs: specToAttrs(spec.attrs),
|
|
408
|
+
parent,
|
|
409
|
+
meta: { ...defaultMeta(), synthetic: true }
|
|
410
|
+
});
|
|
411
|
+
if (spec.classes) el.computed = cloneStyleMap(spec.classes);
|
|
412
|
+
doc.nodes.set(id, el);
|
|
413
|
+
for (const child of spec.children ?? []) {
|
|
414
|
+
childIds2.push(materialize(state, child, id));
|
|
415
|
+
}
|
|
416
|
+
el.children = childIds2;
|
|
417
|
+
return id;
|
|
418
|
+
}
|
|
419
|
+
case "text": {
|
|
420
|
+
const t = createText(id, spec.value, { parent });
|
|
421
|
+
t.meta.synthetic = true;
|
|
422
|
+
doc.nodes.set(id, t);
|
|
423
|
+
return id;
|
|
424
|
+
}
|
|
425
|
+
case "expr": {
|
|
426
|
+
const e = createExpr(id, spec.expr, { parent });
|
|
427
|
+
e.meta.synthetic = true;
|
|
428
|
+
doc.nodes.set(id, e);
|
|
429
|
+
return id;
|
|
430
|
+
}
|
|
431
|
+
case "comment": {
|
|
432
|
+
const c = createComment(id, spec.value, { parent });
|
|
433
|
+
c.meta.synthetic = true;
|
|
434
|
+
doc.nodes.set(id, c);
|
|
435
|
+
return id;
|
|
436
|
+
}
|
|
437
|
+
case "fragment": {
|
|
438
|
+
const frag = createFragment(id, { parent });
|
|
439
|
+
frag.meta.synthetic = true;
|
|
440
|
+
doc.nodes.set(id, frag);
|
|
441
|
+
const childIds2 = [];
|
|
442
|
+
for (const child of spec.children) childIds2.push(materialize(state, child, id));
|
|
443
|
+
frag.children = childIds2;
|
|
444
|
+
return id;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
function elementSpecToNode(state, spec, parent) {
|
|
449
|
+
return materialize(state, spec, parent);
|
|
450
|
+
}
|
|
451
|
+
function isInherited(state, decl) {
|
|
452
|
+
if (decl.inherited) return true;
|
|
453
|
+
const table = state.ctx.normalizer?.inherited;
|
|
454
|
+
if (table) {
|
|
455
|
+
try {
|
|
456
|
+
return table.isInherited(decl.property);
|
|
457
|
+
} catch {
|
|
458
|
+
return decl.inherited;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return decl.inherited;
|
|
462
|
+
}
|
|
463
|
+
function mergeStyleMaps(target, source, policy) {
|
|
464
|
+
const blocks = new Map(cloneStyleMap(target).blocks);
|
|
465
|
+
let conflict = false;
|
|
466
|
+
for (const [key, srcBlock] of source.blocks) {
|
|
467
|
+
const existing = blocks.get(key);
|
|
468
|
+
if (!existing) {
|
|
469
|
+
blocks.set(key, {
|
|
470
|
+
condition: srcBlock.condition,
|
|
471
|
+
decls: new Map(srcBlock.decls)
|
|
472
|
+
});
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
const decls = new Map(existing.decls);
|
|
476
|
+
for (const [prop, srcDecl] of srcBlock.decls) {
|
|
477
|
+
const had = decls.get(prop);
|
|
478
|
+
if (had && had.value !== srcDecl.value) {
|
|
479
|
+
conflict = true;
|
|
480
|
+
if (policy === "target-wins") continue;
|
|
481
|
+
}
|
|
482
|
+
if (policy === "target-wins" && had) continue;
|
|
483
|
+
decls.set(prop, srcDecl);
|
|
484
|
+
}
|
|
485
|
+
blocks.set(key, { condition: existing.condition, decls });
|
|
486
|
+
}
|
|
487
|
+
return { map: { blocks }, conflict };
|
|
488
|
+
}
|
|
489
|
+
function safetyOk(state, op, targetId) {
|
|
490
|
+
const opSafety = op.origin.safety;
|
|
491
|
+
if (opSafety > state.ceiling) {
|
|
492
|
+
return diag(
|
|
493
|
+
"DF_SAFETY_CEILING_EXCEEDED",
|
|
494
|
+
`op '${op.op}' safety ${opSafety} exceeds ceiling ${state.ceiling}`,
|
|
495
|
+
{ nodeId: targetId, pattern: op.origin.pattern, severity: "error" }
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
const node = state.doc.nodes.get(targetId);
|
|
499
|
+
if (node && opSafety > node.meta.safetyFloor) {
|
|
500
|
+
return diag(
|
|
501
|
+
"DF_SAFETY_CEILING_EXCEEDED",
|
|
502
|
+
`op '${op.op}' safety ${opSafety} exceeds node ${targetId} floor ${node.meta.safetyFloor}`,
|
|
503
|
+
{ nodeId: targetId, pattern: op.origin.pattern, severity: "error" }
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
function applyOne(state, op) {
|
|
509
|
+
const { doc } = state;
|
|
510
|
+
const primary = primaryTarget(op);
|
|
511
|
+
if (primary != null) {
|
|
512
|
+
if (!doc.nodes.get(primary)) {
|
|
513
|
+
return [
|
|
514
|
+
diag("DF_OP_PRECONDITION_FAILED", `target node ${primary} not found`, {
|
|
515
|
+
nodeId: primary,
|
|
516
|
+
pattern: op.origin.pattern,
|
|
517
|
+
severity: "error"
|
|
518
|
+
})
|
|
519
|
+
];
|
|
520
|
+
}
|
|
521
|
+
const safety = safetyOk(state, op, primary);
|
|
522
|
+
if (safety) return [safety];
|
|
523
|
+
}
|
|
524
|
+
switch (op.op) {
|
|
525
|
+
case "removeNode": {
|
|
526
|
+
const siblings = getParentChildren(doc, op.target);
|
|
527
|
+
if (siblings) {
|
|
528
|
+
const i = siblings.indexOf(op.target);
|
|
529
|
+
if (i >= 0) siblings.splice(i, 1);
|
|
530
|
+
}
|
|
531
|
+
removeSubtree(state, op.target);
|
|
532
|
+
return [];
|
|
533
|
+
}
|
|
534
|
+
case "unwrap": {
|
|
535
|
+
const node = doc.nodes.get(op.target);
|
|
536
|
+
if (!node || node.kind !== "element" && node.kind !== "fragment") {
|
|
537
|
+
return [precond(op, op.target, "unwrap target is not a container")];
|
|
538
|
+
}
|
|
539
|
+
const siblings = getParentChildren(doc, op.target);
|
|
540
|
+
if (!siblings) return [precond(op, op.target, "unwrap target has no parent")];
|
|
541
|
+
const at = siblings.indexOf(op.target);
|
|
542
|
+
const kids = node.children;
|
|
543
|
+
for (const k of kids) {
|
|
544
|
+
const kn = doc.nodes.get(k);
|
|
545
|
+
if (kn) kn.parent = node.parent;
|
|
546
|
+
}
|
|
547
|
+
siblings.splice(at, 1, ...kids);
|
|
548
|
+
doc.nodes.delete(op.target);
|
|
549
|
+
state.removed.add(op.target);
|
|
550
|
+
if (node.parent != null) markTouched(state, node.parent);
|
|
551
|
+
return [];
|
|
552
|
+
}
|
|
553
|
+
case "replaceWith": {
|
|
554
|
+
const siblings = getParentChildren(doc, op.target);
|
|
555
|
+
if (!siblings) return [precond(op, op.target, "replaceWith target has no parent")];
|
|
556
|
+
const at = siblings.indexOf(op.target);
|
|
557
|
+
const parentId = doc.nodes.get(op.target)?.parent ?? null;
|
|
558
|
+
const newId = materialize(state, op.replacement, parentId);
|
|
559
|
+
siblings.splice(at, 1, newId);
|
|
560
|
+
removeSubtree(state, op.target);
|
|
561
|
+
if (parentId != null) markTouched(state, parentId);
|
|
562
|
+
return [];
|
|
563
|
+
}
|
|
564
|
+
case "wrap": {
|
|
565
|
+
const siblings = getParentChildren(doc, op.target);
|
|
566
|
+
if (!siblings) return [precond(op, op.target, "wrap target has no parent")];
|
|
567
|
+
const at = siblings.indexOf(op.target);
|
|
568
|
+
const parentId = doc.nodes.get(op.target)?.parent ?? null;
|
|
569
|
+
const wrapperId = elementSpecToNode(state, op.wrapper, parentId);
|
|
570
|
+
const wrapper = doc.nodes.get(wrapperId);
|
|
571
|
+
const targetNode = doc.nodes.get(op.target);
|
|
572
|
+
if (wrapper && (wrapper.kind === "element" || wrapper.kind === "fragment")) {
|
|
573
|
+
wrapper.children.push(op.target);
|
|
574
|
+
}
|
|
575
|
+
if (targetNode) targetNode.parent = wrapperId;
|
|
576
|
+
siblings.splice(at, 1, wrapperId);
|
|
577
|
+
return [];
|
|
578
|
+
}
|
|
579
|
+
case "insertBefore":
|
|
580
|
+
case "insertAfter": {
|
|
581
|
+
const siblings = getParentChildren(doc, op.anchor);
|
|
582
|
+
if (!siblings) return [precond(op, op.anchor, "insert anchor has no parent")];
|
|
583
|
+
const at = siblings.indexOf(op.anchor);
|
|
584
|
+
const parentId = doc.nodes.get(op.anchor)?.parent ?? null;
|
|
585
|
+
const newId = materialize(state, op.node, parentId);
|
|
586
|
+
siblings.splice(op.op === "insertBefore" ? at : at + 1, 0, newId);
|
|
587
|
+
if (parentId != null) markTouched(state, parentId);
|
|
588
|
+
return [];
|
|
589
|
+
}
|
|
590
|
+
case "moveNode": {
|
|
591
|
+
const newParent = doc.nodes.get(op.newParent);
|
|
592
|
+
if (!newParent || newParent.kind !== "element" && newParent.kind !== "fragment") {
|
|
593
|
+
return [precond(op, op.newParent, "moveNode newParent is not a container")];
|
|
594
|
+
}
|
|
595
|
+
const siblings = getParentChildren(doc, op.target);
|
|
596
|
+
if (siblings) {
|
|
597
|
+
const i = siblings.indexOf(op.target);
|
|
598
|
+
if (i >= 0) siblings.splice(i, 1);
|
|
599
|
+
}
|
|
600
|
+
const target = doc.nodes.get(op.target);
|
|
601
|
+
if (target) target.parent = op.newParent;
|
|
602
|
+
const idx = Math.max(0, Math.min(op.index, newParent.children.length));
|
|
603
|
+
newParent.children.splice(idx, 0, op.target);
|
|
604
|
+
markTouched(state, op.newParent);
|
|
605
|
+
return [];
|
|
606
|
+
}
|
|
607
|
+
case "mergeSiblings": {
|
|
608
|
+
const first = doc.nodes.get(op.first);
|
|
609
|
+
const second = doc.nodes.get(op.second);
|
|
610
|
+
if (!first || !second) return [precond(op, op.first, "mergeSiblings node missing")];
|
|
611
|
+
if ((first.kind === "element" || first.kind === "fragment") && (second.kind === "element" || second.kind === "fragment")) {
|
|
612
|
+
for (const c of second.children) {
|
|
613
|
+
const cn = doc.nodes.get(c);
|
|
614
|
+
if (cn) cn.parent = op.first;
|
|
615
|
+
first.children.push(c);
|
|
616
|
+
}
|
|
617
|
+
second.children = [];
|
|
618
|
+
}
|
|
619
|
+
const siblings = getParentChildren(doc, op.second);
|
|
620
|
+
if (siblings) {
|
|
621
|
+
const i = siblings.indexOf(op.second);
|
|
622
|
+
if (i >= 0) siblings.splice(i, 1);
|
|
623
|
+
}
|
|
624
|
+
doc.nodes.delete(op.second);
|
|
625
|
+
state.removed.add(op.second);
|
|
626
|
+
markTouched(state, op.first);
|
|
627
|
+
return [];
|
|
628
|
+
}
|
|
629
|
+
case "setClassList": {
|
|
630
|
+
const el = doc.nodes.get(op.target);
|
|
631
|
+
if (!el || el.kind !== "element") {
|
|
632
|
+
return [precond(op, op.target, "setClassList target is not an element")];
|
|
633
|
+
}
|
|
634
|
+
el.computed = cloneStyleMap(op.style);
|
|
635
|
+
markTouched(state, op.target);
|
|
636
|
+
return [];
|
|
637
|
+
}
|
|
638
|
+
case "mergeStyle": {
|
|
639
|
+
const el = doc.nodes.get(op.target);
|
|
640
|
+
if (!el || el.kind !== "element") {
|
|
641
|
+
return [precond(op, op.target, "mergeStyle target is not an element")];
|
|
642
|
+
}
|
|
643
|
+
const report = mergeStyleMaps(el.computed, op.style, op.onConflict);
|
|
644
|
+
if (report.conflict && op.onConflict === "abort") {
|
|
645
|
+
return [
|
|
646
|
+
diag("DF_STYLE_CONFLICT_UNRESOLVED", `mergeStyle aborted on conflict at ${op.target}`, {
|
|
647
|
+
nodeId: op.target,
|
|
648
|
+
pattern: op.origin.pattern,
|
|
649
|
+
severity: "error"
|
|
650
|
+
})
|
|
651
|
+
];
|
|
652
|
+
}
|
|
653
|
+
el.computed = report.map;
|
|
654
|
+
if (op.source != null) {
|
|
655
|
+
const src = doc.nodes.get(op.source);
|
|
656
|
+
if (src) markTouched(state, op.source);
|
|
657
|
+
}
|
|
658
|
+
markTouched(state, op.target);
|
|
659
|
+
return [];
|
|
660
|
+
}
|
|
661
|
+
case "foldInheritedStyles":
|
|
662
|
+
return applyFold(state, op);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
function applyFold(state, op) {
|
|
666
|
+
const { doc } = state;
|
|
667
|
+
const from = doc.nodes.get(op.from);
|
|
668
|
+
if (!from || from.kind !== "element") {
|
|
669
|
+
return [precond(op, op.from, "fold source is not an element")];
|
|
670
|
+
}
|
|
671
|
+
const issues = [];
|
|
672
|
+
const onlyProps = op.properties === "all-inherited" ? null : new Set(op.properties);
|
|
673
|
+
const conditionKeys = op.conditions === "all" ? [...from.computed.blocks.keys()] : [BASE_CONDITION_KEY];
|
|
674
|
+
for (const intoId of op.into) {
|
|
675
|
+
const into = doc.nodes.get(intoId);
|
|
676
|
+
if (!into || into.kind !== "element") {
|
|
677
|
+
issues.push(precond(op, intoId, "fold target is not an element"));
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
const nextBlocks = new Map(cloneStyleMap(into.computed).blocks);
|
|
681
|
+
let folded = false;
|
|
682
|
+
for (const key of conditionKeys) {
|
|
683
|
+
const srcBlock = from.computed.blocks.get(key);
|
|
684
|
+
if (!srcBlock) continue;
|
|
685
|
+
const dstBlock = nextBlocks.get(key);
|
|
686
|
+
const decls = dstBlock ? new Map(dstBlock.decls) : /* @__PURE__ */ new Map();
|
|
687
|
+
for (const [prop, decl] of srcBlock.decls) {
|
|
688
|
+
if (onlyProps && !onlyProps.has(prop)) continue;
|
|
689
|
+
if (!isInherited(state, decl)) continue;
|
|
690
|
+
if (decl.relativeToParent) {
|
|
691
|
+
issues.push(
|
|
692
|
+
diag(
|
|
693
|
+
"DF_RELATIVE_UNIT_FOLD",
|
|
694
|
+
`refused to fold relative-unit declaration '${decl.property}' onto ${intoId}`,
|
|
695
|
+
{ nodeId: intoId, pattern: op.origin.pattern, severity: "warn" }
|
|
696
|
+
)
|
|
697
|
+
);
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
if (!decls.has(prop)) {
|
|
701
|
+
decls.set(prop, decl);
|
|
702
|
+
folded = true;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
nextBlocks.set(key, { condition: srcBlock.condition, decls });
|
|
706
|
+
}
|
|
707
|
+
if (folded) {
|
|
708
|
+
into.computed = { blocks: nextBlocks };
|
|
709
|
+
markTouched(state, intoId);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
for (const d of issues) state.diagnostics.push(d);
|
|
713
|
+
return [];
|
|
714
|
+
}
|
|
715
|
+
function precond(op, nodeId, message) {
|
|
716
|
+
return diag("DF_OP_PRECONDITION_FAILED", message, {
|
|
717
|
+
nodeId,
|
|
718
|
+
pattern: op.origin.pattern,
|
|
719
|
+
severity: "error"
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
function primaryTarget(op) {
|
|
723
|
+
switch (op.op) {
|
|
724
|
+
case "removeNode":
|
|
725
|
+
case "unwrap":
|
|
726
|
+
case "replaceWith":
|
|
727
|
+
case "wrap":
|
|
728
|
+
case "moveNode":
|
|
729
|
+
case "setClassList":
|
|
730
|
+
case "mergeStyle":
|
|
731
|
+
return op.target;
|
|
732
|
+
case "insertBefore":
|
|
733
|
+
case "insertAfter":
|
|
734
|
+
return op.anchor;
|
|
735
|
+
case "mergeSiblings":
|
|
736
|
+
return op.first;
|
|
737
|
+
case "foldInheritedStyles":
|
|
738
|
+
return op.from;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
function applyOps(doc, ops, ctx) {
|
|
742
|
+
const cloned = cloneDocument(doc);
|
|
743
|
+
const state = {
|
|
744
|
+
doc: cloned,
|
|
745
|
+
touched: /* @__PURE__ */ new Set(),
|
|
746
|
+
removed: /* @__PURE__ */ new Set(),
|
|
747
|
+
created: /* @__PURE__ */ new Set(),
|
|
748
|
+
diagnostics: [],
|
|
749
|
+
skipped: [],
|
|
750
|
+
appliedGroups: 0,
|
|
751
|
+
ceiling: ctx?.safetyCeiling ?? 3,
|
|
752
|
+
ctx: { doc: cloned, safetyCeiling: ctx?.safetyCeiling ?? 3, ...ctx }
|
|
753
|
+
};
|
|
754
|
+
for (const op of ops) {
|
|
755
|
+
const issues = applyOne(state, op);
|
|
756
|
+
if (issues.length > 0) {
|
|
757
|
+
state.skipped.push({
|
|
758
|
+
group: { pattern: op.origin.pattern, anchor: primaryTarget(op) ?? doc.root, ops: [op] },
|
|
759
|
+
issues: issues.map((d) => ({ op, code: d.code, message: d.message }))
|
|
760
|
+
});
|
|
761
|
+
for (const d of issues) state.diagnostics.push(d);
|
|
762
|
+
} else {
|
|
763
|
+
state.appliedGroups += 1;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
return { doc: cloned, result: finalize(state) };
|
|
767
|
+
}
|
|
768
|
+
function applyGroups(doc, groups, ctx) {
|
|
769
|
+
let current = cloneDocument(doc);
|
|
770
|
+
const touched = /* @__PURE__ */ new Set();
|
|
771
|
+
const removed = /* @__PURE__ */ new Set();
|
|
772
|
+
const created = /* @__PURE__ */ new Set();
|
|
773
|
+
const diagnostics = [];
|
|
774
|
+
const skipped = [];
|
|
775
|
+
let appliedGroups = 0;
|
|
776
|
+
for (const group of groups) {
|
|
777
|
+
const attempt = applyOps(current, group.ops, ctx);
|
|
778
|
+
if (attempt.result.skipped.length > 0) {
|
|
779
|
+
skipped.push({
|
|
780
|
+
group,
|
|
781
|
+
issues: attempt.result.skipped.flatMap((s) => s.issues)
|
|
782
|
+
});
|
|
783
|
+
for (const s of attempt.result.skipped) for (const i of s.issues) {
|
|
784
|
+
diagnostics.push(diag(i.code, i.message, { pattern: group.pattern }));
|
|
785
|
+
}
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
current = attempt.doc;
|
|
789
|
+
appliedGroups += 1;
|
|
790
|
+
for (const id of attempt.result.touched) touched.add(id);
|
|
791
|
+
for (const id of attempt.result.removed) removed.add(id);
|
|
792
|
+
for (const id of attempt.result.created) created.add(id);
|
|
793
|
+
for (const d of attempt.result.diagnostics) diagnostics.push(d);
|
|
794
|
+
}
|
|
795
|
+
const result = {
|
|
796
|
+
touched,
|
|
797
|
+
removed,
|
|
798
|
+
created,
|
|
799
|
+
appliedGroups,
|
|
800
|
+
skipped,
|
|
801
|
+
journal: [],
|
|
802
|
+
diagnostics
|
|
803
|
+
};
|
|
804
|
+
return { doc: current, result };
|
|
805
|
+
}
|
|
806
|
+
function finalize(state) {
|
|
807
|
+
return {
|
|
808
|
+
touched: state.touched,
|
|
809
|
+
removed: state.removed,
|
|
810
|
+
created: state.created,
|
|
811
|
+
appliedGroups: state.appliedGroups,
|
|
812
|
+
skipped: state.skipped,
|
|
813
|
+
journal: [],
|
|
814
|
+
diagnostics: state.diagnostics
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// ../core/src/pass-manager.ts
|
|
819
|
+
var DEFAULT_FIXPOINT = {
|
|
820
|
+
maxIterations: 16,
|
|
821
|
+
phases: { flatten: 16, compress: 8, extract: 4 },
|
|
822
|
+
onBudgetExhausted: "warn",
|
|
823
|
+
detectOscillation: true
|
|
824
|
+
};
|
|
825
|
+
var PHASE_ORDER = ["flatten", "compress", "extract"];
|
|
826
|
+
function createNullResolver() {
|
|
827
|
+
const empty = {
|
|
828
|
+
styles: emptyStyleMap(),
|
|
829
|
+
resolved: [],
|
|
830
|
+
unknown: [],
|
|
831
|
+
opaque: [],
|
|
832
|
+
warnings: []
|
|
833
|
+
};
|
|
834
|
+
const usage = {
|
|
835
|
+
asSubject: false,
|
|
836
|
+
asAncestor: false,
|
|
837
|
+
asCompound: false,
|
|
838
|
+
asSibling: false,
|
|
839
|
+
asHasArgument: false,
|
|
840
|
+
asStructural: false,
|
|
841
|
+
droppable: true
|
|
842
|
+
};
|
|
843
|
+
return {
|
|
844
|
+
id: "null",
|
|
845
|
+
provider: "null@0.0.0",
|
|
846
|
+
fingerprint: "null",
|
|
847
|
+
owns() {
|
|
848
|
+
return false;
|
|
849
|
+
},
|
|
850
|
+
resolve(_input) {
|
|
851
|
+
return empty;
|
|
852
|
+
},
|
|
853
|
+
emit(_styles, _ctx) {
|
|
854
|
+
return { classes: [], exact: true, warnings: [] };
|
|
855
|
+
},
|
|
856
|
+
selectorUsage() {
|
|
857
|
+
return usage;
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
function createNullSelectorIndex() {
|
|
862
|
+
const none = /* @__PURE__ */ new Set();
|
|
863
|
+
return {
|
|
864
|
+
targetedByCombinator() {
|
|
865
|
+
return false;
|
|
866
|
+
},
|
|
867
|
+
targetedByStructuralPseudo() {
|
|
868
|
+
return false;
|
|
869
|
+
},
|
|
870
|
+
reparentImpact() {
|
|
871
|
+
return none;
|
|
872
|
+
}
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
function hasComplexSelectors(r) {
|
|
876
|
+
return typeof r.complexSelectors === "function";
|
|
877
|
+
}
|
|
878
|
+
function buildSelectorIndex(doc, resolver) {
|
|
879
|
+
if (!hasComplexSelectors(resolver) || resolver.complexSelectors().length === 0) {
|
|
880
|
+
return createNullSelectorIndex();
|
|
881
|
+
}
|
|
882
|
+
const combinator = /* @__PURE__ */ new Set();
|
|
883
|
+
const structural = /* @__PURE__ */ new Set();
|
|
884
|
+
for (const id of elementIds(doc)) {
|
|
885
|
+
const el = getElement(doc, id);
|
|
886
|
+
if (!el) continue;
|
|
887
|
+
for (const seg of el.classes.segments) {
|
|
888
|
+
if (seg.kind !== "static") continue;
|
|
889
|
+
for (const t of seg.tokens) {
|
|
890
|
+
const u = resolver.selectorUsage(t.value);
|
|
891
|
+
if (u.asAncestor || u.asSibling || u.asHasArgument) combinator.add(id);
|
|
892
|
+
if (u.asStructural) structural.add(id);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
const impact = /* @__PURE__ */ new Map();
|
|
897
|
+
for (const id of elementIds(doc)) {
|
|
898
|
+
const el = getElement(doc, id);
|
|
899
|
+
if (!el) continue;
|
|
900
|
+
if (!combinator.has(id) && !structural.has(id)) continue;
|
|
901
|
+
const set = /* @__PURE__ */ new Set([id]);
|
|
902
|
+
for (const c of el.children) {
|
|
903
|
+
const cn = doc.nodes.get(c);
|
|
904
|
+
if (cn && cn.kind === "element") set.add(c);
|
|
905
|
+
}
|
|
906
|
+
impact.set(id, set);
|
|
907
|
+
}
|
|
908
|
+
const empty = /* @__PURE__ */ new Set();
|
|
909
|
+
return {
|
|
910
|
+
targetedByCombinator: (id) => combinator.has(id),
|
|
911
|
+
targetedByStructuralPseudo: (id) => structural.has(id),
|
|
912
|
+
reparentImpact: (id) => impact.get(id) ?? empty
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
function idOf(n) {
|
|
916
|
+
return n.id;
|
|
917
|
+
}
|
|
918
|
+
function createRewriteFactory() {
|
|
919
|
+
return {
|
|
920
|
+
unwrap(target) {
|
|
921
|
+
return { op: "unwrap", target: idOf(target) };
|
|
922
|
+
},
|
|
923
|
+
removeNode(target) {
|
|
924
|
+
return { op: "removeNode", target: idOf(target) };
|
|
925
|
+
},
|
|
926
|
+
replaceWith(target, replacement) {
|
|
927
|
+
return { op: "replaceWith", target: idOf(target), replacement };
|
|
928
|
+
},
|
|
929
|
+
wrap(target, wrapper) {
|
|
930
|
+
return { op: "wrap", target: idOf(target), wrapper };
|
|
931
|
+
},
|
|
932
|
+
insertBefore(anchor, node) {
|
|
933
|
+
return { op: "insertBefore", anchor: idOf(anchor), node };
|
|
934
|
+
},
|
|
935
|
+
insertAfter(anchor, node) {
|
|
936
|
+
return { op: "insertAfter", anchor: idOf(anchor), node };
|
|
937
|
+
},
|
|
938
|
+
moveNode(target, newParent, index) {
|
|
939
|
+
return { op: "moveNode", target: idOf(target), newParent: idOf(newParent), index };
|
|
940
|
+
},
|
|
941
|
+
mergeSiblings(first, second) {
|
|
942
|
+
return { op: "mergeSiblings", first: idOf(first), second: idOf(second) };
|
|
943
|
+
},
|
|
944
|
+
setClassList(target, style, preserveOpaque = true) {
|
|
945
|
+
return { op: "setClassList", target: idOf(target), style, preserveOpaque };
|
|
946
|
+
},
|
|
947
|
+
mergeStyle(target, source, style, onConflict = "abort") {
|
|
948
|
+
return {
|
|
949
|
+
op: "mergeStyle",
|
|
950
|
+
target: idOf(target),
|
|
951
|
+
source: source ? idOf(source) : null,
|
|
952
|
+
style,
|
|
953
|
+
onConflict
|
|
954
|
+
};
|
|
955
|
+
},
|
|
956
|
+
foldInheritedStyles(from, into, opts) {
|
|
957
|
+
const list = Array.isArray(into) ? into : [into];
|
|
958
|
+
return {
|
|
959
|
+
op: "foldInheritedStyles",
|
|
960
|
+
from: idOf(from),
|
|
961
|
+
into: list.map((t) => idOf(t)),
|
|
962
|
+
properties: opts?.only ?? "all-inherited",
|
|
963
|
+
conditions: opts?.conditions ?? "base"
|
|
964
|
+
};
|
|
965
|
+
},
|
|
966
|
+
element(spec) {
|
|
967
|
+
return spec;
|
|
968
|
+
},
|
|
969
|
+
text(value) {
|
|
970
|
+
return { kind: "text", value };
|
|
971
|
+
},
|
|
972
|
+
keep(node) {
|
|
973
|
+
return { kind: "ref", ref: idOf(node) };
|
|
974
|
+
}
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
function ro(v) {
|
|
978
|
+
return v;
|
|
979
|
+
}
|
|
980
|
+
function buildMatchContext(doc, elementId, resolver, selectors, safety, phase, iteration) {
|
|
981
|
+
const self = getElement(doc, elementId);
|
|
982
|
+
const parentEl = () => {
|
|
983
|
+
if (self.parent == null) return null;
|
|
984
|
+
const p = doc.nodes.get(self.parent);
|
|
985
|
+
return p && p.kind === "element" ? ro(p) : null;
|
|
986
|
+
};
|
|
987
|
+
const elementChildren = () => {
|
|
988
|
+
const out = [];
|
|
989
|
+
for (const c of self.children) {
|
|
990
|
+
const cn = doc.nodes.get(c);
|
|
991
|
+
if (cn && cn.kind === "element") out.push(ro(cn));
|
|
992
|
+
}
|
|
993
|
+
return out;
|
|
994
|
+
};
|
|
995
|
+
const ancestors = () => {
|
|
996
|
+
const out = [];
|
|
997
|
+
let cur = self.parent;
|
|
998
|
+
while (cur != null) {
|
|
999
|
+
const n = doc.nodes.get(cur);
|
|
1000
|
+
if (!n) break;
|
|
1001
|
+
if (n.kind === "element") out.push(ro(n));
|
|
1002
|
+
cur = n.parent;
|
|
1003
|
+
}
|
|
1004
|
+
return out;
|
|
1005
|
+
};
|
|
1006
|
+
const siblingAt = (delta) => {
|
|
1007
|
+
if (self.parent == null) return null;
|
|
1008
|
+
const p = doc.nodes.get(self.parent);
|
|
1009
|
+
if (!p || p.kind !== "element" && p.kind !== "fragment") return null;
|
|
1010
|
+
const i = p.children.indexOf(elementId);
|
|
1011
|
+
const sib = p.children[i + delta];
|
|
1012
|
+
if (sib == null) return null;
|
|
1013
|
+
const sn = doc.nodes.get(sib);
|
|
1014
|
+
return sn ? ro(sn) : null;
|
|
1015
|
+
};
|
|
1016
|
+
const computedOf = (n) => {
|
|
1017
|
+
const node = doc.nodes.get(n.id);
|
|
1018
|
+
return node && node.kind === "element" ? node.computed : emptyStyleMap();
|
|
1019
|
+
};
|
|
1020
|
+
return {
|
|
1021
|
+
node: ro(self),
|
|
1022
|
+
doc: ro(doc),
|
|
1023
|
+
resolver,
|
|
1024
|
+
selectors,
|
|
1025
|
+
safety,
|
|
1026
|
+
phase,
|
|
1027
|
+
iteration,
|
|
1028
|
+
parent: parentEl,
|
|
1029
|
+
elementChildren,
|
|
1030
|
+
onlyElementChild() {
|
|
1031
|
+
const els = elementChildren();
|
|
1032
|
+
return els.length === 1 ? els[0] : null;
|
|
1033
|
+
},
|
|
1034
|
+
computed() {
|
|
1035
|
+
return self.computed;
|
|
1036
|
+
},
|
|
1037
|
+
computedOf,
|
|
1038
|
+
isOpaque(n) {
|
|
1039
|
+
const target = n ? doc.nodes.get(n.id) : self;
|
|
1040
|
+
if (!target || target.kind !== "element") return true;
|
|
1041
|
+
return target.classes.opaque || target.meta.hasSpreadAttrs;
|
|
1042
|
+
},
|
|
1043
|
+
ancestors,
|
|
1044
|
+
closest(pred) {
|
|
1045
|
+
for (const a of ancestors()) if (pred(a)) return a;
|
|
1046
|
+
return null;
|
|
1047
|
+
},
|
|
1048
|
+
prevSibling() {
|
|
1049
|
+
return siblingAt(-1);
|
|
1050
|
+
},
|
|
1051
|
+
nextSibling() {
|
|
1052
|
+
return siblingAt(1);
|
|
1053
|
+
},
|
|
1054
|
+
nthChildIndex() {
|
|
1055
|
+
if (self.parent == null) return 1;
|
|
1056
|
+
const p = doc.nodes.get(self.parent);
|
|
1057
|
+
if (!p || p.kind !== "element" && p.kind !== "fragment") return 1;
|
|
1058
|
+
let idx = 0;
|
|
1059
|
+
for (const c of p.children) {
|
|
1060
|
+
const cn = doc.nodes.get(c);
|
|
1061
|
+
if (cn && cn.kind === "element") {
|
|
1062
|
+
idx += 1;
|
|
1063
|
+
if (c === elementId) return idx;
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
return idx;
|
|
1067
|
+
}
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
function stampOrigin(draft, pattern2) {
|
|
1071
|
+
return {
|
|
1072
|
+
...draft,
|
|
1073
|
+
origin: { pattern: pattern2.name, category: pattern2.category, safety: pattern2.safety }
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
function patternsForPhase(passes, phase) {
|
|
1077
|
+
const out = [];
|
|
1078
|
+
for (const pass of passes) {
|
|
1079
|
+
if (pass.phase !== phase) continue;
|
|
1080
|
+
for (const p of pass.patterns) out.push(p);
|
|
1081
|
+
}
|
|
1082
|
+
out.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
1083
|
+
return out;
|
|
1084
|
+
}
|
|
1085
|
+
function probeSink() {
|
|
1086
|
+
return {
|
|
1087
|
+
register(s) {
|
|
1088
|
+
return s.className;
|
|
1089
|
+
},
|
|
1090
|
+
drain() {
|
|
1091
|
+
return [];
|
|
1092
|
+
}
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
function styleWriteTargets(ops) {
|
|
1096
|
+
const ids = [];
|
|
1097
|
+
for (const op of ops) {
|
|
1098
|
+
if (op.op === "mergeStyle" || op.op === "setClassList") ids.push(op.target);
|
|
1099
|
+
else if (op.op === "foldInheritedStyles") ids.push(...op.into);
|
|
1100
|
+
}
|
|
1101
|
+
return ids;
|
|
1102
|
+
}
|
|
1103
|
+
function emitIsExact(resolver, normalizer2, sm) {
|
|
1104
|
+
if (sm.blocks.size === 0) return true;
|
|
1105
|
+
try {
|
|
1106
|
+
const ctx = { normalizer: normalizer2, sink: probeSink() };
|
|
1107
|
+
const r = resolver.emit(sm, ctx);
|
|
1108
|
+
return r.exact && r.residual == null;
|
|
1109
|
+
} catch {
|
|
1110
|
+
return true;
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
function flattenWouldDropStyle(before, after, ops, resolver, normalizer2) {
|
|
1114
|
+
for (const id of styleWriteTargets(ops)) {
|
|
1115
|
+
const newEl = getElement(after, id);
|
|
1116
|
+
if (!newEl) continue;
|
|
1117
|
+
if (newEl.classes.opaque || newEl.classes.hasDynamic) continue;
|
|
1118
|
+
if (emitIsExact(resolver, normalizer2, newEl.computed)) continue;
|
|
1119
|
+
const oldEl = getElement(before, id);
|
|
1120
|
+
const wasExact = !oldEl || emitIsExact(resolver, normalizer2, oldEl.computed);
|
|
1121
|
+
if (wasExact) return true;
|
|
1122
|
+
}
|
|
1123
|
+
return false;
|
|
1124
|
+
}
|
|
1125
|
+
function runSweep(state, patterns, ctx, factory, phase, iteration, touched, diagnostics, flattenBarred) {
|
|
1126
|
+
let appliedOps = 0;
|
|
1127
|
+
const resolver = ctx.resolver;
|
|
1128
|
+
const selectors = ctx.selectors;
|
|
1129
|
+
for (const elId of elementIds(state.doc)) {
|
|
1130
|
+
const el = getElement(state.doc, elId);
|
|
1131
|
+
if (!el) continue;
|
|
1132
|
+
if (phase === "flatten" && flattenBarred.has(elId)) continue;
|
|
1133
|
+
for (const pattern2 of patterns) {
|
|
1134
|
+
if (pattern2.safety > ctx.safetyCeiling) continue;
|
|
1135
|
+
let drafts = [];
|
|
1136
|
+
try {
|
|
1137
|
+
const mctx = buildMatchContext(
|
|
1138
|
+
state.doc,
|
|
1139
|
+
elId,
|
|
1140
|
+
resolver,
|
|
1141
|
+
selectors,
|
|
1142
|
+
ctx.safetyCeiling,
|
|
1143
|
+
phase,
|
|
1144
|
+
iteration
|
|
1145
|
+
);
|
|
1146
|
+
const result = pattern2.evaluate(mctx, factory);
|
|
1147
|
+
if (!result) continue;
|
|
1148
|
+
drafts = result.ops;
|
|
1149
|
+
if (result.diagnostics) for (const d of result.diagnostics) diagnostics.push(d);
|
|
1150
|
+
} catch (err) {
|
|
1151
|
+
diagnostics.push({
|
|
1152
|
+
code: "DF_PATTERN_THREW",
|
|
1153
|
+
severity: "error",
|
|
1154
|
+
message: `pattern '${pattern2.name}' threw: ${String(err?.message ?? err)}`,
|
|
1155
|
+
nodeId: elId,
|
|
1156
|
+
pattern: pattern2.name,
|
|
1157
|
+
phase,
|
|
1158
|
+
iteration,
|
|
1159
|
+
cause: err
|
|
1160
|
+
});
|
|
1161
|
+
continue;
|
|
1162
|
+
}
|
|
1163
|
+
if (drafts.length === 0) continue;
|
|
1164
|
+
const ops = drafts.map((d) => stampOrigin(d, pattern2));
|
|
1165
|
+
const outcome = applyOps(state.doc, ops, ctx);
|
|
1166
|
+
for (const d of outcome.result.diagnostics) diagnostics.push(d);
|
|
1167
|
+
if (outcome.result.appliedGroups > 0) {
|
|
1168
|
+
if (phase === "flatten" && flattenWouldDropStyle(state.doc, outcome.doc, ops, resolver, ctx.normalizer)) {
|
|
1169
|
+
diagnostics.push({
|
|
1170
|
+
code: "DF_VERIFY_REVERTED",
|
|
1171
|
+
severity: "warn",
|
|
1172
|
+
message: `flatten '${pattern2.name}' reverted on node ${elId}: residual style is not reproducible by resolver '${resolver.id}', so flattening would drop it`,
|
|
1173
|
+
nodeId: elId,
|
|
1174
|
+
pattern: pattern2.name,
|
|
1175
|
+
phase,
|
|
1176
|
+
iteration
|
|
1177
|
+
});
|
|
1178
|
+
flattenBarred.add(elId);
|
|
1179
|
+
break;
|
|
1180
|
+
}
|
|
1181
|
+
state.doc = outcome.doc;
|
|
1182
|
+
appliedOps += outcome.result.appliedGroups;
|
|
1183
|
+
for (const id of outcome.result.touched) touched.add(id);
|
|
1184
|
+
for (const id of outcome.result.created) touched.add(id);
|
|
1185
|
+
break;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
return appliedOps;
|
|
1190
|
+
}
|
|
1191
|
+
function docFingerprint(doc) {
|
|
1192
|
+
const parts = [];
|
|
1193
|
+
const visit = (id) => {
|
|
1194
|
+
const n = doc.nodes.get(id);
|
|
1195
|
+
if (!n) return;
|
|
1196
|
+
parts.push(`${id}:${n.kind}`);
|
|
1197
|
+
for (const c of childIds(n)) visit(c);
|
|
1198
|
+
};
|
|
1199
|
+
visit(doc.root);
|
|
1200
|
+
return parts.join("|");
|
|
1201
|
+
}
|
|
1202
|
+
function runPasses(doc, passes, ctx, config) {
|
|
1203
|
+
const cfg = { ...DEFAULT_FIXPOINT, ...config };
|
|
1204
|
+
const factory = createRewriteFactory();
|
|
1205
|
+
const state = { doc };
|
|
1206
|
+
const results = [];
|
|
1207
|
+
const flattenBarred = /* @__PURE__ */ new Set();
|
|
1208
|
+
for (const phase of PHASE_ORDER) {
|
|
1209
|
+
const patterns = patternsForPhase(passes, phase);
|
|
1210
|
+
const phaseTouched = /* @__PURE__ */ new Set();
|
|
1211
|
+
const diagnostics = [];
|
|
1212
|
+
const budget = cfg.phases[phase] ?? cfg.maxIterations;
|
|
1213
|
+
let iterations = 0;
|
|
1214
|
+
let converged = false;
|
|
1215
|
+
let haltReason = "converged";
|
|
1216
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1217
|
+
if (patterns.length === 0) {
|
|
1218
|
+
results.push({
|
|
1219
|
+
phase,
|
|
1220
|
+
iterations: 0,
|
|
1221
|
+
converged: true,
|
|
1222
|
+
haltReason: "converged",
|
|
1223
|
+
touched: phaseTouched,
|
|
1224
|
+
diagnostics
|
|
1225
|
+
});
|
|
1226
|
+
continue;
|
|
1227
|
+
}
|
|
1228
|
+
while (iterations < budget) {
|
|
1229
|
+
iterations += 1;
|
|
1230
|
+
const applied = runSweep(
|
|
1231
|
+
state,
|
|
1232
|
+
patterns,
|
|
1233
|
+
ctx,
|
|
1234
|
+
factory,
|
|
1235
|
+
phase,
|
|
1236
|
+
iterations,
|
|
1237
|
+
phaseTouched,
|
|
1238
|
+
diagnostics,
|
|
1239
|
+
flattenBarred
|
|
1240
|
+
);
|
|
1241
|
+
if (applied === 0) {
|
|
1242
|
+
converged = true;
|
|
1243
|
+
haltReason = "converged";
|
|
1244
|
+
break;
|
|
1245
|
+
}
|
|
1246
|
+
if (cfg.detectOscillation) {
|
|
1247
|
+
const fp = docFingerprint(state.doc);
|
|
1248
|
+
if (seen.has(fp)) {
|
|
1249
|
+
haltReason = "oscillation";
|
|
1250
|
+
diagnostics.push({
|
|
1251
|
+
code: "DF_FIXPOINT_OSCILLATION",
|
|
1252
|
+
severity: "warn",
|
|
1253
|
+
message: `phase '${phase}' oscillated; halting`,
|
|
1254
|
+
phase,
|
|
1255
|
+
iteration: iterations
|
|
1256
|
+
});
|
|
1257
|
+
break;
|
|
1258
|
+
}
|
|
1259
|
+
seen.add(fp);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
if (!converged && haltReason !== "oscillation") {
|
|
1263
|
+
haltReason = "budget";
|
|
1264
|
+
diagnostics.push({
|
|
1265
|
+
code: "DF_FIXPOINT_BUDGET",
|
|
1266
|
+
severity: cfg.onBudgetExhausted === "error" ? "error" : "warn",
|
|
1267
|
+
message: `phase '${phase}' exhausted ${budget}-iteration budget`,
|
|
1268
|
+
phase,
|
|
1269
|
+
iteration: iterations
|
|
1270
|
+
});
|
|
1271
|
+
}
|
|
1272
|
+
results.push({
|
|
1273
|
+
phase,
|
|
1274
|
+
iterations,
|
|
1275
|
+
converged,
|
|
1276
|
+
haltReason,
|
|
1277
|
+
touched: phaseTouched,
|
|
1278
|
+
diagnostics
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
return { doc: state.doc, results };
|
|
1282
|
+
}
|
|
1283
|
+
function createPassManager(passes) {
|
|
1284
|
+
return {
|
|
1285
|
+
run(doc, ctx, config) {
|
|
1286
|
+
return runPasses(doc, passes, ctx, config).results;
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
// ../core/src/pipeline.ts
|
|
1292
|
+
function createSyntheticSink() {
|
|
1293
|
+
const map = /* @__PURE__ */ new Map();
|
|
1294
|
+
return {
|
|
1295
|
+
register(s) {
|
|
1296
|
+
if (!map.has(s.className)) map.set(s.className, s);
|
|
1297
|
+
return s.className;
|
|
1298
|
+
},
|
|
1299
|
+
drain() {
|
|
1300
|
+
return [...map.values()];
|
|
1301
|
+
}
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
function staticClassTokens(el) {
|
|
1305
|
+
const out = [];
|
|
1306
|
+
for (const seg of el.classes.segments) {
|
|
1307
|
+
if (seg.kind === "static") for (const t of seg.tokens) out.push(t.value);
|
|
1308
|
+
}
|
|
1309
|
+
return out;
|
|
1310
|
+
}
|
|
1311
|
+
function resolveStyles(doc, input, diagnostics) {
|
|
1312
|
+
const { resolver, normalizer: normalizer2 } = input;
|
|
1313
|
+
for (const id of elementIds(doc)) {
|
|
1314
|
+
const el = getElement(doc, id);
|
|
1315
|
+
if (!el || el.classes.opaque) continue;
|
|
1316
|
+
const tokens = staticClassTokens(el);
|
|
1317
|
+
if (tokens.length === 0) continue;
|
|
1318
|
+
try {
|
|
1319
|
+
const result = resolver.resolve({
|
|
1320
|
+
classes: tokens,
|
|
1321
|
+
element: {
|
|
1322
|
+
tagName: el.tag,
|
|
1323
|
+
namespace: el.namespace === "svg" ? "svg" : "html"
|
|
1324
|
+
}
|
|
1325
|
+
});
|
|
1326
|
+
let styles = result.styles;
|
|
1327
|
+
try {
|
|
1328
|
+
styles = normalizer2.normalizeStyleMap(styles);
|
|
1329
|
+
} catch {
|
|
1330
|
+
}
|
|
1331
|
+
el.computed = styles;
|
|
1332
|
+
for (const w of result.warnings) {
|
|
1333
|
+
diagnostics.push({
|
|
1334
|
+
code: "DF_STYLE_CONFLICT_UNRESOLVED",
|
|
1335
|
+
severity: w.severity,
|
|
1336
|
+
message: w.message,
|
|
1337
|
+
nodeId: id
|
|
1338
|
+
});
|
|
1339
|
+
}
|
|
1340
|
+
} catch (err) {
|
|
1341
|
+
diagnostics.push({
|
|
1342
|
+
code: "DF_OP_PRECONDITION_FAILED",
|
|
1343
|
+
severity: "debug",
|
|
1344
|
+
message: `resolve skipped for <${el.tag}>: ${String(err?.message ?? err)}`,
|
|
1345
|
+
nodeId: id,
|
|
1346
|
+
cause: err
|
|
1347
|
+
});
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
var ZERO_ITERATIONS = {
|
|
1352
|
+
flatten: 0,
|
|
1353
|
+
compress: 0,
|
|
1354
|
+
extract: 0
|
|
1355
|
+
};
|
|
1356
|
+
var DefaultPipeline = class {
|
|
1357
|
+
run(input) {
|
|
1358
|
+
const startedAt = now();
|
|
1359
|
+
const diagnostics = [];
|
|
1360
|
+
const report = (d) => {
|
|
1361
|
+
diagnostics.push(d);
|
|
1362
|
+
input.reporter?.report(d);
|
|
1363
|
+
};
|
|
1364
|
+
const parseCtx = {
|
|
1365
|
+
id: input.id,
|
|
1366
|
+
kind: input.kind,
|
|
1367
|
+
resolver: input.resolver,
|
|
1368
|
+
normalizer: input.normalizer,
|
|
1369
|
+
config: { preserveComments: input.config?.preserveComments ?? true },
|
|
1370
|
+
onDiagnostic: report
|
|
1371
|
+
};
|
|
1372
|
+
if (input.babelAst !== void 0) parseCtx.babelAst = input.babelAst;
|
|
1373
|
+
const parsed = input.frontend.parse(input.code, parseCtx);
|
|
1374
|
+
for (const d of parsed.diagnostics) report(d);
|
|
1375
|
+
const doc = parsed.doc;
|
|
1376
|
+
const nodesIn = doc.nodes.size;
|
|
1377
|
+
resolveStyles(doc, input, diagnostics);
|
|
1378
|
+
const ctx = {
|
|
1379
|
+
doc,
|
|
1380
|
+
safetyCeiling: input.config?.safety ?? 2,
|
|
1381
|
+
normalizer: input.normalizer,
|
|
1382
|
+
selectors: createNullSelectorIndex(),
|
|
1383
|
+
resolver: input.resolver
|
|
1384
|
+
};
|
|
1385
|
+
const { doc: optimized, results } = runPasses(doc, input.passes, ctx, fixpointFrom(input));
|
|
1386
|
+
let opsApplied = 0;
|
|
1387
|
+
const iterations = { ...ZERO_ITERATIONS };
|
|
1388
|
+
for (const r of results) {
|
|
1389
|
+
iterations[r.phase] = r.iterations;
|
|
1390
|
+
for (const d of r.diagnostics) {
|
|
1391
|
+
diagnostics.push(d);
|
|
1392
|
+
input.reporter?.report(d);
|
|
1393
|
+
}
|
|
1394
|
+
opsApplied += r.touched.size;
|
|
1395
|
+
}
|
|
1396
|
+
const editPlan = {
|
|
1397
|
+
moduleId: input.id,
|
|
1398
|
+
ops: [],
|
|
1399
|
+
provenance: /* @__PURE__ */ new Map()
|
|
1400
|
+
};
|
|
1401
|
+
const backendCtx = {
|
|
1402
|
+
normalizer: input.normalizer,
|
|
1403
|
+
resolver: input.resolver,
|
|
1404
|
+
sink: createSyntheticSink(),
|
|
1405
|
+
eol: eolOf(optimized),
|
|
1406
|
+
onDiagnostic: report
|
|
1407
|
+
};
|
|
1408
|
+
const printed = input.backend.print(optimized, editPlan, backendCtx);
|
|
1409
|
+
for (const d of printed.diagnostics) {
|
|
1410
|
+
diagnostics.push(d);
|
|
1411
|
+
input.reporter?.report(d);
|
|
1412
|
+
}
|
|
1413
|
+
const stats = {
|
|
1414
|
+
nodesIn,
|
|
1415
|
+
nodesOut: optimized.nodes.size,
|
|
1416
|
+
opsApplied,
|
|
1417
|
+
iterations,
|
|
1418
|
+
durationMs: now() - startedAt
|
|
1419
|
+
};
|
|
1420
|
+
const touched = printed.edits.map((e) => e.span);
|
|
1421
|
+
return {
|
|
1422
|
+
code: printed.code,
|
|
1423
|
+
map: printed.map,
|
|
1424
|
+
changed: printed.code !== input.code,
|
|
1425
|
+
touched,
|
|
1426
|
+
diagnostics,
|
|
1427
|
+
stats,
|
|
1428
|
+
doc: optimized,
|
|
1429
|
+
editPlan
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
};
|
|
1433
|
+
function fixpointFrom(input) {
|
|
1434
|
+
const fp = input.config?.fixpoint;
|
|
1435
|
+
if (!fp) return void 0;
|
|
1436
|
+
return {
|
|
1437
|
+
maxIterations: fp.maxIterations ?? 16,
|
|
1438
|
+
phases: fp.phases ?? {},
|
|
1439
|
+
onBudgetExhausted: fp.onBudgetExhausted ?? "warn",
|
|
1440
|
+
detectOscillation: fp.detectOscillation ?? true
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
function eolOf(doc) {
|
|
1444
|
+
for (const src of doc.sources.values()) return src.eol;
|
|
1445
|
+
return "\n";
|
|
1446
|
+
}
|
|
1447
|
+
function now() {
|
|
1448
|
+
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
1449
|
+
}
|
|
1450
|
+
function createPipeline() {
|
|
1451
|
+
return new DefaultPipeline();
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
// ../core/src/reverse-emit.ts
|
|
1455
|
+
function staticTokensOf(cl) {
|
|
1456
|
+
const out = [];
|
|
1457
|
+
for (const seg of cl.segments) {
|
|
1458
|
+
if (seg.kind === "static") for (const t of seg.tokens) out.push(t.value);
|
|
1459
|
+
}
|
|
1460
|
+
return out;
|
|
1461
|
+
}
|
|
1462
|
+
function staticClassList(prev, tokens) {
|
|
1463
|
+
const classTokens = tokens.map((value) => ({ value }));
|
|
1464
|
+
const seg = { kind: "static", tokens: classTokens };
|
|
1465
|
+
return {
|
|
1466
|
+
form: "string-literal",
|
|
1467
|
+
segments: [seg],
|
|
1468
|
+
valueSpan: prev.valueSpan,
|
|
1469
|
+
attrSpan: prev.attrSpan,
|
|
1470
|
+
hasDynamic: false,
|
|
1471
|
+
opaque: false,
|
|
1472
|
+
rewritable: true
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
function sameTokens(a, b) {
|
|
1476
|
+
if (a.length !== b.length) return false;
|
|
1477
|
+
for (let i = 0; i < a.length; i += 1) if (a[i] !== b[i]) return false;
|
|
1478
|
+
return true;
|
|
1479
|
+
}
|
|
1480
|
+
function syncClassesFromComputed(doc, resolver, norm) {
|
|
1481
|
+
const sink = createSyntheticSink();
|
|
1482
|
+
for (const id of elementIds(doc)) {
|
|
1483
|
+
const el = getElement(doc, id);
|
|
1484
|
+
if (!el || !el.meta.touched) continue;
|
|
1485
|
+
if (el.classes.opaque || el.classes.hasDynamic) continue;
|
|
1486
|
+
const tokens = staticTokensOf(el.classes);
|
|
1487
|
+
const ctx = { normalizer: norm, sink };
|
|
1488
|
+
const emitted = resolver.emit(el.computed, ctx).classes;
|
|
1489
|
+
if (emitted.length === 0) continue;
|
|
1490
|
+
const emittedSet = new Set(emitted);
|
|
1491
|
+
const next = [];
|
|
1492
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1493
|
+
for (const t of tokens) {
|
|
1494
|
+
if (seen.has(t)) continue;
|
|
1495
|
+
const keep = emittedSet.has(t) || !resolver.selectorUsage(t).droppable;
|
|
1496
|
+
if (keep) {
|
|
1497
|
+
next.push(t);
|
|
1498
|
+
seen.add(t);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
for (const c of emitted) {
|
|
1502
|
+
if (seen.has(c)) continue;
|
|
1503
|
+
next.push(c);
|
|
1504
|
+
seen.add(c);
|
|
1505
|
+
}
|
|
1506
|
+
if (sameTokens(next, tokens)) continue;
|
|
1507
|
+
el.classes = staticClassList(el.classes, next);
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
// ../pattern-kit/src/define.ts
|
|
1512
|
+
var PHASES = /* @__PURE__ */ new Set(["flatten", "compress", "extract"]);
|
|
1513
|
+
var SAFETY_LEVELS = /* @__PURE__ */ new Set([0, 1, 2, 3]);
|
|
1514
|
+
function fail(name, why) {
|
|
1515
|
+
throw new Error(`definePattern(${name || "<anonymous>"}): ${why}`);
|
|
1516
|
+
}
|
|
1517
|
+
function definePattern(spec) {
|
|
1518
|
+
if (spec == null || typeof spec !== "object") {
|
|
1519
|
+
throw new Error("definePattern: spec must be an object");
|
|
1520
|
+
}
|
|
1521
|
+
const name = spec.name;
|
|
1522
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
1523
|
+
fail(String(name), "name must be a non-empty string");
|
|
1524
|
+
}
|
|
1525
|
+
if (typeof spec.category !== "string" || !spec.category.includes("/")) {
|
|
1526
|
+
fail(name, `category must be a "<phase>/<slug>" string (got ${JSON.stringify(spec.category)})`);
|
|
1527
|
+
}
|
|
1528
|
+
const phase = spec.category.split("/", 1)[0];
|
|
1529
|
+
if (!PHASES.has(phase)) {
|
|
1530
|
+
fail(name, `category phase must be one of flatten|compress|extract (got "${phase}")`);
|
|
1531
|
+
}
|
|
1532
|
+
if (!SAFETY_LEVELS.has(spec.safety)) {
|
|
1533
|
+
fail(name, `safety must be 0|1|2|3 (got ${JSON.stringify(spec.safety)})`);
|
|
1534
|
+
}
|
|
1535
|
+
if (typeof spec.evaluate !== "function") {
|
|
1536
|
+
fail(name, "evaluate must be a function");
|
|
1537
|
+
}
|
|
1538
|
+
if (spec.priority !== void 0 && !Number.isFinite(spec.priority)) {
|
|
1539
|
+
fail(name, "priority must be a finite number when provided");
|
|
1540
|
+
}
|
|
1541
|
+
return Object.freeze({ ...spec });
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
// ../pattern-kit/src/normalize.ts
|
|
1545
|
+
var INHERITED_PROPERTIES = [
|
|
1546
|
+
"azimuth",
|
|
1547
|
+
"border-collapse",
|
|
1548
|
+
"border-spacing",
|
|
1549
|
+
"caption-side",
|
|
1550
|
+
"color",
|
|
1551
|
+
"cursor",
|
|
1552
|
+
"direction",
|
|
1553
|
+
"empty-cells",
|
|
1554
|
+
"font-family",
|
|
1555
|
+
"font-feature-settings",
|
|
1556
|
+
"font-kerning",
|
|
1557
|
+
"font-size",
|
|
1558
|
+
"font-size-adjust",
|
|
1559
|
+
"font-stretch",
|
|
1560
|
+
"font-style",
|
|
1561
|
+
"font-variant",
|
|
1562
|
+
"font-variant-caps",
|
|
1563
|
+
"font-variant-numeric",
|
|
1564
|
+
"font-weight",
|
|
1565
|
+
"hyphens",
|
|
1566
|
+
"letter-spacing",
|
|
1567
|
+
"line-height",
|
|
1568
|
+
"list-style-image",
|
|
1569
|
+
"list-style-position",
|
|
1570
|
+
"list-style-type",
|
|
1571
|
+
"orphans",
|
|
1572
|
+
"overflow-wrap",
|
|
1573
|
+
"quotes",
|
|
1574
|
+
"tab-size",
|
|
1575
|
+
"text-align",
|
|
1576
|
+
"text-align-last",
|
|
1577
|
+
"text-decoration-color",
|
|
1578
|
+
"text-indent",
|
|
1579
|
+
"text-justify",
|
|
1580
|
+
"text-rendering",
|
|
1581
|
+
"text-shadow",
|
|
1582
|
+
"text-transform",
|
|
1583
|
+
"text-underline-position",
|
|
1584
|
+
"visibility",
|
|
1585
|
+
"white-space",
|
|
1586
|
+
"widows",
|
|
1587
|
+
"word-break",
|
|
1588
|
+
"word-spacing",
|
|
1589
|
+
"writing-mode",
|
|
1590
|
+
"-webkit-font-smoothing"
|
|
1591
|
+
];
|
|
1592
|
+
function createInheritedTable() {
|
|
1593
|
+
const properties = new Set(INHERITED_PROPERTIES);
|
|
1594
|
+
return {
|
|
1595
|
+
version: "domflax-inherited@1",
|
|
1596
|
+
properties,
|
|
1597
|
+
isInherited(property) {
|
|
1598
|
+
return String(property).startsWith("--") || properties.has(property);
|
|
1599
|
+
}
|
|
1600
|
+
};
|
|
1601
|
+
}
|
|
1602
|
+
var ZERO_LENGTH_RE = /\b0(?:px|em|rem|ex|ch|vh|vw|vmin|vmax|vi|vb|pt|pc|cm|mm|in|q|lh|rlh|fr|deg|rad|turn|s|ms|%)\b/g;
|
|
1603
|
+
var FUNC_ARGS_RE = /\b(rgba?|hsla?|hwb|lab|lch|oklab|oklch)\(([^()]*)\)/gi;
|
|
1604
|
+
var RELATIVE_UNIT_RE = /(?:\d*\.?\d+)(?:em|ex|ch|lh)\b|%/i;
|
|
1605
|
+
function canonValue(raw) {
|
|
1606
|
+
let v = raw.trim().replace(/\s+/g, " ");
|
|
1607
|
+
v = v.replace(/#([0-9a-fA-F]{3,8})\b/g, (_m, hex) => "#" + hex.toLowerCase());
|
|
1608
|
+
v = v.replace(
|
|
1609
|
+
/#([0-9a-f])([0-9a-f])([0-9a-f])(?![0-9a-f])/g,
|
|
1610
|
+
(_m, r, g, b) => `#${r}${r}${g}${g}${b}${b}`
|
|
1611
|
+
);
|
|
1612
|
+
v = v.replace(/\btransparent\b/gi, "rgba(0, 0, 0, 0)");
|
|
1613
|
+
v = v.replace(/#00000000\b/g, "rgba(0, 0, 0, 0)");
|
|
1614
|
+
v = v.replace(ZERO_LENGTH_RE, "0");
|
|
1615
|
+
v = v.replace(FUNC_ARGS_RE, (_m, fn, args) => {
|
|
1616
|
+
const parts = args.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
1617
|
+
return `${fn.toLowerCase()}(${parts.join(", ")})`;
|
|
1618
|
+
});
|
|
1619
|
+
return v;
|
|
1620
|
+
}
|
|
1621
|
+
function isRelativeValue(value) {
|
|
1622
|
+
return RELATIVE_UNIT_RE.test(value);
|
|
1623
|
+
}
|
|
1624
|
+
var BOX_SIDES = {
|
|
1625
|
+
padding: ["padding-top", "padding-right", "padding-bottom", "padding-left"],
|
|
1626
|
+
margin: ["margin-top", "margin-right", "margin-bottom", "margin-left"],
|
|
1627
|
+
inset: ["top", "right", "bottom", "left"],
|
|
1628
|
+
"border-width": [
|
|
1629
|
+
"border-top-width",
|
|
1630
|
+
"border-right-width",
|
|
1631
|
+
"border-bottom-width",
|
|
1632
|
+
"border-left-width"
|
|
1633
|
+
],
|
|
1634
|
+
"border-style": [
|
|
1635
|
+
"border-top-style",
|
|
1636
|
+
"border-right-style",
|
|
1637
|
+
"border-bottom-style",
|
|
1638
|
+
"border-left-style"
|
|
1639
|
+
],
|
|
1640
|
+
"border-color": [
|
|
1641
|
+
"border-top-color",
|
|
1642
|
+
"border-right-color",
|
|
1643
|
+
"border-bottom-color",
|
|
1644
|
+
"border-left-color"
|
|
1645
|
+
]
|
|
1646
|
+
};
|
|
1647
|
+
function splitTopLevel(value) {
|
|
1648
|
+
const out = [];
|
|
1649
|
+
let depth = 0;
|
|
1650
|
+
let cur = "";
|
|
1651
|
+
for (const ch of value) {
|
|
1652
|
+
if (ch === "(") depth += 1;
|
|
1653
|
+
else if (ch === ")") depth = Math.max(0, depth - 1);
|
|
1654
|
+
if (depth === 0 && /\s/.test(ch)) {
|
|
1655
|
+
if (cur.length > 0) {
|
|
1656
|
+
out.push(cur);
|
|
1657
|
+
cur = "";
|
|
1658
|
+
}
|
|
1659
|
+
continue;
|
|
1660
|
+
}
|
|
1661
|
+
cur += ch;
|
|
1662
|
+
}
|
|
1663
|
+
if (cur.length > 0) out.push(cur);
|
|
1664
|
+
return out;
|
|
1665
|
+
}
|
|
1666
|
+
function boxFourSides(values) {
|
|
1667
|
+
const [a, b, c, d] = values;
|
|
1668
|
+
switch (values.length) {
|
|
1669
|
+
case 1:
|
|
1670
|
+
return [a, a, a, a];
|
|
1671
|
+
case 2:
|
|
1672
|
+
return [a, b, a, b];
|
|
1673
|
+
case 3:
|
|
1674
|
+
return [a, b, c, b];
|
|
1675
|
+
default:
|
|
1676
|
+
return [a, b, c, d];
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
function expandShorthand(prop, value) {
|
|
1680
|
+
const box = BOX_SIDES[prop];
|
|
1681
|
+
if (box) {
|
|
1682
|
+
const parts = splitTopLevel(value);
|
|
1683
|
+
if (parts.length >= 1 && parts.length <= 4) {
|
|
1684
|
+
const sides = boxFourSides(parts);
|
|
1685
|
+
return box.map((p, i) => [p, sides[i]]);
|
|
1686
|
+
}
|
|
1687
|
+
return [[prop, value]];
|
|
1688
|
+
}
|
|
1689
|
+
if (prop === "gap" || prop === "grid-gap") {
|
|
1690
|
+
const parts = splitTopLevel(value);
|
|
1691
|
+
if (parts.length === 1) {
|
|
1692
|
+
return [
|
|
1693
|
+
["row-gap", parts[0]],
|
|
1694
|
+
["column-gap", parts[0]]
|
|
1695
|
+
];
|
|
1696
|
+
}
|
|
1697
|
+
if (parts.length === 2) {
|
|
1698
|
+
return [
|
|
1699
|
+
["row-gap", parts[0]],
|
|
1700
|
+
["column-gap", parts[1]]
|
|
1701
|
+
];
|
|
1702
|
+
}
|
|
1703
|
+
return [[prop, value]];
|
|
1704
|
+
}
|
|
1705
|
+
return [[prop, value]];
|
|
1706
|
+
}
|
|
1707
|
+
function makeDecl(table, prop, rawValue, important) {
|
|
1708
|
+
const property = prop.trim().toLowerCase();
|
|
1709
|
+
const value = canonValue(rawValue);
|
|
1710
|
+
return {
|
|
1711
|
+
property,
|
|
1712
|
+
value,
|
|
1713
|
+
important,
|
|
1714
|
+
relativeToParent: isRelativeValue(value),
|
|
1715
|
+
inherited: table.isInherited(property)
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1718
|
+
function createNormalizer() {
|
|
1719
|
+
const inherited = createInheritedTable();
|
|
1720
|
+
const normalizeDeclaration = (prop, value, important) => {
|
|
1721
|
+
const p = prop.trim().toLowerCase();
|
|
1722
|
+
const expanded = expandShorthand(p, value.trim());
|
|
1723
|
+
return expanded.map(([lp, lv]) => makeDecl(inherited, lp, lv, important));
|
|
1724
|
+
};
|
|
1725
|
+
const normalizeValue = (prop, raw) => {
|
|
1726
|
+
void prop;
|
|
1727
|
+
return canonValue(raw);
|
|
1728
|
+
};
|
|
1729
|
+
const normalizeStyleMap = (sm) => {
|
|
1730
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
1731
|
+
for (const block of sm.blocks.values()) {
|
|
1732
|
+
const decls = /* @__PURE__ */ new Map();
|
|
1733
|
+
for (const decl of block.decls.values()) {
|
|
1734
|
+
const next = {
|
|
1735
|
+
...decl,
|
|
1736
|
+
value: canonValue(String(decl.value)),
|
|
1737
|
+
relativeToParent: isRelativeValue(String(decl.value)),
|
|
1738
|
+
inherited: inherited.isInherited(decl.property)
|
|
1739
|
+
};
|
|
1740
|
+
decls.set(next.property, next);
|
|
1741
|
+
}
|
|
1742
|
+
const sorted = new Map(
|
|
1743
|
+
[...decls.entries()].sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0)
|
|
1744
|
+
);
|
|
1745
|
+
const key = conditionKey(block.condition);
|
|
1746
|
+
blocks.set(key, { condition: block.condition, decls: sorted });
|
|
1747
|
+
}
|
|
1748
|
+
return { blocks };
|
|
1749
|
+
};
|
|
1750
|
+
const equals = (a, b) => {
|
|
1751
|
+
const na = normalizeStyleMap(a);
|
|
1752
|
+
const nb = normalizeStyleMap(b);
|
|
1753
|
+
if (na.blocks.size !== nb.blocks.size) return false;
|
|
1754
|
+
for (const [key, blockA] of na.blocks) {
|
|
1755
|
+
const blockB = nb.blocks.get(key);
|
|
1756
|
+
if (!blockB) return false;
|
|
1757
|
+
if (blockA.decls.size !== blockB.decls.size) return false;
|
|
1758
|
+
for (const [prop, declA] of blockA.decls) {
|
|
1759
|
+
const declB = blockB.decls.get(prop);
|
|
1760
|
+
if (!declB) return false;
|
|
1761
|
+
if (declA.value !== declB.value || declA.important !== declB.important) return false;
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
return true;
|
|
1765
|
+
};
|
|
1766
|
+
return {
|
|
1767
|
+
version: "domflax-normalizer@1",
|
|
1768
|
+
normalizeDeclaration,
|
|
1769
|
+
normalizeValue,
|
|
1770
|
+
normalizeStyleMap,
|
|
1771
|
+
equals,
|
|
1772
|
+
inherited
|
|
1773
|
+
};
|
|
1774
|
+
}
|
|
1775
|
+
var normalizer = createNormalizer();
|
|
1776
|
+
function isStyleSuperset(full, partial, norm = normalizer) {
|
|
1777
|
+
const nf = norm.normalizeStyleMap(full);
|
|
1778
|
+
const np = norm.normalizeStyleMap(partial);
|
|
1779
|
+
for (const [key, want] of np.blocks) {
|
|
1780
|
+
const have = nf.blocks.get(key) ?? nf.blocks.get(conditionKey(want.condition));
|
|
1781
|
+
if (!have) return false;
|
|
1782
|
+
for (const [prop, decl] of want.decls) {
|
|
1783
|
+
const got = have.decls.get(prop);
|
|
1784
|
+
if (!got || got.value !== decl.value) return false;
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
return true;
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
// ../pattern-kit/src/combinators.ts
|
|
1791
|
+
function asElement(node) {
|
|
1792
|
+
const n = node;
|
|
1793
|
+
return n.kind === "element" ? n : null;
|
|
1794
|
+
}
|
|
1795
|
+
function elementChildrenOf(el, ctx) {
|
|
1796
|
+
const out = [];
|
|
1797
|
+
for (const childId of el.children) {
|
|
1798
|
+
const child = ctx.doc.nodes.get(childId);
|
|
1799
|
+
if (child && child.kind === "element") out.push(child);
|
|
1800
|
+
}
|
|
1801
|
+
return out;
|
|
1802
|
+
}
|
|
1803
|
+
function and(...matchers) {
|
|
1804
|
+
return (node, ctx) => matchers.every((m) => m(node, ctx));
|
|
1805
|
+
}
|
|
1806
|
+
function not(matcher) {
|
|
1807
|
+
return (node, ctx) => !matcher(node, ctx);
|
|
1808
|
+
}
|
|
1809
|
+
function isElement(tag) {
|
|
1810
|
+
const want = tag?.toLowerCase();
|
|
1811
|
+
return (node) => {
|
|
1812
|
+
const el = asElement(node);
|
|
1813
|
+
if (!el) return false;
|
|
1814
|
+
return want === void 0 || el.tag.toLowerCase() === want;
|
|
1815
|
+
};
|
|
1816
|
+
}
|
|
1817
|
+
var hasSingleElementChild = (node, ctx) => {
|
|
1818
|
+
const el = asElement(node);
|
|
1819
|
+
if (!el) return false;
|
|
1820
|
+
return elementChildrenOf(el, ctx).length === 1;
|
|
1821
|
+
};
|
|
1822
|
+
function computed(partial) {
|
|
1823
|
+
return (node, ctx) => {
|
|
1824
|
+
const el = asElement(node);
|
|
1825
|
+
if (!el) return false;
|
|
1826
|
+
const full = ctx.computedOf(el) ?? el.computed;
|
|
1827
|
+
return isStyleSuperset(full, partial, normalizer);
|
|
1828
|
+
};
|
|
1829
|
+
}
|
|
1830
|
+
var VISUAL_PROPERTIES = /* @__PURE__ */ new Set([
|
|
1831
|
+
"background",
|
|
1832
|
+
"background-color",
|
|
1833
|
+
"background-image",
|
|
1834
|
+
"border-top-width",
|
|
1835
|
+
"border-right-width",
|
|
1836
|
+
"border-bottom-width",
|
|
1837
|
+
"border-left-width",
|
|
1838
|
+
"border-top-style",
|
|
1839
|
+
"border-right-style",
|
|
1840
|
+
"border-bottom-style",
|
|
1841
|
+
"border-left-style",
|
|
1842
|
+
"border-top-color",
|
|
1843
|
+
"border-right-color",
|
|
1844
|
+
"border-bottom-color",
|
|
1845
|
+
"border-left-color",
|
|
1846
|
+
"border-radius",
|
|
1847
|
+
"box-shadow",
|
|
1848
|
+
"outline",
|
|
1849
|
+
"outline-width",
|
|
1850
|
+
"outline-style",
|
|
1851
|
+
"outline-color",
|
|
1852
|
+
"text-shadow",
|
|
1853
|
+
"filter",
|
|
1854
|
+
"backdrop-filter",
|
|
1855
|
+
"mix-blend-mode",
|
|
1856
|
+
"opacity"
|
|
1857
|
+
]);
|
|
1858
|
+
var EMPTY_VISUAL_VALUES = /* @__PURE__ */ new Set([
|
|
1859
|
+
"none",
|
|
1860
|
+
"0",
|
|
1861
|
+
"normal",
|
|
1862
|
+
"transparent",
|
|
1863
|
+
"rgba(0, 0, 0, 0)",
|
|
1864
|
+
"initial",
|
|
1865
|
+
"unset",
|
|
1866
|
+
"auto"
|
|
1867
|
+
]);
|
|
1868
|
+
var hasOwnVisualStyle = (node, ctx) => {
|
|
1869
|
+
const el = asElement(node);
|
|
1870
|
+
if (!el) return false;
|
|
1871
|
+
if (el.meta.hasOwnVisualStyle) return true;
|
|
1872
|
+
const computedMap = ctx.computedOf(el) ?? el.computed;
|
|
1873
|
+
const norm = normalizer.normalizeStyleMap(computedMap);
|
|
1874
|
+
for (const block of norm.blocks.values()) {
|
|
1875
|
+
for (const decl of block.decls.values()) {
|
|
1876
|
+
if (!VISUAL_PROPERTIES.has(String(decl.property))) continue;
|
|
1877
|
+
if (!EMPTY_VISUAL_VALUES.has(String(decl.value))) return true;
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
return false;
|
|
1881
|
+
};
|
|
1882
|
+
var hasRef = (node) => asElement(node)?.meta.hasRef ?? false;
|
|
1883
|
+
var hasEventHandlers = (node) => asElement(node)?.meta.hasEventHandlers ?? false;
|
|
1884
|
+
var hasDynamicChildren = (node) => asElement(node)?.meta.hasDynamicChildren ?? false;
|
|
1885
|
+
var hasDynamicClasses = (node) => asElement(node)?.classes.hasDynamic ?? false;
|
|
1886
|
+
var targetedByCombinator = (node, ctx) => {
|
|
1887
|
+
const el = asElement(node);
|
|
1888
|
+
if (!el) return false;
|
|
1889
|
+
if (el.meta.targetedByCombinator) return true;
|
|
1890
|
+
return ctx.selectors.targetedByCombinator(el.id);
|
|
1891
|
+
};
|
|
1892
|
+
|
|
1893
|
+
// ../pattern-kit/src/pattern.ts
|
|
1894
|
+
function camelToKebab(key) {
|
|
1895
|
+
if (key.startsWith("--")) return key;
|
|
1896
|
+
return key.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
1897
|
+
}
|
|
1898
|
+
function plainToStyleMap(style) {
|
|
1899
|
+
const decls = /* @__PURE__ */ new Map();
|
|
1900
|
+
for (const [rawKey, rawValue] of Object.entries(style)) {
|
|
1901
|
+
const prop = camelToKebab(rawKey);
|
|
1902
|
+
for (const decl of normalizer.normalizeDeclaration(prop, String(rawValue), false)) {
|
|
1903
|
+
decls.set(decl.property, decl);
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
const block = { condition: BASE_CONDITION, decls };
|
|
1907
|
+
return { blocks: /* @__PURE__ */ new Map([[conditionKey(BASE_CONDITION), block]]) };
|
|
1908
|
+
}
|
|
1909
|
+
function asElement2(node) {
|
|
1910
|
+
const n = node;
|
|
1911
|
+
return n.kind === "element" ? n : null;
|
|
1912
|
+
}
|
|
1913
|
+
function metaFlag(flag) {
|
|
1914
|
+
return (node) => Boolean(asElement2(node)?.meta[flag]);
|
|
1915
|
+
}
|
|
1916
|
+
var hasRawHtml = metaFlag("hasDangerousHtml");
|
|
1917
|
+
var affectsSelectorMatching = (node, ctx) => {
|
|
1918
|
+
const el = asElement2(node);
|
|
1919
|
+
if (!el) return false;
|
|
1920
|
+
return ctx.selectors.reparentImpact(el.id).size > 0;
|
|
1921
|
+
};
|
|
1922
|
+
var FLATTEN_GUARDS = and(
|
|
1923
|
+
not(hasRef),
|
|
1924
|
+
not(hasEventHandlers),
|
|
1925
|
+
not(hasDynamicChildren),
|
|
1926
|
+
not(hasRawHtml),
|
|
1927
|
+
not(targetedByCombinator),
|
|
1928
|
+
not(affectsSelectorMatching)
|
|
1929
|
+
);
|
|
1930
|
+
function isFlattenCategory(category) {
|
|
1931
|
+
return category.split("/", 1)[0] === "flatten";
|
|
1932
|
+
}
|
|
1933
|
+
function compileDeclarativeMatch(m) {
|
|
1934
|
+
const parts = [isElement(m.tag)];
|
|
1935
|
+
if (m.style) parts.push(computed(plainToStyleMap(m.style)));
|
|
1936
|
+
if (m.onlyChild === "element") parts.push(hasSingleElementChild);
|
|
1937
|
+
if (m.paintsNothing) parts.push(not(hasOwnVisualStyle));
|
|
1938
|
+
if (m.where) {
|
|
1939
|
+
const extra = Array.isArray(m.where) ? m.where : [m.where];
|
|
1940
|
+
for (const w of extra) parts.push(w);
|
|
1941
|
+
}
|
|
1942
|
+
return and(...parts);
|
|
1943
|
+
}
|
|
1944
|
+
function compileMatch(match, category) {
|
|
1945
|
+
if (typeof match === "function") return match;
|
|
1946
|
+
const declarative = compileDeclarativeMatch(match ?? {});
|
|
1947
|
+
const guarded = isFlattenCategory(category) ? and(declarative, FLATTEN_GUARDS) : declarative;
|
|
1948
|
+
return (node, ctx) => guarded(node, ctx);
|
|
1949
|
+
}
|
|
1950
|
+
function pruneShadowed(sm, drop) {
|
|
1951
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
1952
|
+
for (const [key, block] of sm.blocks) {
|
|
1953
|
+
const decls = /* @__PURE__ */ new Map();
|
|
1954
|
+
for (const [prop, decl] of block.decls) {
|
|
1955
|
+
const filtered = (decl.shadowed ?? []).filter(
|
|
1956
|
+
(o) => !(o.kind === "class" && drop.has(o.className))
|
|
1957
|
+
);
|
|
1958
|
+
const rest = { ...decl };
|
|
1959
|
+
delete rest.shadowed;
|
|
1960
|
+
const next = filtered.length > 0 ? { ...rest, shadowed: filtered } : rest;
|
|
1961
|
+
decls.set(prop, next);
|
|
1962
|
+
}
|
|
1963
|
+
blocks.set(key, { condition: block.condition, decls });
|
|
1964
|
+
}
|
|
1965
|
+
return { blocks };
|
|
1966
|
+
}
|
|
1967
|
+
function compileFlattenInto(recipe) {
|
|
1968
|
+
const childGains = recipe.childGains ? plainToStyleMap(recipe.childGains) : null;
|
|
1969
|
+
const fold = recipe.foldInherited !== false;
|
|
1970
|
+
return (ctx, rw) => {
|
|
1971
|
+
const wrapper = ctx.node;
|
|
1972
|
+
const child = ctx.onlyElementChild();
|
|
1973
|
+
if (!child) return null;
|
|
1974
|
+
const ops = [];
|
|
1975
|
+
if (fold) ops.push(rw.foldInheritedStyles(wrapper, child, { conditions: "all" }));
|
|
1976
|
+
if (childGains) ops.push(rw.mergeStyle(child, null, childGains, "source-wins"));
|
|
1977
|
+
ops.push(rw.unwrap(wrapper));
|
|
1978
|
+
return ops;
|
|
1979
|
+
};
|
|
1980
|
+
}
|
|
1981
|
+
function compileRewrite(rewrite) {
|
|
1982
|
+
if (typeof rewrite === "function") return rewrite;
|
|
1983
|
+
if ("flattenInto" in rewrite) return compileFlattenInto(rewrite);
|
|
1984
|
+
if ("rewriteClasses" in rewrite) {
|
|
1985
|
+
const preserveOpaque = rewrite.preserveOpaque ?? true;
|
|
1986
|
+
return (ctx, rw) => {
|
|
1987
|
+
const next = rewrite.rewriteClasses(ctx.computed(), ctx);
|
|
1988
|
+
if (!next) return null;
|
|
1989
|
+
return [rw.setClassList(ctx.node, next, preserveOpaque)];
|
|
1990
|
+
};
|
|
1991
|
+
}
|
|
1992
|
+
if ("dropClasses" in rewrite) {
|
|
1993
|
+
const preserveOpaque = rewrite.preserveOpaque ?? true;
|
|
1994
|
+
return (ctx, rw) => {
|
|
1995
|
+
const drop = new Set(rewrite.dropClasses(ctx.computed(), ctx));
|
|
1996
|
+
if (drop.size === 0) return null;
|
|
1997
|
+
return [rw.setClassList(ctx.node, pruneShadowed(ctx.computed(), drop), preserveOpaque)];
|
|
1998
|
+
};
|
|
1999
|
+
}
|
|
2000
|
+
const style = plainToStyleMap(rewrite.mergeStyle);
|
|
2001
|
+
const onConflict = rewrite.onConflict ?? "abort";
|
|
2002
|
+
return (ctx, rw) => [rw.mergeStyle(ctx.node, null, style, onConflict)];
|
|
2003
|
+
}
|
|
2004
|
+
function pattern(config) {
|
|
2005
|
+
const matchFn = compileMatch(config.match, config.category);
|
|
2006
|
+
const rewriteFn = compileRewrite(config.rewrite);
|
|
2007
|
+
const spec = {
|
|
2008
|
+
name: config.name,
|
|
2009
|
+
category: config.category,
|
|
2010
|
+
safety: config.safety,
|
|
2011
|
+
priority: config.priority,
|
|
2012
|
+
precondition: config.precondition,
|
|
2013
|
+
doc: config.doc,
|
|
2014
|
+
examples: config.examples,
|
|
2015
|
+
evaluate(ctx, rw) {
|
|
2016
|
+
if (!matchFn(ctx.node, ctx)) return null;
|
|
2017
|
+
const ops = rewriteFn(ctx, rw);
|
|
2018
|
+
if (!ops || ops.length === 0) return null;
|
|
2019
|
+
return { ops };
|
|
2020
|
+
}
|
|
2021
|
+
};
|
|
2022
|
+
return definePattern(spec);
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
// ../patterns/src/flatten/empty-style-div.pattern.ts
|
|
2026
|
+
function asEl(node) {
|
|
2027
|
+
const n = node;
|
|
2028
|
+
return n.kind === "element" ? n : null;
|
|
2029
|
+
}
|
|
2030
|
+
function metaFlag2(flag) {
|
|
2031
|
+
return (node) => Boolean(asEl(node)?.meta[flag]);
|
|
2032
|
+
}
|
|
2033
|
+
var establishesBox = metaFlag2("establishesBox");
|
|
2034
|
+
var establishesFormattingContext = metaFlag2("establishesFormattingContext");
|
|
2035
|
+
var establishesStackingContext = metaFlag2("establishesStackingContext");
|
|
2036
|
+
var isContainingBlock = metaFlag2("isContainingBlock");
|
|
2037
|
+
var declaresCustomProperties = metaFlag2("declaresCustomProperties");
|
|
2038
|
+
var targetedByStructuralPseudo = (node, ctx) => {
|
|
2039
|
+
const el = asEl(node);
|
|
2040
|
+
if (!el) return false;
|
|
2041
|
+
if (el.meta.targetedByStructuralPseudo) return true;
|
|
2042
|
+
return ctx.selectors.targetedByStructuralPseudo(el.id);
|
|
2043
|
+
};
|
|
2044
|
+
var DISPLAY = "display";
|
|
2045
|
+
var hasNonBlockDisplay = (node, ctx) => {
|
|
2046
|
+
const el = asEl(node);
|
|
2047
|
+
if (!el) return false;
|
|
2048
|
+
const sm = ctx.computedOf(el) ?? el.computed;
|
|
2049
|
+
for (const block of sm.blocks.values()) {
|
|
2050
|
+
const decl = block.decls.get(DISPLAY);
|
|
2051
|
+
if (decl && String(decl.value) !== "block") return true;
|
|
2052
|
+
}
|
|
2053
|
+
return false;
|
|
2054
|
+
};
|
|
2055
|
+
var emptyStyleDiv = pattern({
|
|
2056
|
+
name: "empty-style-div",
|
|
2057
|
+
category: "flatten/empty-style-div",
|
|
2058
|
+
safety: 1,
|
|
2059
|
+
doc: {
|
|
2060
|
+
title: "Flatten empty-style div wrapper",
|
|
2061
|
+
summary: "A layout-neutral div (display:block default, no box/visual styles) that wraps a single child is removed; the child is hoisted into its place.",
|
|
2062
|
+
before: "<div><Child/></div>",
|
|
2063
|
+
after: "<Child/>",
|
|
2064
|
+
safetyRationale: "Wrapper is a plain block box that paints nothing, establishes no box/formatting/stacking context, is no containing block, has no custom-property coupling, carries no ref/handlers/dynamic children/raw HTML, and is neither a combinator subject nor a structural-pseudo target; inheritable styles are folded onto the child before removal."
|
|
2065
|
+
},
|
|
2066
|
+
match: {
|
|
2067
|
+
tag: "div",
|
|
2068
|
+
onlyChild: "element",
|
|
2069
|
+
paintsNothing: true,
|
|
2070
|
+
where: [
|
|
2071
|
+
not(hasNonBlockDisplay),
|
|
2072
|
+
not(establishesBox),
|
|
2073
|
+
not(establishesFormattingContext),
|
|
2074
|
+
not(establishesStackingContext),
|
|
2075
|
+
not(isContainingBlock),
|
|
2076
|
+
not(declaresCustomProperties),
|
|
2077
|
+
not(targetedByStructuralPseudo)
|
|
2078
|
+
]
|
|
2079
|
+
},
|
|
2080
|
+
rewrite: { flattenInto: "child" },
|
|
2081
|
+
examples: [
|
|
2082
|
+
{
|
|
2083
|
+
before: '<div><span className="bg-red-200">Hi</span></div>',
|
|
2084
|
+
after: '<span className="bg-red-200">Hi</span>'
|
|
2085
|
+
},
|
|
2086
|
+
{
|
|
2087
|
+
// The wrapper paints its own background (own visual style) → not layout-neutral, kept.
|
|
2088
|
+
noMatch: '<div className="bg-blue-500"><span className="bg-red-200">Hi</span></div>'
|
|
2089
|
+
}
|
|
2090
|
+
]
|
|
2091
|
+
});
|
|
2092
|
+
|
|
2093
|
+
// ../patterns/src/flatten/flex-center-wrapper.pattern.ts
|
|
2094
|
+
var flexCenterWrapper = pattern({
|
|
2095
|
+
name: "flex-center-wrapper",
|
|
2096
|
+
category: "flatten/flex-center-wrapper",
|
|
2097
|
+
safety: 2,
|
|
2098
|
+
doc: {
|
|
2099
|
+
title: "Flatten flex-centering wrapper",
|
|
2100
|
+
summary: "A div that only centers a single child (display:flex; align-items:center; justify-content:center) is removed; the child gains place-self:center.",
|
|
2101
|
+
before: '<div style="display:flex;align-items:center;justify-content:center"><Child/></div>',
|
|
2102
|
+
after: '<Child style="place-self:center"/>',
|
|
2103
|
+
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."
|
|
2104
|
+
},
|
|
2105
|
+
match: {
|
|
2106
|
+
tag: "div",
|
|
2107
|
+
style: { display: "flex", alignItems: "center", justifyContent: "center" },
|
|
2108
|
+
onlyChild: "element",
|
|
2109
|
+
paintsNothing: true
|
|
2110
|
+
},
|
|
2111
|
+
rewrite: {
|
|
2112
|
+
flattenInto: "child",
|
|
2113
|
+
childGains: { placeSelf: "center" }
|
|
2114
|
+
},
|
|
2115
|
+
examples: [
|
|
2116
|
+
{
|
|
2117
|
+
// The wrapper is removed; the surviving child gains `place-self-center` (reverse-emitted
|
|
2118
|
+
// from the folded computed style by the resolver).
|
|
2119
|
+
before: '<div className="flex justify-center items-center"><div className="bg-red-200">Hello</div></div>',
|
|
2120
|
+
after: '<div className="bg-red-200 place-self-center">Hello</div>'
|
|
2121
|
+
},
|
|
2122
|
+
{
|
|
2123
|
+
// onClick is a hard opacity barrier → the wrapper is load-bearing, no flatten.
|
|
2124
|
+
noMatch: '<div className="flex justify-center items-center" onClick={handleClick}><div className="bg-red-200">Hello</div></div>'
|
|
2125
|
+
}
|
|
2126
|
+
]
|
|
2127
|
+
});
|
|
2128
|
+
|
|
2129
|
+
// ../patterns/src/flatten/nested-flex-merge.pattern.ts
|
|
2130
|
+
function baseConditionStyleMap(decls) {
|
|
2131
|
+
const map = /* @__PURE__ */ new Map();
|
|
2132
|
+
for (const [prop, value] of decls) {
|
|
2133
|
+
for (const decl of normalizer.normalizeDeclaration(prop, value, false)) {
|
|
2134
|
+
map.set(decl.property, decl);
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
const block = { condition: BASE_CONDITION, decls: map };
|
|
2138
|
+
const blocks = /* @__PURE__ */ new Map([[conditionKey(BASE_CONDITION), block]]);
|
|
2139
|
+
return { blocks };
|
|
2140
|
+
}
|
|
2141
|
+
var DISPLAY_FLEX = baseConditionStyleMap([["display", "flex"]]);
|
|
2142
|
+
var FLEX_CONTAINER_PROPERTIES = /* @__PURE__ */ new Set([
|
|
2143
|
+
"display",
|
|
2144
|
+
"flex-direction",
|
|
2145
|
+
"flex-wrap",
|
|
2146
|
+
"justify-content",
|
|
2147
|
+
"align-items",
|
|
2148
|
+
"align-content",
|
|
2149
|
+
"place-content",
|
|
2150
|
+
"place-items",
|
|
2151
|
+
"row-gap",
|
|
2152
|
+
"column-gap"
|
|
2153
|
+
]);
|
|
2154
|
+
function outerMergeSafe(sm) {
|
|
2155
|
+
const norm = normalizer.normalizeStyleMap(sm);
|
|
2156
|
+
for (const block of norm.blocks.values()) {
|
|
2157
|
+
for (const decl of block.decls.values()) {
|
|
2158
|
+
if (FLEX_CONTAINER_PROPERTIES.has(String(decl.property))) continue;
|
|
2159
|
+
if (decl.inherited) continue;
|
|
2160
|
+
return false;
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
return true;
|
|
2164
|
+
}
|
|
2165
|
+
function flexConflict(outer, inner) {
|
|
2166
|
+
const a = normalizer.normalizeStyleMap(outer);
|
|
2167
|
+
const b = normalizer.normalizeStyleMap(inner);
|
|
2168
|
+
for (const [key, blockA] of a.blocks) {
|
|
2169
|
+
const blockB = b.blocks.get(key);
|
|
2170
|
+
if (!blockB) continue;
|
|
2171
|
+
for (const [prop, declA] of blockA.decls) {
|
|
2172
|
+
if (!FLEX_CONTAINER_PROPERTIES.has(String(prop))) continue;
|
|
2173
|
+
const declB = blockB.decls.get(prop);
|
|
2174
|
+
if (declB && declB.value !== declA.value) return true;
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
return false;
|
|
2178
|
+
}
|
|
2179
|
+
function extractFlexStyle(sm) {
|
|
2180
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
2181
|
+
for (const [key, block] of sm.blocks) {
|
|
2182
|
+
const decls = /* @__PURE__ */ new Map();
|
|
2183
|
+
for (const [prop, decl] of block.decls) {
|
|
2184
|
+
if (FLEX_CONTAINER_PROPERTIES.has(String(prop))) decls.set(prop, decl);
|
|
2185
|
+
}
|
|
2186
|
+
if (decls.size > 0) blocks.set(key, { condition: block.condition, decls });
|
|
2187
|
+
}
|
|
2188
|
+
return { blocks };
|
|
2189
|
+
}
|
|
2190
|
+
var isInnerFlex = and(
|
|
2191
|
+
isElement("div"),
|
|
2192
|
+
computed(DISPLAY_FLEX),
|
|
2193
|
+
not(targetedByCombinator)
|
|
2194
|
+
);
|
|
2195
|
+
var nestedFlexMerge = pattern({
|
|
2196
|
+
name: "nested-flex-merge",
|
|
2197
|
+
category: "flatten/nested-flex-merge",
|
|
2198
|
+
safety: 2,
|
|
2199
|
+
doc: {
|
|
2200
|
+
title: "Merge nested flex containers",
|
|
2201
|
+
summary: "A flex container whose only child is itself a flex container with non-conflicting flex properties is collapsed into one; the wrapper is removed and its flex declarations merge onto the surviving child.",
|
|
2202
|
+
before: '<div style="display:flex;align-items:center;gap:8px"><div style="display:flex;flex-direction:column"/></div>',
|
|
2203
|
+
after: '<div style="display:flex;flex-direction:column;align-items:center;gap:8px"/>',
|
|
2204
|
+
safetyRationale: "The wrapper paints nothing, declares only flex-container/inheritable properties, carries no ref/handlers/dynamic children, and is not a combinator subject; the two containers do not conflict on any flex property, so the union is unambiguous and lossless."
|
|
2205
|
+
},
|
|
2206
|
+
match: {
|
|
2207
|
+
tag: "div",
|
|
2208
|
+
style: { display: "flex" },
|
|
2209
|
+
onlyChild: "element",
|
|
2210
|
+
paintsNothing: true
|
|
2211
|
+
},
|
|
2212
|
+
rewrite: (ctx, rw) => {
|
|
2213
|
+
const outer = ctx.node;
|
|
2214
|
+
const inner = ctx.onlyElementChild();
|
|
2215
|
+
if (!inner) return null;
|
|
2216
|
+
if (!isInnerFlex(inner, ctx)) return null;
|
|
2217
|
+
const outerStyle = ctx.computed();
|
|
2218
|
+
const innerStyle = ctx.computedOf(inner);
|
|
2219
|
+
if (!outerMergeSafe(outerStyle)) return null;
|
|
2220
|
+
if (flexConflict(outerStyle, innerStyle)) return null;
|
|
2221
|
+
return [
|
|
2222
|
+
// 1. Preserve inheritable values (color/font/…) by folding them onto the child first.
|
|
2223
|
+
rw.foldInheritedStyles(outer, inner, { conditions: "all" }),
|
|
2224
|
+
// 2. Transfer the wrapper's flex-container declarations onto the child (target-wins keeps the
|
|
2225
|
+
// child's value for any shared property — identical anyway, we proved non-conflict).
|
|
2226
|
+
rw.mergeStyle(inner, null, extractFlexStyle(outerStyle), "target-wins"),
|
|
2227
|
+
// 3. Remove the wrapper (structural-safe; hoists the child and preserves its IRNodeId).
|
|
2228
|
+
rw.unwrap(outer)
|
|
2229
|
+
];
|
|
2230
|
+
},
|
|
2231
|
+
examples: [
|
|
2232
|
+
{
|
|
2233
|
+
// The wrapper's flex declarations (align-items / gap) merge onto the inner flex container,
|
|
2234
|
+
// then the wrapper is removed (its own `data-x` here just blocks the more aggressive
|
|
2235
|
+
// passthrough-wrapper so this merge is the one that fires).
|
|
2236
|
+
before: '<div className="flex items-center gap-2" data-x="1"><div className="flex flex-col">X</div></div>',
|
|
2237
|
+
after: '<div className="flex flex-col gap-2 items-center">X</div>'
|
|
2238
|
+
},
|
|
2239
|
+
{
|
|
2240
|
+
// A non-flex wrapper does not match the flex-container signature → left unchanged.
|
|
2241
|
+
noMatch: '<div className="block bg-blue-500"><div className="flex flex-col">X</div></div>'
|
|
2242
|
+
}
|
|
2243
|
+
]
|
|
2244
|
+
});
|
|
2245
|
+
|
|
2246
|
+
// ../patterns/src/flatten/passthrough-wrapper.pattern.ts
|
|
2247
|
+
function metaOf(node) {
|
|
2248
|
+
const n = node;
|
|
2249
|
+
return n.kind === "element" ? n.meta : null;
|
|
2250
|
+
}
|
|
2251
|
+
function elementOf(node) {
|
|
2252
|
+
const n = node;
|
|
2253
|
+
return n.kind === "element" ? n : null;
|
|
2254
|
+
}
|
|
2255
|
+
var establishesContext = (node) => {
|
|
2256
|
+
const m = metaOf(node);
|
|
2257
|
+
if (!m) return false;
|
|
2258
|
+
return m.establishesBox || m.establishesFormattingContext || m.establishesStackingContext || m.isContainingBlock || m.declaresCustomProperties;
|
|
2259
|
+
};
|
|
2260
|
+
var hasSpreadAttrs = (node) => metaOf(node)?.hasSpreadAttrs ?? false;
|
|
2261
|
+
var isComponentNode = (node) => metaOf(node)?.isComponent ?? false;
|
|
2262
|
+
var hasOwnAttrs = (node) => {
|
|
2263
|
+
const el = elementOf(node);
|
|
2264
|
+
if (!el) return false;
|
|
2265
|
+
return el.attrs.entries.size > 0 || el.attrs.spreads.length > 0;
|
|
2266
|
+
};
|
|
2267
|
+
var targetedByStructuralPseudo2 = (node, ctx) => {
|
|
2268
|
+
const el = elementOf(node);
|
|
2269
|
+
if (!el) return false;
|
|
2270
|
+
if (el.meta.targetedByStructuralPseudo) return true;
|
|
2271
|
+
return ctx.selectors.targetedByStructuralPseudo(el.id);
|
|
2272
|
+
};
|
|
2273
|
+
var passthroughWrapper = pattern({
|
|
2274
|
+
name: "passthrough-wrapper",
|
|
2275
|
+
category: "flatten/passthrough-wrapper",
|
|
2276
|
+
safety: 2,
|
|
2277
|
+
doc: {
|
|
2278
|
+
title: "Flatten passthrough wrapper",
|
|
2279
|
+
summary: "A div 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.",
|
|
2280
|
+
before: "<div><Child/></div>",
|
|
2281
|
+
after: "<Child/>",
|
|
2282
|
+
safetyRationale: "Wrapper paints nothing and establishes no layout/paint/var context, carries no ref/handlers/dynamic-children/html/spread/component identity, owns no targetable attrs, and is not a combinator/structural-pseudo subject (reparenting changes no match-set); inheritable styles are folded onto the child before removal."
|
|
2283
|
+
},
|
|
2284
|
+
match: {
|
|
2285
|
+
tag: "div",
|
|
2286
|
+
onlyChild: "element",
|
|
2287
|
+
paintsNothing: true,
|
|
2288
|
+
where: [
|
|
2289
|
+
not(establishesContext),
|
|
2290
|
+
not(hasOwnAttrs),
|
|
2291
|
+
not(hasDynamicClasses),
|
|
2292
|
+
not(hasSpreadAttrs),
|
|
2293
|
+
not(isComponentNode),
|
|
2294
|
+
not(targetedByStructuralPseudo2)
|
|
2295
|
+
]
|
|
2296
|
+
},
|
|
2297
|
+
rewrite: { flattenInto: "child" },
|
|
2298
|
+
examples: [
|
|
2299
|
+
{
|
|
2300
|
+
before: '<div className="flex"><a className="bg-red-200">Link</a></div>',
|
|
2301
|
+
after: '<a className="bg-red-200">Link</a>'
|
|
2302
|
+
},
|
|
2303
|
+
{
|
|
2304
|
+
// A ref pins the wrapper's element identity (a hard opacity barrier) → not a passthrough.
|
|
2305
|
+
noMatch: '<div ref={rootRef}><a className="bg-red-200">Link</a></div>'
|
|
2306
|
+
}
|
|
2307
|
+
]
|
|
2308
|
+
});
|
|
2309
|
+
|
|
2310
|
+
// ../patterns/src/flatten/redundant-fragment.pattern.ts
|
|
2311
|
+
function parentIsRedundantFragment(node, ctx) {
|
|
2312
|
+
const el = node;
|
|
2313
|
+
if (el.kind !== "element") return false;
|
|
2314
|
+
const parentId = el.parent;
|
|
2315
|
+
if (parentId == null) return false;
|
|
2316
|
+
const parent = ctx.doc.nodes.get(parentId);
|
|
2317
|
+
if (!parent || parent.kind !== "fragment") return false;
|
|
2318
|
+
if (parent.parent == null) return false;
|
|
2319
|
+
if (parent.children.length !== 1) return false;
|
|
2320
|
+
const m = parent.meta;
|
|
2321
|
+
if (m.hasKey || m.hasRef || m.hasEventHandlers || m.hasDynamicChildren || m.hasDangerousHtml || m.hasSpreadAttrs || m.isComponent) {
|
|
2322
|
+
return false;
|
|
2323
|
+
}
|
|
2324
|
+
if (m.targetedByCombinator || m.targetedByStructuralPseudo) return false;
|
|
2325
|
+
const fid = parentId;
|
|
2326
|
+
if (ctx.selectors.targetedByCombinator(fid) || ctx.selectors.targetedByStructuralPseudo(fid)) {
|
|
2327
|
+
return false;
|
|
2328
|
+
}
|
|
2329
|
+
if (ctx.selectors.reparentImpact(fid).size > 0) return false;
|
|
2330
|
+
return true;
|
|
2331
|
+
}
|
|
2332
|
+
var redundantFragment = pattern({
|
|
2333
|
+
name: "redundant-fragment",
|
|
2334
|
+
category: "flatten/redundant-fragment",
|
|
2335
|
+
safety: 1,
|
|
2336
|
+
doc: {
|
|
2337
|
+
title: "Flatten redundant single-child fragment",
|
|
2338
|
+
summary: "A fragment whose only child is a single node is removed; the child is spliced up into the fragment's slot, preserving its IRNodeId, siblings, attributes and the CSS cascade.",
|
|
2339
|
+
before: "<><Child/></>",
|
|
2340
|
+
after: "<Child/>",
|
|
2341
|
+
safetyRationale: "A fragment paints nothing and renders no box; with exactly one child its removal changes no sibling/structural-pseudo match-set. Keyed fragments and fragments carrying ref/handlers/dynamic-children/raw-html/spread are excluded as opacity barriers."
|
|
2342
|
+
},
|
|
2343
|
+
match: parentIsRedundantFragment,
|
|
2344
|
+
rewrite: (ctx, rw) => {
|
|
2345
|
+
const parentId = ctx.node.parent;
|
|
2346
|
+
if (parentId == null) return null;
|
|
2347
|
+
const fragment = ctx.doc.nodes.get(parentId);
|
|
2348
|
+
if (!fragment || fragment.kind !== "fragment") return null;
|
|
2349
|
+
return [rw.unwrap(fragment)];
|
|
2350
|
+
},
|
|
2351
|
+
examples: [
|
|
2352
|
+
{
|
|
2353
|
+
before: '<><span className="bg-red-200">Hi</span></>',
|
|
2354
|
+
after: '<span className="bg-red-200">Hi</span>'
|
|
2355
|
+
},
|
|
2356
|
+
{
|
|
2357
|
+
// Two children ⇒ not a single-child fragment, so the fragment is load-bearing and stays.
|
|
2358
|
+
noMatch: '<><span className="bg-red-200">A</span><span className="bg-green-200">B</span></>'
|
|
2359
|
+
}
|
|
2360
|
+
]
|
|
2361
|
+
});
|
|
2362
|
+
|
|
2363
|
+
// ../patterns/src/compress/dedupe-classes.pattern.ts
|
|
2364
|
+
function elementOf2(node) {
|
|
2365
|
+
const n = node;
|
|
2366
|
+
return n.kind === "element" ? n : null;
|
|
2367
|
+
}
|
|
2368
|
+
var hasDangerousHtml = (node) => elementOf2(node)?.meta.hasDangerousHtml ?? false;
|
|
2369
|
+
var isOpaque = (node, ctx) => ctx.isOpaque(node);
|
|
2370
|
+
function findRedundantClasses(computed2) {
|
|
2371
|
+
const winners = /* @__PURE__ */ new Set();
|
|
2372
|
+
const shadowed = /* @__PURE__ */ new Set();
|
|
2373
|
+
for (const block of computed2.blocks.values()) {
|
|
2374
|
+
for (const decl of block.decls.values()) {
|
|
2375
|
+
if (decl.origin && decl.origin.kind === "class") winners.add(decl.origin.className);
|
|
2376
|
+
for (const o of decl.shadowed ?? []) {
|
|
2377
|
+
if (o.kind === "class") shadowed.add(o.className);
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
return { winners, shadowed };
|
|
2382
|
+
}
|
|
2383
|
+
var dedupeClasses = pattern({
|
|
2384
|
+
name: "dedupe-classes",
|
|
2385
|
+
category: "compress/dedupe-classes",
|
|
2386
|
+
safety: 1,
|
|
2387
|
+
doc: {
|
|
2388
|
+
title: "Dedupe fully-overridden class tokens",
|
|
2389
|
+
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.",
|
|
2390
|
+
before: '<p class="text-sm text-lg" />',
|
|
2391
|
+
after: '<p class="text-lg" />',
|
|
2392
|
+
safetyRationale: "A fully-overridden token contributes nothing to the computed style in any condition, so removing it changes no pixels. Dynamic/opaque class lists, ref/handler/dynamic-children/raw-html barriers, combinator subjects, and selector-bound (non-droppable) tokens are excluded."
|
|
2393
|
+
},
|
|
2394
|
+
match: {
|
|
2395
|
+
where: [
|
|
2396
|
+
not(hasRef),
|
|
2397
|
+
not(hasEventHandlers),
|
|
2398
|
+
not(hasDynamicChildren),
|
|
2399
|
+
not(hasDangerousHtml),
|
|
2400
|
+
not(hasDynamicClasses),
|
|
2401
|
+
not(isOpaque),
|
|
2402
|
+
not(targetedByCombinator)
|
|
2403
|
+
]
|
|
2404
|
+
},
|
|
2405
|
+
rewrite: {
|
|
2406
|
+
dropClasses(computed2, ctx) {
|
|
2407
|
+
const { winners, shadowed } = findRedundantClasses(computed2);
|
|
2408
|
+
const drop = /* @__PURE__ */ new Set();
|
|
2409
|
+
for (const cls of shadowed) {
|
|
2410
|
+
if (winners.has(cls)) continue;
|
|
2411
|
+
if (!ctx.resolver.selectorUsage(cls).droppable) continue;
|
|
2412
|
+
drop.add(cls);
|
|
2413
|
+
}
|
|
2414
|
+
return drop;
|
|
2415
|
+
}
|
|
2416
|
+
},
|
|
2417
|
+
examples: [
|
|
2418
|
+
{
|
|
2419
|
+
// `text-sm` is fully overridden by `text-lg` (both set font-size + line-height). The resolver
|
|
2420
|
+
// records that shadowing in provenance and reports the Tailwind utility as droppable, so the
|
|
2421
|
+
// pattern drops `text-sm`; the reverse-emit then re-derives the minimal set (`text-lg`).
|
|
2422
|
+
before: '<p className="text-sm text-lg">Hi</p>',
|
|
2423
|
+
after: '<p className="text-lg">Hi</p>'
|
|
2424
|
+
},
|
|
2425
|
+
{
|
|
2426
|
+
// Both tokens win a distinct property (no full override) → nothing to dedupe.
|
|
2427
|
+
noMatch: '<p className="text-lg font-bold">Hi</p>'
|
|
2428
|
+
}
|
|
2429
|
+
]
|
|
2430
|
+
});
|
|
2431
|
+
|
|
2432
|
+
// ../patterns/src/compress/inset-shorthand.pattern.ts
|
|
2433
|
+
var TOP = "top";
|
|
2434
|
+
var RIGHT = "right";
|
|
2435
|
+
var BOTTOM = "bottom";
|
|
2436
|
+
var LEFT = "left";
|
|
2437
|
+
var INSET = "inset";
|
|
2438
|
+
var INSET_BLOCK = "inset-block";
|
|
2439
|
+
var INSET_INLINE = "inset-inline";
|
|
2440
|
+
var hasRawHtml2 = (node) => {
|
|
2441
|
+
const n = node;
|
|
2442
|
+
return n.kind === "element" ? n.meta.hasDangerousHtml : false;
|
|
2443
|
+
};
|
|
2444
|
+
function sameSide(a, b) {
|
|
2445
|
+
return a !== void 0 && b !== void 0 && a.value === b.value && a.important === b.important;
|
|
2446
|
+
}
|
|
2447
|
+
function asProperty(src, property) {
|
|
2448
|
+
return { ...src, property, inherited: normalizer.inherited.isInherited(property) };
|
|
2449
|
+
}
|
|
2450
|
+
function withBaseDecls(src, baseDecls) {
|
|
2451
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
2452
|
+
for (const [key, block] of src.blocks) {
|
|
2453
|
+
const decls = key === BASE_CONDITION_KEY ? baseDecls : new Map(block.decls);
|
|
2454
|
+
blocks.set(key, { condition: block.condition, decls });
|
|
2455
|
+
}
|
|
2456
|
+
return { blocks };
|
|
2457
|
+
}
|
|
2458
|
+
var insetShorthand = pattern({
|
|
2459
|
+
name: "inset-shorthand",
|
|
2460
|
+
category: "compress/inset-shorthand",
|
|
2461
|
+
safety: 2,
|
|
2462
|
+
doc: {
|
|
2463
|
+
title: "Compress inset longhands into a shorthand",
|
|
2464
|
+
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).",
|
|
2465
|
+
before: '<div style="top:10px;right:10px;bottom:10px;left:10px"/>',
|
|
2466
|
+
after: '<div style="inset:10px"/>',
|
|
2467
|
+
safetyRationale: "Meaning-preserving shorthand compaction; the element is not a combinator subject and carries no ref/handlers/dynamic children/raw HTML, so neither selector matching nor behaviour changes."
|
|
2468
|
+
},
|
|
2469
|
+
match: {
|
|
2470
|
+
where: [
|
|
2471
|
+
not(hasRef),
|
|
2472
|
+
not(hasEventHandlers),
|
|
2473
|
+
not(hasDynamicChildren),
|
|
2474
|
+
not(hasRawHtml2),
|
|
2475
|
+
not(hasDynamicClasses),
|
|
2476
|
+
not(targetedByCombinator)
|
|
2477
|
+
]
|
|
2478
|
+
},
|
|
2479
|
+
rewrite: {
|
|
2480
|
+
rewriteClasses(computed2) {
|
|
2481
|
+
const base = computed2.blocks.get(BASE_CONDITION_KEY);
|
|
2482
|
+
if (!base) return null;
|
|
2483
|
+
const top = base.decls.get(TOP);
|
|
2484
|
+
const right = base.decls.get(RIGHT);
|
|
2485
|
+
const bottom = base.decls.get(BOTTOM);
|
|
2486
|
+
const left = base.decls.get(LEFT);
|
|
2487
|
+
const next = new Map(base.decls);
|
|
2488
|
+
if (top && sameSide(top, right) && sameSide(top, bottom) && sameSide(top, left)) {
|
|
2489
|
+
next.delete(TOP);
|
|
2490
|
+
next.delete(RIGHT);
|
|
2491
|
+
next.delete(BOTTOM);
|
|
2492
|
+
next.delete(LEFT);
|
|
2493
|
+
next.set(INSET, asProperty(top, INSET));
|
|
2494
|
+
} else {
|
|
2495
|
+
let collapsed = false;
|
|
2496
|
+
if (sameSide(top, bottom)) {
|
|
2497
|
+
next.delete(TOP);
|
|
2498
|
+
next.delete(BOTTOM);
|
|
2499
|
+
next.set(INSET_BLOCK, asProperty(top, INSET_BLOCK));
|
|
2500
|
+
collapsed = true;
|
|
2501
|
+
}
|
|
2502
|
+
if (sameSide(left, right)) {
|
|
2503
|
+
next.delete(LEFT);
|
|
2504
|
+
next.delete(RIGHT);
|
|
2505
|
+
next.set(INSET_INLINE, asProperty(left, INSET_INLINE));
|
|
2506
|
+
collapsed = true;
|
|
2507
|
+
}
|
|
2508
|
+
if (!collapsed) return null;
|
|
2509
|
+
}
|
|
2510
|
+
return withBaseDecls(computed2, next);
|
|
2511
|
+
}
|
|
2512
|
+
},
|
|
2513
|
+
examples: [
|
|
2514
|
+
{
|
|
2515
|
+
// The four equal inset longhands collapse to an `inset` shorthand at the IR level; the
|
|
2516
|
+
// minimizing reverse-emit expands it back to top/right/bottom/left and picks the single utility
|
|
2517
|
+
// covering all four (`inset-0`), replacing the four physical-side tokens. `bg-red-200` survives.
|
|
2518
|
+
before: '<div className="top-0 right-0 bottom-0 left-0 bg-red-200">box</div>',
|
|
2519
|
+
after: '<div className="bg-red-200 inset-0">box</div>'
|
|
2520
|
+
},
|
|
2521
|
+
{
|
|
2522
|
+
// No matching inset pair (all four distinct) → nothing collapses.
|
|
2523
|
+
noMatch: '<div className="top-0 right-1 bottom-2 left-3 bg-red-200">box</div>'
|
|
2524
|
+
}
|
|
2525
|
+
]
|
|
2526
|
+
});
|
|
2527
|
+
|
|
2528
|
+
// ../patterns/src/compress/margin-shorthand.pattern.ts
|
|
2529
|
+
var MARGIN_SIDES = [
|
|
2530
|
+
"margin-top",
|
|
2531
|
+
"margin-right",
|
|
2532
|
+
"margin-bottom",
|
|
2533
|
+
"margin-left"
|
|
2534
|
+
];
|
|
2535
|
+
var MARGIN_SIDE_SET = new Set(MARGIN_SIDES);
|
|
2536
|
+
var BASE_KEY = conditionKey(BASE_CONDITION);
|
|
2537
|
+
function asElement3(node) {
|
|
2538
|
+
const n = node;
|
|
2539
|
+
return n.kind === "element" ? n : null;
|
|
2540
|
+
}
|
|
2541
|
+
var hasDangerousHtml2 = (node) => asElement3(node)?.meta.hasDangerousHtml ?? false;
|
|
2542
|
+
function collapseMarginValue(top, right, bottom, left) {
|
|
2543
|
+
if (right === left) {
|
|
2544
|
+
if (top === bottom) {
|
|
2545
|
+
return top === right ? top : `${top} ${right}`;
|
|
2546
|
+
}
|
|
2547
|
+
return `${top} ${right} ${bottom}`;
|
|
2548
|
+
}
|
|
2549
|
+
return `${top} ${right} ${bottom} ${left}`;
|
|
2550
|
+
}
|
|
2551
|
+
function withFoldedMargin(sm, marginDecl) {
|
|
2552
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
2553
|
+
for (const [key, block] of sm.blocks) {
|
|
2554
|
+
if (key !== BASE_KEY) {
|
|
2555
|
+
blocks.set(key, block);
|
|
2556
|
+
continue;
|
|
2557
|
+
}
|
|
2558
|
+
const decls = /* @__PURE__ */ new Map();
|
|
2559
|
+
for (const [prop, decl] of block.decls) {
|
|
2560
|
+
if (!MARGIN_SIDE_SET.has(String(prop))) decls.set(prop, decl);
|
|
2561
|
+
}
|
|
2562
|
+
decls.set(marginDecl.property, marginDecl);
|
|
2563
|
+
blocks.set(key, { condition: block.condition, decls });
|
|
2564
|
+
}
|
|
2565
|
+
return { blocks };
|
|
2566
|
+
}
|
|
2567
|
+
var marginShorthand = pattern({
|
|
2568
|
+
name: "margin-shorthand",
|
|
2569
|
+
category: "compress/margin-shorthand",
|
|
2570
|
+
safety: 2,
|
|
2571
|
+
doc: {
|
|
2572
|
+
title: "Compress margin longhands into the `margin` shorthand",
|
|
2573
|
+
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.",
|
|
2574
|
+
before: '<div style="margin-top:8px;margin-right:8px;margin-bottom:8px;margin-left:8px"/>',
|
|
2575
|
+
after: '<div style="margin:8px"/>',
|
|
2576
|
+
safetyRationale: "Pure representation change (no pixels move); skips nodes with ref/handlers/dynamic children/raw html, dynamic class segments, or combinator-subject selectors."
|
|
2577
|
+
},
|
|
2578
|
+
match: {
|
|
2579
|
+
where: [
|
|
2580
|
+
not(hasRef),
|
|
2581
|
+
not(hasEventHandlers),
|
|
2582
|
+
not(hasDynamicChildren),
|
|
2583
|
+
not(hasDynamicClasses),
|
|
2584
|
+
not(hasDangerousHtml2),
|
|
2585
|
+
not(targetedByCombinator)
|
|
2586
|
+
]
|
|
2587
|
+
},
|
|
2588
|
+
rewrite: {
|
|
2589
|
+
rewriteClasses(computed2) {
|
|
2590
|
+
const base = computed2.blocks.get(BASE_KEY);
|
|
2591
|
+
if (!base) return null;
|
|
2592
|
+
const sides = MARGIN_SIDES.map((p) => base.decls.get(p));
|
|
2593
|
+
if (sides.some((d) => d === void 0)) return null;
|
|
2594
|
+
const [mt, mr, mb, ml] = sides;
|
|
2595
|
+
if (mt.important || mr.important || mb.important || ml.important) return null;
|
|
2596
|
+
const value = collapseMarginValue(
|
|
2597
|
+
String(mt.value),
|
|
2598
|
+
String(mr.value),
|
|
2599
|
+
String(mb.value),
|
|
2600
|
+
String(ml.value)
|
|
2601
|
+
);
|
|
2602
|
+
const marginDecl = {
|
|
2603
|
+
property: "margin",
|
|
2604
|
+
value,
|
|
2605
|
+
important: false,
|
|
2606
|
+
relativeToParent: mt.relativeToParent || mr.relativeToParent || mb.relativeToParent || ml.relativeToParent,
|
|
2607
|
+
inherited: false
|
|
2608
|
+
// margin is not an inherited property
|
|
2609
|
+
};
|
|
2610
|
+
return withFoldedMargin(computed2, marginDecl);
|
|
2611
|
+
}
|
|
2612
|
+
},
|
|
2613
|
+
examples: [
|
|
2614
|
+
{
|
|
2615
|
+
// The four equal margin longhands collapse to a `margin` shorthand at the IR level, and the
|
|
2616
|
+
// minimizing reverse-emit picks the single shortest utility (`m-2`) reproducing it, replacing
|
|
2617
|
+
// the four `m{t,r,b,l}-2` tokens. `bg-red-200` is preserved.
|
|
2618
|
+
before: '<div className="mt-2 mr-2 mb-2 ml-2 bg-red-200">box</div>',
|
|
2619
|
+
after: '<div className="bg-red-200 m-2">box</div>'
|
|
2620
|
+
},
|
|
2621
|
+
{
|
|
2622
|
+
// Only two margin sides set → the four-longhand `margin` collapse does not apply.
|
|
2623
|
+
noMatch: '<div className="mt-2 mb-2 bg-red-200">box</div>'
|
|
2624
|
+
}
|
|
2625
|
+
]
|
|
2626
|
+
});
|
|
2627
|
+
|
|
2628
|
+
// ../patterns/src/compress/padding-shorthand.pattern.ts
|
|
2629
|
+
var PADDING_SIDES = [
|
|
2630
|
+
"padding-top",
|
|
2631
|
+
"padding-right",
|
|
2632
|
+
"padding-bottom",
|
|
2633
|
+
"padding-left"
|
|
2634
|
+
];
|
|
2635
|
+
var PADDING_SIDE_SET = new Set(PADDING_SIDES);
|
|
2636
|
+
var BASE_KEY2 = conditionKey(BASE_CONDITION);
|
|
2637
|
+
function analyzePadding(sm) {
|
|
2638
|
+
const block = sm.blocks.get(BASE_KEY2);
|
|
2639
|
+
if (!block) return null;
|
|
2640
|
+
const sides = [];
|
|
2641
|
+
for (const side of PADDING_SIDES) {
|
|
2642
|
+
const decl = block.decls.get(side);
|
|
2643
|
+
if (!decl) return null;
|
|
2644
|
+
sides.push(decl);
|
|
2645
|
+
}
|
|
2646
|
+
const [top, right, bottom, left] = sides;
|
|
2647
|
+
if (!(top.important === right.important && right.important === bottom.important && bottom.important === left.important)) {
|
|
2648
|
+
return null;
|
|
2649
|
+
}
|
|
2650
|
+
const tv = String(top.value);
|
|
2651
|
+
const rv = String(right.value);
|
|
2652
|
+
const bv = String(bottom.value);
|
|
2653
|
+
const lv = String(left.value);
|
|
2654
|
+
if (tv !== bv || lv !== rv) return null;
|
|
2655
|
+
const value = tv === lv ? tv : `${tv} ${lv}`;
|
|
2656
|
+
const relative = sides.some((d) => d.relativeToParent);
|
|
2657
|
+
return { value, important: top.important, relative };
|
|
2658
|
+
}
|
|
2659
|
+
var isInert = (node) => {
|
|
2660
|
+
const n = node;
|
|
2661
|
+
if (n.kind !== "element") return false;
|
|
2662
|
+
const el = n;
|
|
2663
|
+
return !el.meta.hasDangerousHtml && !el.meta.hasSpreadAttrs && !el.isComponent;
|
|
2664
|
+
};
|
|
2665
|
+
function withFoldedPadding(sm, fold) {
|
|
2666
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
2667
|
+
for (const [key, block] of sm.blocks) {
|
|
2668
|
+
if (key !== BASE_KEY2) {
|
|
2669
|
+
blocks.set(key, block);
|
|
2670
|
+
continue;
|
|
2671
|
+
}
|
|
2672
|
+
const decls = /* @__PURE__ */ new Map();
|
|
2673
|
+
for (const [prop, decl] of block.decls) {
|
|
2674
|
+
if (PADDING_SIDE_SET.has(String(prop))) continue;
|
|
2675
|
+
decls.set(prop, decl);
|
|
2676
|
+
}
|
|
2677
|
+
const shorthand = {
|
|
2678
|
+
property: "padding",
|
|
2679
|
+
value: fold.value,
|
|
2680
|
+
important: fold.important,
|
|
2681
|
+
relativeToParent: fold.relative,
|
|
2682
|
+
inherited: false
|
|
2683
|
+
// padding is never inherited
|
|
2684
|
+
};
|
|
2685
|
+
decls.set(shorthand.property, shorthand);
|
|
2686
|
+
blocks.set(key, { condition: block.condition, decls });
|
|
2687
|
+
}
|
|
2688
|
+
return { blocks };
|
|
2689
|
+
}
|
|
2690
|
+
var paddingShorthand = pattern({
|
|
2691
|
+
name: "padding-shorthand",
|
|
2692
|
+
category: "compress/padding-shorthand",
|
|
2693
|
+
safety: 1,
|
|
2694
|
+
doc: {
|
|
2695
|
+
title: "Collapse padding longhands to shorthand",
|
|
2696
|
+
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-*).",
|
|
2697
|
+
before: '<div class="pt-4 pr-4 pb-4 pl-4"/>',
|
|
2698
|
+
after: '<div class="p-4"/>',
|
|
2699
|
+
safetyRationale: "A value-preserving re-serialization of the same computed styles on the same node; it skips nodes with ref/handlers/dynamic children/dynamic classes/dangerous html and combinator subjects, so no JS identity, behaviour, or project selector is disturbed."
|
|
2700
|
+
},
|
|
2701
|
+
match: {
|
|
2702
|
+
where: [
|
|
2703
|
+
not(hasRef),
|
|
2704
|
+
not(hasEventHandlers),
|
|
2705
|
+
not(hasDynamicChildren),
|
|
2706
|
+
not(hasDynamicClasses),
|
|
2707
|
+
not(targetedByCombinator),
|
|
2708
|
+
isInert
|
|
2709
|
+
]
|
|
2710
|
+
},
|
|
2711
|
+
rewrite: {
|
|
2712
|
+
rewriteClasses(computed2) {
|
|
2713
|
+
const fold = analyzePadding(computed2);
|
|
2714
|
+
return fold ? withFoldedPadding(computed2, fold) : null;
|
|
2715
|
+
}
|
|
2716
|
+
},
|
|
2717
|
+
examples: [
|
|
2718
|
+
{
|
|
2719
|
+
// The four equal padding longhands collapse to a `padding` shorthand at the IR level, and the
|
|
2720
|
+
// minimizing reverse-emit picks the single shortest utility (`p-4`) that reproduces it,
|
|
2721
|
+
// replacing the four `p{t,r,b,l}-4` tokens. `bg-red-200` is preserved (its order is stable).
|
|
2722
|
+
before: '<div className="pt-4 pr-4 pb-4 pl-4 bg-red-200">box</div>',
|
|
2723
|
+
after: '<div className="bg-red-200 p-4">box</div>'
|
|
2724
|
+
},
|
|
2725
|
+
{
|
|
2726
|
+
// Asymmetric padding (top != bottom) cannot fold into a shorthand.
|
|
2727
|
+
noMatch: '<div className="pt-2 pr-4 pb-8 pl-4 bg-red-200">box</div>'
|
|
2728
|
+
}
|
|
2729
|
+
]
|
|
2730
|
+
});
|
|
2731
|
+
|
|
2732
|
+
// ../patterns/src/compress/size-shorthand.pattern.ts
|
|
2733
|
+
var WIDTH = "width";
|
|
2734
|
+
var HEIGHT = "height";
|
|
2735
|
+
var SIZE = "size";
|
|
2736
|
+
var NON_COLLAPSIBLE_VALUES = /* @__PURE__ */ new Set(["auto", "initial", "unset"]);
|
|
2737
|
+
function asElement4(node) {
|
|
2738
|
+
const n = node;
|
|
2739
|
+
return n.kind === "element" ? n : null;
|
|
2740
|
+
}
|
|
2741
|
+
function baseBlock(sm) {
|
|
2742
|
+
return sm.blocks.get(conditionKey(BASE_CONDITION));
|
|
2743
|
+
}
|
|
2744
|
+
var hasDangerousHtml3 = (node) => asElement4(node)?.meta.hasDangerousHtml ?? false;
|
|
2745
|
+
function withSizeShorthand(sm, value, important) {
|
|
2746
|
+
const baseKey = conditionKey(BASE_CONDITION);
|
|
2747
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
2748
|
+
for (const [key, block] of sm.blocks) {
|
|
2749
|
+
if (key !== baseKey) {
|
|
2750
|
+
blocks.set(key, block);
|
|
2751
|
+
continue;
|
|
2752
|
+
}
|
|
2753
|
+
const decls = new Map(block.decls);
|
|
2754
|
+
decls.delete(WIDTH);
|
|
2755
|
+
decls.delete(HEIGHT);
|
|
2756
|
+
for (const decl of normalizer.normalizeDeclaration(String(SIZE), value, important)) {
|
|
2757
|
+
decls.set(decl.property, decl);
|
|
2758
|
+
}
|
|
2759
|
+
blocks.set(key, { condition: block.condition, decls });
|
|
2760
|
+
}
|
|
2761
|
+
return { blocks };
|
|
2762
|
+
}
|
|
2763
|
+
var sizeShorthand = pattern({
|
|
2764
|
+
name: "size-shorthand",
|
|
2765
|
+
category: "compress/size-shorthand",
|
|
2766
|
+
safety: 2,
|
|
2767
|
+
doc: {
|
|
2768
|
+
title: "Collapse equal width/height into size-*",
|
|
2769
|
+
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).",
|
|
2770
|
+
before: '<div style="width:1rem;height:1rem"/>',
|
|
2771
|
+
after: '<div class="size-4"/>',
|
|
2772
|
+
safetyRationale: "size-* is value-identical to equal width+height; the element carries no ref/handlers/dynamic children/dangerous HTML, no dynamic class segment, and is not a combinator subject."
|
|
2773
|
+
},
|
|
2774
|
+
match: {
|
|
2775
|
+
where: [
|
|
2776
|
+
not(hasRef),
|
|
2777
|
+
not(hasEventHandlers),
|
|
2778
|
+
not(hasDynamicChildren),
|
|
2779
|
+
not(hasDangerousHtml3),
|
|
2780
|
+
not(hasDynamicClasses),
|
|
2781
|
+
not(targetedByCombinator)
|
|
2782
|
+
]
|
|
2783
|
+
},
|
|
2784
|
+
rewrite: {
|
|
2785
|
+
rewriteClasses(computed2) {
|
|
2786
|
+
const base = baseBlock(computed2);
|
|
2787
|
+
const w = base?.decls.get(WIDTH);
|
|
2788
|
+
const h = base?.decls.get(HEIGHT);
|
|
2789
|
+
if (!w || !h) return null;
|
|
2790
|
+
if (w.important !== h.important) return null;
|
|
2791
|
+
if (NON_COLLAPSIBLE_VALUES.has(String(w.value))) return null;
|
|
2792
|
+
if (w.value !== h.value) return null;
|
|
2793
|
+
return withSizeShorthand(computed2, String(w.value), w.important);
|
|
2794
|
+
}
|
|
2795
|
+
},
|
|
2796
|
+
examples: [
|
|
2797
|
+
{
|
|
2798
|
+
// Equal width/height collapse to a `size` decl at the IR level; the minimizing reverse-emit
|
|
2799
|
+
// expands `size` back to width+height, finds the single utility covering both (`size-10`), and
|
|
2800
|
+
// replaces the `h-10`+`w-10` pair with it. `bg-red-200` is preserved.
|
|
2801
|
+
before: '<div className="h-10 w-10 bg-red-200">box</div>',
|
|
2802
|
+
after: '<div className="bg-red-200 size-10">box</div>'
|
|
2803
|
+
},
|
|
2804
|
+
{
|
|
2805
|
+
// Width and height differ → no equal-axis collapse.
|
|
2806
|
+
noMatch: '<div className="h-10 w-20 bg-red-200">box</div>'
|
|
2807
|
+
}
|
|
2808
|
+
]
|
|
2809
|
+
});
|
|
2810
|
+
|
|
2811
|
+
// ../patterns/src/_registry.generated.ts
|
|
2812
|
+
var builtinPatterns = [
|
|
2813
|
+
emptyStyleDiv,
|
|
2814
|
+
flexCenterWrapper,
|
|
2815
|
+
nestedFlexMerge,
|
|
2816
|
+
passthroughWrapper,
|
|
2817
|
+
redundantFragment,
|
|
2818
|
+
dedupeClasses,
|
|
2819
|
+
insetShorthand,
|
|
2820
|
+
marginShorthand,
|
|
2821
|
+
paddingShorthand,
|
|
2822
|
+
sizeShorthand
|
|
2823
|
+
];
|
|
2824
|
+
|
|
2825
|
+
// ../frontend-jsx/src/frontend.ts
|
|
2826
|
+
var import_parser = require("@babel/parser");
|
|
2827
|
+
var import_traverse = __toESM(require("@babel/traverse"), 1);
|
|
2828
|
+
var traverse = typeof import_traverse.default === "function" ? import_traverse.default : import_traverse.default.default;
|
|
2829
|
+
var JSX_LANGS = ["jsx", "tsx"];
|
|
2830
|
+
var FILE_ID = 1;
|
|
2831
|
+
function jsxName(node) {
|
|
2832
|
+
switch (node.type) {
|
|
2833
|
+
case "JSXIdentifier":
|
|
2834
|
+
return node.name;
|
|
2835
|
+
case "JSXMemberExpression":
|
|
2836
|
+
return `${jsxName(node.object)}.${node.property.name}`;
|
|
2837
|
+
case "JSXNamespacedName":
|
|
2838
|
+
return `${node.namespace.name}:${node.name.name}`;
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
function isComponentName(node) {
|
|
2842
|
+
if (node.type === "JSXMemberExpression") return true;
|
|
2843
|
+
if (node.type === "JSXNamespacedName") return false;
|
|
2844
|
+
return /^[A-Z]/.test(node.name);
|
|
2845
|
+
}
|
|
2846
|
+
function attrName(name) {
|
|
2847
|
+
return name.type === "JSXNamespacedName" ? `${name.namespace.name}:${name.name.name}` : name.name;
|
|
2848
|
+
}
|
|
2849
|
+
function exprKind(node) {
|
|
2850
|
+
switch (node.type) {
|
|
2851
|
+
case "CallExpression":
|
|
2852
|
+
case "OptionalCallExpression":
|
|
2853
|
+
return "call";
|
|
2854
|
+
case "MemberExpression":
|
|
2855
|
+
case "OptionalMemberExpression":
|
|
2856
|
+
return "member";
|
|
2857
|
+
case "ConditionalExpression":
|
|
2858
|
+
case "LogicalExpression":
|
|
2859
|
+
return "conditional";
|
|
2860
|
+
case "TemplateLiteral":
|
|
2861
|
+
case "TaggedTemplateExpression":
|
|
2862
|
+
return "template";
|
|
2863
|
+
case "Identifier":
|
|
2864
|
+
return "identifier";
|
|
2865
|
+
case "SpreadElement":
|
|
2866
|
+
return "spread";
|
|
2867
|
+
default:
|
|
2868
|
+
return "other";
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
function classFormOf(node) {
|
|
2872
|
+
switch (node.type) {
|
|
2873
|
+
case "TemplateLiteral":
|
|
2874
|
+
case "TaggedTemplateExpression":
|
|
2875
|
+
return "template-literal";
|
|
2876
|
+
case "CallExpression":
|
|
2877
|
+
case "OptionalCallExpression":
|
|
2878
|
+
return "call";
|
|
2879
|
+
case "ConditionalExpression":
|
|
2880
|
+
case "LogicalExpression":
|
|
2881
|
+
return "conditional";
|
|
2882
|
+
case "MemberExpression":
|
|
2883
|
+
case "OptionalMemberExpression":
|
|
2884
|
+
case "Identifier":
|
|
2885
|
+
return "member";
|
|
2886
|
+
default:
|
|
2887
|
+
return "call";
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
function looksLikeJsx(id, code) {
|
|
2891
|
+
if (/\.[jt]sx$/i.test(id)) return true;
|
|
2892
|
+
return /<\/?[A-Za-z][\w.-]*|<>/.test(code);
|
|
2893
|
+
}
|
|
2894
|
+
function doParse(code, ctx) {
|
|
2895
|
+
const diagnostics = [];
|
|
2896
|
+
const doc = createDocument("jsx");
|
|
2897
|
+
const backref = doc.backref;
|
|
2898
|
+
const ast = (0, import_parser.parse)(code, {
|
|
2899
|
+
sourceType: "module",
|
|
2900
|
+
plugins: ["jsx", "typescript"]
|
|
2901
|
+
});
|
|
2902
|
+
const eol = code.includes("\r\n") ? "\r\n" : "\n";
|
|
2903
|
+
const sourceFile = {
|
|
2904
|
+
id: FILE_ID,
|
|
2905
|
+
path: ctx.id,
|
|
2906
|
+
text: code,
|
|
2907
|
+
frontend: "jsx",
|
|
2908
|
+
eol,
|
|
2909
|
+
indentUnit: " ",
|
|
2910
|
+
native: ast
|
|
2911
|
+
};
|
|
2912
|
+
doc.sources.set(FILE_ID, sourceFile);
|
|
2913
|
+
const spanOf = (node) => {
|
|
2914
|
+
if (node.start == null || node.end == null) return null;
|
|
2915
|
+
const span = {
|
|
2916
|
+
file: FILE_ID,
|
|
2917
|
+
start: node.start,
|
|
2918
|
+
end: node.end,
|
|
2919
|
+
startLoc: node.loc ? { line: node.loc.start.line, column: node.loc.start.column } : void 0,
|
|
2920
|
+
endLoc: node.loc ? { line: node.loc.end.line, column: node.loc.end.column } : void 0
|
|
2921
|
+
};
|
|
2922
|
+
return span;
|
|
2923
|
+
};
|
|
2924
|
+
const sliceOf = (node) => node.start == null || node.end == null ? "" : code.slice(node.start, node.end);
|
|
2925
|
+
const internExpr = (node, spread) => {
|
|
2926
|
+
const payload = { text: sliceOf(node), spread };
|
|
2927
|
+
return doc.exprs.intern({
|
|
2928
|
+
span: spanOf(node) ?? { file: FILE_ID, start: 0, end: 0 },
|
|
2929
|
+
kind: exprKind(node),
|
|
2930
|
+
payload
|
|
2931
|
+
});
|
|
2932
|
+
};
|
|
2933
|
+
const splitTokens = (raw) => raw.split(/\s+/).filter((t) => t.length > 0).map((value) => ({ value }));
|
|
2934
|
+
const buildClassList = (attr) => {
|
|
2935
|
+
const attrSpan = spanOf(attr) ?? void 0;
|
|
2936
|
+
const v = attr.value;
|
|
2937
|
+
const staticList = (tokens, valueSpan) => {
|
|
2938
|
+
const seg = { kind: "static", span: valueSpan ?? void 0, tokens };
|
|
2939
|
+
return {
|
|
2940
|
+
form: "string-literal",
|
|
2941
|
+
segments: [seg],
|
|
2942
|
+
valueSpan,
|
|
2943
|
+
attrSpan,
|
|
2944
|
+
hasDynamic: false,
|
|
2945
|
+
opaque: false,
|
|
2946
|
+
rewritable: true
|
|
2947
|
+
};
|
|
2948
|
+
};
|
|
2949
|
+
if (v == null) return staticList([], null);
|
|
2950
|
+
if (v.type === "StringLiteral") {
|
|
2951
|
+
return staticList(splitTokens(v.value), spanOf(v));
|
|
2952
|
+
}
|
|
2953
|
+
if (v.type === "JSXExpressionContainer") {
|
|
2954
|
+
const expr = v.expression;
|
|
2955
|
+
if (expr.type === "StringLiteral") {
|
|
2956
|
+
return staticList(splitTokens(expr.value), spanOf(expr));
|
|
2957
|
+
}
|
|
2958
|
+
if (expr.type === "JSXEmptyExpression") return staticList([], null);
|
|
2959
|
+
const ref = internExpr(expr, false);
|
|
2960
|
+
const valueSpan = spanOf(expr);
|
|
2961
|
+
const seg = { kind: "dynamic", span: valueSpan ?? void 0, expr: ref };
|
|
2962
|
+
return {
|
|
2963
|
+
form: classFormOf(expr),
|
|
2964
|
+
segments: [seg],
|
|
2965
|
+
valueSpan,
|
|
2966
|
+
attrSpan,
|
|
2967
|
+
hasDynamic: true,
|
|
2968
|
+
opaque: true,
|
|
2969
|
+
rewritable: false
|
|
2970
|
+
};
|
|
2971
|
+
}
|
|
2972
|
+
return emptyClassList();
|
|
2973
|
+
};
|
|
2974
|
+
const staticTokensOf3 = (classes) => {
|
|
2975
|
+
const out = [];
|
|
2976
|
+
for (const seg of classes.segments) {
|
|
2977
|
+
if (seg.kind === "static") for (const t of seg.tokens) out.push(t.value);
|
|
2978
|
+
}
|
|
2979
|
+
return out;
|
|
2980
|
+
};
|
|
2981
|
+
const buildAttrValue = (attr) => {
|
|
2982
|
+
const v = attr.value;
|
|
2983
|
+
if (v == null) return { kind: "static", value: true, span: spanOf(attr) ?? void 0 };
|
|
2984
|
+
if (v.type === "StringLiteral") {
|
|
2985
|
+
return { kind: "static", value: v.value, span: spanOf(v) ?? void 0 };
|
|
2986
|
+
}
|
|
2987
|
+
if (v.type === "JSXExpressionContainer") {
|
|
2988
|
+
if (v.expression.type === "JSXEmptyExpression") {
|
|
2989
|
+
return { kind: "static", value: true, span: spanOf(v) ?? void 0 };
|
|
2990
|
+
}
|
|
2991
|
+
return { kind: "dynamic", expr: internExpr(v.expression, false), span: spanOf(v) ?? void 0 };
|
|
2992
|
+
}
|
|
2993
|
+
return { kind: "dynamic", expr: internExpr(v, false), span: spanOf(v) ?? void 0 };
|
|
2994
|
+
};
|
|
2995
|
+
const buildChild = (node, parentId) => {
|
|
2996
|
+
switch (node.type) {
|
|
2997
|
+
case "JSXText": {
|
|
2998
|
+
const id = doc.alloc.next();
|
|
2999
|
+
doc.nodes.set(
|
|
3000
|
+
id,
|
|
3001
|
+
createText(id, node.value, {
|
|
3002
|
+
parent: parentId,
|
|
3003
|
+
span: spanOf(node),
|
|
3004
|
+
collapsible: /^\s*$/.test(node.value)
|
|
3005
|
+
})
|
|
3006
|
+
);
|
|
3007
|
+
return id;
|
|
3008
|
+
}
|
|
3009
|
+
case "JSXExpressionContainer": {
|
|
3010
|
+
if (node.expression.type === "JSXEmptyExpression") return null;
|
|
3011
|
+
const id = doc.alloc.next();
|
|
3012
|
+
const ref = internExpr(node.expression, false);
|
|
3013
|
+
doc.nodes.set(id, createExpr(id, ref, { parent: parentId, span: spanOf(node) }));
|
|
3014
|
+
return id;
|
|
3015
|
+
}
|
|
3016
|
+
case "JSXSpreadChild": {
|
|
3017
|
+
const id = doc.alloc.next();
|
|
3018
|
+
const ref = internExpr(node.expression, true);
|
|
3019
|
+
doc.nodes.set(id, createExpr(id, ref, { parent: parentId, span: spanOf(node) }));
|
|
3020
|
+
return id;
|
|
3021
|
+
}
|
|
3022
|
+
case "JSXElement":
|
|
3023
|
+
return buildElement(node, parentId);
|
|
3024
|
+
case "JSXFragment":
|
|
3025
|
+
return buildFragment(node, parentId);
|
|
3026
|
+
default:
|
|
3027
|
+
return null;
|
|
3028
|
+
}
|
|
3029
|
+
};
|
|
3030
|
+
const buildFragment = (node, parentId) => {
|
|
3031
|
+
const id = doc.alloc.next();
|
|
3032
|
+
const children = [];
|
|
3033
|
+
for (const c of node.children) {
|
|
3034
|
+
const cid = buildChild(c, id);
|
|
3035
|
+
if (cid != null) children.push(cid);
|
|
3036
|
+
}
|
|
3037
|
+
doc.nodes.set(id, createFragment(id, { children, parent: parentId, span: spanOf(node) }));
|
|
3038
|
+
backref.set(id, {
|
|
3039
|
+
nodeId: id,
|
|
3040
|
+
span: spanOf(node) ?? { file: FILE_ID, start: 0, end: 0 },
|
|
3041
|
+
openTagSpan: spanOf(node.openingFragment),
|
|
3042
|
+
closeTagSpan: spanOf(node.closingFragment),
|
|
3043
|
+
innerSpan: null,
|
|
3044
|
+
selfClosing: false
|
|
3045
|
+
});
|
|
3046
|
+
return id;
|
|
3047
|
+
};
|
|
3048
|
+
const buildElement = (node, parentId) => {
|
|
3049
|
+
const id = doc.alloc.next();
|
|
3050
|
+
const opening = node.openingElement;
|
|
3051
|
+
const tag = jsxName(opening.name);
|
|
3052
|
+
const component = isComponentName(opening.name);
|
|
3053
|
+
const meta = defaultMeta();
|
|
3054
|
+
meta.isComponent = component;
|
|
3055
|
+
let classes = emptyClassList();
|
|
3056
|
+
const entries = /* @__PURE__ */ new Map();
|
|
3057
|
+
const order = [];
|
|
3058
|
+
const spreads = [];
|
|
3059
|
+
for (const attr of opening.attributes) {
|
|
3060
|
+
if (attr.type === "JSXSpreadAttribute") {
|
|
3061
|
+
spreads.push(internExpr(attr.argument, true));
|
|
3062
|
+
meta.hasSpreadAttrs = true;
|
|
3063
|
+
continue;
|
|
3064
|
+
}
|
|
3065
|
+
const name = attrName(attr.name);
|
|
3066
|
+
if (name === "className" || name === "class") {
|
|
3067
|
+
classes = buildClassList(attr);
|
|
3068
|
+
continue;
|
|
3069
|
+
}
|
|
3070
|
+
if (name === "ref") meta.hasRef = true;
|
|
3071
|
+
else if (name === "key") meta.hasKey = true;
|
|
3072
|
+
else if (name === "dangerouslySetInnerHTML") meta.hasDangerousHtml = true;
|
|
3073
|
+
else if (/^on[A-Z]/.test(name)) meta.hasEventHandlers = true;
|
|
3074
|
+
entries.set(name, buildAttrValue(attr));
|
|
3075
|
+
order.push(name);
|
|
3076
|
+
}
|
|
3077
|
+
const attrs = { entries, spreads, order };
|
|
3078
|
+
const children = [];
|
|
3079
|
+
for (const c of node.children) {
|
|
3080
|
+
const cid = buildChild(c, id);
|
|
3081
|
+
if (cid != null) children.push(cid);
|
|
3082
|
+
}
|
|
3083
|
+
for (const cid of children) {
|
|
3084
|
+
const cn = doc.nodes.get(cid);
|
|
3085
|
+
if (cn && cn.kind === "expr") {
|
|
3086
|
+
meta.hasDynamicChildren = true;
|
|
3087
|
+
break;
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3090
|
+
let computed2 = emptyStyleMap();
|
|
3091
|
+
if (!classes.hasDynamic) {
|
|
3092
|
+
const tokens = staticTokensOf3(classes);
|
|
3093
|
+
if (tokens.length > 0) {
|
|
3094
|
+
const res = ctx.resolver.resolve({
|
|
3095
|
+
classes: tokens,
|
|
3096
|
+
element: { tagName: tag, namespace: component ? void 0 : "html" }
|
|
3097
|
+
});
|
|
3098
|
+
computed2 = ctx.normalizer.normalizeStyleMap(res.styles);
|
|
3099
|
+
for (const w of res.warnings) {
|
|
3100
|
+
diagnostics.push({
|
|
3101
|
+
code: "DF_STYLE_CONFLICT_UNRESOLVED",
|
|
3102
|
+
severity: w.severity,
|
|
3103
|
+
message: w.message,
|
|
3104
|
+
nodeId: id
|
|
3105
|
+
});
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
const namespace = component ? "component" : "html";
|
|
3110
|
+
const el = createElement(id, {
|
|
3111
|
+
tag,
|
|
3112
|
+
namespace,
|
|
3113
|
+
isComponent: component,
|
|
3114
|
+
selfClosing: opening.selfClosing,
|
|
3115
|
+
classes,
|
|
3116
|
+
computed: computed2,
|
|
3117
|
+
attrs,
|
|
3118
|
+
children,
|
|
3119
|
+
parent: parentId,
|
|
3120
|
+
span: spanOf(node),
|
|
3121
|
+
meta
|
|
3122
|
+
});
|
|
3123
|
+
doc.nodes.set(id, el);
|
|
3124
|
+
const inner = children.length > 0 ? spanOf(node.children[0]) && spanOf(node.children.at(-1)) ? {
|
|
3125
|
+
file: FILE_ID,
|
|
3126
|
+
start: spanOf(node.children[0]).start,
|
|
3127
|
+
end: spanOf(node.children.at(-1)).end
|
|
3128
|
+
} : null : null;
|
|
3129
|
+
backref.set(id, {
|
|
3130
|
+
nodeId: id,
|
|
3131
|
+
span: spanOf(node) ?? { file: FILE_ID, start: 0, end: 0 },
|
|
3132
|
+
openTagSpan: spanOf(opening),
|
|
3133
|
+
closeTagSpan: node.closingElement ? spanOf(node.closingElement) : null,
|
|
3134
|
+
innerSpan: inner,
|
|
3135
|
+
selfClosing: opening.selfClosing
|
|
3136
|
+
});
|
|
3137
|
+
return id;
|
|
3138
|
+
};
|
|
3139
|
+
const roots = [];
|
|
3140
|
+
traverse(ast, {
|
|
3141
|
+
JSXElement(path3) {
|
|
3142
|
+
roots.push(path3.node);
|
|
3143
|
+
path3.skip();
|
|
3144
|
+
},
|
|
3145
|
+
JSXFragment(path3) {
|
|
3146
|
+
roots.push(path3.node);
|
|
3147
|
+
path3.skip();
|
|
3148
|
+
}
|
|
3149
|
+
});
|
|
3150
|
+
const rootFrag = doc.nodes.get(doc.root);
|
|
3151
|
+
for (const r of roots) {
|
|
3152
|
+
const id = r.type === "JSXFragment" ? buildFragment(r, doc.root) : buildElement(r, doc.root);
|
|
3153
|
+
rootFrag.children.push(id);
|
|
3154
|
+
}
|
|
3155
|
+
return { doc, diagnostics };
|
|
3156
|
+
}
|
|
3157
|
+
var jsxFrontend = {
|
|
3158
|
+
name: "babel-jsx",
|
|
3159
|
+
langs: JSX_LANGS,
|
|
3160
|
+
canParse(id, code) {
|
|
3161
|
+
return looksLikeJsx(id, code);
|
|
3162
|
+
},
|
|
3163
|
+
parse(code, ctx) {
|
|
3164
|
+
return doParse(code, ctx);
|
|
3165
|
+
}
|
|
3166
|
+
};
|
|
3167
|
+
function createJsxFrontend() {
|
|
3168
|
+
return jsxFrontend;
|
|
3169
|
+
}
|
|
3170
|
+
|
|
3171
|
+
// ../frontend-jsx/src/backend.ts
|
|
3172
|
+
var import_magic_string = __toESM(require("magic-string"), 1);
|
|
3173
|
+
var JSX_LANGS2 = ["jsx", "tsx"];
|
|
3174
|
+
function exprText(doc, ref) {
|
|
3175
|
+
const rec = doc.exprs.get(ref);
|
|
3176
|
+
const payload = rec?.payload;
|
|
3177
|
+
if (payload && typeof payload.text === "string") {
|
|
3178
|
+
return { text: payload.text, spread: payload.spread === true };
|
|
3179
|
+
}
|
|
3180
|
+
if (rec) {
|
|
3181
|
+
const sf = doc.sources.get(rec.span.file);
|
|
3182
|
+
if (sf) return { text: sf.text.slice(rec.span.start, rec.span.end), spread: false };
|
|
3183
|
+
}
|
|
3184
|
+
return { text: "", spread: false };
|
|
3185
|
+
}
|
|
3186
|
+
function staticTokensOf2(classes) {
|
|
3187
|
+
const out = [];
|
|
3188
|
+
for (const seg of classes.segments) {
|
|
3189
|
+
if (seg.kind === "static") for (const t of seg.tokens) out.push(t.value);
|
|
3190
|
+
}
|
|
3191
|
+
return out;
|
|
3192
|
+
}
|
|
3193
|
+
function primarySource(doc) {
|
|
3194
|
+
for (const sf of doc.sources.values()) {
|
|
3195
|
+
if (typeof sf.text === "string" && sf.text.length > 0) return sf;
|
|
3196
|
+
}
|
|
3197
|
+
return null;
|
|
3198
|
+
}
|
|
3199
|
+
function collectKept(doc) {
|
|
3200
|
+
const out = [];
|
|
3201
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3202
|
+
const visit = (id) => {
|
|
3203
|
+
if (seen.has(id)) return;
|
|
3204
|
+
seen.add(id);
|
|
3205
|
+
const n = doc.nodes.get(id);
|
|
3206
|
+
if (!n) return;
|
|
3207
|
+
out.push(n);
|
|
3208
|
+
if (n.kind === "element" || n.kind === "fragment") for (const c of n.children) visit(c);
|
|
3209
|
+
};
|
|
3210
|
+
visit(doc.root);
|
|
3211
|
+
return out;
|
|
3212
|
+
}
|
|
3213
|
+
function strictlyContains(a, b) {
|
|
3214
|
+
if (a.file !== b.file) return false;
|
|
3215
|
+
if (a.start <= b.start && b.end <= a.end) return !(a.start === b.start && a.end === b.end);
|
|
3216
|
+
return false;
|
|
3217
|
+
}
|
|
3218
|
+
function editClasses(ms, doc, sf, el) {
|
|
3219
|
+
const classes = el.classes;
|
|
3220
|
+
if (classes.hasDynamic || classes.opaque) return false;
|
|
3221
|
+
const tokens = staticTokensOf2(classes);
|
|
3222
|
+
const valueSpan = classes.valueSpan;
|
|
3223
|
+
if (valueSpan && valueSpan.file === sf.id) {
|
|
3224
|
+
const current = sf.text.slice(valueSpan.start, valueSpan.end);
|
|
3225
|
+
const quote = current.startsWith("'") ? "'" : '"';
|
|
3226
|
+
const next = `${quote}${tokens.join(" ")}${quote}`;
|
|
3227
|
+
if (current !== next) {
|
|
3228
|
+
ms.overwrite(valueSpan.start, valueSpan.end, next);
|
|
3229
|
+
return true;
|
|
3230
|
+
}
|
|
3231
|
+
return false;
|
|
3232
|
+
}
|
|
3233
|
+
if (tokens.length === 0) return false;
|
|
3234
|
+
if (el.isComponent) return false;
|
|
3235
|
+
const back = doc.backref.get(el.id);
|
|
3236
|
+
const openTag = back?.openTagSpan;
|
|
3237
|
+
if (!openTag || openTag.file !== sf.id) return false;
|
|
3238
|
+
const insertAt = openTag.start + 1 + el.tag.length;
|
|
3239
|
+
ms.appendLeft(insertAt, ` className="${tokens.join(" ")}"`);
|
|
3240
|
+
return true;
|
|
3241
|
+
}
|
|
3242
|
+
function surgicalPrint(doc) {
|
|
3243
|
+
const sf = primarySource(doc);
|
|
3244
|
+
if (!sf) return null;
|
|
3245
|
+
const ms = new import_magic_string.default(sf.text);
|
|
3246
|
+
const kept = collectKept(doc);
|
|
3247
|
+
const keptSpans = [];
|
|
3248
|
+
for (const n of kept) if (n.span && n.span.file === sf.id) keptSpans.push(n.span);
|
|
3249
|
+
const removed = [];
|
|
3250
|
+
for (const id of backrefIds(doc)) {
|
|
3251
|
+
if (doc.nodes.has(id)) continue;
|
|
3252
|
+
const back = doc.backref.get(id);
|
|
3253
|
+
if (!back || back.span.file !== sf.id) continue;
|
|
3254
|
+
const unwrapped = keptSpans.some((k) => strictlyContains(back.span, k));
|
|
3255
|
+
removed.push({ backref: back, unwrapped });
|
|
3256
|
+
}
|
|
3257
|
+
const fullRemovals = removed.filter((r) => !r.unwrapped).map((r) => r.backref.span);
|
|
3258
|
+
for (const r of removed) {
|
|
3259
|
+
const span = r.backref.span;
|
|
3260
|
+
const coveredByFull = fullRemovals.some((f) => f !== span && strictlyContains(f, span));
|
|
3261
|
+
if (coveredByFull) continue;
|
|
3262
|
+
if (r.unwrapped) {
|
|
3263
|
+
const open = r.backref.openTagSpan;
|
|
3264
|
+
const close = r.backref.closeTagSpan;
|
|
3265
|
+
if (open && open.file === sf.id && open.end > open.start) ms.remove(open.start, open.end);
|
|
3266
|
+
if (close && close.file === sf.id && close.end > close.start) {
|
|
3267
|
+
ms.remove(close.start, close.end);
|
|
3268
|
+
}
|
|
3269
|
+
} else {
|
|
3270
|
+
ms.remove(span.start, span.end);
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3273
|
+
for (const n of kept) {
|
|
3274
|
+
if (n.kind === "element") editClasses(ms, doc, sf, n);
|
|
3275
|
+
}
|
|
3276
|
+
return ms.toString();
|
|
3277
|
+
}
|
|
3278
|
+
function backrefIds(doc) {
|
|
3279
|
+
const out = [];
|
|
3280
|
+
const max = doc.alloc.peek;
|
|
3281
|
+
for (let i = 1; i < max; i += 1) {
|
|
3282
|
+
const id = i;
|
|
3283
|
+
if (doc.backref.get(id)) out.push(id);
|
|
3284
|
+
}
|
|
3285
|
+
return out;
|
|
3286
|
+
}
|
|
3287
|
+
function classText(doc, classes) {
|
|
3288
|
+
if (classes.form === "absent" || classes.segments.length === 0) return null;
|
|
3289
|
+
const dynamic = classes.segments.find((s) => s.kind === "dynamic");
|
|
3290
|
+
if (dynamic && dynamic.kind === "dynamic") {
|
|
3291
|
+
return `className={${exprText(doc, dynamic.expr).text}}`;
|
|
3292
|
+
}
|
|
3293
|
+
const tokens = staticTokensOf2(classes);
|
|
3294
|
+
return `className="${tokens.join(" ")}"`;
|
|
3295
|
+
}
|
|
3296
|
+
function attrText(doc, name, value) {
|
|
3297
|
+
if (value.kind === "static") {
|
|
3298
|
+
if (value.value === true) return name;
|
|
3299
|
+
if (value.value === false) return "";
|
|
3300
|
+
return `${name}="${String(value.value)}"`;
|
|
3301
|
+
}
|
|
3302
|
+
return `${name}={${exprText(doc, value.expr).text}}`;
|
|
3303
|
+
}
|
|
3304
|
+
function printElement(doc, el) {
|
|
3305
|
+
const parts = [];
|
|
3306
|
+
const cls = classText(doc, el.classes);
|
|
3307
|
+
if (cls !== null) parts.push(cls);
|
|
3308
|
+
for (const name of el.attrs.order) {
|
|
3309
|
+
const v = el.attrs.entries.get(name);
|
|
3310
|
+
if (!v) continue;
|
|
3311
|
+
const text = attrText(doc, name, v);
|
|
3312
|
+
if (text.length > 0) parts.push(text);
|
|
3313
|
+
}
|
|
3314
|
+
for (const ref of el.attrs.spreads) parts.push(`{...${exprText(doc, ref).text}}`);
|
|
3315
|
+
const attrStr = parts.length > 0 ? ` ${parts.join(" ")}` : "";
|
|
3316
|
+
const tag = el.tag;
|
|
3317
|
+
if (el.children.length === 0) {
|
|
3318
|
+
return el.selfClosing ? `<${tag}${attrStr} />` : `<${tag}${attrStr}></${tag}>`;
|
|
3319
|
+
}
|
|
3320
|
+
const inner = el.children.map((c) => printNode(doc, c)).join("");
|
|
3321
|
+
return `<${tag}${attrStr}>${inner}</${tag}>`;
|
|
3322
|
+
}
|
|
3323
|
+
function printNode(doc, id) {
|
|
3324
|
+
const node = doc.nodes.get(id);
|
|
3325
|
+
if (!node) return "";
|
|
3326
|
+
switch (node.kind) {
|
|
3327
|
+
case "text":
|
|
3328
|
+
return node.value;
|
|
3329
|
+
case "comment":
|
|
3330
|
+
return `{/*${node.value}*/}`;
|
|
3331
|
+
case "expr": {
|
|
3332
|
+
const { text, spread } = exprText(doc, node.expr);
|
|
3333
|
+
return spread ? `{...${text}}` : `{${text}}`;
|
|
3334
|
+
}
|
|
3335
|
+
case "fragment":
|
|
3336
|
+
return `<>${node.children.map((c) => printNode(doc, c)).join("")}</>`;
|
|
3337
|
+
case "element":
|
|
3338
|
+
return printElement(doc, node);
|
|
3339
|
+
}
|
|
3340
|
+
}
|
|
3341
|
+
function rePrint(doc) {
|
|
3342
|
+
const root = doc.nodes.get(doc.root);
|
|
3343
|
+
if (!root || root.kind !== "fragment") return printNode(doc, doc.root);
|
|
3344
|
+
return root.children.map((c) => printNode(doc, c)).join("");
|
|
3345
|
+
}
|
|
3346
|
+
function doPrint(doc) {
|
|
3347
|
+
const surgical = surgicalPrint(doc);
|
|
3348
|
+
return surgical ?? rePrint(doc);
|
|
3349
|
+
}
|
|
3350
|
+
var jsxBackend = {
|
|
3351
|
+
name: "babel-jsx",
|
|
3352
|
+
langs: JSX_LANGS2,
|
|
3353
|
+
print(doc, _plan, _ctx) {
|
|
3354
|
+
const code = doPrint(doc);
|
|
3355
|
+
return { code, map: null, edits: [], diagnostics: [] };
|
|
3356
|
+
}
|
|
3357
|
+
};
|
|
3358
|
+
function createJsxBackend() {
|
|
3359
|
+
return jsxBackend;
|
|
3360
|
+
}
|
|
3361
|
+
|
|
3362
|
+
// ../resolver-tailwind/src/index.ts
|
|
3363
|
+
var import_node_module = require("module");
|
|
3364
|
+
var path = __toESM(require("path"), 1);
|
|
3365
|
+
function moduleBase() {
|
|
3366
|
+
return typeof __filename === "string" ? __filename : importMetaUrl;
|
|
3367
|
+
}
|
|
3368
|
+
function projectRequire(projectRoot) {
|
|
3369
|
+
const bases = [];
|
|
3370
|
+
if (projectRoot) bases.push(path.join(projectRoot, "__domflax__.js"));
|
|
3371
|
+
bases.push(path.join(process.cwd(), "__domflax__.js"));
|
|
3372
|
+
bases.push(moduleBase());
|
|
3373
|
+
for (const base of bases) {
|
|
3374
|
+
try {
|
|
3375
|
+
const candidate = (0, import_node_module.createRequire)(base);
|
|
3376
|
+
candidate.resolve("tailwindcss/package.json");
|
|
3377
|
+
return candidate;
|
|
3378
|
+
} catch {
|
|
3379
|
+
}
|
|
3380
|
+
}
|
|
3381
|
+
return null;
|
|
3382
|
+
}
|
|
3383
|
+
function loadEngine(options) {
|
|
3384
|
+
const req = projectRequire(options.projectRoot);
|
|
3385
|
+
if (!req) return null;
|
|
3386
|
+
try {
|
|
3387
|
+
const resolveConfig = req("tailwindcss/resolveConfig.js");
|
|
3388
|
+
const { createContext } = req("tailwindcss/lib/lib/setupContextUtils.js");
|
|
3389
|
+
const { generateRules } = req("tailwindcss/lib/lib/generateRules.js");
|
|
3390
|
+
const pkg = req("tailwindcss/package.json");
|
|
3391
|
+
let userConfig = options.config ?? { content: [{ raw: "" }] };
|
|
3392
|
+
if (options.configPath !== void 0) {
|
|
3393
|
+
const loadConfig = req("tailwindcss/loadConfig.js");
|
|
3394
|
+
userConfig = loadConfig(options.configPath);
|
|
3395
|
+
}
|
|
3396
|
+
const resolved = resolveConfig(userConfig);
|
|
3397
|
+
const context = createContext(resolved);
|
|
3398
|
+
return {
|
|
3399
|
+
version: pkg.version,
|
|
3400
|
+
context,
|
|
3401
|
+
generate(candidates) {
|
|
3402
|
+
const rules = generateRules(new Set(candidates), context);
|
|
3403
|
+
return rules.map(([, node]) => node);
|
|
3404
|
+
}
|
|
3405
|
+
};
|
|
3406
|
+
} catch {
|
|
3407
|
+
return null;
|
|
3408
|
+
}
|
|
3409
|
+
}
|
|
3410
|
+
var LEGACY_PSEUDO_ELEMENTS = /* @__PURE__ */ new Set([
|
|
3411
|
+
":before",
|
|
3412
|
+
":after",
|
|
3413
|
+
":first-line",
|
|
3414
|
+
":first-letter"
|
|
3415
|
+
]);
|
|
3416
|
+
function parseSelector(selector) {
|
|
3417
|
+
const sel = selector.trim();
|
|
3418
|
+
if (sel.length === 0 || sel[0] !== ".") return { kind: "complex" };
|
|
3419
|
+
let i = 1;
|
|
3420
|
+
for (; i < sel.length; i += 1) {
|
|
3421
|
+
const c = sel[i];
|
|
3422
|
+
if (c === "\\") {
|
|
3423
|
+
i += 1;
|
|
3424
|
+
continue;
|
|
3425
|
+
}
|
|
3426
|
+
if (c === ":" || c === "." || c === "[" || c === " " || c === ">" || c === "+" || c === "~" || c === ",") {
|
|
3427
|
+
break;
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
const remainder = sel.slice(i);
|
|
3431
|
+
if (remainder.length === 0) {
|
|
3432
|
+
return { kind: "simple", states: [], pseudoElement: "" };
|
|
3433
|
+
}
|
|
3434
|
+
if (!/^(?:::?[-a-z]+(?:\([^()]*\))?)+$/i.test(remainder)) {
|
|
3435
|
+
return { kind: "complex" };
|
|
3436
|
+
}
|
|
3437
|
+
const parts = remainder.match(/::?[-a-z]+(?:\([^()]*\))?/gi) ?? [];
|
|
3438
|
+
const states = [];
|
|
3439
|
+
let pseudoElement = "";
|
|
3440
|
+
for (const part of parts) {
|
|
3441
|
+
if (part.startsWith("::") || LEGACY_PSEUDO_ELEMENTS.has(part)) {
|
|
3442
|
+
pseudoElement = part.startsWith("::") ? part : `:${part}`;
|
|
3443
|
+
} else {
|
|
3444
|
+
states.push(part);
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
return { kind: "simple", states, pseudoElement };
|
|
3448
|
+
}
|
|
3449
|
+
function makeCondition(media, states, pseudoElement) {
|
|
3450
|
+
return {
|
|
3451
|
+
media,
|
|
3452
|
+
states: [...new Set(states)].sort(),
|
|
3453
|
+
pseudoElement
|
|
3454
|
+
};
|
|
3455
|
+
}
|
|
3456
|
+
function collectRules(node, mediaStack, inUnsupportedAtRule, out) {
|
|
3457
|
+
if (node.type === "rule") {
|
|
3458
|
+
out.push({ rule: node, media: mediaStack, unsupported: inUnsupportedAtRule });
|
|
3459
|
+
return;
|
|
3460
|
+
}
|
|
3461
|
+
if (node.type === "atrule") {
|
|
3462
|
+
const at = node;
|
|
3463
|
+
const children = at.nodes ?? [];
|
|
3464
|
+
if (at.name === "media") {
|
|
3465
|
+
const nextStack = at.params ? [...mediaStack, at.params] : mediaStack;
|
|
3466
|
+
for (const child of children) collectRules(child, nextStack, inUnsupportedAtRule, out);
|
|
3467
|
+
} else {
|
|
3468
|
+
for (const child of children) collectRules(child, mediaStack, true, out);
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
}
|
|
3472
|
+
function extractToken(token, nodes) {
|
|
3473
|
+
if (nodes.length === 0) return { blocks: [], produced: false };
|
|
3474
|
+
const leaves = [];
|
|
3475
|
+
for (const node of nodes) collectRules(node, [], false, leaves);
|
|
3476
|
+
const blocks = [];
|
|
3477
|
+
let sawComplex = false;
|
|
3478
|
+
for (const { rule, media, unsupported } of leaves) {
|
|
3479
|
+
const parsed = parseSelector(rule.selector);
|
|
3480
|
+
if (parsed.kind === "complex" || unsupported) {
|
|
3481
|
+
sawComplex = true;
|
|
3482
|
+
continue;
|
|
3483
|
+
}
|
|
3484
|
+
const decls = [];
|
|
3485
|
+
for (const child of rule.nodes ?? []) {
|
|
3486
|
+
if (child.type !== "decl") continue;
|
|
3487
|
+
const d = child;
|
|
3488
|
+
if (typeof d.value !== "string") continue;
|
|
3489
|
+
decls.push([d.prop, d.value, d.important === true]);
|
|
3490
|
+
}
|
|
3491
|
+
if (decls.length === 0) continue;
|
|
3492
|
+
const mediaQuery = media.join(" and ");
|
|
3493
|
+
blocks.push({ condition: makeCondition(mediaQuery, parsed.states, parsed.pseudoElement), decls });
|
|
3494
|
+
}
|
|
3495
|
+
const opaque = sawComplex && blocks.length === 0 ? { token, reason: "combinator-variant", detail: "utility targets descendants/siblings, not its own box" } : void 0;
|
|
3496
|
+
return { blocks, produced: true, opaque };
|
|
3497
|
+
}
|
|
3498
|
+
function buildStyleMap(blockMaps) {
|
|
3499
|
+
if (blockMaps.size === 0) return emptyStyleMap();
|
|
3500
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
3501
|
+
for (const { condition, decls } of blockMaps.values()) {
|
|
3502
|
+
if (decls.size === 0) continue;
|
|
3503
|
+
blocks.set(conditionKey(condition), { condition, decls });
|
|
3504
|
+
}
|
|
3505
|
+
if (blocks.size === 0) return emptyStyleMap();
|
|
3506
|
+
return normalizer.normalizeStyleMap({ blocks });
|
|
3507
|
+
}
|
|
3508
|
+
function shadowedBy(prev) {
|
|
3509
|
+
const out = [];
|
|
3510
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3511
|
+
const add = (o) => {
|
|
3512
|
+
if (!o || o.kind !== "class" || seen.has(o.className)) return;
|
|
3513
|
+
seen.add(o.className);
|
|
3514
|
+
out.push(o);
|
|
3515
|
+
};
|
|
3516
|
+
for (const o of prev.shadowed ?? []) add(o);
|
|
3517
|
+
add(prev.origin);
|
|
3518
|
+
return out.length > 0 ? out : void 0;
|
|
3519
|
+
}
|
|
3520
|
+
var OPAQUE_USAGE = {
|
|
3521
|
+
asSubject: true,
|
|
3522
|
+
asAncestor: true,
|
|
3523
|
+
asCompound: true,
|
|
3524
|
+
asSibling: true,
|
|
3525
|
+
asHasArgument: true,
|
|
3526
|
+
asStructural: true,
|
|
3527
|
+
droppable: false
|
|
3528
|
+
};
|
|
3529
|
+
var DROPPABLE_USAGE = {
|
|
3530
|
+
asSubject: true,
|
|
3531
|
+
asAncestor: false,
|
|
3532
|
+
asCompound: false,
|
|
3533
|
+
asSibling: false,
|
|
3534
|
+
asHasArgument: false,
|
|
3535
|
+
asStructural: false,
|
|
3536
|
+
droppable: true
|
|
3537
|
+
};
|
|
3538
|
+
function fnv1a(input) {
|
|
3539
|
+
let h = 2166136261;
|
|
3540
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
3541
|
+
h ^= input.charCodeAt(i);
|
|
3542
|
+
h = Math.imul(h, 16777619);
|
|
3543
|
+
}
|
|
3544
|
+
return (h >>> 0).toString(16).padStart(8, "0");
|
|
3545
|
+
}
|
|
3546
|
+
var TailwindResolver = class {
|
|
3547
|
+
id = "tailwind";
|
|
3548
|
+
provider;
|
|
3549
|
+
fingerprint;
|
|
3550
|
+
#engine;
|
|
3551
|
+
/** Per-token extraction cache (engine output is pure for a fixed config). */
|
|
3552
|
+
#tokenCache = /* @__PURE__ */ new Map();
|
|
3553
|
+
/** Per-class-set forward-resolution cache. */
|
|
3554
|
+
#resolveCache = /* @__PURE__ */ new Map();
|
|
3555
|
+
/** Lazily built reverse index for {@link emit}. */
|
|
3556
|
+
#reverseIndex = null;
|
|
3557
|
+
constructor(config = {}) {
|
|
3558
|
+
this.#engine = loadEngine(config);
|
|
3559
|
+
this.provider = config.provider ?? (this.#engine ? `tailwindcss@${this.#engine.version}` : "tailwindcss");
|
|
3560
|
+
const seed = JSON.stringify(config.config ?? {}) + (config.configPath ?? "");
|
|
3561
|
+
this.fingerprint = config.fingerprint ?? `${this.provider}/${fnv1a(seed)}`;
|
|
3562
|
+
}
|
|
3563
|
+
/** Engine-backed, cached single-token extraction. */
|
|
3564
|
+
#extract(token) {
|
|
3565
|
+
const cached = this.#tokenCache.get(token);
|
|
3566
|
+
if (cached) return cached;
|
|
3567
|
+
let result;
|
|
3568
|
+
if (!this.#engine) {
|
|
3569
|
+
result = { blocks: [], produced: false };
|
|
3570
|
+
} else {
|
|
3571
|
+
try {
|
|
3572
|
+
result = extractToken(token, this.#engine.generate([token]));
|
|
3573
|
+
} catch {
|
|
3574
|
+
result = { blocks: [], produced: false };
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3577
|
+
this.#tokenCache.set(token, result);
|
|
3578
|
+
return result;
|
|
3579
|
+
}
|
|
3580
|
+
owns(token) {
|
|
3581
|
+
if (token.length === 0) return false;
|
|
3582
|
+
return this.#extract(token).produced;
|
|
3583
|
+
}
|
|
3584
|
+
resolve(input) {
|
|
3585
|
+
const key = JSON.stringify(input.classes);
|
|
3586
|
+
const cached = this.#resolveCache.get(key);
|
|
3587
|
+
if (cached) return cached;
|
|
3588
|
+
const blockMaps = /* @__PURE__ */ new Map();
|
|
3589
|
+
const resolved = [];
|
|
3590
|
+
const unknown = [];
|
|
3591
|
+
const opaque = [];
|
|
3592
|
+
input.classes.forEach((token, tokenIndex) => {
|
|
3593
|
+
const extracted = this.#extract(token);
|
|
3594
|
+
if (!extracted.produced) {
|
|
3595
|
+
unknown.push(token);
|
|
3596
|
+
return;
|
|
3597
|
+
}
|
|
3598
|
+
if (extracted.opaque) opaque.push(extracted.opaque);
|
|
3599
|
+
if (extracted.blocks.length === 0) return;
|
|
3600
|
+
const origin = { kind: "class", tokenIndex, className: token };
|
|
3601
|
+
let contributed = false;
|
|
3602
|
+
for (const block of extracted.blocks) {
|
|
3603
|
+
const ck = conditionKey(block.condition);
|
|
3604
|
+
let bucket = blockMaps.get(ck);
|
|
3605
|
+
if (!bucket) {
|
|
3606
|
+
bucket = { condition: block.condition, decls: /* @__PURE__ */ new Map() };
|
|
3607
|
+
blockMaps.set(ck, bucket);
|
|
3608
|
+
}
|
|
3609
|
+
for (const [prop, value, important] of block.decls) {
|
|
3610
|
+
for (const decl of normalizer.normalizeDeclaration(prop, value, important)) {
|
|
3611
|
+
const prev = bucket.decls.get(decl.property);
|
|
3612
|
+
const shadowed = prev ? shadowedBy(prev) : void 0;
|
|
3613
|
+
bucket.decls.set(decl.property, shadowed ? { ...decl, origin, shadowed } : { ...decl, origin });
|
|
3614
|
+
contributed = true;
|
|
3615
|
+
}
|
|
3616
|
+
}
|
|
3617
|
+
}
|
|
3618
|
+
if (contributed) resolved.push(token);
|
|
3619
|
+
});
|
|
3620
|
+
const result = {
|
|
3621
|
+
styles: buildStyleMap(blockMaps),
|
|
3622
|
+
resolved,
|
|
3623
|
+
unknown,
|
|
3624
|
+
opaque,
|
|
3625
|
+
warnings: []
|
|
3626
|
+
};
|
|
3627
|
+
this.#resolveCache.set(key, result);
|
|
3628
|
+
return result;
|
|
3629
|
+
}
|
|
3630
|
+
/**
|
|
3631
|
+
* Lazily build the reverse index from the engine's own enumerable class list. Each indexable
|
|
3632
|
+
* utility maps to its NORMALIZED BASE longhand declarations (property → canonical value). Utilities
|
|
3633
|
+
* with variant conditions, combinator selectors, or no BASE declarations are skipped. Sorted by
|
|
3634
|
+
* declaration count (desc) so greedier (shorthand-like) utilities are tried first.
|
|
3635
|
+
*/
|
|
3636
|
+
#buildReverseIndex() {
|
|
3637
|
+
if (this.#reverseIndex) return this.#reverseIndex;
|
|
3638
|
+
const index = [];
|
|
3639
|
+
if (this.#engine) {
|
|
3640
|
+
try {
|
|
3641
|
+
const classes = this.#engine.context.getClassList().filter((c) => typeof c === "string");
|
|
3642
|
+
const nodes = this.#engine.generate(classes);
|
|
3643
|
+
for (const node of nodes) {
|
|
3644
|
+
if (node.type !== "rule") continue;
|
|
3645
|
+
const rule = node;
|
|
3646
|
+
const parsed = parseSelector(rule.selector);
|
|
3647
|
+
if (parsed.kind !== "simple" || parsed.states.length > 0 || parsed.pseudoElement !== "") {
|
|
3648
|
+
continue;
|
|
3649
|
+
}
|
|
3650
|
+
const className = unescapeClass(rule.selector);
|
|
3651
|
+
if (className === null) continue;
|
|
3652
|
+
const decls = /* @__PURE__ */ new Map();
|
|
3653
|
+
for (const child of rule.nodes ?? []) {
|
|
3654
|
+
if (child.type !== "decl") continue;
|
|
3655
|
+
const d = child;
|
|
3656
|
+
if (typeof d.value !== "string") continue;
|
|
3657
|
+
for (const decl of normalizer.normalizeDeclaration(d.prop, d.value, d.important === true)) {
|
|
3658
|
+
decls.set(decl.property, String(decl.value));
|
|
3659
|
+
}
|
|
3660
|
+
}
|
|
3661
|
+
if (decls.size > 0) index.push([className, decls]);
|
|
3662
|
+
}
|
|
3663
|
+
} catch {
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
index.sort((a, b) => b[1].size - a[1].size || (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
|
|
3667
|
+
this.#reverseIndex = index;
|
|
3668
|
+
return index;
|
|
3669
|
+
}
|
|
3670
|
+
emit(styles, ctx) {
|
|
3671
|
+
const norm = ctx.normalizer ?? normalizer;
|
|
3672
|
+
const normalized = norm.normalizeStyleMap(styles);
|
|
3673
|
+
const base = normalized.blocks.get(conditionKey(BASE_CONDITION));
|
|
3674
|
+
if (!base || base.decls.size === 0) return { classes: [], exact: true, warnings: [] };
|
|
3675
|
+
const hasNonBase = normalized.blocks.size > 1;
|
|
3676
|
+
const target = /* @__PURE__ */ new Map();
|
|
3677
|
+
for (const [prop, decl] of base.decls) {
|
|
3678
|
+
for (const [lp, lv] of expandForEmit(norm, String(prop), String(decl.value), decl.important)) {
|
|
3679
|
+
target.set(lp, lv);
|
|
3680
|
+
}
|
|
3681
|
+
}
|
|
3682
|
+
const candidates = [];
|
|
3683
|
+
for (const entry of this.#buildReverseIndex()) {
|
|
3684
|
+
const [, declMap] = entry;
|
|
3685
|
+
if (declMap.size === 0 || declMap.size > target.size) continue;
|
|
3686
|
+
let fits = true;
|
|
3687
|
+
for (const [prop, value] of declMap) {
|
|
3688
|
+
if (target.get(prop) !== value) {
|
|
3689
|
+
fits = false;
|
|
3690
|
+
break;
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3693
|
+
if (fits) candidates.push(entry);
|
|
3694
|
+
}
|
|
3695
|
+
const remaining = new Map(target);
|
|
3696
|
+
const classes = [];
|
|
3697
|
+
while (remaining.size > 0) {
|
|
3698
|
+
let best = null;
|
|
3699
|
+
let bestCover = 0;
|
|
3700
|
+
for (const entry of candidates) {
|
|
3701
|
+
const [token, declMap] = entry;
|
|
3702
|
+
let cover = 0;
|
|
3703
|
+
for (const prop of declMap.keys()) if (remaining.has(prop)) cover += 1;
|
|
3704
|
+
if (cover === 0) continue;
|
|
3705
|
+
const better = best === null || cover > bestCover || cover === bestCover && declMap.size < best[1].size || cover === bestCover && declMap.size === best[1].size && token < best[0];
|
|
3706
|
+
if (better) {
|
|
3707
|
+
best = entry;
|
|
3708
|
+
bestCover = cover;
|
|
3709
|
+
}
|
|
3710
|
+
}
|
|
3711
|
+
if (!best) break;
|
|
3712
|
+
classes.push(best[0]);
|
|
3713
|
+
for (const prop of best[1].keys()) remaining.delete(prop);
|
|
3714
|
+
}
|
|
3715
|
+
const exact = remaining.size === 0 && !hasNonBase;
|
|
3716
|
+
if (remaining.size === 0) return { classes, exact, warnings: [] };
|
|
3717
|
+
const residual = synthesizeResidual(remaining, ctx);
|
|
3718
|
+
return residual ? { classes, residual, exact, warnings: [] } : { classes, exact, warnings: [] };
|
|
3719
|
+
}
|
|
3720
|
+
selectorUsage(token) {
|
|
3721
|
+
const ex = this.#extract(token);
|
|
3722
|
+
if (!ex.produced || ex.opaque) return OPAQUE_USAGE;
|
|
3723
|
+
const baseOnly = ex.blocks.length > 0 && ex.blocks.every((b) => conditionKey(b.condition) === conditionKey(BASE_CONDITION));
|
|
3724
|
+
if (!baseOnly) return OPAQUE_USAGE;
|
|
3725
|
+
return DROPPABLE_USAGE;
|
|
3726
|
+
}
|
|
3727
|
+
};
|
|
3728
|
+
function expandForEmit(norm, prop, value, important) {
|
|
3729
|
+
const pairsFor = (p, v) => norm.normalizeDeclaration(p, v, important).map((d) => [d.property, String(d.value)]);
|
|
3730
|
+
if (prop === "size") {
|
|
3731
|
+
return [...pairsFor("width", value), ...pairsFor("height", value)];
|
|
3732
|
+
}
|
|
3733
|
+
if (prop === "inset-block" || prop === "inset-inline") {
|
|
3734
|
+
const parts = value.split(/\s+/).filter((s) => s.length > 0);
|
|
3735
|
+
const a = parts[0] ?? value;
|
|
3736
|
+
const b = parts[1] ?? a;
|
|
3737
|
+
const sides = prop === "inset-block" ? ["top", "bottom"] : ["left", "right"];
|
|
3738
|
+
return [...pairsFor(sides[0], a), ...pairsFor(sides[1], b)];
|
|
3739
|
+
}
|
|
3740
|
+
return pairsFor(prop, value);
|
|
3741
|
+
}
|
|
3742
|
+
function synthesizeResidual(remaining, ctx) {
|
|
3743
|
+
if (remaining.size === 0) return void 0;
|
|
3744
|
+
const norm = ctx.normalizer ?? normalizer;
|
|
3745
|
+
const decls = /* @__PURE__ */ new Map();
|
|
3746
|
+
for (const [prop, value] of remaining) {
|
|
3747
|
+
for (const decl of norm.normalizeDeclaration(String(prop), value, false)) {
|
|
3748
|
+
decls.set(decl.property, decl);
|
|
3749
|
+
}
|
|
3750
|
+
}
|
|
3751
|
+
const block = { condition: BASE_CONDITION, decls };
|
|
3752
|
+
const styleMap = { blocks: /* @__PURE__ */ new Map([[conditionKey(BASE_CONDITION), block]]) };
|
|
3753
|
+
const css = [...remaining].map(([p, v]) => `${p}:${v}`).join(";");
|
|
3754
|
+
const className = `df-${fnv1a(css)}`;
|
|
3755
|
+
const synthetic = { className, decls: styleMap, css: `.${className}{${css}}` };
|
|
3756
|
+
try {
|
|
3757
|
+
ctx.sink.register(synthetic);
|
|
3758
|
+
} catch {
|
|
3759
|
+
}
|
|
3760
|
+
return synthetic;
|
|
3761
|
+
}
|
|
3762
|
+
function unescapeClass(selector) {
|
|
3763
|
+
const sel = selector.trim();
|
|
3764
|
+
if (sel[0] !== ".") return null;
|
|
3765
|
+
let out = "";
|
|
3766
|
+
for (let i = 1; i < sel.length; i += 1) {
|
|
3767
|
+
const c = sel[i];
|
|
3768
|
+
if (c === "\\") {
|
|
3769
|
+
i += 1;
|
|
3770
|
+
if (i < sel.length) out += sel[i];
|
|
3771
|
+
continue;
|
|
3772
|
+
}
|
|
3773
|
+
if (c === ":" || c === "." || c === "[" || c === " " || c === ">" || c === "+" || c === "~" || c === ",") {
|
|
3774
|
+
return null;
|
|
3775
|
+
}
|
|
3776
|
+
out += c;
|
|
3777
|
+
}
|
|
3778
|
+
return out.length > 0 ? out : null;
|
|
3779
|
+
}
|
|
3780
|
+
function createTailwindResolver(config) {
|
|
3781
|
+
return new TailwindResolver(config);
|
|
3782
|
+
}
|
|
3783
|
+
|
|
3784
|
+
// ../resolver-css/src/index.ts
|
|
3785
|
+
var import_node_fs = require("fs");
|
|
3786
|
+
var import_node_module2 = require("module");
|
|
3787
|
+
var path2 = __toESM(require("path"), 1);
|
|
3788
|
+
function moduleBase2() {
|
|
3789
|
+
return typeof __filename === "string" ? __filename : importMetaUrl;
|
|
3790
|
+
}
|
|
3791
|
+
function loadPostcssEngine(projectRoot) {
|
|
3792
|
+
const bases = [];
|
|
3793
|
+
if (projectRoot) bases.push(path2.join(projectRoot, "__domflax__.js"));
|
|
3794
|
+
bases.push(path2.join(process.cwd(), "__domflax__.js"));
|
|
3795
|
+
bases.push(moduleBase2());
|
|
3796
|
+
for (const base of bases) {
|
|
3797
|
+
try {
|
|
3798
|
+
const req = (0, import_node_module2.createRequire)(base);
|
|
3799
|
+
req.resolve("postcss");
|
|
3800
|
+
req.resolve("postcss-selector-parser");
|
|
3801
|
+
const postcss = req("postcss");
|
|
3802
|
+
const raw = req("postcss-selector-parser");
|
|
3803
|
+
const selector = raw.default ?? raw;
|
|
3804
|
+
return { parse: postcss.parse, selectorParser: selector };
|
|
3805
|
+
} catch {
|
|
3806
|
+
}
|
|
3807
|
+
}
|
|
3808
|
+
return null;
|
|
3809
|
+
}
|
|
3810
|
+
var pc = null;
|
|
3811
|
+
var sp = null;
|
|
3812
|
+
function ensurePostcss(projectRoot) {
|
|
3813
|
+
if (pc && sp) return;
|
|
3814
|
+
const engine = loadPostcssEngine(projectRoot);
|
|
3815
|
+
if (!engine) {
|
|
3816
|
+
throw new Error(
|
|
3817
|
+
'@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'
|
|
3818
|
+
);
|
|
3819
|
+
}
|
|
3820
|
+
pc = engine.parse;
|
|
3821
|
+
sp = engine.selectorParser;
|
|
3822
|
+
}
|
|
3823
|
+
var CSS_RESOLVER_ID = "css";
|
|
3824
|
+
var CSS_RESOLVER_PROVIDER = "custom-css";
|
|
3825
|
+
var ENGINE_VERSION = "css-index@1";
|
|
3826
|
+
var STRUCTURAL_PSEUDOS = /* @__PURE__ */ new Set([
|
|
3827
|
+
":nth-child",
|
|
3828
|
+
":nth-last-child",
|
|
3829
|
+
":first-child",
|
|
3830
|
+
":last-child",
|
|
3831
|
+
":only-child",
|
|
3832
|
+
":nth-of-type",
|
|
3833
|
+
":nth-last-of-type",
|
|
3834
|
+
":first-of-type",
|
|
3835
|
+
":last-of-type",
|
|
3836
|
+
":only-of-type"
|
|
3837
|
+
]);
|
|
3838
|
+
var FUNCTIONAL_PSEUDOS = /* @__PURE__ */ new Set([
|
|
3839
|
+
":not",
|
|
3840
|
+
":is",
|
|
3841
|
+
":where",
|
|
3842
|
+
":has",
|
|
3843
|
+
":matches"
|
|
3844
|
+
]);
|
|
3845
|
+
var LEGACY_PSEUDO_ELEMENTS2 = /* @__PURE__ */ new Set([
|
|
3846
|
+
":before",
|
|
3847
|
+
":after",
|
|
3848
|
+
":first-line",
|
|
3849
|
+
":first-letter"
|
|
3850
|
+
]);
|
|
3851
|
+
var CustomCSSResolver = class {
|
|
3852
|
+
id = CSS_RESOLVER_ID;
|
|
3853
|
+
provider = CSS_RESOLVER_PROVIDER;
|
|
3854
|
+
fingerprint;
|
|
3855
|
+
#files;
|
|
3856
|
+
/** Forward map: class token → simple-`.class` rule contributions (source order). */
|
|
3857
|
+
#classIndex = /* @__PURE__ */ new Map();
|
|
3858
|
+
/** Selector-participation facts per class token. */
|
|
3859
|
+
#usage = /* @__PURE__ */ new Map();
|
|
3860
|
+
/** Every class referenced anywhere in the stylesheets (forward-resolvable or not). */
|
|
3861
|
+
#known = /* @__PURE__ */ new Set();
|
|
3862
|
+
/** Distinct COMPLEX selectors (combinator or structural pseudo), sorted. */
|
|
3863
|
+
#complex;
|
|
3864
|
+
#reverse = null;
|
|
3865
|
+
constructor(cssFiles = [], options = {}) {
|
|
3866
|
+
ensurePostcss(options.projectRoot);
|
|
3867
|
+
const fromDisk = (options.files ?? []).map(readCssPath);
|
|
3868
|
+
this.#files = [...cssFiles, ...fromDisk];
|
|
3869
|
+
this.fingerprint = options.fingerprint ?? deriveFingerprint(this.provider, this.#files);
|
|
3870
|
+
const complex = /* @__PURE__ */ new Set();
|
|
3871
|
+
let order = 0;
|
|
3872
|
+
for (const file of this.#files) {
|
|
3873
|
+
order = this.#indexFile(file, order, complex);
|
|
3874
|
+
}
|
|
3875
|
+
this.#complex = [...complex].sort();
|
|
3876
|
+
}
|
|
3877
|
+
/** The stylesheets this resolver was constructed with (raw sources + any read from disk). */
|
|
3878
|
+
get files() {
|
|
3879
|
+
return this.#files;
|
|
3880
|
+
}
|
|
3881
|
+
/** Owns any plain class token referenced by one of {@link files}. */
|
|
3882
|
+
owns(token) {
|
|
3883
|
+
return isPlainClassToken(token) && this.#known.has(token);
|
|
3884
|
+
}
|
|
3885
|
+
resolve(input) {
|
|
3886
|
+
const styles = this.#resolveTokens(input.classes, input.classes);
|
|
3887
|
+
const resolved = [];
|
|
3888
|
+
const unknown = [];
|
|
3889
|
+
for (const token of input.classes) {
|
|
3890
|
+
if (this.#classIndex.has(token)) resolved.push(token);
|
|
3891
|
+
else unknown.push(token);
|
|
3892
|
+
}
|
|
3893
|
+
return { styles, resolved, unknown, opaque: [], warnings: [] };
|
|
3894
|
+
}
|
|
3895
|
+
emit(styles, ctx) {
|
|
3896
|
+
const norm = ctx.normalizer ?? normalizer;
|
|
3897
|
+
const remaining = /* @__PURE__ */ new Map();
|
|
3898
|
+
for (const [ck, block] of norm.normalizeStyleMap(styles).blocks) {
|
|
3899
|
+
for (const [prop, decl] of block.decls) {
|
|
3900
|
+
remaining.set(`${ck}\0${prop}`, String(decl.value));
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
if (remaining.size === 0) return { classes: [], exact: true, warnings: [] };
|
|
3904
|
+
const classes = [];
|
|
3905
|
+
for (const { token, keyed } of this.#reverseIndex()) {
|
|
3906
|
+
let matches = true;
|
|
3907
|
+
for (const [key, value] of keyed) {
|
|
3908
|
+
if (remaining.get(key) !== value) {
|
|
3909
|
+
matches = false;
|
|
3910
|
+
break;
|
|
3911
|
+
}
|
|
3912
|
+
}
|
|
3913
|
+
if (!matches) continue;
|
|
3914
|
+
classes.push(token);
|
|
3915
|
+
for (const key of keyed.keys()) remaining.delete(key);
|
|
3916
|
+
if (remaining.size === 0) break;
|
|
3917
|
+
}
|
|
3918
|
+
return { classes, exact: remaining.size === 0, warnings: [] };
|
|
3919
|
+
}
|
|
3920
|
+
selectorUsage(token) {
|
|
3921
|
+
const u = this.#usage.get(token);
|
|
3922
|
+
if (!u) {
|
|
3923
|
+
return {
|
|
3924
|
+
asSubject: false,
|
|
3925
|
+
asAncestor: false,
|
|
3926
|
+
asCompound: false,
|
|
3927
|
+
asSibling: false,
|
|
3928
|
+
asHasArgument: false,
|
|
3929
|
+
asStructural: false,
|
|
3930
|
+
droppable: true
|
|
3931
|
+
};
|
|
3932
|
+
}
|
|
3933
|
+
return {
|
|
3934
|
+
asSubject: u.asSubject,
|
|
3935
|
+
asAncestor: u.asAncestor,
|
|
3936
|
+
asCompound: u.asCompound,
|
|
3937
|
+
asSibling: u.asSibling,
|
|
3938
|
+
asHasArgument: u.asHasArgument,
|
|
3939
|
+
asStructural: u.asStructural,
|
|
3940
|
+
// Safe to drop/rename only when every reference is the lone subject of a bare `.x {}`.
|
|
3941
|
+
droppable: u.referenced && !u.loadBearing
|
|
3942
|
+
};
|
|
3943
|
+
}
|
|
3944
|
+
/**
|
|
3945
|
+
* The distinct COMPLEX selectors found across all stylesheets — anything containing a combinator
|
|
3946
|
+
* (descendant / `>` / `+` / `~`) or a structural pseudo (`:nth-child`, `:first-child`, …). Feeds
|
|
3947
|
+
* domflax's CSS-selector-safety guard.
|
|
3948
|
+
*/
|
|
3949
|
+
complexSelectors() {
|
|
3950
|
+
return this.#complex;
|
|
3951
|
+
}
|
|
3952
|
+
/* ─────────────────────────── internals ─────────────────────────── */
|
|
3953
|
+
/** Parse one stylesheet and fold its rules into the indexes. Returns the advanced order counter. */
|
|
3954
|
+
#indexFile(file, startOrder, complex) {
|
|
3955
|
+
let order = startOrder;
|
|
3956
|
+
let root;
|
|
3957
|
+
try {
|
|
3958
|
+
root = pc(file.css, { from: file.id });
|
|
3959
|
+
} catch {
|
|
3960
|
+
return order;
|
|
3961
|
+
}
|
|
3962
|
+
root.walkRules((rule) => {
|
|
3963
|
+
const media = mediaContext(rule);
|
|
3964
|
+
if (media.skip) return;
|
|
3965
|
+
const decls = collectDecls(rule);
|
|
3966
|
+
let ast;
|
|
3967
|
+
try {
|
|
3968
|
+
ast = sp().astSync(rule.selector);
|
|
3969
|
+
} catch {
|
|
3970
|
+
return;
|
|
3971
|
+
}
|
|
3972
|
+
for (const sel of ast.nodes) {
|
|
3973
|
+
const thisOrder = order;
|
|
3974
|
+
this.#analyzeSelector(sel, media.media, decls, thisOrder, complex);
|
|
3975
|
+
}
|
|
3976
|
+
order += 1;
|
|
3977
|
+
});
|
|
3978
|
+
return order;
|
|
3979
|
+
}
|
|
3980
|
+
/** Analyze one comma-segment selector: forward indexing, usage facts, complex detection. */
|
|
3981
|
+
#analyzeSelector(selector, media, decls, order, complex) {
|
|
3982
|
+
const compounds = splitCompounds(selector);
|
|
3983
|
+
let hasCombinator = false;
|
|
3984
|
+
let hasStructural = false;
|
|
3985
|
+
compounds.forEach((compound, index) => {
|
|
3986
|
+
const isSubject = index === compounds.length - 1;
|
|
3987
|
+
const rightCombinator = index < compounds.length - 1 ? compounds[index + 1].leftCombinator : null;
|
|
3988
|
+
if (rightCombinator) hasCombinator = true;
|
|
3989
|
+
const classes = compound.nodes.filter((n) => sp.isClassName(n));
|
|
3990
|
+
const otherSimple = compound.nodes.some(
|
|
3991
|
+
(n) => sp.isTag(n) || sp.isIdentifier(n) || sp.isAttribute(n) || sp.isUniversal(n) || sp.isNesting(n)
|
|
3992
|
+
);
|
|
3993
|
+
const pseudos = compound.nodes.filter((n) => sp.isPseudo(n));
|
|
3994
|
+
const structuralPseudo = pseudos.some((p) => STRUCTURAL_PSEUDOS.has(pseudoName(p)));
|
|
3995
|
+
const functionalPseudo = pseudos.some((p) => FUNCTIONAL_PSEUDOS.has(pseudoName(p)));
|
|
3996
|
+
const statePseudos = pseudos.filter(
|
|
3997
|
+
(p) => sp.isPseudoClass(p) && !STRUCTURAL_PSEUDOS.has(pseudoName(p)) && !FUNCTIONAL_PSEUDOS.has(pseudoName(p))
|
|
3998
|
+
);
|
|
3999
|
+
const elementPseudos = pseudos.filter((p) => isPseudoElement(p));
|
|
4000
|
+
const qualified = classes.length > 1 || otherSimple || functionalPseudo || statePseudos.length > 0;
|
|
4001
|
+
if (structuralPseudo) hasStructural = true;
|
|
4002
|
+
for (const cls of classes) {
|
|
4003
|
+
const token = cls.value;
|
|
4004
|
+
this.#known.add(token);
|
|
4005
|
+
const u = this.#getUsage(token);
|
|
4006
|
+
u.referenced = true;
|
|
4007
|
+
if (isSubject) u.asSubject = true;
|
|
4008
|
+
if (rightCombinator === " " || rightCombinator === ">") u.asAncestor = true;
|
|
4009
|
+
if (rightCombinator === "+" || rightCombinator === "~") u.asSibling = true;
|
|
4010
|
+
if (qualified) u.asCompound = true;
|
|
4011
|
+
if (structuralPseudo) u.asStructural = true;
|
|
4012
|
+
if (rightCombinator !== null || qualified || structuralPseudo || elementPseudos.length > 0) {
|
|
4013
|
+
u.loadBearing = true;
|
|
4014
|
+
}
|
|
4015
|
+
const forwardEligible = compounds.length === 1 && classes.length === 1 && !otherSimple && !structuralPseudo && !functionalPseudo && elementPseudos.length <= 1;
|
|
4016
|
+
if (forwardEligible && decls.length > 0) {
|
|
4017
|
+
const condition = {
|
|
4018
|
+
media,
|
|
4019
|
+
states: statePseudos.map(pseudoName).sort(),
|
|
4020
|
+
pseudoElement: elementPseudos.length === 1 ? normalizePseudoElement(elementPseudos[0]) : ""
|
|
4021
|
+
};
|
|
4022
|
+
this.#addRuleEntry(token, { order, token, condition, decls });
|
|
4023
|
+
}
|
|
4024
|
+
}
|
|
4025
|
+
for (const p of pseudos) {
|
|
4026
|
+
const isHas = pseudoName(p) === ":has";
|
|
4027
|
+
p.walkClasses((inner) => {
|
|
4028
|
+
const token = inner.value;
|
|
4029
|
+
this.#known.add(token);
|
|
4030
|
+
const u = this.#getUsage(token);
|
|
4031
|
+
u.referenced = true;
|
|
4032
|
+
u.loadBearing = true;
|
|
4033
|
+
if (isHas) u.asHasArgument = true;
|
|
4034
|
+
});
|
|
4035
|
+
}
|
|
4036
|
+
});
|
|
4037
|
+
if (hasCombinator || hasStructural) {
|
|
4038
|
+
complex.add(selector.toString().trim());
|
|
4039
|
+
}
|
|
4040
|
+
}
|
|
4041
|
+
#addRuleEntry(token, entry) {
|
|
4042
|
+
const list = this.#classIndex.get(token);
|
|
4043
|
+
if (list) list.push(entry);
|
|
4044
|
+
else this.#classIndex.set(token, [entry]);
|
|
4045
|
+
}
|
|
4046
|
+
#getUsage(token) {
|
|
4047
|
+
let u = this.#usage.get(token);
|
|
4048
|
+
if (!u) {
|
|
4049
|
+
u = {
|
|
4050
|
+
referenced: false,
|
|
4051
|
+
asSubject: false,
|
|
4052
|
+
asAncestor: false,
|
|
4053
|
+
asCompound: false,
|
|
4054
|
+
asSibling: false,
|
|
4055
|
+
asHasArgument: false,
|
|
4056
|
+
asStructural: false,
|
|
4057
|
+
loadBearing: false
|
|
4058
|
+
};
|
|
4059
|
+
this.#usage.set(token, u);
|
|
4060
|
+
}
|
|
4061
|
+
return u;
|
|
4062
|
+
}
|
|
4063
|
+
/**
|
|
4064
|
+
* Resolve a set of tokens into a normalized condition-keyed StyleMap. `tokenList` is the original
|
|
4065
|
+
* class list (for per-declaration `tokenIndex` provenance); `request` is the set being resolved.
|
|
4066
|
+
*/
|
|
4067
|
+
#resolveTokens(request, tokenList) {
|
|
4068
|
+
const entries = [];
|
|
4069
|
+
for (const token of new Set(request)) {
|
|
4070
|
+
const list = this.#classIndex.get(token);
|
|
4071
|
+
if (list) entries.push(...list);
|
|
4072
|
+
}
|
|
4073
|
+
if (entries.length === 0) return emptyStyleMap();
|
|
4074
|
+
entries.sort((a, b) => a.order - b.order);
|
|
4075
|
+
const acc = /* @__PURE__ */ new Map();
|
|
4076
|
+
for (const entry of entries) {
|
|
4077
|
+
const key = conditionKey(entry.condition);
|
|
4078
|
+
let block = acc.get(key);
|
|
4079
|
+
if (!block) {
|
|
4080
|
+
block = { condition: entry.condition, decls: /* @__PURE__ */ new Map() };
|
|
4081
|
+
acc.set(key, block);
|
|
4082
|
+
}
|
|
4083
|
+
const tokenIndex = tokenList.indexOf(entry.token);
|
|
4084
|
+
const origin = { kind: "class", tokenIndex, className: entry.token };
|
|
4085
|
+
for (const [prop, value, important] of entry.decls) {
|
|
4086
|
+
for (const decl of normalizer.normalizeDeclaration(prop, value, important)) {
|
|
4087
|
+
block.decls.set(decl.property, { ...decl, origin });
|
|
4088
|
+
}
|
|
4089
|
+
}
|
|
4090
|
+
}
|
|
4091
|
+
const rawBlocks = /* @__PURE__ */ new Map();
|
|
4092
|
+
for (const [key, block] of acc) {
|
|
4093
|
+
if (block.decls.size === 0) continue;
|
|
4094
|
+
rawBlocks.set(key, { condition: block.condition, decls: block.decls });
|
|
4095
|
+
}
|
|
4096
|
+
if (rawBlocks.size === 0) return emptyStyleMap();
|
|
4097
|
+
return normalizer.normalizeStyleMap({ blocks: rawBlocks });
|
|
4098
|
+
}
|
|
4099
|
+
/** Build (once) the reverse index used by {@link emit}. */
|
|
4100
|
+
#reverseIndex() {
|
|
4101
|
+
if (this.#reverse) return this.#reverse;
|
|
4102
|
+
const out = [];
|
|
4103
|
+
for (const token of this.#classIndex.keys()) {
|
|
4104
|
+
const styles = this.#resolveTokens([token], [token]);
|
|
4105
|
+
const keyed = /* @__PURE__ */ new Map();
|
|
4106
|
+
for (const [ck, block] of styles.blocks) {
|
|
4107
|
+
for (const [prop, decl] of block.decls) keyed.set(`${ck}\0${prop}`, String(decl.value));
|
|
4108
|
+
}
|
|
4109
|
+
if (keyed.size > 0) out.push({ token, keyed });
|
|
4110
|
+
}
|
|
4111
|
+
out.sort((a, b) => b.keyed.size - a.keyed.size);
|
|
4112
|
+
this.#reverse = out;
|
|
4113
|
+
return out;
|
|
4114
|
+
}
|
|
4115
|
+
};
|
|
4116
|
+
function createCssResolver(cssFiles = [], options) {
|
|
4117
|
+
return new CustomCSSResolver(cssFiles, options);
|
|
4118
|
+
}
|
|
4119
|
+
function splitCompounds(selector) {
|
|
4120
|
+
const compounds = [];
|
|
4121
|
+
let current = [];
|
|
4122
|
+
let leftCombinator = null;
|
|
4123
|
+
for (const node of selector.nodes) {
|
|
4124
|
+
if (sp.isCombinator(node)) {
|
|
4125
|
+
compounds.push({ leftCombinator, nodes: current });
|
|
4126
|
+
current = [];
|
|
4127
|
+
leftCombinator = combinatorValue(node);
|
|
4128
|
+
} else {
|
|
4129
|
+
current.push(node);
|
|
4130
|
+
}
|
|
4131
|
+
}
|
|
4132
|
+
compounds.push({ leftCombinator, nodes: current });
|
|
4133
|
+
return compounds;
|
|
4134
|
+
}
|
|
4135
|
+
function combinatorValue(node) {
|
|
4136
|
+
const v = node.value;
|
|
4137
|
+
return v.trim() === "" ? " " : v.trim();
|
|
4138
|
+
}
|
|
4139
|
+
function pseudoName(node) {
|
|
4140
|
+
return node.value.toLowerCase();
|
|
4141
|
+
}
|
|
4142
|
+
function isPseudoElement(node) {
|
|
4143
|
+
return sp.isPseudoElement(node) || LEGACY_PSEUDO_ELEMENTS2.has(pseudoName(node));
|
|
4144
|
+
}
|
|
4145
|
+
function normalizePseudoElement(node) {
|
|
4146
|
+
const name = pseudoName(node);
|
|
4147
|
+
return name.startsWith("::") ? name : `::${name.replace(/^:/, "")}`;
|
|
4148
|
+
}
|
|
4149
|
+
function mediaContext(rule) {
|
|
4150
|
+
const parts = [];
|
|
4151
|
+
let skip = false;
|
|
4152
|
+
let parent = rule.parent;
|
|
4153
|
+
while (parent && parent.type === "atrule") {
|
|
4154
|
+
const at = parent;
|
|
4155
|
+
const name = at.name.toLowerCase();
|
|
4156
|
+
if (name === "media") parts.unshift(at.params.trim().replace(/\s+/g, " "));
|
|
4157
|
+
else if (name === "keyframes" || name.endsWith("keyframes") || name === "font-face") skip = true;
|
|
4158
|
+
parent = parent.parent;
|
|
4159
|
+
}
|
|
4160
|
+
return { media: parts.join(" and "), skip };
|
|
4161
|
+
}
|
|
4162
|
+
function collectDecls(rule) {
|
|
4163
|
+
const out = [];
|
|
4164
|
+
for (const node of rule.nodes) {
|
|
4165
|
+
if (node.type === "decl") out.push([node.prop, node.value, node.important === true]);
|
|
4166
|
+
}
|
|
4167
|
+
return out;
|
|
4168
|
+
}
|
|
4169
|
+
function isPlainClassToken(token) {
|
|
4170
|
+
return token.length > 0 && !/[\s.#>+~:[\]()]/.test(token);
|
|
4171
|
+
}
|
|
4172
|
+
function readCssPath(path3) {
|
|
4173
|
+
try {
|
|
4174
|
+
return { id: path3, css: (0, import_node_fs.readFileSync)(path3, "utf8") };
|
|
4175
|
+
} catch (cause) {
|
|
4176
|
+
throw new Error(`resolver-css: cannot read CSS file "${path3}"`, { cause });
|
|
4177
|
+
}
|
|
4178
|
+
}
|
|
4179
|
+
function deriveFingerprint(provider, files) {
|
|
4180
|
+
const parts = files.map((f) => `${f.id}:${f.css.length}`).sort();
|
|
4181
|
+
return `${provider}/${ENGINE_VERSION}::${parts.join("|")}`;
|
|
4182
|
+
}
|
|
4183
|
+
|
|
4184
|
+
// src/index.ts
|
|
4185
|
+
var import_node_path = require("path");
|
|
4186
|
+
var import_node_url = require("url");
|
|
4187
|
+
var DEFAULT_INCLUDE = [".jsx", ".tsx", ".html"];
|
|
4188
|
+
function resolveOptions(options) {
|
|
4189
|
+
return {
|
|
4190
|
+
provider: options.provider ?? "auto",
|
|
4191
|
+
cssFiles: options.cssFiles ?? [],
|
|
4192
|
+
dryRun: options.dryRun ?? false,
|
|
4193
|
+
safety: options.safety ?? 2,
|
|
4194
|
+
include: options.include ?? DEFAULT_INCLUDE
|
|
4195
|
+
};
|
|
4196
|
+
}
|
|
4197
|
+
function isSupported(id, include) {
|
|
4198
|
+
const clean = id.split("?", 1)[0] ?? id;
|
|
4199
|
+
return include.some((ext) => clean.endsWith(ext));
|
|
4200
|
+
}
|
|
4201
|
+
function jsxKindOf(id) {
|
|
4202
|
+
const clean = id.split("?", 1)[0] ?? id;
|
|
4203
|
+
if (clean.endsWith(".tsx")) return "tsx";
|
|
4204
|
+
if (clean.endsWith(".jsx")) return "jsx";
|
|
4205
|
+
return null;
|
|
4206
|
+
}
|
|
4207
|
+
function eolOf2(doc) {
|
|
4208
|
+
for (const src of doc.sources.values()) return src.eol;
|
|
4209
|
+
return "\n";
|
|
4210
|
+
}
|
|
4211
|
+
function buildPasses(patterns) {
|
|
4212
|
+
const byPhase = /* @__PURE__ */ new Map();
|
|
4213
|
+
for (const p of patterns) {
|
|
4214
|
+
const phase = p.category.split("/", 1)[0] ?? "flatten";
|
|
4215
|
+
let bucket = byPhase.get(phase);
|
|
4216
|
+
if (!bucket) {
|
|
4217
|
+
bucket = [];
|
|
4218
|
+
byPhase.set(phase, bucket);
|
|
4219
|
+
}
|
|
4220
|
+
bucket.push(p);
|
|
4221
|
+
}
|
|
4222
|
+
const passes = [];
|
|
4223
|
+
for (const [phase, pats] of byPhase) {
|
|
4224
|
+
passes.push({ phase, category: `${phase}/builtin`, patterns: pats });
|
|
4225
|
+
}
|
|
4226
|
+
return passes;
|
|
4227
|
+
}
|
|
4228
|
+
function runJsxPipeline(code, id, kind, resolver, patterns, safety) {
|
|
4229
|
+
const parsed = createJsxFrontend().parse(code, {
|
|
4230
|
+
id,
|
|
4231
|
+
kind,
|
|
4232
|
+
resolver,
|
|
4233
|
+
normalizer,
|
|
4234
|
+
config: {},
|
|
4235
|
+
onDiagnostic: () => {
|
|
4236
|
+
}
|
|
4237
|
+
});
|
|
4238
|
+
const doc = parsed.doc;
|
|
4239
|
+
for (const node of doc.nodes.values()) node.meta.safetyFloor = 3;
|
|
4240
|
+
const ctx = {
|
|
4241
|
+
doc,
|
|
4242
|
+
safetyCeiling: safety,
|
|
4243
|
+
normalizer,
|
|
4244
|
+
// Real CSS-selector-safety index from the active resolver: a wrapper a combinator/structural
|
|
4245
|
+
// selector depends on is flagged so the flatten guards refuse to flatten it. Tailwind (no
|
|
4246
|
+
// complexSelectors) degrades to the null index — behaviour unchanged.
|
|
4247
|
+
selectors: buildSelectorIndex(doc, resolver),
|
|
4248
|
+
resolver
|
|
4249
|
+
};
|
|
4250
|
+
const { doc: optimized } = runPasses(doc, buildPasses(patterns), ctx);
|
|
4251
|
+
syncClassesFromComputed(optimized, resolver, normalizer);
|
|
4252
|
+
const printed = createJsxBackend().print(
|
|
4253
|
+
optimized,
|
|
4254
|
+
{ moduleId: id, ops: [], provenance: /* @__PURE__ */ new Map() },
|
|
4255
|
+
{
|
|
4256
|
+
normalizer,
|
|
4257
|
+
resolver,
|
|
4258
|
+
sink: createSyntheticSink(),
|
|
4259
|
+
eol: eolOf2(optimized),
|
|
4260
|
+
onDiagnostic: () => {
|
|
4261
|
+
}
|
|
4262
|
+
}
|
|
4263
|
+
);
|
|
4264
|
+
return printed.code;
|
|
4265
|
+
}
|
|
4266
|
+
function createResolver(resolved) {
|
|
4267
|
+
if (resolved.provider === "custom") {
|
|
4268
|
+
return createCssResolver([], { files: resolved.cssFiles });
|
|
4269
|
+
}
|
|
4270
|
+
return createTailwindResolver();
|
|
4271
|
+
}
|
|
4272
|
+
function createDomflax(options = {}) {
|
|
4273
|
+
const resolved = resolveOptions(options);
|
|
4274
|
+
const pipeline = createPipeline();
|
|
4275
|
+
const patterns = builtinPatterns;
|
|
4276
|
+
let cachedResolver = null;
|
|
4277
|
+
const getResolver = () => cachedResolver ??= createResolver(resolved);
|
|
4278
|
+
return {
|
|
4279
|
+
options: resolved,
|
|
4280
|
+
pipeline,
|
|
4281
|
+
get resolver() {
|
|
4282
|
+
return getResolver();
|
|
4283
|
+
},
|
|
4284
|
+
patterns,
|
|
4285
|
+
transform(code, id) {
|
|
4286
|
+
if (!isSupported(id, resolved.include)) return { code, map: null };
|
|
4287
|
+
const kind = jsxKindOf(id);
|
|
4288
|
+
if (kind === null) return { code, map: null };
|
|
4289
|
+
const out = runJsxPipeline(code, id, kind, getResolver(), patterns, resolved.safety);
|
|
4290
|
+
return { code: out, map: null };
|
|
4291
|
+
}
|
|
4292
|
+
};
|
|
4293
|
+
}
|
|
4294
|
+
function vite(options = {}) {
|
|
4295
|
+
const engine = createDomflax(options);
|
|
4296
|
+
return {
|
|
4297
|
+
name: "domflax",
|
|
4298
|
+
enforce: "pre",
|
|
4299
|
+
transform(code, id) {
|
|
4300
|
+
if (!isSupported(id, engine.options.include)) return null;
|
|
4301
|
+
const out = engine.transform(code, id);
|
|
4302
|
+
return out.code === code ? null : out;
|
|
4303
|
+
}
|
|
4304
|
+
};
|
|
4305
|
+
}
|
|
4306
|
+
var WEBPACK_JSX_TEST = /\.[jt]sx$/;
|
|
4307
|
+
function webpackLoaderPath() {
|
|
4308
|
+
const here = (0, import_node_path.dirname)((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
4309
|
+
return (0, import_node_path.join)(here, "webpack-loader.cjs");
|
|
4310
|
+
}
|
|
4311
|
+
function webpack(options = {}) {
|
|
4312
|
+
createDomflax(options);
|
|
4313
|
+
return {
|
|
4314
|
+
name: "domflax",
|
|
4315
|
+
apply(compiler) {
|
|
4316
|
+
const host = compiler.options ?? compiler;
|
|
4317
|
+
const mod = host.module ??= {};
|
|
4318
|
+
const rules = mod.rules ??= [];
|
|
4319
|
+
const rule = {
|
|
4320
|
+
test: WEBPACK_JSX_TEST,
|
|
4321
|
+
enforce: "pre",
|
|
4322
|
+
exclude: /node_modules/,
|
|
4323
|
+
use: [{ loader: webpackLoaderPath(), options }]
|
|
4324
|
+
};
|
|
4325
|
+
rules.push(rule);
|
|
4326
|
+
}
|
|
4327
|
+
};
|
|
4328
|
+
}
|
|
4329
|
+
var domflax = { createDomflax, vite, webpack };
|
|
4330
|
+
var src_default = domflax;
|
|
4331
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
4332
|
+
0 && (module.exports = {
|
|
4333
|
+
BASE_CONDITION,
|
|
4334
|
+
BASE_CONDITION_KEY,
|
|
4335
|
+
DEFAULT_FIXPOINT,
|
|
4336
|
+
applyGroups,
|
|
4337
|
+
applyOps,
|
|
4338
|
+
buildSelectorIndex,
|
|
4339
|
+
builtinPatterns,
|
|
4340
|
+
childIds,
|
|
4341
|
+
cloneDocument,
|
|
4342
|
+
conditionKey,
|
|
4343
|
+
createBackrefTable,
|
|
4344
|
+
createComment,
|
|
4345
|
+
createDocument,
|
|
4346
|
+
createDomflax,
|
|
4347
|
+
createElement,
|
|
4348
|
+
createExpr,
|
|
4349
|
+
createExprRegistry,
|
|
4350
|
+
createFragment,
|
|
4351
|
+
createIdAllocator,
|
|
4352
|
+
createNullResolver,
|
|
4353
|
+
createNullSelectorIndex,
|
|
4354
|
+
createPassManager,
|
|
4355
|
+
createPipeline,
|
|
4356
|
+
createRewriteFactory,
|
|
4357
|
+
createSyntheticSink,
|
|
4358
|
+
createText,
|
|
4359
|
+
dedupeClasses,
|
|
4360
|
+
defaultMeta,
|
|
4361
|
+
elementIds,
|
|
4362
|
+
emptyAttrMap,
|
|
4363
|
+
emptyClassList,
|
|
4364
|
+
emptyInlineStyle,
|
|
4365
|
+
emptyStyleDiv,
|
|
4366
|
+
emptyStyleMap,
|
|
4367
|
+
flexCenterWrapper,
|
|
4368
|
+
getElement,
|
|
4369
|
+
getNode,
|
|
4370
|
+
insetShorthand,
|
|
4371
|
+
marginShorthand,
|
|
4372
|
+
nestedFlexMerge,
|
|
4373
|
+
paddingShorthand,
|
|
4374
|
+
passthroughWrapper,
|
|
4375
|
+
redundantFragment,
|
|
4376
|
+
runPasses,
|
|
4377
|
+
sizeShorthand,
|
|
4378
|
+
syncClassesFromComputed,
|
|
4379
|
+
vite,
|
|
4380
|
+
walk,
|
|
4381
|
+
webpack
|
|
4382
|
+
});
|
|
4383
|
+
//# sourceMappingURL=index.cjs.map
|