domflax 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +159 -0
- package/dist/{chunk-4HHISSMR.js → chunk-DNHOGPYV.js} +2675 -1503
- package/dist/chunk-DNHOGPYV.js.map +1 -0
- package/dist/{chunk-ZJ2S36GY.js → chunk-DOQEBGWB.js} +33 -20
- package/dist/chunk-DOQEBGWB.js.map +1 -0
- package/dist/{chunk-77SLHRN6.js → chunk-DWLB7FRR.js} +341 -176
- package/dist/chunk-DWLB7FRR.js.map +1 -0
- package/dist/cli.cjs +2169 -760
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +183 -91
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +3021 -1699
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +477 -54
- package/dist/index.d.ts +477 -54
- package/dist/index.js +49 -3
- package/dist/pattern-CV607P87.d.ts +547 -0
- package/dist/pattern-F5xBtIE-.d.cts +547 -0
- package/dist/pattern-kit.cjs +60 -39
- package/dist/pattern-kit.cjs.map +1 -1
- package/dist/pattern-kit.d.cts +3 -18
- package/dist/pattern-kit.d.ts +3 -18
- package/dist/pattern-kit.js +3 -1
- package/dist/pattern-kit.js.map +1 -1
- package/dist/{types-BQ7l6dVe.d.ts → resolve-ops-DIwEelH-.d.cts} +26 -251
- package/dist/{types-BQ7l6dVe.d.cts → resolve-ops-DIwEelH-.d.ts} +26 -251
- package/dist/verify.d.cts +1 -1
- package/dist/verify.d.ts +1 -1
- package/dist/webpack-loader.cjs +2975 -1699
- package/dist/webpack-loader.cjs.map +1 -1
- package/dist/webpack-loader.d.cts +2 -2
- package/dist/webpack-loader.d.ts +2 -2
- package/dist/webpack-loader.js +3 -3
- package/package.json +3 -6
- package/dist/chunk-4HHISSMR.js.map +0 -1
- package/dist/chunk-77SLHRN6.js.map +0 -1
- package/dist/chunk-ZJ2S36GY.js.map +0 -1
- package/dist/pattern-CX6iBzTD.d.ts +0 -237
- package/dist/pattern-P4FIKAUB.d.cts +0 -237
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
import { d as RewriteOp, e as IRDocument, f as SafetyLevel, c as StyleNormalizer, g as SelectorIndex, h as StyleResolver, I as IRNodeId, P as PatternName, D as DiagnosticCode, i as Diagnostic, F as FileKind, j as SyntheticSink, k as SourceSpan, l as PassPhase, m as PassCategory, n as NodeMeta, o as DeepReadonly, p as IRElement, S as StyleMap, N as NodeLike, E as ElementLike, q as IRNode, r as RewriteFactory, R as RewriteOpDraft, s as Reporter, t as StyleCondition, u as ConditionKey, v as IRNamespace, w as ClassList, x as InlineStyle, A as AttrMap, B as BackrefTable, y as Backref, z as IRComment, G as FrontendKind, H as ExprRef, J as IRExpr, K as ExprRegistry, L as IRFragment, M as IdAllocator, O as IRText, V as Visitor, a as StyleConflictPolicy } from './resolve-ops-DIwEelH-.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @domflax/core — type contract, part 3/3: the pattern contract + match/rewrite contexts, the pass
|
|
5
|
+
* manager + applier, frontends/backends, and the pure single-file pipeline.
|
|
6
|
+
*
|
|
7
|
+
* Pure type/interface declarations only: ZERO runtime. Depends on the IR/style primitives in `./ir`
|
|
8
|
+
* and the resolver/op types in `./resolve-ops`.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
type Captures = Record<string, unknown>;
|
|
12
|
+
/** Read-only style predicate over a normalized StyleMap (queries MEANING, not strings). */
|
|
13
|
+
type StylePredicate = (sm: StyleMap) => boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Relational precondition (review-2 blocker): describes ancestor/sibling/child subtree shape AND
|
|
16
|
+
* ancestor LAYOUT context (review-4 blocker) so fuzzProve generates trees that exercise the
|
|
17
|
+
* relational + parent-constraint branches, not just node-local ones.
|
|
18
|
+
*/
|
|
19
|
+
interface TreeShapeSketch {
|
|
20
|
+
readonly tag?: string | readonly string[];
|
|
21
|
+
readonly requiredComputed?: Readonly<Record<string, string>>;
|
|
22
|
+
readonly meta?: Partial<Record<keyof NodeMeta, boolean>>;
|
|
23
|
+
readonly children?: readonly TreeShapeSketch[];
|
|
24
|
+
}
|
|
25
|
+
type ParentLayoutContext = 'block-flow' | 'flex-item' | 'flex-item-stretch' | 'grid-item' | 'fixed-size-ancestor' | 'percentage-sized-child' | 'inline-context';
|
|
26
|
+
interface PreconditionSketch {
|
|
27
|
+
readonly requiredComputed?: Readonly<Record<string, string>>;
|
|
28
|
+
readonly childCount?: {
|
|
29
|
+
readonly min?: number;
|
|
30
|
+
readonly max?: number;
|
|
31
|
+
};
|
|
32
|
+
readonly forbid?: readonly string[];
|
|
33
|
+
readonly ancestor?: TreeShapeSketch;
|
|
34
|
+
readonly siblings?: readonly TreeShapeSketch[];
|
|
35
|
+
readonly childShapes?: readonly TreeShapeSketch[];
|
|
36
|
+
readonly parentContexts?: readonly ParentLayoutContext[];
|
|
37
|
+
}
|
|
38
|
+
interface PatternDoc {
|
|
39
|
+
readonly title: string;
|
|
40
|
+
readonly summary: string;
|
|
41
|
+
readonly before?: string;
|
|
42
|
+
readonly after?: string;
|
|
43
|
+
readonly safetyRationale?: string;
|
|
44
|
+
}
|
|
45
|
+
/** Result of a successful match: op drafts (origin stamped later) + optional captures/diagnostics. */
|
|
46
|
+
interface MatchResult<C extends Captures = Captures> {
|
|
47
|
+
readonly ops: readonly RewriteOpDraft[];
|
|
48
|
+
readonly captures?: C;
|
|
49
|
+
readonly diagnostics?: readonly Diagnostic[];
|
|
50
|
+
}
|
|
51
|
+
interface MatchContext {
|
|
52
|
+
readonly node: DeepReadonly<IRElement>;
|
|
53
|
+
readonly doc: DeepReadonly<IRDocument>;
|
|
54
|
+
readonly resolver: StyleResolver;
|
|
55
|
+
readonly selectors: SelectorIndex;
|
|
56
|
+
readonly safety: SafetyLevel;
|
|
57
|
+
readonly phase: PassPhase;
|
|
58
|
+
readonly iteration: number;
|
|
59
|
+
parent(): DeepReadonly<IRElement> | null;
|
|
60
|
+
elementChildren(): readonly DeepReadonly<IRElement>[];
|
|
61
|
+
onlyElementChild(): DeepReadonly<IRElement> | null;
|
|
62
|
+
computed(): StyleMap;
|
|
63
|
+
computedOf(n: NodeLike): StyleMap;
|
|
64
|
+
isOpaque(n?: ElementLike): boolean;
|
|
65
|
+
ancestors(): readonly DeepReadonly<IRElement>[];
|
|
66
|
+
closest(pred: (el: DeepReadonly<IRElement>) => boolean): DeepReadonly<IRElement> | null;
|
|
67
|
+
prevSibling(): DeepReadonly<IRNode> | null;
|
|
68
|
+
nextSibling(): DeepReadonly<IRNode> | null;
|
|
69
|
+
nthChildIndex(): number;
|
|
70
|
+
}
|
|
71
|
+
/** Context handed to the `rewrite` phase: a MatchContext plus the typed captures. */
|
|
72
|
+
interface RewriteContext<C extends Captures = Captures> extends MatchContext {
|
|
73
|
+
readonly captures: C;
|
|
74
|
+
}
|
|
75
|
+
interface Pattern {
|
|
76
|
+
readonly name: PatternName;
|
|
77
|
+
readonly category: PassCategory;
|
|
78
|
+
readonly safety: SafetyLevel;
|
|
79
|
+
readonly priority?: number;
|
|
80
|
+
readonly precondition?: PreconditionSketch;
|
|
81
|
+
readonly doc?: PatternDoc;
|
|
82
|
+
/** Pure. Returns a MatchResult (op drafts) or null on no-match. MUST NOT mutate. */
|
|
83
|
+
evaluate(ctx: MatchContext, rw: RewriteFactory): MatchResult | null;
|
|
84
|
+
}
|
|
85
|
+
interface Pass {
|
|
86
|
+
readonly phase: PassPhase;
|
|
87
|
+
readonly category: PassCategory;
|
|
88
|
+
readonly patterns: readonly Pattern[];
|
|
89
|
+
}
|
|
90
|
+
interface FixpointConfig {
|
|
91
|
+
readonly maxIterations: number;
|
|
92
|
+
readonly phases: Partial<Record<PassPhase, number>>;
|
|
93
|
+
readonly onBudgetExhausted: 'warn' | 'error';
|
|
94
|
+
readonly detectOscillation: boolean;
|
|
95
|
+
}
|
|
96
|
+
type HaltReason = 'converged' | 'budget' | 'oscillation' | 'error';
|
|
97
|
+
interface PhaseRunResult {
|
|
98
|
+
readonly phase: PassPhase;
|
|
99
|
+
readonly iterations: number;
|
|
100
|
+
readonly converged: boolean;
|
|
101
|
+
readonly haltReason: HaltReason;
|
|
102
|
+
readonly touched: ReadonlySet<IRNodeId>;
|
|
103
|
+
readonly diagnostics: readonly Diagnostic[];
|
|
104
|
+
}
|
|
105
|
+
interface RewriteGroup {
|
|
106
|
+
readonly pattern: PatternName;
|
|
107
|
+
readonly anchor: IRNodeId;
|
|
108
|
+
readonly ops: readonly RewriteOp[];
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* VERIFIER-GATED flatten policy (the "identical UI" safety guarantee).
|
|
112
|
+
*
|
|
113
|
+
* • `'all'` — commit every flatten the patterns produce (subject only to the existing
|
|
114
|
+
* emittability revert). The historical behaviour; the pattern auto-test
|
|
115
|
+
* harness runs in this mode so authored examples are validated in isolation.
|
|
116
|
+
* • `'provably-safe'` — commit ONLY flattens that provably change nothing renderable (the wrapper
|
|
117
|
+
* contributes no own box/formatting context and drops no style, and the
|
|
118
|
+
* rewrite makes no parent-context assumption). Every other flatten is
|
|
119
|
+
* reverted via the inverse journal. This is the default for the `domflax`
|
|
120
|
+
* orchestrator + CLI: domflax never changes rendering by default, and it is
|
|
121
|
+
* the ONLY user-facing behaviour — the transform is fully static and never
|
|
122
|
+
* launches a browser.
|
|
123
|
+
*/
|
|
124
|
+
type FlattenGate = 'all' | 'provably-safe';
|
|
125
|
+
interface ApplyContext {
|
|
126
|
+
readonly doc: IRDocument;
|
|
127
|
+
readonly safetyCeiling: SafetyLevel;
|
|
128
|
+
readonly normalizer: StyleNormalizer;
|
|
129
|
+
readonly selectors: SelectorIndex;
|
|
130
|
+
readonly resolver: StyleResolver;
|
|
131
|
+
/** Flatten safety policy. Defaults to `'all'` when omitted (historical behaviour). */
|
|
132
|
+
readonly gate?: FlattenGate;
|
|
133
|
+
}
|
|
134
|
+
interface StructuralInverse {
|
|
135
|
+
readonly kind: 'structural';
|
|
136
|
+
readonly describe: string;
|
|
137
|
+
readonly snapshot: unknown;
|
|
138
|
+
}
|
|
139
|
+
interface AppliedOp {
|
|
140
|
+
readonly op: RewriteOp;
|
|
141
|
+
readonly inverse: RewriteOp | StructuralInverse;
|
|
142
|
+
}
|
|
143
|
+
interface OpValidationIssue {
|
|
144
|
+
readonly op: RewriteOp;
|
|
145
|
+
readonly code: DiagnosticCode;
|
|
146
|
+
readonly message: string;
|
|
147
|
+
}
|
|
148
|
+
interface SkippedOpGroup {
|
|
149
|
+
readonly group: RewriteGroup;
|
|
150
|
+
readonly issues: readonly OpValidationIssue[];
|
|
151
|
+
}
|
|
152
|
+
interface ApplyResult {
|
|
153
|
+
readonly touched: ReadonlySet<IRNodeId>;
|
|
154
|
+
readonly removed: ReadonlySet<IRNodeId>;
|
|
155
|
+
readonly created: ReadonlySet<IRNodeId>;
|
|
156
|
+
readonly appliedGroups: number;
|
|
157
|
+
readonly skipped: readonly SkippedOpGroup[];
|
|
158
|
+
readonly journal: readonly AppliedOp[];
|
|
159
|
+
readonly diagnostics: readonly Diagnostic[];
|
|
160
|
+
}
|
|
161
|
+
interface PassManager {
|
|
162
|
+
run(doc: IRDocument, ctx: ApplyContext, config?: FixpointConfig): readonly PhaseRunResult[];
|
|
163
|
+
}
|
|
164
|
+
interface FrontendConfig {
|
|
165
|
+
readonly jsxImportSource?: string;
|
|
166
|
+
readonly preserveComments?: boolean;
|
|
167
|
+
readonly [key: string]: unknown;
|
|
168
|
+
}
|
|
169
|
+
interface FrontendParseContext {
|
|
170
|
+
readonly id: string;
|
|
171
|
+
readonly kind: FileKind;
|
|
172
|
+
readonly resolver: StyleResolver;
|
|
173
|
+
readonly normalizer: StyleNormalizer;
|
|
174
|
+
readonly config: FrontendConfig;
|
|
175
|
+
onDiagnostic(d: Diagnostic): void;
|
|
176
|
+
babelAst?: unknown;
|
|
177
|
+
}
|
|
178
|
+
interface ParseResult {
|
|
179
|
+
readonly doc: IRDocument;
|
|
180
|
+
readonly diagnostics: readonly Diagnostic[];
|
|
181
|
+
}
|
|
182
|
+
interface Frontend {
|
|
183
|
+
readonly name: string;
|
|
184
|
+
readonly langs: readonly FileKind[];
|
|
185
|
+
canParse(id: string, code: string): boolean;
|
|
186
|
+
parse(code: string, ctx: FrontendParseContext): ParseResult;
|
|
187
|
+
}
|
|
188
|
+
interface ReindentSpec {
|
|
189
|
+
readonly baseIndent: string;
|
|
190
|
+
readonly delta: number;
|
|
191
|
+
}
|
|
192
|
+
interface TextEdit {
|
|
193
|
+
readonly span: SourceSpan;
|
|
194
|
+
readonly replacement: string;
|
|
195
|
+
readonly reindent?: ReindentSpec;
|
|
196
|
+
readonly origin: string;
|
|
197
|
+
}
|
|
198
|
+
interface EditPlan {
|
|
199
|
+
readonly moduleId: string;
|
|
200
|
+
readonly ops: readonly RewriteOp[];
|
|
201
|
+
readonly provenance: ReadonlyMap<number, PatternName>;
|
|
202
|
+
}
|
|
203
|
+
interface EncodedSourceMap {
|
|
204
|
+
readonly version: 3;
|
|
205
|
+
readonly sources: readonly string[];
|
|
206
|
+
readonly sourcesContent?: readonly (string | null)[];
|
|
207
|
+
readonly names: readonly string[];
|
|
208
|
+
readonly mappings: string;
|
|
209
|
+
readonly file?: string;
|
|
210
|
+
}
|
|
211
|
+
interface BackendContext {
|
|
212
|
+
readonly normalizer: StyleNormalizer;
|
|
213
|
+
readonly resolver: StyleResolver;
|
|
214
|
+
readonly sink: SyntheticSink;
|
|
215
|
+
readonly eol: '\n' | '\r\n';
|
|
216
|
+
onDiagnostic(d: Diagnostic): void;
|
|
217
|
+
}
|
|
218
|
+
interface CodegenResult {
|
|
219
|
+
readonly code: string;
|
|
220
|
+
readonly map: EncodedSourceMap | null;
|
|
221
|
+
readonly edits: readonly TextEdit[];
|
|
222
|
+
readonly diagnostics: readonly Diagnostic[];
|
|
223
|
+
}
|
|
224
|
+
interface Backend {
|
|
225
|
+
readonly name: string;
|
|
226
|
+
readonly langs: readonly FileKind[];
|
|
227
|
+
print(doc: IRDocument, plan: EditPlan, ctx: BackendContext): CodegenResult;
|
|
228
|
+
}
|
|
229
|
+
interface PipelineConfig {
|
|
230
|
+
readonly safety: SafetyLevel;
|
|
231
|
+
readonly fixpoint?: Partial<FixpointConfig>;
|
|
232
|
+
readonly preserveComments?: boolean;
|
|
233
|
+
readonly emitSourceMap?: boolean;
|
|
234
|
+
}
|
|
235
|
+
interface PipelineInput {
|
|
236
|
+
readonly code: string;
|
|
237
|
+
readonly id: string;
|
|
238
|
+
readonly kind: FileKind;
|
|
239
|
+
readonly frontend: Frontend;
|
|
240
|
+
readonly backend: Backend;
|
|
241
|
+
readonly resolver: StyleResolver;
|
|
242
|
+
readonly normalizer: StyleNormalizer;
|
|
243
|
+
readonly passes: readonly Pass[];
|
|
244
|
+
readonly config?: PipelineConfig;
|
|
245
|
+
readonly reporter?: Reporter;
|
|
246
|
+
readonly babelAst?: unknown;
|
|
247
|
+
}
|
|
248
|
+
interface PipelineStats {
|
|
249
|
+
readonly nodesIn: number;
|
|
250
|
+
readonly nodesOut: number;
|
|
251
|
+
readonly opsApplied: number;
|
|
252
|
+
readonly iterations: Readonly<Record<PassPhase, number>>;
|
|
253
|
+
readonly durationMs: number;
|
|
254
|
+
}
|
|
255
|
+
interface PipelineOutput {
|
|
256
|
+
readonly code: string;
|
|
257
|
+
readonly map: EncodedSourceMap | null;
|
|
258
|
+
readonly changed: boolean;
|
|
259
|
+
readonly touched: readonly SourceSpan[];
|
|
260
|
+
readonly diagnostics: readonly Diagnostic[];
|
|
261
|
+
readonly stats: PipelineStats;
|
|
262
|
+
readonly doc: IRDocument;
|
|
263
|
+
readonly editPlan: EditPlan;
|
|
264
|
+
}
|
|
265
|
+
/** The pure single-file pipeline. Adapters/orchestrator call this; the verifier reuses it. */
|
|
266
|
+
interface Pipeline {
|
|
267
|
+
run(input: PipelineInput): PipelineOutput;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* @domflax/core — runtime builders + traversal.
|
|
272
|
+
*
|
|
273
|
+
* Pure, dependency-free helpers to construct IR nodes, assemble an {@link IRDocument},
|
|
274
|
+
* and walk the tree honouring {@link VisitSignal}. No heavy third-party deps: only the
|
|
275
|
+
* type contract in `./types`.
|
|
276
|
+
*/
|
|
277
|
+
|
|
278
|
+
/** Monotonic IRNodeId allocator. `peek` reports the id `next()` would return. */
|
|
279
|
+
declare function createIdAllocator(start?: number): IdAllocator;
|
|
280
|
+
/** Minimal in-memory ExprRegistry. */
|
|
281
|
+
declare function createExprRegistry(start?: number): ExprRegistry;
|
|
282
|
+
/** Mutable BackrefTable: frontends register backrefs as they parse. */
|
|
283
|
+
interface MutableBackrefTable extends BackrefTable {
|
|
284
|
+
set(id: IRNodeId, backref: Backref): void;
|
|
285
|
+
}
|
|
286
|
+
declare function createBackrefTable(): MutableBackrefTable;
|
|
287
|
+
/** A NodeMeta with every barrier/flag cleared. */
|
|
288
|
+
declare function defaultMeta(safetyFloor?: SafetyLevel): NodeMeta;
|
|
289
|
+
/** Canonical base style condition (`media:'' states:[] pseudoElement:''`). */
|
|
290
|
+
declare const BASE_CONDITION: StyleCondition;
|
|
291
|
+
/** Stable serialization of a StyleCondition into a ConditionKey. */
|
|
292
|
+
declare function conditionKey(c: StyleCondition): ConditionKey;
|
|
293
|
+
declare const BASE_CONDITION_KEY: ConditionKey;
|
|
294
|
+
/** An empty StyleMap (no blocks). */
|
|
295
|
+
declare function emptyStyleMap(): StyleMap;
|
|
296
|
+
/** An empty (absent) ClassList. */
|
|
297
|
+
declare function emptyClassList(): ClassList;
|
|
298
|
+
/** An empty AttrMap. */
|
|
299
|
+
declare function emptyAttrMap(): AttrMap;
|
|
300
|
+
/** An empty InlineStyle. */
|
|
301
|
+
declare function emptyInlineStyle(): InlineStyle;
|
|
302
|
+
interface ElementInit {
|
|
303
|
+
readonly tag: string;
|
|
304
|
+
readonly namespace?: IRNamespace;
|
|
305
|
+
readonly isComponent?: boolean;
|
|
306
|
+
readonly selfClosing?: boolean;
|
|
307
|
+
readonly classes?: ClassList;
|
|
308
|
+
readonly inlineStyle?: InlineStyle;
|
|
309
|
+
readonly computed?: StyleMap;
|
|
310
|
+
readonly attrs?: AttrMap;
|
|
311
|
+
readonly children?: IRNodeId[];
|
|
312
|
+
readonly parent?: IRNodeId | null;
|
|
313
|
+
readonly span?: SourceSpan | null;
|
|
314
|
+
readonly meta?: NodeMeta;
|
|
315
|
+
}
|
|
316
|
+
declare function createElement(id: IRNodeId, init: ElementInit): IRElement;
|
|
317
|
+
declare function createText(id: IRNodeId, value: string, opts?: {
|
|
318
|
+
collapsible?: boolean;
|
|
319
|
+
parent?: IRNodeId | null;
|
|
320
|
+
span?: SourceSpan | null;
|
|
321
|
+
}): IRText;
|
|
322
|
+
declare function createExpr(id: IRNodeId, expr: ExprRef, opts?: {
|
|
323
|
+
parent?: IRNodeId | null;
|
|
324
|
+
span?: SourceSpan | null;
|
|
325
|
+
}): IRExpr;
|
|
326
|
+
declare function createFragment(id: IRNodeId, opts?: {
|
|
327
|
+
children?: IRNodeId[];
|
|
328
|
+
parent?: IRNodeId | null;
|
|
329
|
+
span?: SourceSpan | null;
|
|
330
|
+
}): IRFragment;
|
|
331
|
+
declare function createComment(id: IRNodeId, value: string, opts?: {
|
|
332
|
+
parent?: IRNodeId | null;
|
|
333
|
+
span?: SourceSpan | null;
|
|
334
|
+
}): IRComment;
|
|
335
|
+
/** Build an empty document whose root is a fresh fragment. */
|
|
336
|
+
declare function createDocument(frontend: FrontendKind): IRDocument;
|
|
337
|
+
/** Returns the child id list for container nodes, or an empty array. */
|
|
338
|
+
declare function childIds(node: IRNode): readonly IRNodeId[];
|
|
339
|
+
/** Returns the node, or undefined. */
|
|
340
|
+
declare function getNode(doc: IRDocument, id: IRNodeId): IRNode | undefined;
|
|
341
|
+
/** Returns the node iff it is an element. */
|
|
342
|
+
declare function getElement(doc: IRDocument, id: IRNodeId): IRElement | undefined;
|
|
343
|
+
/** Pre-order list of every element id reachable from the root. */
|
|
344
|
+
declare function elementIds(doc: IRDocument): IRNodeId[];
|
|
345
|
+
/**
|
|
346
|
+
* Depth-first pre/post-order walk. `enter` may return `'skip'` (don't descend) or `'stop'`
|
|
347
|
+
* (abort the whole walk); `exit` may return `'stop'`. The visitor receives a live {@link IRNode}
|
|
348
|
+
* plus a {@link VisitContext} exposing depth and the parent node.
|
|
349
|
+
*/
|
|
350
|
+
declare function walk(doc: IRDocument, visitor: Visitor): void;
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* @domflax/pattern-kit — composable matcher vocabulary.
|
|
354
|
+
*
|
|
355
|
+
* A {@link Matcher} is a PURE predicate over a node + its {@link MatchContext}. Matchers never
|
|
356
|
+
* mutate; they only read the (DeepReadonly) IR and the precomputed targeting/selector facts the
|
|
357
|
+
* context exposes. Authors compose them with {@link and}/{@link or}/{@link not} and feed the
|
|
358
|
+
* result into a pattern's `evaluate`.
|
|
359
|
+
*
|
|
360
|
+
* Style-aware matchers (`computed`, `hasOwnVisualStyle`) reason over the NORMALIZED StyleMap via
|
|
361
|
+
* the shared normalizer in `./normalize`, so they query meaning, not raw CSS strings.
|
|
362
|
+
*/
|
|
363
|
+
|
|
364
|
+
/** A pure predicate: does `node` satisfy this condition in the given match context? */
|
|
365
|
+
type Matcher = (node: NodeLike, ctx: MatchContext) => boolean;
|
|
366
|
+
/** Logical AND. Empty list ⇒ always matches. Short-circuits on the first failure. */
|
|
367
|
+
declare function and(...matchers: readonly Matcher[]): Matcher;
|
|
368
|
+
/** Logical OR. Empty list ⇒ never matches. Short-circuits on the first success. */
|
|
369
|
+
declare function or(...matchers: readonly Matcher[]): Matcher;
|
|
370
|
+
/** Logical NOT. */
|
|
371
|
+
declare function not(matcher: Matcher): Matcher;
|
|
372
|
+
/** Matches any element; with `tag`, only elements whose (case-insensitive) tag equals it. */
|
|
373
|
+
declare function isElement(tag?: string): Matcher;
|
|
374
|
+
/** Matches an element with exactly one ELEMENT child (text/expr/comment children ignored). */
|
|
375
|
+
declare const hasSingleElementChild: Matcher;
|
|
376
|
+
/**
|
|
377
|
+
* Matches when the node's computed StyleMap is a SUPERSET of `partial` — i.e. every declaration
|
|
378
|
+
* in `partial` is present in `node.computed` with an equal normalized value. Comparison is
|
|
379
|
+
* meaning-based (both sides normalized first). Empty `partial` always matches.
|
|
380
|
+
*/
|
|
381
|
+
declare function computed(partial: StyleMap): Matcher;
|
|
382
|
+
/**
|
|
383
|
+
* Matches when the element paints something of its own: a meaningful background, border, shadow,
|
|
384
|
+
* outline, filter, etc. across ANY style condition. Honours the frontend-set `meta.hasOwnVisualStyle`
|
|
385
|
+
* fast-path, then falls back to scanning the normalized computed StyleMap.
|
|
386
|
+
*/
|
|
387
|
+
declare const hasOwnVisualStyle: Matcher;
|
|
388
|
+
/** Element carries a `ref` (hard opacity barrier). */
|
|
389
|
+
declare const hasRef: Matcher;
|
|
390
|
+
/** Element has event handlers (onClick, …). */
|
|
391
|
+
declare const hasEventHandlers: Matcher;
|
|
392
|
+
/** Element has dynamic children (mapped/conditional islands). */
|
|
393
|
+
declare const hasDynamicChildren: Matcher;
|
|
394
|
+
/** Element's class list contains a dynamic segment (template/expr) → not freely rewritable. */
|
|
395
|
+
declare const hasDynamicClasses: Matcher;
|
|
396
|
+
/**
|
|
397
|
+
* Element's class list is wholly dynamic / spread-derived (`classes.opaque`, or spread attrs) — its
|
|
398
|
+
* concrete tokens can't be seen or statically rewritten, so a class-rewriting (compress) pattern
|
|
399
|
+
* must decline. Delegates to the context's authoritative {@link MatchContext.isOpaque}.
|
|
400
|
+
*/
|
|
401
|
+
declare const opaque: Matcher;
|
|
402
|
+
/**
|
|
403
|
+
* Element is the subject of a combinator selector (`>`/`+`/`~`). Honours the frontend-set meta
|
|
404
|
+
* flag and the precomputed {@link SelectorIndex} in the context.
|
|
405
|
+
*/
|
|
406
|
+
declare const targetedByCombinator: Matcher;
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* @domflax/pattern-kit — `definePattern()`: THE declarative pattern-authoring surface.
|
|
410
|
+
*
|
|
411
|
+
* `definePattern(config)` is the single public way to author a rewrite pattern: definition AND its
|
|
412
|
+
* tests are co-located in one call. It compiles down to the private lower-level
|
|
413
|
+
* {@link import('./define').validatePattern}/{@link Pattern} contract (it never replaces the
|
|
414
|
+
* engine). Authors describe the match as a plain DATA object and the rewrite as a named RECIPE; this
|
|
415
|
+
* module maps each key to the existing matcher combinators and op-draft factories, auto-applies the
|
|
416
|
+
* phase-appropriate safety guards (the full opacity-barrier + selector set for `flatten/*`; the
|
|
417
|
+
* narrower class-rewrite-safety set for `compress/*`) so authors never hand-write them, and threads
|
|
418
|
+
* `doc`/`test` through. Two escape hatches — a `match` predicate and a `rewrite` function — keep
|
|
419
|
+
* exotic patterns (e.g. ones anchored on a parent fragment) expressible.
|
|
420
|
+
*
|
|
421
|
+
* The co-located {@link PatternTest} (`provider`/`cssFiles`/`cases`/`noMatch`/`custom`) is carried on
|
|
422
|
+
* the compiled {@link AuthoredPattern} as `.test`, where the generic harness (`./testing`) reads it:
|
|
423
|
+
* each `case` asserts `before → after`, each `noMatch` asserts the input is left unchanged, and the
|
|
424
|
+
* optional `custom` hook runs arbitrary assertions against the built transform.
|
|
425
|
+
*
|
|
426
|
+
* `style` blocks in the declarative match (and in `childGains`/`mergeStyle` recipes) are PLAIN
|
|
427
|
+
* objects (camelCase or kebab keys) auto-normalized into a superset StyleMap via the shared
|
|
428
|
+
* normalizer — authors never import the normalizer or hand-build a StyleMap.
|
|
429
|
+
*
|
|
430
|
+
* `pattern` remains exported as a DEPRECATED alias of `definePattern` for backward compatibility.
|
|
431
|
+
*/
|
|
432
|
+
|
|
433
|
+
/** A plain CSS style object: camelCase or kebab-case keys, string or number values. */
|
|
434
|
+
type PlainStyle = Readonly<Record<string, string | number>>;
|
|
435
|
+
/**
|
|
436
|
+
* Declarative match as DATA. Every key maps to one of the existing matcher combinators; an empty
|
|
437
|
+
* object matches any element. Use the `match` FUNCTION escape hatch for anything not expressible
|
|
438
|
+
* here (relational/ancestor/sibling shapes, parent-anchored patterns, …).
|
|
439
|
+
*/
|
|
440
|
+
interface DeclarativeMatch {
|
|
441
|
+
/** Restrict to a tag (case-insensitive). Omit to match any element. */
|
|
442
|
+
readonly tag?: string;
|
|
443
|
+
/** Computed style the node must be a SUPERSET of (plain object, auto-normalized). */
|
|
444
|
+
readonly style?: PlainStyle;
|
|
445
|
+
/** Require exactly one ELEMENT child. */
|
|
446
|
+
readonly onlyChild?: 'element';
|
|
447
|
+
/** Require the element to paint nothing of its own (no own visual style). */
|
|
448
|
+
readonly paintsNothing?: boolean;
|
|
449
|
+
/** Extra, hand-written predicate AND-ed into the declarative match. */
|
|
450
|
+
readonly where?: Matcher | readonly Matcher[];
|
|
451
|
+
}
|
|
452
|
+
/** Escape hatch: a raw match predicate (no auto-guards are added). */
|
|
453
|
+
type MatchFn = (node: NodeLike, ctx: MatchContext) => boolean;
|
|
454
|
+
/**
|
|
455
|
+
* Flatten recipe: fold inherited styles onto the sole element child (default on), optionally merge
|
|
456
|
+
* `childGains` onto it, then unwrap the node (id-preserving). Mirrors the flatten exemplars.
|
|
457
|
+
*/
|
|
458
|
+
interface FlattenIntoRecipe {
|
|
459
|
+
readonly flattenInto: 'child';
|
|
460
|
+
/** Plain style merged onto the surviving child (source-wins) before unwrap. */
|
|
461
|
+
readonly childGains?: PlainStyle;
|
|
462
|
+
/** Fold inheritable declarations onto the child first. Default `true`. */
|
|
463
|
+
readonly foldInherited?: boolean;
|
|
464
|
+
}
|
|
465
|
+
/** Compress recipe: rebuild the element's class StyleMap; return `null` to decline. */
|
|
466
|
+
interface RewriteClassesRecipe {
|
|
467
|
+
readonly rewriteClasses: (computed: StyleMap, ctx: MatchContext) => StyleMap | null;
|
|
468
|
+
/** Keep opaque/selector-bound tokens verbatim. Default `true`. */
|
|
469
|
+
readonly preserveOpaque?: boolean;
|
|
470
|
+
}
|
|
471
|
+
/** Compress recipe: drop fully-overridden class tokens (provenance is pruned automatically). */
|
|
472
|
+
interface DropClassesRecipe {
|
|
473
|
+
readonly dropClasses: (computed: StyleMap, ctx: MatchContext) => Iterable<string>;
|
|
474
|
+
/** Keep opaque/selector-bound tokens verbatim. Default `true`. */
|
|
475
|
+
readonly preserveOpaque?: boolean;
|
|
476
|
+
}
|
|
477
|
+
/** Merge a literal plain style onto the matched element. */
|
|
478
|
+
interface MergeStyleRecipe {
|
|
479
|
+
readonly mergeStyle: PlainStyle;
|
|
480
|
+
readonly onConflict?: StyleConflictPolicy;
|
|
481
|
+
}
|
|
482
|
+
type RewriteRecipe = FlattenIntoRecipe | RewriteClassesRecipe | DropClassesRecipe | MergeStyleRecipe;
|
|
483
|
+
/** Escape hatch: a raw rewrite that returns op drafts (or `null`/`[]` for no-op). */
|
|
484
|
+
type RewriteFn = (ctx: MatchContext, rw: RewriteFactory) => readonly RewriteOpDraft[] | null;
|
|
485
|
+
/** A single positive before→after assertion run through the pattern's built transform. */
|
|
486
|
+
interface PatternTestCase {
|
|
487
|
+
readonly name?: string;
|
|
488
|
+
readonly before: string;
|
|
489
|
+
readonly after: string;
|
|
490
|
+
}
|
|
491
|
+
/** Helpers handed to a {@link PatternTest.custom} hook (the built transform + expectation sugar). */
|
|
492
|
+
interface TestHelpers {
|
|
493
|
+
/** Run the pattern's transform on `code` (default filename `'X.tsx'`). */
|
|
494
|
+
readonly transform: (code: string, filename?: string) => string;
|
|
495
|
+
/** Assert `before` transforms to `after` (whitespace-normalized). */
|
|
496
|
+
readonly expectTransforms: (before: string, after: string) => void;
|
|
497
|
+
/** Assert `code` is left unchanged (whitespace-normalized). */
|
|
498
|
+
readonly expectUnchanged: (code: string) => void;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Co-located test spec for a pattern. The generic harness (`./testing`) builds a transform for the
|
|
502
|
+
* declared `provider` (default `'tailwind'`; `'custom'` resolves the listed `cssFiles`), then runs
|
|
503
|
+
* every `case` (`before → after`), every `noMatch` (left unchanged), and the optional `custom` hook.
|
|
504
|
+
*/
|
|
505
|
+
interface PatternTest {
|
|
506
|
+
/** Which style provider the harness builds the transform from. Default `'tailwind'`. */
|
|
507
|
+
readonly provider?: 'tailwind' | 'custom';
|
|
508
|
+
/** For `provider: 'custom'` — the project stylesheet paths backing the CSS resolver. */
|
|
509
|
+
readonly cssFiles?: readonly string[];
|
|
510
|
+
/** Positive before→after assertions. */
|
|
511
|
+
readonly cases?: readonly PatternTestCase[];
|
|
512
|
+
/** Inputs the pattern must leave UNCHANGED (barriers, non-matching shapes, safety reverts). */
|
|
513
|
+
readonly noMatch?: readonly string[];
|
|
514
|
+
/** Arbitrary extra assertions against the built transform. */
|
|
515
|
+
readonly custom?: (h: TestHelpers) => void;
|
|
516
|
+
}
|
|
517
|
+
interface PatternConfig {
|
|
518
|
+
readonly name: string;
|
|
519
|
+
readonly category: PassCategory;
|
|
520
|
+
readonly safety: SafetyLevel;
|
|
521
|
+
readonly priority?: number;
|
|
522
|
+
readonly precondition?: PreconditionSketch;
|
|
523
|
+
readonly doc?: PatternDoc;
|
|
524
|
+
/** Co-located tests consumed by the generic harness (`./testing`). */
|
|
525
|
+
readonly test?: PatternTest;
|
|
526
|
+
/** Declarative match DATA, or a raw predicate escape hatch. Defaults to "any element". */
|
|
527
|
+
readonly match?: DeclarativeMatch | MatchFn;
|
|
528
|
+
/** A named rewrite recipe, or a raw op-draft factory escape hatch. */
|
|
529
|
+
readonly rewrite: RewriteRecipe | RewriteFn;
|
|
530
|
+
}
|
|
531
|
+
/** A {@link Pattern} that also carries its co-located {@link PatternTest} for the test harness. */
|
|
532
|
+
interface AuthoredPattern<C extends Captures = Captures> extends Pattern {
|
|
533
|
+
readonly test?: PatternTest;
|
|
534
|
+
evaluate(ctx: MatchContext, rw: RewriteFactory): MatchResult<C> | null;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* THE declarative pattern-authoring function. Compile a {@link PatternConfig} (definition + co-located
|
|
538
|
+
* {@link PatternTest}) into a validated {@link AuthoredPattern}: a normal {@link Pattern} (registerable
|
|
539
|
+
* into any {@link import('@domflax/core').Pass}) that also exposes its `test` for the generic harness.
|
|
540
|
+
*/
|
|
541
|
+
declare function definePattern(config: PatternConfig): AuthoredPattern;
|
|
542
|
+
/**
|
|
543
|
+
* @deprecated Use {@link definePattern}. Retained as a thin alias for backward compatibility.
|
|
544
|
+
*/
|
|
545
|
+
declare const pattern: typeof definePattern;
|
|
546
|
+
|
|
547
|
+
export { type Frontend as $, type AuthoredPattern as A, type ApplyContext as B, type MatchContext as C, type DeclarativeMatch as D, type FixpointConfig as E, type FlattenIntoRecipe as F, type Pass as G, type HaltReason as H, type PassManager as I, type Pattern as J, type PhaseRunResult as K, type Pipeline as L, type MatchFn as M, type Captures as N, type EncodedSourceMap as O, type PatternConfig as P, type AppliedOp as Q, type RewriteClassesRecipe as R, BASE_CONDITION as S, type TestHelpers as T, BASE_CONDITION_KEY as U, type Backend as V, type BackendContext as W, type CodegenResult as X, type EditPlan as Y, type ElementInit as Z, type FlattenGate as _, type DropClassesRecipe as a, type FrontendConfig as a0, type FrontendParseContext as a1, type MatchResult as a2, type MutableBackrefTable as a3, type OpValidationIssue as a4, type ParentLayoutContext as a5, type ParseResult as a6, type PatternDoc as a7, type PipelineConfig as a8, type PipelineInput as a9, getElement as aA, getNode as aB, walk as aC, type PipelineOutput as aa, type PipelineStats as ab, type PreconditionSketch as ac, type ReindentSpec as ad, type RewriteContext as ae, type SkippedOpGroup as af, type StructuralInverse as ag, type StylePredicate as ah, type TextEdit as ai, type TreeShapeSketch as aj, childIds as ak, conditionKey as al, createBackrefTable as am, createComment as an, createDocument as ao, createElement as ap, createExpr as aq, createExprRegistry as ar, createFragment as as, createIdAllocator as at, createText as au, defaultMeta as av, elementIds as aw, emptyAttrMap as ax, emptyClassList as ay, emptyInlineStyle as az, type Matcher as b, type MergeStyleRecipe as c, type PatternTest as d, type PatternTestCase as e, type PlainStyle as f, type RewriteFn as g, type RewriteRecipe as h, and as i, computed as j, definePattern as k, emptyStyleMap as l, hasDynamicChildren as m, hasDynamicClasses as n, hasEventHandlers as o, hasOwnVisualStyle as p, hasRef as q, hasSingleElementChild as r, isElement as s, not as t, opaque as u, or as v, pattern as w, targetedByCombinator as x, type ApplyResult as y, type RewriteGroup as z };
|