@wdprlib/parser 5.4.0 → 6.0.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.
Files changed (44) hide show
  1. package/dist/index.cjs +420 -127
  2. package/dist/index.d.cts +93 -8
  3. package/dist/index.d.ts +93 -8
  4. package/dist/index.js +380 -84
  5. package/package.json +2 -2
  6. package/src/build-info.generated.ts +2 -2
  7. package/src/index.ts +3 -0
  8. package/src/parser/rules/block/module/index.ts +3 -2
  9. package/src/parser/rules/block/module/listpages/extraction/listpages.ts +5 -0
  10. package/src/parser/rules/block/module/listpages/extraction/query.ts +1 -0
  11. package/src/parser/rules/block/module/listpages/extraction/template.ts +16 -0
  12. package/src/parser/rules/block/module/listpages/extraction/variables.ts +1 -0
  13. package/src/parser/rules/block/module/listpages/index.ts +1 -0
  14. package/src/parser/rules/block/module/listpages/normalization/order-parent.ts +9 -1
  15. package/src/parser/rules/block/module/listpages/normalize.ts +1 -0
  16. package/src/parser/rules/block/module/listpages/parser.ts +1 -0
  17. package/src/parser/rules/block/module/listpages/resolve.ts +4 -2
  18. package/src/parser/rules/block/module/listpages/template/excerpt.ts +63 -0
  19. package/src/parser/rules/block/module/listpages/template/format/content.ts +0 -32
  20. package/src/parser/rules/block/module/listpages/template/format/index.ts +1 -1
  21. package/src/parser/rules/block/module/listpages/template/getters/index.ts +14 -0
  22. package/src/parser/rules/block/module/listpages/template/getters/parameterized.ts +3 -1
  23. package/src/parser/rules/block/module/listpages/template/getters/registered.ts +52 -0
  24. package/src/parser/rules/block/module/listpages/template/getters/simple.ts +15 -5
  25. package/src/parser/rules/block/module/listpages/template/literal.ts +6 -0
  26. package/src/parser/rules/block/module/listpages/template/syntax.ts +10 -1
  27. package/src/parser/rules/block/module/listpages/types/data-requirements.ts +6 -0
  28. package/src/parser/rules/block/module/listpages/types/external-data.ts +36 -2
  29. package/src/parser/rules/block/module/listpages/types/normalized-query.ts +14 -4
  30. package/src/parser/rules/block/module/listpages/types/query.ts +2 -0
  31. package/src/parser/rules/block/module/listpages/types/variables.ts +5 -0
  32. package/src/parser/rules/block/module/listpages/url-resolution/fields.ts +2 -0
  33. package/src/parser/rules/block/module/listusers/resolve.ts +2 -1
  34. package/src/parser/rules/block/module/mapping.ts +2 -1
  35. package/src/parser/rules/block/module/rate/index.ts +16 -19
  36. package/src/parser/rules/block/module/rate/resolve.ts +108 -0
  37. package/src/parser/rules/block/module/rate/types.ts +6 -16
  38. package/src/parser/rules/block/module/resolution/resolve-async.ts +2 -1
  39. package/src/parser/rules/block/module/resolve.ts +10 -8
  40. package/src/parser/rules/block/module/types-common.ts +4 -0
  41. package/src/parser/rules/inline/parsing/inline-content.ts +10 -1
  42. package/src/parser/rules/inline/size/content.ts +31 -4
  43. package/src/pipeline/process.ts +13 -0
  44. package/src/pipeline/types.ts +19 -0
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // packages/parser/src/build-info.generated.ts
2
2
  var buildInfo = Object.freeze({
3
- version: "5.4.0",
4
- sha: "5e221ddeccc585e64dd1a2eb49b14817c3c1be5d",
3
+ version: "6.0.0",
4
+ sha: "705ff97e22abd7bfb49d7f8f30087e6f09654c91",
5
5
  dirty: false
6
6
  });
7
7
 
