@stll/template-conditions 0.5.4 → 0.6.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/dist/index.d.ts +15 -3
- package/dist/index.js +52 -11
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -181,9 +181,14 @@ type MarkerMeta = {
|
|
|
181
181
|
} | {
|
|
182
182
|
kind: "if";
|
|
183
183
|
expr: string;
|
|
184
|
+
/** Filters on a tag whose whole body is one field path configure THAT
|
|
185
|
+
* boolean: a question the document only asks has no value marker to
|
|
186
|
+
* carry a chain. A tag holding a real expression carries none. */
|
|
187
|
+
filters: readonly FilterCall[];
|
|
184
188
|
} | {
|
|
185
189
|
kind: "elif";
|
|
186
190
|
expr: string;
|
|
191
|
+
filters: readonly FilterCall[];
|
|
187
192
|
} | {
|
|
188
193
|
kind: "else";
|
|
189
194
|
} | {
|
|
@@ -525,17 +530,24 @@ type ConditionTagOptions = {
|
|
|
525
530
|
kind: "if" | "elif";
|
|
526
531
|
/** The `{% if %}` expression, already serialized. */
|
|
527
532
|
expression: string;
|
|
533
|
+
/** The chain the boolean this tag names carries. Only a tag whose whole
|
|
534
|
+
* expression is one field path reads one back, so an expression tag writes
|
|
535
|
+
* none. */
|
|
536
|
+
filters: readonly FilterCall[];
|
|
528
537
|
/** The placement the tag already carried. */
|
|
529
538
|
prefix: MarkerPrefix;
|
|
530
539
|
};
|
|
531
540
|
/**
|
|
532
|
-
* One `{% if %}` / `{% elif %}` opener carrying its expression
|
|
541
|
+
* One `{% if %}` / `{% elif %}` opener carrying its expression, and the field
|
|
542
|
+
* configuration of the boolean it asks for.
|
|
533
543
|
*
|
|
534
544
|
* A rule that decides whether a block shows belongs in the tag that shows it:
|
|
535
545
|
* the tag is the only place a document has for it, so a builder's rule is
|
|
536
|
-
* serialized here rather than stored beside the document under a name.
|
|
546
|
+
* serialized here rather than stored beside the document under a name. The
|
|
547
|
+
* same is true of what to call the question and how to decide it: a condition
|
|
548
|
+
* the document never prints has no value marker to hold that chain either.
|
|
537
549
|
*/
|
|
538
|
-
export declare const renderConditionTag: ({ expression, kind, prefix }: ConditionTagOptions) => string;
|
|
550
|
+
export declare const renderConditionTag: ({ expression, filters, kind, prefix }: ConditionTagOptions) => string;
|
|
539
551
|
type ForOpenerOptions = {
|
|
540
552
|
alias: string;
|
|
541
553
|
path: string;
|
package/dist/index.js
CHANGED
|
@@ -945,6 +945,32 @@ const readPrefix = (token) => token === "p" || token === "tr" ? PREFIX_BY_TOKEN[
|
|
|
945
945
|
const TAG_RE = /^(?<tag>[\p{L}_][\p{L}\p{N}_]*)\b(?<rest>[\s\S]*)$/u;
|
|
946
946
|
const FOR_RE = /^(?<alias>[\p{L}_][\p{L}\p{N}_-]*)\s+in\s+(?<path>[\p{L}\p{N}_.-]+)\s*(?<filters>\|[\s\S]*)?$/u;
|
|
947
947
|
const CALL_RE = /^(?<name>[\p{L}_][\p{L}\p{N}_]*)\s*\(/u;
|
|
948
|
+
const CONDITION_RE = /^(?<path>[\p{L}\p{N}_.-]+)\s*(?<filters>\|[\s\S]*)$/u;
|
|
949
|
+
/**
|
|
950
|
+
* Read an `{% if %}` / `{% elif %}` body: one field path plus the chain that
|
|
951
|
+
* configures the boolean it names, or an expression the author wrote.
|
|
952
|
+
*
|
|
953
|
+
* A tail the filter catalogue does not read is part of that expression
|
|
954
|
+
* (`items|length > 0`), never a configuration, so the body stays verbatim
|
|
955
|
+
* rather than being rejected: a tag the grammar refuses would take its whole
|
|
956
|
+
* block with it.
|
|
957
|
+
*/
|
|
958
|
+
const readConditionBody = (rest) => {
|
|
959
|
+
const match = CONDITION_RE.exec(rest);
|
|
960
|
+
if (!match) return {
|
|
961
|
+
expr: rest,
|
|
962
|
+
filters: []
|
|
963
|
+
};
|
|
964
|
+
const chain = scanFilterChain(match.groups?.["filters"] ?? "");
|
|
965
|
+
if (chain === null || "unknownFilter" in chain || chain.filters.length === 0) return {
|
|
966
|
+
expr: rest,
|
|
967
|
+
filters: []
|
|
968
|
+
};
|
|
969
|
+
return {
|
|
970
|
+
expr: match.groups?.["path"] ?? rest,
|
|
971
|
+
filters: chain.filters
|
|
972
|
+
};
|
|
973
|
+
};
|
|
948
974
|
/** Classify a `{% ... %}` tag. */
|
|
949
975
|
const classifyStatement = (inner) => {
|
|
950
976
|
const match = TAG_RE.exec(inner);
|
|
@@ -952,14 +978,22 @@ const classifyStatement = (inner) => {
|
|
|
952
978
|
const tag = match.groups?.["tag"] ?? "";
|
|
953
979
|
const rest = (match.groups?.["rest"] ?? "").trim();
|
|
954
980
|
switch (tag) {
|
|
955
|
-
case "if":
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
}
|
|
981
|
+
case "if": {
|
|
982
|
+
const { expr, filters } = readConditionBody(rest);
|
|
983
|
+
return {
|
|
984
|
+
kind: "if",
|
|
985
|
+
expr,
|
|
986
|
+
filters
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
case "elif": {
|
|
990
|
+
const { expr, filters } = readConditionBody(rest);
|
|
991
|
+
return {
|
|
992
|
+
kind: "elif",
|
|
993
|
+
expr,
|
|
994
|
+
filters
|
|
995
|
+
};
|
|
996
|
+
}
|
|
963
997
|
case "else": return rest === "" ? { kind: "else" } : null;
|
|
964
998
|
case "endif": return rest === "" ? { kind: "endif" } : null;
|
|
965
999
|
case "endfor": return rest === "" ? { kind: "endfor" } : null;
|
|
@@ -1466,13 +1500,20 @@ const PREFIX_TOKEN = {
|
|
|
1466
1500
|
row: "tr"
|
|
1467
1501
|
};
|
|
1468
1502
|
/**
|
|
1469
|
-
* One `{% if %}` / `{% elif %}` opener carrying its expression
|
|
1503
|
+
* One `{% if %}` / `{% elif %}` opener carrying its expression, and the field
|
|
1504
|
+
* configuration of the boolean it asks for.
|
|
1470
1505
|
*
|
|
1471
1506
|
* A rule that decides whether a block shows belongs in the tag that shows it:
|
|
1472
1507
|
* the tag is the only place a document has for it, so a builder's rule is
|
|
1473
|
-
* serialized here rather than stored beside the document under a name.
|
|
1508
|
+
* serialized here rather than stored beside the document under a name. The
|
|
1509
|
+
* same is true of what to call the question and how to decide it: a condition
|
|
1510
|
+
* the document never prints has no value marker to hold that chain either.
|
|
1474
1511
|
*/
|
|
1475
|
-
const renderConditionTag = ({ expression, kind, prefix }) =>
|
|
1512
|
+
const renderConditionTag = ({ expression, filters, kind, prefix }) => {
|
|
1513
|
+
const chain = renderFilterChain(filters);
|
|
1514
|
+
const head = `{%${PREFIX_TOKEN[prefix]} ${kind} ${expression}`;
|
|
1515
|
+
return chain === "" ? `${head} %}` : `${head} | ${chain} %}`;
|
|
1516
|
+
};
|
|
1476
1517
|
/** One `{% for %}` opener, keeping its placement prefix. */
|
|
1477
1518
|
const renderForOpener = ({ alias, filters, path, prefix }) => {
|
|
1478
1519
|
const chain = renderFilterChain(filters);
|
package/package.json
CHANGED