@stll/template-conditions 0.2.2 → 0.4.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/README.md +1 -1
- package/dist/index.d.ts +214 -57
- package/dist/index.js +786 -132
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ functions usable on both backend (Bun) and frontend (browser).
|
|
|
7
7
|
|
|
8
8
|
Boolean conditions are evaluated through the canonical
|
|
9
9
|
[`@stll/conditions`](https://github.com/stella/stella/tree/main/packages/conditions)
|
|
10
|
-
AST and its single evaluator, so template `{
|
|
10
|
+
AST and its single evaluator, so template `{% if ... %}` conditionals share one
|
|
11
11
|
set of operators and semantics with the rest of the system. This package owns
|
|
12
12
|
the template surface: the marker grammar, the string parser, the named-condition
|
|
13
13
|
resolver, the numeric expression evaluator, and the no-code builder serializer.
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,22 @@ declare const resolvePath: (path: string, data: Record<string, unknown>) => unkn
|
|
|
6
6
|
//#region src/parse.d.ts
|
|
7
7
|
declare const parseCondition: (expression: string) => ConditionNode$1 | null;
|
|
8
8
|
//#endregion
|
|
9
|
+
//#region src/referenced-paths.d.ts
|
|
10
|
+
/**
|
|
11
|
+
* What a `{% if %}` expression reads. Structural, not evaluative: the paths a
|
|
12
|
+
* condition depends on, for callers that decide something about the condition
|
|
13
|
+
* itself rather than about a document being filled.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Every field path an expression reads, in source order.
|
|
17
|
+
*
|
|
18
|
+
* `null` says the expression does not answer the question: it did not parse,
|
|
19
|
+
* or it reads an operand whose own references cannot be enumerated. A caller
|
|
20
|
+
* deciding what a condition depends on must not read that as "depends on
|
|
21
|
+
* nothing".
|
|
22
|
+
*/
|
|
23
|
+
declare const referencedConditionPaths: (expression: string) => string[] | null;
|
|
24
|
+
//#endregion
|
|
9
25
|
//#region src/compute.d.ts
|
|
10
26
|
/**
|
|
11
27
|
* Arithmetic evaluator for computed template fields.
|
|
@@ -122,22 +138,47 @@ declare const serializeCondition: (node: ConditionNode$1) => string;
|
|
|
122
138
|
//#endregion
|
|
123
139
|
//#region src/markers.d.ts
|
|
124
140
|
/**
|
|
125
|
-
* Canonical grammar for
|
|
126
|
-
* shared by the fill pipeline (api), the editor highlighter (folio), and
|
|
127
|
-
* read-only preview (web).
|
|
141
|
+
* Canonical grammar for the docxtpl dialect of Jinja — the single source of
|
|
142
|
+
* truth shared by the fill pipeline (api), the editor highlighter (folio), and
|
|
143
|
+
* the read-only preview (web).
|
|
144
|
+
*
|
|
145
|
+
* Two spans carry meaning: an output marker `{{ ... }}` prints a value, and a
|
|
146
|
+
* tag `{% ... %}` opens or closes a block. A tag may carry a docxtpl placement
|
|
147
|
+
* prefix — `{%p ... %}` owns its paragraph, `{%tr ... %}` owns its table row —
|
|
148
|
+
* and a bare tag is placed by the leniency rules the consumer applies.
|
|
128
149
|
*
|
|
129
150
|
* Add a new directive to `MarkerMeta` and teach {@link classifyMarker} to
|
|
130
151
|
* recognize it. Compile-time totality checks then require its ordered runtime
|
|
131
152
|
* value and its block/inline disposition, while exhaustive consumer switches
|
|
132
153
|
* fail until they handle it.
|
|
133
154
|
*
|
|
134
|
-
* No consumer should define its own
|
|
155
|
+
* No consumer should define its own marker regex. The low-level pattern
|
|
135
156
|
* factories below exist so handlers that mutate raw XML/text reuse the exact
|
|
136
157
|
* same literals; recognizers should prefer the high-level scanner.
|
|
137
158
|
*/
|
|
159
|
+
/** A literal an author can write inside a filter or function call. */
|
|
160
|
+
type MarkerLiteral = string | number | boolean;
|
|
161
|
+
/** One argument of a filter call: positional, or `name=value`. */
|
|
162
|
+
type FilterArgument = {
|
|
163
|
+
kind: "positional";
|
|
164
|
+
value: MarkerLiteral;
|
|
165
|
+
} | {
|
|
166
|
+
kind: "keyword";
|
|
167
|
+
name: string;
|
|
168
|
+
value: MarkerLiteral;
|
|
169
|
+
};
|
|
170
|
+
/** One `| name` / `| name(args)` step of an output marker's filter chain. */
|
|
171
|
+
type FilterCall = {
|
|
172
|
+
name: FilterName;
|
|
173
|
+
args: readonly FilterArgument[];
|
|
174
|
+
};
|
|
175
|
+
/** The loop properties addressable inside a `{% for %}` body. */
|
|
176
|
+
declare const LOOP_PROPERTIES: readonly ["index", "index0", "first", "last", "length"];
|
|
177
|
+
type LoopProperty = (typeof LOOP_PROPERTIES)[number];
|
|
138
178
|
type MarkerMeta = {
|
|
139
179
|
kind: "placeholder";
|
|
140
180
|
expr: string;
|
|
181
|
+
filters: readonly FilterCall[];
|
|
141
182
|
} | {
|
|
142
183
|
kind: "clause";
|
|
143
184
|
name: string;
|
|
@@ -149,126 +190,241 @@ type MarkerMeta = {
|
|
|
149
190
|
kind: "ref";
|
|
150
191
|
key: string;
|
|
151
192
|
} | {
|
|
152
|
-
kind: "
|
|
153
|
-
|
|
154
|
-
kind: "count";
|
|
193
|
+
kind: "loop";
|
|
194
|
+
property: LoopProperty;
|
|
155
195
|
} | {
|
|
156
196
|
kind: "if";
|
|
157
197
|
expr: string;
|
|
158
198
|
} | {
|
|
159
|
-
kind: "
|
|
199
|
+
kind: "elif";
|
|
160
200
|
expr: string;
|
|
161
201
|
} | {
|
|
162
202
|
kind: "else";
|
|
163
203
|
} | {
|
|
164
204
|
kind: "endif";
|
|
165
205
|
} | {
|
|
166
|
-
kind: "
|
|
167
|
-
|
|
206
|
+
kind: "for";
|
|
207
|
+
alias: string;
|
|
208
|
+
path: string;
|
|
209
|
+
/** Filters on the loop path configure the ARRAY: how many rows it takes
|
|
210
|
+
* and what to call it. */
|
|
211
|
+
filters: readonly FilterCall[];
|
|
168
212
|
} | {
|
|
169
|
-
kind: "
|
|
213
|
+
kind: "endfor";
|
|
170
214
|
};
|
|
171
215
|
type DirectiveKind = MarkerMeta["kind"];
|
|
172
|
-
declare const DIRECTIVE_KINDS: readonly ["placeholder", "clause", "num", "ref", "
|
|
216
|
+
declare const DIRECTIVE_KINDS: readonly ["placeholder", "clause", "num", "ref", "loop", "if", "elif", "else", "endif", "for", "endfor"];
|
|
173
217
|
declare const DIRECTIVE_PLACEMENT: {
|
|
174
218
|
readonly placeholder: "inline";
|
|
175
219
|
readonly clause: "inline";
|
|
176
220
|
readonly num: "inline";
|
|
177
221
|
readonly ref: "inline";
|
|
178
|
-
readonly
|
|
179
|
-
readonly count: "inline";
|
|
222
|
+
readonly loop: "inline";
|
|
180
223
|
readonly if: "block";
|
|
181
|
-
readonly
|
|
224
|
+
readonly elif: "block";
|
|
182
225
|
readonly else: "block";
|
|
183
226
|
readonly endif: "block";
|
|
184
|
-
readonly
|
|
185
|
-
readonly
|
|
227
|
+
readonly for: "block";
|
|
228
|
+
readonly endfor: "block";
|
|
186
229
|
};
|
|
187
230
|
type BlockDirectiveKind = { [TKind in DirectiveKind]: (typeof DIRECTIVE_PLACEMENT)[TKind] extends "block" ? TKind : never; }[DirectiveKind];
|
|
188
231
|
/** Directives that occupy their own paragraph, in canonical grammar order. */
|
|
189
|
-
declare const BLOCK_DIRECTIVE_KINDS: readonly ["if", "
|
|
232
|
+
declare const BLOCK_DIRECTIVE_KINDS: readonly ["if", "elif", "else", "endif", "for", "endfor"];
|
|
190
233
|
declare const isBlockDirectiveKind: (kind: unknown) => kind is BlockDirectiveKind;
|
|
191
|
-
/**
|
|
234
|
+
/**
|
|
235
|
+
* Every filter the dialect accepts on a fillable value. The names are the
|
|
236
|
+
* grammar's; which manifest property each one writes is decided by the field
|
|
237
|
+
* catalogue in the api, which is total over BOTH this list and the manifest
|
|
238
|
+
* keys, so neither side can gain an entry the other has not decided.
|
|
239
|
+
*/
|
|
240
|
+
declare const FILTER_NAMES: readonly ["text", "number", "date", "checkbox", "select", "options_from", "label", "hint", "required", "pattern", "min", "max", "min_length", "max_length", "min_items", "max_items", "ai", "lookup", "matter", "contact", "party", "attorney", "firm", "formula", "condition"];
|
|
241
|
+
type FilterName = (typeof FILTER_NAMES)[number];
|
|
242
|
+
/** Normalise one marker's inner text: typographic quotes become straight
|
|
243
|
+
* quotes, fixed-width spaces become ordinary ones. */
|
|
244
|
+
declare const normalizeMarkerInner: (inner: string) => string;
|
|
245
|
+
/**
|
|
246
|
+
* What an output marker holds between its braces: anything but a brace or a
|
|
247
|
+
* double quote, plus whole double-quoted arguments, whose braces are content
|
|
248
|
+
* rather than the marker's own delimiters — `pattern("^[0-9]{5}$")` is a regex,
|
|
249
|
+
* not a nested marker. An apostrophe stays an ordinary character, so
|
|
250
|
+
* `label("Owner's")` reads as it always did.
|
|
251
|
+
*
|
|
252
|
+
* The alternatives cannot overlap (the plain one excludes every character the
|
|
253
|
+
* quoted one may start with) and a quoted run's end is forced by its body, so
|
|
254
|
+
* the scan stays linear on adversarial input: a `{{` with no closing `}}`
|
|
255
|
+
* costs one failed pass, not a polynomial retry.
|
|
256
|
+
*/
|
|
257
|
+
declare const MARKER_OUTPUT_BODY: string;
|
|
258
|
+
/** What a tag holds between `{%` and `%}`: anything but a brace, and a `%`
|
|
259
|
+
* only where it does not close the tag. */
|
|
260
|
+
declare const MARKER_STATEMENT_BODY: string;
|
|
261
|
+
/**
|
|
262
|
+
* Any marker span: an output `{{...}}` (group `output`) or a tag `{%...%}`
|
|
263
|
+
* (group `statement`, with the docxtpl placement in group `prefix`).
|
|
264
|
+
*/
|
|
192
265
|
declare const markerPattern: () => RegExp;
|
|
193
|
-
/** Inline
|
|
266
|
+
/** Inline value marker, including its filter chain; group `name` is the path
|
|
267
|
+
* the value comes from and group `filters` the untouched chain. */
|
|
194
268
|
declare const placeholderPattern: () => RegExp;
|
|
195
|
-
/** `{{
|
|
269
|
+
/** `{{ clause("Name") }}` / `{{ clause("Name", "v3") }}`. */
|
|
196
270
|
declare const clauseSlotPattern: () => RegExp;
|
|
197
|
-
/** `{{
|
|
271
|
+
/** `{{ num("key") }}` — the `key` group. */
|
|
198
272
|
declare const numPattern: () => RegExp;
|
|
199
|
-
/** `{{
|
|
273
|
+
/** `{{ ref("key") }}` — the `key` group. */
|
|
200
274
|
declare const refPattern: () => RegExp;
|
|
201
|
-
/** `{{
|
|
202
|
-
declare const
|
|
203
|
-
/** `{{@count}}` — the item count of the innermost enclosing loop. */
|
|
204
|
-
declare const countPattern: () => RegExp;
|
|
275
|
+
/** `{{ loop.index }}` and its siblings — the `property` group. */
|
|
276
|
+
declare const loopPattern: () => RegExp;
|
|
205
277
|
/** Cheap presence test for any numbering marker (no capture). */
|
|
206
278
|
declare const hasNumberingPattern: () => RegExp;
|
|
207
|
-
/** Cheap presence test for any
|
|
279
|
+
/** Cheap presence test for any tag (no capture). */
|
|
208
280
|
declare const hasBlockDirectivePattern: () => RegExp;
|
|
209
|
-
/** A whole line that is a single block
|
|
281
|
+
/** A whole line that is a single block tag — `prefix`, `tag` and `expr`. */
|
|
210
282
|
declare const blockDirectiveLinePattern: () => RegExp;
|
|
211
283
|
/** Whether `value` is a valid field path per the marker grammar (dotted
|
|
212
284
|
* segments of letters/digits/underscore/dash — no brackets or spaces). */
|
|
213
285
|
declare const isFieldPath: (value: string) => boolean;
|
|
214
286
|
/** Whether `value` is safe to use as a dotted object path. */
|
|
215
287
|
declare const isSafeFieldPath: (value: string) => boolean;
|
|
216
|
-
/** Whether `value` is a valid name for authoring a `
|
|
217
|
-
* the intersection of the clause-slot parse grammar and the fill pipeline's
|
|
218
|
-
* placeholder charset, so every accepted name round-trips through fill. */
|
|
288
|
+
/** Whether `value` is a valid name for authoring a `clause("NAME")` slot. */
|
|
219
289
|
declare const isClauseSlotName: (value: string) => boolean;
|
|
290
|
+
/** Which brace pair carried the marker text. */
|
|
291
|
+
type MarkerForm = "output" | "statement";
|
|
292
|
+
/** Where a tag's docxtpl prefix says the tag belongs. `"none"` is a bare tag,
|
|
293
|
+
* placed by the consumer's leniency rules, and every output marker. */
|
|
294
|
+
type MarkerPrefix = "none" | "paragraph" | "row";
|
|
220
295
|
/**
|
|
221
|
-
* Classify the inner text of
|
|
222
|
-
*
|
|
296
|
+
* Classify the inner text of one marker. Returns `null` when the text is not a
|
|
297
|
+
* recognized directive (so a recognizer can leave stray braces alone).
|
|
223
298
|
*/
|
|
224
|
-
declare const classifyMarker: (innerRaw: string) => MarkerMeta | null;
|
|
299
|
+
declare const classifyMarker: (innerRaw: string, form?: MarkerForm) => MarkerMeta | null;
|
|
225
300
|
type ScannedMarker = {
|
|
226
|
-
/** Offset of
|
|
301
|
+
/** Offset of the opening brace pair in the source text. */
|
|
227
302
|
start: number;
|
|
228
|
-
/** Offset just past
|
|
303
|
+
/** Offset just past the closing brace pair. */
|
|
229
304
|
end: number;
|
|
230
|
-
/** The full matched marker, e.g. `{
|
|
305
|
+
/** The full matched marker, e.g. `{% endfor %}`. */
|
|
231
306
|
raw: string;
|
|
232
|
-
/** The
|
|
307
|
+
/** The normalized, trimmed inner text. */
|
|
233
308
|
inner: string;
|
|
309
|
+
form: MarkerForm;
|
|
310
|
+
/** The docxtpl placement prefix the author wrote. */
|
|
311
|
+
prefix: MarkerPrefix;
|
|
234
312
|
meta: MarkerMeta;
|
|
235
313
|
};
|
|
236
314
|
/**
|
|
237
|
-
* Find every recognized
|
|
238
|
-
*
|
|
315
|
+
* Find every recognized marker in `text`, in document order, with offsets and
|
|
316
|
+
* classified metadata. Unrecognized spans are skipped.
|
|
239
317
|
*/
|
|
240
318
|
declare const scanMarkers: (text: string) => ScannedMarker[];
|
|
241
|
-
/**
|
|
319
|
+
/**
|
|
320
|
+
* Rewrite every `{{ ... }}` output marker in `text` through `replace`, which
|
|
321
|
+
* receives the whole marker and its raw inner text and returns the replacement.
|
|
322
|
+
* Tags are left untouched. The single scan is the same one the classifier uses,
|
|
323
|
+
* so a caller that rewrites marker text never needs its own regex.
|
|
324
|
+
*/
|
|
325
|
+
declare const replaceOutputMarkers: (text: string, replace: (raw: string, inner: string) => string) => string;
|
|
326
|
+
/**
|
|
327
|
+
* The values-map key one marker substitutes from, or `null` when the marker is
|
|
328
|
+
* not a substitution target (a tag, or a `loop.*` token the loop expander
|
|
329
|
+
* resolves first). One function so the key discovery writes is the key the
|
|
330
|
+
* patcher reads.
|
|
331
|
+
*/
|
|
332
|
+
declare const substitutionKey: (meta: MarkerMeta) => string | null;
|
|
333
|
+
/** The values-map key for one clause slot. */
|
|
334
|
+
declare const clauseSlotKey: (name: string, version?: string) => string;
|
|
335
|
+
/** A marker-shaped span that classifies to nothing. */
|
|
242
336
|
type InvalidMarker = {
|
|
243
|
-
/** Offset of
|
|
337
|
+
/** Offset of the opening brace pair in the source text. */
|
|
244
338
|
start: number;
|
|
245
|
-
/** Offset just past
|
|
339
|
+
/** Offset just past the closing brace pair. */
|
|
246
340
|
end: number;
|
|
247
341
|
/** The full matched span, e.g. `{{my field}}`. */
|
|
248
342
|
raw: string;
|
|
249
|
-
/** The
|
|
343
|
+
/** The normalized, trimmed inner text. */
|
|
250
344
|
inner: string;
|
|
345
|
+
form: MarkerForm;
|
|
251
346
|
};
|
|
252
347
|
/**
|
|
253
|
-
* Find every
|
|
254
|
-
* near-misses an author clearly meant as markers but that every
|
|
255
|
-
* skips, so they print literally at fill time
|
|
256
|
-
* space, `{{@clause:}}` with no name). Offsets and raw text are shaped like
|
|
257
|
-
* {@link scanMarkers} results, minus the classified `meta`. This is the exact
|
|
348
|
+
* Find every marker-shaped span whose inner text is NOT a recognized
|
|
349
|
+
* directive — near-misses an author clearly meant as markers but that every
|
|
350
|
+
* recognizer skips, so they print literally at fill time. This is the exact
|
|
258
351
|
* complement of {@link scanMarkers}: a span is in one list or the other.
|
|
259
352
|
*/
|
|
260
353
|
declare const scanInvalidMarkers: (text: string) => InvalidMarker[];
|
|
354
|
+
/** The loop alias the codemod (and the legacy-marker message) generates for a
|
|
355
|
+
* `{{#each path}}`: the singular of the last plain-word segment, else `item`. */
|
|
356
|
+
declare const legacyLoopAlias: (path: string) => string;
|
|
357
|
+
/** Translate an old-dialect condition expression to the Jinja surface:
|
|
358
|
+
* `!a` becomes `not a`, `a contains "x"` becomes `"x" in a`. */
|
|
359
|
+
declare const translateLegacyExpression: (expr: string) => string;
|
|
360
|
+
/**
|
|
361
|
+
* The Jinja marker that replaces one old-dialect marker's inner text, or
|
|
362
|
+
* `null` when the text is not old-dialect. Shared by the rejection message and
|
|
363
|
+
* the codemod, so the fix an author is told to make is the fix the script
|
|
364
|
+
* would have made.
|
|
365
|
+
*/
|
|
366
|
+
declare const legacyMarkerReplacement: (innerRaw: string) => string | null;
|
|
367
|
+
/**
|
|
368
|
+
* Why a marker-shaped span misses the grammar in a way that names the
|
|
369
|
+
* authoring mistake rather than merely failing to classify. Consumers derive
|
|
370
|
+
* their own warning codes from {@link MARKER_DEFECT_KINDS} so the two never
|
|
371
|
+
* drift.
|
|
372
|
+
*/
|
|
373
|
+
type MarkerDefectKind = "legacy_marker" | "unsupported_tag" | "unknown_filter" | "python_expression" | "bracket_index";
|
|
374
|
+
declare const MARKER_DEFECT_KINDS: readonly ["legacy_marker", "unsupported_tag", "unknown_filter", "python_expression", "bracket_index"];
|
|
375
|
+
/** One diagnosis of a rejected span: the kind plus the construct to quote. */
|
|
376
|
+
type MarkerDefect = {
|
|
377
|
+
kind: MarkerDefectKind;
|
|
378
|
+
/** The construct the author wrote that the grammar refuses. */
|
|
379
|
+
construct: string;
|
|
380
|
+
/** The Jinja that replaces it, when the mistake has one exact fix. */
|
|
381
|
+
replacement?: string;
|
|
382
|
+
};
|
|
383
|
+
/**
|
|
384
|
+
* Name the authoring mistake behind one marker's inner text, or `null` when
|
|
385
|
+
* the text is a recognized directive ({@link classifyMarker} accepts it) or an
|
|
386
|
+
* unrecognizable span with no specific diagnosis (`{{my field}}`).
|
|
387
|
+
*/
|
|
388
|
+
declare const classifyMarkerDefect: (innerRaw: string, form?: MarkerForm) => MarkerDefect | null;
|
|
261
389
|
/** Exhaustiveness guard — pass the discriminant in a `switch` default branch. */
|
|
262
390
|
declare const assertNever: (value: never) => never;
|
|
263
391
|
//#endregion
|
|
392
|
+
//#region src/row-blocks.d.ts
|
|
393
|
+
/** One end of a row block, addressed within the row that was scanned. */
|
|
394
|
+
type RowBlockMarker = {
|
|
395
|
+
/** Index into the row's cells. */
|
|
396
|
+
cellIndex: number;
|
|
397
|
+
/** Index into that cell's paragraphs. */
|
|
398
|
+
paragraphIndex: number;
|
|
399
|
+
marker: ScannedMarker;
|
|
400
|
+
};
|
|
401
|
+
type RowBlockPair = {
|
|
402
|
+
open: RowBlockMarker;
|
|
403
|
+
close: RowBlockMarker;
|
|
404
|
+
};
|
|
405
|
+
/**
|
|
406
|
+
* The one row block a table row declares in the lenient form, or `null`.
|
|
407
|
+
*
|
|
408
|
+
* `cells` is the row's cells in document order, each as its paragraph texts in
|
|
409
|
+
* document order.
|
|
410
|
+
*
|
|
411
|
+
* A row qualifies when exactly two block markers reach outside their own
|
|
412
|
+
* paragraph, they are a matching opener/closer pair, the closer sits in a LATER
|
|
413
|
+
* cell than the opener, the opener prefixes its cell and the closer suffixes
|
|
414
|
+
* its cell. Everything else — one half of a pair, two nested pairs, a stray
|
|
415
|
+
* `{% else %}`, a pair wrapping the paragraphs of a single cell — is left to the
|
|
416
|
+
* engine's existing structure errors rather than guessed at.
|
|
417
|
+
*/
|
|
418
|
+
declare const detectRowBlockPair: (cells: readonly (readonly string[])[]) => RowBlockPair | null;
|
|
419
|
+
//#endregion
|
|
264
420
|
//#region src/index.d.ts
|
|
265
421
|
type NamedCondition = {
|
|
266
422
|
name: string;
|
|
267
|
-
/** The `{
|
|
423
|
+
/** The `{% if %}` expression string. Authoritative unless `node` is set. */
|
|
268
424
|
expression: string;
|
|
269
425
|
/**
|
|
270
426
|
* The canonical AST, set when the condition cannot round-trip through the
|
|
271
|
-
* expression string — i.e. it uses a `formula` operand the `{
|
|
427
|
+
* expression string — i.e. it uses a `formula` operand the `{% if %}` grammar
|
|
272
428
|
* has no syntax for. When present it is evaluated directly and `expression`
|
|
273
429
|
* is ignored.
|
|
274
430
|
*/
|
|
@@ -282,16 +438,17 @@ type NamedCondition = {
|
|
|
282
438
|
*/
|
|
283
439
|
declare const MAX_CONDITION_DEPTH = 50;
|
|
284
440
|
/**
|
|
285
|
-
* Evaluate a template `{
|
|
441
|
+
* Evaluate a template `{% if ... %}` condition. The expression is parsed into
|
|
286
442
|
* the canonical `@stll/conditions` AST and evaluated by the shared evaluator.
|
|
287
443
|
*
|
|
288
|
-
* Supports comparisons (`==`, `!=`, `>`, `<`, `>=`, `<=`),
|
|
289
|
-
*
|
|
290
|
-
*
|
|
444
|
+
* Supports comparisons (`==`, `!=`, `>`, `<`, `>=`, `<=`), membership
|
|
445
|
+
* (`"x" in path`), presence (`is defined` / `is not defined`), truthiness,
|
|
446
|
+
* negation (`not`), logical operators (`and`, `or`), parentheses, dotted
|
|
447
|
+
* paths, numeric underscores, and named-condition references.
|
|
291
448
|
*
|
|
292
449
|
* An empty or unparseable expression is `false` ("no condition" never gates a
|
|
293
450
|
* branch true).
|
|
294
451
|
*/
|
|
295
452
|
declare const evaluateCondition: (expression: string, data: Record<string, unknown>, namedConditions?: readonly NamedCondition[], _resolved?: ReadonlySet<string>, _depth?: number) => boolean;
|
|
296
453
|
//#endregion
|
|
297
|
-
export { BLOCK_DIRECTIVE_KINDS, type BlockDirectiveKind, type CompareNode, type CompareOp, type Condition, type ConditionNode, DATE_FORMAT_EXAMPLE_ISO, DATE_FORMAT_STYLES, DIRECTIVE_KINDS, type DateFormatStyle, type DeterministicFieldConfig, type DirectiveKind, type FieldDateFormat, type GroupNode, type InvalidMarker, MAX_CONDITION_DEPTH, type MarkerMeta, NUMERIC_FUNCTION_NAMES, NamedCondition, type NumericFunctionName, type Operand, type PartConfig, type PredicateNode, type PredicateOp, type ScannedMarker, assertNever, blockDirectiveLinePattern, classifyMarker, clauseSlotPattern, conditionHasFormula, conditionNodeSchema, conditionSchema,
|
|
454
|
+
export { BLOCK_DIRECTIVE_KINDS, type BlockDirectiveKind, type CompareNode, type CompareOp, type Condition, type ConditionNode, DATE_FORMAT_EXAMPLE_ISO, DATE_FORMAT_STYLES, DIRECTIVE_KINDS, type DateFormatStyle, type DeterministicFieldConfig, type DirectiveKind, FILTER_NAMES, type FieldDateFormat, type FilterArgument, type FilterCall, type FilterName, type GroupNode, type InvalidMarker, LOOP_PROPERTIES, type LoopProperty, MARKER_DEFECT_KINDS, MARKER_OUTPUT_BODY, MARKER_STATEMENT_BODY, MAX_CONDITION_DEPTH, type MarkerDefect, type MarkerDefectKind, type MarkerForm, type MarkerLiteral, type MarkerMeta, type MarkerPrefix, NUMERIC_FUNCTION_NAMES, NamedCondition, type NumericFunctionName, type Operand, type PartConfig, type PredicateNode, type PredicateOp, type RowBlockMarker, type RowBlockPair, type ScannedMarker, assertNever, blockDirectiveLinePattern, classifyMarker, classifyMarkerDefect, clauseSlotKey, clauseSlotPattern, conditionHasFormula, conditionNodeSchema, conditionSchema, detectRowBlockPair, emptyCondition, evaluateCondition, evaluateNumericExpression, formatDate, hasBlockDirectivePattern, hasNumberingPattern, isBlockDirectiveKind, isClauseSlotName, isFieldPath, isSafeFieldPath, legacyLoopAlias, legacyMarkerReplacement, loopPattern, markerPattern, normalizeMarkerInner, numPattern, parseCondition, placeholderPattern, refPattern, referencedConditionPaths, renderComposite, renderDeterministicFieldValue, replaceOutputMarkers, resolvePath, scanInvalidMarkers, scanMarkers, serializeCondition, substitutionKey, translateLegacyExpression };
|