@@ -2627,7 +2627,8 @@ function parseInlineUntil(ctx, endType) {
2627
2627
  let pos = ctx.pos;
2628
2628
  let consumedEmptyRaw = false;
2629
2629
  const paragraphMode = endType === "PARAGRAPH_BREAK";
2630
- const multiline = paragraphMode || FORMATTING_CLOSE_TOKENS.has(endType);
2630
+ const preserveNewlines = endType === "EOF";
2631
+ const multiline = paragraphMode || preserveNewlines || FORMATTING_CLOSE_TOKENS.has(endType);
2631
2632
  let inlineEnd = ctx.scope.inlineEnd ?? ctx.tokens.length;
2632
2633
  if (!multiline) {
2633
2634
  for (let end = ctx.pos;end < inlineEnd; end++) {
@@ -2665,6 +2666,12 @@ function parseInlineUntil(ctx, endType) {
2665
2666
  if (!multiline && token5.type === "NEWLINE") {
2666
2667
  break;
2667
2668
  }
2669
+ if (preserveNewlines && token5.type === "NEWLINE") {
2670
+ nodes.push({ element: "text", data: token5.value });
2671
+ pos++;
2672
+ consumed++;
2673
+ continue;
2674
+ }
2668
2675
  if (multiline && token5.type === "NEWLINE" && !ctx.scope.tableFormatting) {
2669
2676
  const boundary = getParagraphNewlineBoundary(ctx, pos, nodes.length > 0);
2670
2677
  if (boundary.shouldBreak) {
@@ -5697,8 +5704,18 @@ var rateModuleRule = {
5697
5704
  name: "module-rate",
5698
5705
  acceptsNames: ["rate"],
5699
5706
  hasBody: false,
5700
- parse() {
5701
- return { module: "rate" };
5707
+ parse(_ctx, _pos, args) {
5708
+ return { module: "rate", ref: Object.keys(args).length === 0 ? { kind: "main" } : null };
5709
+ }
5710
+ };
5711
+ var customRateModuleRule = {
5712
+ name: "module-custom-rate",
5713
+ acceptsNames: ["customrate"],
5714
+ hasBody: false,
5715
+ parse(_ctx, _pos, args) {
5716
+ const key = args.key;
5717
+ const valid = key !== undefined && key !== "" && Object.keys(args).every((name) => name === "key");
5718
+ return { module: "custom-rate", ref: valid ? { kind: "custom", axisKey: key } : null };
5702
5719
  }
5703
5720
  };
5704
5721
 
@@ -5833,6 +5850,7 @@ var listPagesModuleRule = {
5833
5850
  "created-at": createdAt,
5834
5851
  "updated-at": updatedAt,
5835
5852
  rating,
5853
+ "rating-axis": args["rating-axis"],
5836
5854
  votes,
5837
5855
  name,
5838
5856
  fullname,
@@ -5976,6 +5994,7 @@ var tagCloudModuleRule = {
5976
5994
  // packages/parser/src/parser/rules/block/module/mapping.ts
5977
5995
  var MODULE_RULES = [
5978
5996
  rateModuleRule,
5997
+ customRateModuleRule,
5979
5998
  cssModuleRule,
5980
5999
  backlinksModuleRule,
5981
6000
  categoriesModuleRule,
@@ -6546,22 +6565,6 @@ function getDayOfYear(date) {
6546
6565
  function splitContentSections(content) {
6547
6566
  return content.split(/^={4,}$/m).map((section) => section.trim());
6548
6567
  }
6549
- function getSummary(page) {
6550
- if (page.content) {
6551
- const sections = splitContentSections(page.content);
6552
- if (sections.length > 1) {
6553
- return sections[0]?.trim() ?? "";
6554
- }
6555
- }
6556
- return getFirstParagraph(page.content);
6557
- }
6558
- function getFirstParagraph(content) {
6559
- if (!content)
6560
- return "";
6561
- const stripped = content.replace(/^(\+{1,6}) (.*)/gm, "").replace(/^\[\[toc(\s[^\]]+)?\]\]/gim, "").replace(/^\[\[\/?div(\s[^\]]+)?\]\]/gim, "").replace(/^\[\[\/?module(\s[^\]]+)?\]\]/gim, "").trim();
6562
- const paragraphs = stripped.split(/\n{2,}/);
6563
- return paragraphs[0]?.trim() ?? "";
6564
- }
6565
6568
  // packages/parser/src/parser/rules/block/module/listpages/template/format/tags.ts
6566
6569
  function formatTagsLinked(tags, prefix) {
6567
6570
  if (tags.length === 0)
@@ -6574,6 +6577,16 @@ function formatUserLinked(user) {
6574
6577
  return "Anonymous";
6575
6578
  return `[[*user ${user.name}]]`;
6576
6579
  }
6580
+ // packages/parser/src/parser/rules/block/module/listpages/template/getters/parameterized.ts
6581
+ import { excerptText } from "@wdprlib/ast";
6582
+
6583
+ // packages/parser/src/parser/rules/block/module/listpages/template/literal.ts
6584
+ function literalWikitext(value) {
6585
+ if (value === "")
6586
+ return "";
6587
+ return `@<${Array.from(value, (char) => `&#${char.codePointAt(0)};`).join("")}>@`;
6588
+ }
6589
+
6577
6590
  // packages/parser/src/parser/rules/block/module/listpages/template/getters/parameterized.ts
6578
6591
  function createBraceParamGetter(name, param) {
6579
6592
  switch (name) {
@@ -6602,7 +6615,7 @@ function createParenParamGetter(name, param) {
6602
6615
  if (name !== "preview")
6603
6616
  return null;
6604
6617
  const len = Number(param);
6605
- return (ctx) => (ctx.page.content ?? "").slice(0, len);
6618
+ return (ctx) => literalWikitext(excerptText(ctx.page.readableText ?? "", { maxLength: len }));
6606
6619
  }
6607
6620
  function createFormattedGetter(name, format) {
6608
6621
  switch (name) {
@@ -6629,6 +6642,7 @@ function createTagsLinkedGetter(name, format) {
6629
6642
  }
6630
6643
 
6631
6644
  // packages/parser/src/parser/rules/block/module/listpages/template/getters/simple.ts
6645
+ import { countCharacters, excerptText as excerptText2 } from "@wdprlib/ast";
6632
6646
  var DEFAULT_PREVIEW_LENGTH = 200;
6633
6647
  var SIMPLE_GETTERS = {
6634
6648
  created_at: (ctx) => formatDate(ctx.page.createdAt),
@@ -6658,14 +6672,14 @@ var SIMPLE_GETTERS = {
6658
6672
  parent_title: (ctx) => ctx.page.parentTitle ?? "",
6659
6673
  parent_title_linked: (ctx) => ctx.page.parentFullname ? `[[[${ctx.page.parentFullname} | ${ctx.page.parentTitle}]]]` : "",
6660
6674
  content: (ctx) => ctx.page.content ?? "",
6661
- preview: (ctx) => (ctx.page.content ?? "").slice(0, DEFAULT_PREVIEW_LENGTH),
6662
- summary: (ctx) => getSummary(ctx.page),
6663
- first_paragraph: (ctx) => getFirstParagraph(ctx.page.content),
6675
+ preview: (ctx) => literalWikitext(excerptText2(ctx.page.readableText ?? "", { maxLength: DEFAULT_PREVIEW_LENGTH })),
6676
+ summary: (ctx) => literalWikitext(ctx.page.firstParagraph ?? ""),
6677
+ first_paragraph: (ctx) => literalWikitext(ctx.page.firstParagraph ?? ""),
6664
6678
  tags: (ctx) => ctx.page.tags.join(" "),
6665
6679
  _tags: (ctx) => ctx.page.hiddenTags.join(" "),
6666
6680
  children: (ctx) => String(ctx.page.children),
6667
6681
  comments: (ctx) => String(ctx.page.comments),
6668
- size: (ctx) => String(ctx.page.size),
6682
+ size: (ctx) => ctx.page.readableText !== undefined ? String(countCharacters(ctx.page.readableText)) : ctx.page.size !== undefined ? String(ctx.page.size) : "",
6669
6683
  rating: (ctx) => String(ctx.page.rating),
6670
6684
  rating_votes: (ctx) => String(ctx.page.ratingVotes),
6671
6685
  rating_percent: (ctx) => String(ctx.page.ratingPercent ?? 0),
@@ -6679,11 +6693,136 @@ var SIMPLE_GETTERS = {
6679
6693
  site_domain: (ctx) => ctx.site.domain
6680
6694
  };
6681
6695
 
6696
+ // packages/parser/src/parser/rules/block/module/listpages/template/getters/registered.ts
6697
+ function createRegisteredGetter(name, key, format) {
6698
+ if (name === "metadata")
6699
+ return (ctx) => {
6700
+ const metadata = ctx.page.metadata;
6701
+ const entry = metadata && Object.hasOwn(metadata, key) ? metadata[key] : null;
6702
+ if (!entry)
6703
+ return "";
6704
+ let value;
6705
+ switch (entry.type) {
6706
+ case "text":
6707
+ value = entry.value;
6708
+ break;
6709
+ case "number":
6710
+ value = String(entry.value);
6711
+ break;
6712
+ case "date":
6713
+ value = formatDate(entry.value, format);
6714
+ break;
6715
+ case "user":
6716
+ value = format === "id" ? String(entry.value.id) : format === "unix" ? entry.value.unixName : entry.value.name;
6717
+ break;
6718
+ }
6719
+ return literalWikitext(value);
6720
+ };
6721
+ if (name === "customrate" || name === "customrate_votes" || name === "customrate_percent")
6722
+ return (ctx) => {
6723
+ const ratings = ctx.page.customRates;
6724
+ const entry = ratings && Object.hasOwn(ratings, key) ? ratings[key] : null;
6725
+ if (!entry)
6726
+ return "";
6727
+ return String(name === "customrate" ? entry.points : name === "customrate_votes" ? entry.votes : entry.percent);
6728
+ };
6729
+ return null;
6730
+ }
6731
+
6732
+ // packages/parser/src/parser/rules/block/module/listpages/template/getters/index.ts
6733
+ import { compileTextExcerpt } from "@wdprlib/ast";
6734
+
6735
+ // packages/parser/src/parser/rules/block/module/listpages/template/excerpt.ts
6736
+ function findExcerptEnd(template, contentStart) {
6737
+ let quote;
6738
+ for (let cursor = contentStart + "excerpt{".length;cursor < template.length; cursor++) {
6739
+ const char = template[cursor];
6740
+ if (quote) {
6741
+ if (char === "\\")
6742
+ cursor++;
6743
+ else if (char === quote)
6744
+ quote = undefined;
6745
+ } else if (char === '"' || char === "'") {
6746
+ quote = char;
6747
+ } else if (char === "}") {
6748
+ return template.startsWith("%%", cursor + 1) ? cursor + 1 : -1;
6749
+ }
6750
+ }
6751
+ return -1;
6752
+ }
6753
+ function parseExcerptOptions(input) {
6754
+ if (input.length > 8192)
6755
+ return null;
6756
+ const attributes = new Map;
6757
+ let cursor = 0;
6758
+ while (cursor < input.length) {
6759
+ while (cursor < input.length && /\s/.test(input[cursor]))
6760
+ cursor++;
6761
+ if (cursor === input.length)
6762
+ break;
6763
+ const start = cursor;
6764
+ while (cursor < input.length && /[a-z]/.test(input[cursor]))
6765
+ cursor++;
6766
+ const name = input.slice(start, cursor);
6767
+ if (!["pattern", "flags", "group", "match", "max"].includes(name) || attributes.has(name))
6768
+ return null;
6769
+ while (cursor < input.length && /\s/.test(input[cursor]))
6770
+ cursor++;
6771
+ if (input[cursor++] !== "=")
6772
+ return null;
6773
+ while (cursor < input.length && /\s/.test(input[cursor]))
6774
+ cursor++;
6775
+ const quote = input[cursor++];
6776
+ if (quote !== '"' && quote !== "'")
6777
+ return null;
6778
+ let value = "";
6779
+ while (cursor < input.length && input[cursor] !== quote) {
6780
+ if (input[cursor] === "\\" && (input[cursor + 1] === quote || input[cursor + 1] === "\\")) {
6781
+ value += input[cursor + 1] === quote ? quote : "\\\\";
6782
+ cursor += 2;
6783
+ } else
6784
+ value += input[cursor++];
6785
+ }
6786
+ if (input[cursor++] !== quote)
6787
+ return null;
6788
+ if (cursor < input.length && !/\s/.test(input[cursor]))
6789
+ return null;
6790
+ attributes.set(name, value);
6791
+ }
6792
+ const pattern = attributes.get("pattern");
6793
+ if (pattern === undefined)
6794
+ return null;
6795
+ const group = attributes.get("group");
6796
+ const match = attributes.get("match");
6797
+ const max = attributes.get("max");
6798
+ if (match !== undefined && !/^\d+$/.test(match) || max !== undefined && !/^\d+$/.test(max))
6799
+ return null;
6800
+ return {
6801
+ pattern,
6802
+ flags: attributes.get("flags"),
6803
+ group: group === undefined ? undefined : /^\d+$/.test(group) ? Number(group) : group,
6804
+ match: match === undefined ? undefined : Number(match),
6805
+ maxLength: max === undefined ? undefined : Number(max)
6806
+ };
6807
+ }
6808
+
6682
6809
  // packages/parser/src/parser/rules/block/module/listpages/template/getters/index.ts
6683
6810
  function createVariableGetter(name, braceParam, parenParam, format) {
6811
+ if (name === "excerpt") {
6812
+ const options = braceParam === undefined ? null : parseExcerptOptions(braceParam);
6813
+ if (!options || parenParam !== undefined || format !== undefined)
6814
+ return () => "";
6815
+ const extract = compileTextExcerpt(options);
6816
+ return (ctx) => literalWikitext(extract(ctx.page.readableText ?? ""));
6817
+ }
6818
+ const registeredGetter = braceParam !== undefined ? createRegisteredGetter(name, braceParam, format) : null;
6819
+ if (registeredGetter)
6820
+ return registeredGetter;
6684
6821
  const braceGetter = braceParam !== undefined ? createBraceParamGetter(name, braceParam) : null;
6685
6822
  if (braceGetter)
6686
6823
  return braceGetter;
6824
+ if (braceParam !== undefined)
6825
+ return () => "";
6687
6826
  const parenGetter = parenParam !== undefined ? createParenParamGetter(name, parenParam) : null;
6688
6827
  if (parenGetter)
6689
6828
  return parenGetter;
@@ -6705,7 +6844,7 @@ function scanTemplateVariables(template) {
6705
6844
  if (start === -1)
6706
6845
  break;
6707
6846
  const contentStart = start + 2;
6708
- const end = template.indexOf("%%", contentStart);
6847
+ const end = template.slice(contentStart, contentStart + 8).toLowerCase() === "excerpt{" ? findExcerptEnd(template, contentStart) : template.indexOf("%%", contentStart);
6709
6848
  if (end === -1)
6710
6849
  break;
6711
6850
  const parsed = parseTemplateVariableContent(template.slice(contentStart, end));
@@ -6728,6 +6867,11 @@ function parseTemplateVariableContent(content) {
6728
6867
  if (cursor === 0)
6729
6868
  return null;
6730
6869
  const name = content.slice(0, cursor);
6870
+ if (name.toLowerCase() === "excerpt" && content[cursor] === "{") {
6871
+ if (!content.endsWith("}"))
6872
+ return null;
6873
+ return { name, braceParam: content.slice(cursor + 1, -1) };
6874
+ }
6731
6875
  let braceParam;
6732
6876
  let parenParam;
6733
6877
  let format;
@@ -6807,6 +6951,7 @@ function buildQuery(module) {
6807
6951
  updatedAt: module["updated-at"],
6808
6952
  createdBy: module["created-by"],
6809
6953
  rating: module.rating,
6954
+ ratingAxis: module["rating-axis"],
6810
6955
  votes: module.votes,
6811
6956
  name: module.name,
6812
6957
  fullname: module.fullname,
@@ -6861,6 +7006,7 @@ var KNOWN_VARIABLES = new Set([
6861
7006
  "parent_title_linked",
6862
7007
  "content",
6863
7008
  "preview",
7009
+ "excerpt",
6864
7010
  "summary",
6865
7011
  "first_paragraph",
6866
7012
  "tags",
@@ -6898,12 +7044,24 @@ function extractVariablesFromTemplate(template) {
6898
7044
  const contentIndices = new Set;
6899
7045
  const previewLengths = new Set;
6900
7046
  const formFields = new Set;
7047
+ const metadataKeys = new Set;
7048
+ const customRateKeys = new Set;
6901
7049
  let tagsLinkPrefix;
6902
7050
  let hiddenTagsLinkPrefix;
6903
7051
  for (const match of scanTemplateVariables(template)) {
6904
7052
  const varName = match.name.toLowerCase();
6905
7053
  if (match.braceParam !== undefined) {
6906
7054
  switch (varName) {
7055
+ case "metadata":
7056
+ metadataKeys.add(match.braceParam);
7057
+ variables.add("metadata");
7058
+ continue;
7059
+ case "customrate":
7060
+ case "customrate_votes":
7061
+ case "customrate_percent":
7062
+ customRateKeys.add(match.braceParam);
7063
+ variables.add(varName);
7064
+ continue;
6907
7065
  case "content":
6908
7066
  contentIndices.add(Number(match.braceParam));
6909
7067
  variables.add("content_n");
@@ -6953,6 +7111,8 @@ function extractVariablesFromTemplate(template) {
6953
7111
  contentIndices: Array.from(contentIndices).sort((a, b) => a - b),
6954
7112
  previewLengths: Array.from(previewLengths).sort((a, b) => a - b),
6955
7113
  formFields: Array.from(formFields).sort(),
7114
+ metadataKeys: [...metadataKeys].sort(),
7115
+ customRateKeys: [...customRateKeys].sort(),
6956
7116
  tagsLinkPrefix,
6957
7117
  hiddenTagsLinkPrefix
6958
7118
  };
@@ -6974,6 +7134,9 @@ function extractListPagesModule(listPages, state, result) {
6974
7134
  contentSectionIndices: extraction.contentIndices,
6975
7135
  previewLengths: extraction.previewLengths,
6976
7136
  formFields: extraction.formFields,
7137
+ metadataKeys: extraction.metadataKeys,
7138
+ customRateKeys: extraction.customRateKeys,
7139
+ needsReadableText: extraction.variables.some((name) => ["preview", "preview_n", "summary", "first_paragraph", "excerpt"].includes(name)),
6977
7140
  tagsLinkPrefix: extraction.tagsLinkPrefix,
6978
7141
  hiddenTagsLinkPrefix: extraction.hiddenTagsLinkPrefix,
6979
7142
  urlAttrPrefix: listPages["url-attr-prefix"],
@@ -7306,13 +7469,101 @@ function wrapListPagesResult(module, items, parse, data) {
7306
7469
  return result;
7307
7470
  }
7308
7471
 
7472
+ // packages/parser/src/parser/rules/block/module/rate/resolve.ts
7473
+ function refKey(ref) {
7474
+ return ref.kind === "main" ? "main" : `custom:${ref.axisKey}`;
7475
+ }
7476
+ async function resolveRatings(ast, fetchRatings) {
7477
+ const refs = new Map;
7478
+ mapDocument(ast, (element) => {
7479
+ if (isRating(element) && element.data.ref)
7480
+ refs.set(refKey(element.data.ref), element.data.ref);
7481
+ return element;
7482
+ });
7483
+ const states = new Map;
7484
+ if (fetchRatings && refs.size > 0) {
7485
+ for (const state of await fetchRatings([...refs.values()])) {
7486
+ const key = refKey(state.ref);
7487
+ if (refs.has(key))
7488
+ states.set(key, state);
7489
+ }
7490
+ }
7491
+ return mapDocument(ast, (element) => {
7492
+ if (!isRating(element))
7493
+ return element;
7494
+ const { state: _previous, ...data } = element.data;
7495
+ const state = data.ref ? states.get(refKey(data.ref)) : undefined;
7496
+ return { ...element, data: state ? { ...data, state } : data };
7497
+ });
7498
+ }
7499
+ function suppressModuleRatings(result) {
7500
+ const ast = mapDocument(getModuleParseAst(result), (element) => isRating(element) ? null : element);
7501
+ return "ast" in result ? { ...result, ast } : ast;
7502
+ }
7503
+ function isRating(element) {
7504
+ return element.element === "module" && (element.data.module === "rate" || element.data.module === "custom-rate");
7505
+ }
7506
+ function mapDocument(ast, transform) {
7507
+ const map = (elements) => elements.flatMap((original) => {
7508
+ const element = transform(original);
7509
+ if (!element)
7510
+ return [];
7511
+ if (element.element === "if") {
7512
+ return [
7513
+ {
7514
+ ...element,
7515
+ data: {
7516
+ ...element.data,
7517
+ then: map(element.data.then),
7518
+ else: map(element.data.else)
7519
+ }
7520
+ }
7521
+ ];
7522
+ }
7523
+ if (element.element === "ifexpr") {
7524
+ return [
7525
+ {
7526
+ ...element,
7527
+ data: {
7528
+ ...element.data,
7529
+ then: map(element.data.then),
7530
+ else: map(element.data.else)
7531
+ }
7532
+ }
7533
+ ];
7534
+ }
7535
+ if (element.element === "bibliography-block") {
7536
+ return [
7537
+ {
7538
+ ...element,
7539
+ data: {
7540
+ ...element.data,
7541
+ entries: element.data.entries.map((entry) => ({
7542
+ ...entry,
7543
+ key: map(entry.key),
7544
+ value: map(entry.value)
7545
+ }))
7546
+ }
7547
+ }
7548
+ ];
7549
+ }
7550
+ return [mapElementChildren(element, map)];
7551
+ });
7552
+ return {
7553
+ ...ast,
7554
+ elements: map(ast.elements),
7555
+ ...ast.footnotes ? { footnotes: ast.footnotes.map(map) } : {}
7556
+ };
7557
+ }
7558
+
7309
7559
  // packages/parser/src/parser/rules/block/module/listpages/resolve.ts
7310
7560
  function isListPagesModule2(module) {
7311
7561
  return module.module === "list-pages";
7312
7562
  }
7313
7563
  function resolveListPages(module, data, compiledTemplate, parse) {
7314
- const items = renderListPagesItems(module, data, compiledTemplate, parse);
7315
- return wrapListPagesResult(module, items, parse, data);
7564
+ const parseFragment = (source) => suppressModuleRatings(parse(source));
7565
+ const items = renderListPagesItems(module, data, compiledTemplate, parseFragment);
7566
+ return wrapListPagesResult(module, items, parseFragment, data);
7316
7567
  }
7317
7568
  // packages/parser/src/parser/rules/block/module/listpages/types/external-data.ts
7318
7569
  function definePageData(input) {
@@ -7326,7 +7577,6 @@ function definePageData(input) {
7326
7577
  hiddenTags: input.hiddenTags ?? [],
7327
7578
  children: input.children ?? 0,
7328
7579
  comments: input.comments ?? 0,
7329
- size: input.size ?? 0,
7330
7580
  rating: input.rating ?? 0,
7331
7581
  ratingVotes: input.ratingVotes ?? 0,
7332
7582
  revisions: input.revisions ?? 0
@@ -7404,6 +7654,7 @@ var URL_RESOLVABLE_FIELDS = [
7404
7654
  aliases: ["createdby", "created_by"]
7405
7655
  },
7406
7656
  { attr: "rating", queryKey: "rating", type: "string" },
7657
+ { attr: "rating-axis", queryKey: "ratingAxis", type: "string" },
7407
7658
  { attr: "votes", queryKey: "votes", type: "string" },
7408
7659
  { attr: "reverse", queryKey: "reverse", type: "boolean" }
7409
7660
  ];
@@ -7505,6 +7756,7 @@ var ORDER_FIELD_MAP = {
7505
7756
  votes: "votes",
7506
7757
  revisions: "revisions",
7507
7758
  comments: "comments",
7759
+ commented_at: "commented_at",
7508
7760
  pagelength: "size",
7509
7761
  size: "size",
7510
7762
  random: "random",
@@ -7512,6 +7764,13 @@ var ORDER_FIELD_MAP = {
7512
7764
  updated_at: "updated_at"
7513
7765
  };
7514
7766
  function parseOrder(value) {
7767
+ const metadata = /^metadata\{([^}]*)\}(?:\s+(asc|desc))?$/i.exec(value.trim());
7768
+ if (metadata)
7769
+ return {
7770
+ field: "metadata",
7771
+ key: metadata[1],
7772
+ direction: metadata[2]?.toLowerCase() === "asc" ? "asc" : "desc"
7773
+ };
7515
7774
  const defaultOrder = { field: "created_at", direction: "desc" };
7516
7775
  const trimmed = value.trim().toLowerCase();
7517
7776
  if (!trimmed)
@@ -7631,6 +7890,8 @@ function normalizeQuery(query) {
7631
7890
  result.linkTo = query.linkTo;
7632
7891
  if (query.createdBy)
7633
7892
  result.createdBy = query.createdBy;
7893
+ if (query.ratingAxis !== undefined)
7894
+ result.ratingAxis = query.ratingAxis;
7634
7895
  if (query.name)
7635
7896
  result.name = query.name;
7636
7897
  if (query.fullname)
@@ -8538,7 +8799,7 @@ function isListUsersModule2(module) {
8538
8799
  function resolveListUsers(_module, data, compiledTemplate, parse) {
8539
8800
  const ctx = { user: data.user };
8540
8801
  const substituted = compiledTemplate(ctx);
8541
- const itemAst = getModuleParseAst(parse(substituted));
8802
+ const itemAst = getModuleParseAst(suppressModuleRatings(parse(substituted)));
8542
8803
  return itemAst.elements;
8543
8804
  }
8544
8805
  // packages/parser/src/parser/rules/block/module/resolution/data-maps.ts
@@ -9334,11 +9595,11 @@ async function resolveModules(ast, dataProvider, options) {
9334
9595
  if (mergedStyles.length > 0)
9335
9596
  result.styles = mergedStyles;
9336
9597
  options.onDiagnostics?.(registry.diagnostics);
9337
- return registry.finalize(result, finalElements, pageTags, containsSyntaxFootnoteBlock(ast.elements));
9598
+ return resolveRatings(registry.finalize(result, finalElements, pageTags, containsSyntaxFootnoteBlock(ast.elements)), dataProvider.fetchRatings);
9338
9599
  }
9339
9600
  function createModuleParseFunction(options, dataProvider, registry) {
9340
9601
  const transform = createModuleSourceTransform(options, dataProvider);
9341
- return (source) => registry.register(options.parse(transform ? transform(source) : source), {
9602
+ return (source) => registry.register(suppressModuleRatings(options.parse(transform ? transform(source) : source)), {
9342
9603
  stripLegacyImplicitFootnoteBlock: true
9343
9604
  });
9344
9605
  }
@@ -12811,55 +13072,6 @@ var dateRule = {
12811
13072
  }
12812
13073
  };
12813
13074
 
12814
- // packages/parser/src/parser/rules/inline/size/content.ts
12815
- function parseSizeContent(ctx, startPos) {
12816
- const children = [];
12817
- let pos = startPos;
12818
- let consumed = 0;
12819
- while (pos < ctx.tokens.length) {
12820
- const token5 = ctx.tokens[pos];
12821
- if (!token5 || token5.type === "EOF") {
12822
- break;
12823
- }
12824
- const closeResult = tryConsumeSizeClose(ctx, pos);
12825
- if (closeResult) {
12826
- return {
12827
- children,
12828
- consumed: consumed + closeResult.consumed,
12829
- foundClose: true
12830
- };
12831
- }
12832
- const inlineCtx = { ...ctx, pos };
12833
- const inlineResult = parseInlineUntil(inlineCtx, "BLOCK_END_OPEN");
12834
- if (inlineResult.elements.length > 0) {
12835
- for (const element of inlineResult.elements)
12836
- children.push(element);
12837
- pos += inlineResult.consumed;
12838
- consumed += inlineResult.consumed;
12839
- } else {
12840
- children.push({ element: "text", data: token5.value });
12841
- pos++;
12842
- consumed++;
12843
- }
12844
- }
12845
- return { children, consumed, foundClose: false };
12846
- }
12847
- function tryConsumeSizeClose(ctx, pos) {
12848
- if (ctx.tokens[pos]?.type !== "BLOCK_END_OPEN") {
12849
- return null;
12850
- }
12851
- const closeNameResult = parseBlockName(ctx, pos + 1);
12852
- if (!closeNameResult || closeNameResult.name !== "size") {
12853
- return null;
12854
- }
12855
- let closePos = pos + 1 + closeNameResult.consumed;
12856
- let consumed = 1 + closeNameResult.consumed;
12857
- if (ctx.tokens[closePos]?.type === "BLOCK_CLOSE") {
12858
- consumed++;
12859
- }
12860
- return { consumed };
12861
- }
12862
-
12863
13075
  // packages/parser/src/parser/rules/inline/size/value.ts
12864
13076
  var VALID_SIZE_UNITS = ["px", "em", "rem", "ex", "%", "cm", "mm", "in", "pc"];
12865
13077
  var VALID_SIZE_KEYWORDS = [
@@ -12929,6 +13141,81 @@ function parseSizeOpen(ctx) {
12929
13141
  return { size: sizeResult.size, bodyStart: pos, consumed };
12930
13142
  }
12931
13143
 
13144
+ // packages/parser/src/parser/rules/inline/size/content.ts
13145
+ function parseSizeContent(ctx, startPos) {
13146
+ const children = [];
13147
+ let pos = startPos;
13148
+ let consumed = 0;
13149
+ const inlineEnd = findSizeClose(ctx, startPos);
13150
+ while (pos < (ctx.scope.inlineEnd ?? ctx.tokens.length)) {
13151
+ const token5 = ctx.tokens[pos];
13152
+ if (!token5 || token5.type === "EOF") {
13153
+ break;
13154
+ }
13155
+ const closeResult = tryConsumeSizeClose(ctx, pos);
13156
+ if (closeResult) {
13157
+ return {
13158
+ children,
13159
+ consumed: consumed + closeResult.consumed,
13160
+ foundClose: true
13161
+ };
13162
+ }
13163
+ const inlineCtx = { ...ctx, pos, scope: { ...ctx.scope, inlineEnd } };
13164
+ const inlineResult = parseInlineUntil(inlineCtx, "EOF");
13165
+ if (inlineResult.consumed > 0) {
13166
+ for (const element of inlineResult.elements)
13167
+ children.push(element);
13168
+ pos += inlineResult.consumed;
13169
+ consumed += inlineResult.consumed;
13170
+ } else {
13171
+ children.push({ element: "text", data: token5.value });
13172
+ pos++;
13173
+ consumed++;
13174
+ }
13175
+ }
13176
+ return { children, consumed, foundClose: false };
13177
+ }
13178
+ function findSizeClose(ctx, startPos) {
13179
+ const end = ctx.scope.inlineEnd ?? ctx.tokens.length;
13180
+ let depth = 0;
13181
+ for (let pos = startPos;pos < end; pos++) {
13182
+ const protectedEnd = protectedInlineRegionEnd(ctx.tokens, pos, end);
13183
+ if (protectedEnd > pos) {
13184
+ pos = protectedEnd - 1;
13185
+ continue;
13186
+ }
13187
+ const open = ctx.tokens[pos]?.type === "BLOCK_OPEN" ? parseSizeOpen({ ...ctx, pos }) : null;
13188
+ if (open) {
13189
+ depth++;
13190
+ pos = open.bodyStart - 1;
13191
+ continue;
13192
+ }
13193
+ const close = tryConsumeSizeClose(ctx, pos);
13194
+ if (!close)
13195
+ continue;
13196
+ if (depth === 0)
13197
+ return pos;
13198
+ depth--;
13199
+ pos += close.consumed - 1;
13200
+ }
13201
+ return end;
13202
+ }
13203
+ function tryConsumeSizeClose(ctx, pos) {
13204
+ if (ctx.tokens[pos]?.type !== "BLOCK_END_OPEN") {
13205
+ return null;
13206
+ }
13207
+ const closeNameResult = parseBlockName(ctx, pos + 1);
13208
+ if (!closeNameResult || closeNameResult.name !== "size") {
13209
+ return null;
13210
+ }
13211
+ let closePos = pos + 1 + closeNameResult.consumed;
13212
+ let consumed = 1 + closeNameResult.consumed;
13213
+ if (ctx.tokens[closePos]?.type === "BLOCK_CLOSE") {
13214
+ consumed++;
13215
+ }
13216
+ return { consumed };
13217
+ }
13218
+
12932
13219
  // packages/parser/src/parser/rules/inline/size/index.ts
12933
13220
  var sizeRule = {
12934
13221
  name: "size",
@@ -15341,14 +15628,17 @@ function parseSource(source, options, deferInclude) {
15341
15628
  }
15342
15629
  // packages/parser/src/pipeline/process.ts
15343
15630
  import {
15344
- DEFAULT_SETTINGS as DEFAULT_SETTINGS2
15631
+ DEFAULT_SETTINGS as DEFAULT_SETTINGS2,
15632
+ extractReadableText,
15633
+ extractFirstParagraph,
15634
+ countCharacters as countCharacters2
15345
15635
  } from "@wdprlib/ast";
15346
15636
 
15347
15637
  // packages/parser/src/parser/rules/block/module/resolution/resolve-async.ts
15348
15638
  async function resolveModulesWithAsyncParse(ast, dataProvider, options) {
15349
15639
  const registry = new ModuleDocumentRegistry;
15350
15640
  registry.register(ast);
15351
- const parse2 = async (source) => registry.register(await options.parse(source));
15641
+ const parse2 = async (source) => registry.register(suppressModuleRatings(await options.parse(source)));
15352
15642
  const [listPagesData, listUsersData, tagCloudData] = await Promise.all([
15353
15643
  buildListPagesDataMap(dataProvider, options.requirements.listPages ?? [], options.urlPath, options.paginationState),
15354
15644
  buildListUsersDataMap(dataProvider, options.requirements.listUsers ?? []),
@@ -15584,8 +15874,13 @@ async function processWikitext(source, options) {
15584
15874
  if (hasResolvableRequirements(extractDataRequirements(ast).requirements, options.dataProvider)) {
15585
15875
  diagnostics.push(createLimitDiagnostic("module-resolution-limit", `Module resolution stopped after ${DEFAULT_MODULE_MAX_PASSES} passes.`));
15586
15876
  }
15877
+ ast = await resolveRatings(ast, dataProvider.fetchRatings);
15878
+ const readableText = extractReadableText(ast, options.readableText);
15587
15879
  return {
15588
15880
  ast,
15881
+ readableText,
15882
+ firstParagraph: extractFirstParagraph(ast, options.readableText),
15883
+ characterCount: countCharacters2(readableText),
15589
15884
  page: options.page,
15590
15885
  settings,
15591
15886
  diagnostics,
@@ -15614,6 +15909,7 @@ function createLimitDiagnostic(code, message) {
15614
15909
  function createModuleDataProvider(options, context) {
15615
15910
  const provider = options.dataProvider;
15616
15911
  return {
15912
+ fetchRatings: provider?.fetchRatings ? (refs) => provider.fetchRatings(refs, context) : undefined,
15617
15913
  fetchListPages: provider?.fetchListPages ? (query, requirement) => provider.fetchListPages(query, requirement, context) : undefined,
15618
15914
  fetchListUsers: provider?.fetchListUsers ? (requirement) => provider.fetchListUsers(requirement, context) : undefined,
15619
15915
  fetchTagCloud: provider?.fetchTagCloud ? (requirement) => provider.fetchTagCloud(requirement, context) : undefined,