@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 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": return {
956
- kind: "if",
957
- expr: rest
958
- };
959
- case "elif": return {
960
- kind: "elif",
961
- expr: rest
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 }) => `{%${PREFIX_TOKEN[prefix]} ${kind} ${expression} %}`;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/template-conditions",
3
- "version": "0.5.4",
3
+ "version": "0.6.0",
4
4
  "description": "A document-template marker and condition engine: {{...}} marker grammar, parse, compute, serialize, field values.",
5
5
  "keywords": [
6
6
  "condition",