@wdprlib/parser 3.1.1 → 3.2.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 (124) hide show
  1. package/dist/index.cjs +312 -121
  2. package/dist/index.js +289 -98
  3. package/package.json +5 -3
  4. package/src/index.ts +163 -0
  5. package/src/lexer/index.ts +20 -0
  6. package/src/lexer/lexer.ts +687 -0
  7. package/src/lexer/tokens.ts +141 -0
  8. package/src/parser/constants.ts +173 -0
  9. package/src/parser/depth.ts +251 -0
  10. package/src/parser/index.ts +18 -0
  11. package/src/parser/parse.ts +315 -0
  12. package/src/parser/postprocess/divAdjacentParagraph.ts +76 -0
  13. package/src/parser/postprocess/index.ts +15 -0
  14. package/src/parser/postprocess/spanStrip.ts +697 -0
  15. package/src/parser/preprocess/expr.ts +265 -0
  16. package/src/parser/preprocess/index.ts +38 -0
  17. package/src/parser/preprocess/typography.ts +67 -0
  18. package/src/parser/preprocess/utils.ts +250 -0
  19. package/src/parser/preprocess/whitespace.ts +111 -0
  20. package/src/parser/rules/block/align.ts +282 -0
  21. package/src/parser/rules/block/bibliography.ts +359 -0
  22. package/src/parser/rules/block/block-list.ts +689 -0
  23. package/src/parser/rules/block/blockquote.ts +238 -0
  24. package/src/parser/rules/block/center.ts +87 -0
  25. package/src/parser/rules/block/clear-float.ts +75 -0
  26. package/src/parser/rules/block/code.ts +187 -0
  27. package/src/parser/rules/block/collapsible.ts +337 -0
  28. package/src/parser/rules/block/comment.ts +73 -0
  29. package/src/parser/rules/block/content-separator.ts +79 -0
  30. package/src/parser/rules/block/definition-list.ts +270 -0
  31. package/src/parser/rules/block/div.ts +400 -0
  32. package/src/parser/rules/block/embed-block.ts +153 -0
  33. package/src/parser/rules/block/footnoteblock.ts +200 -0
  34. package/src/parser/rules/block/heading.ts +142 -0
  35. package/src/parser/rules/block/horizontal-rule.ts +61 -0
  36. package/src/parser/rules/block/html.ts +222 -0
  37. package/src/parser/rules/block/iframe.ts +239 -0
  38. package/src/parser/rules/block/iftags.ts +150 -0
  39. package/src/parser/rules/block/include.ts +179 -0
  40. package/src/parser/rules/block/index.ts +127 -0
  41. package/src/parser/rules/block/list.ts +244 -0
  42. package/src/parser/rules/block/math.ts +183 -0
  43. package/src/parser/rules/block/module/backlinks/index.ts +31 -0
  44. package/src/parser/rules/block/module/backlinks/types.ts +21 -0
  45. package/src/parser/rules/block/module/categories/index.ts +34 -0
  46. package/src/parser/rules/block/module/categories/types.ts +21 -0
  47. package/src/parser/rules/block/module/css/index.ts +37 -0
  48. package/src/parser/rules/block/module/iftags/condition.ts +109 -0
  49. package/src/parser/rules/block/module/iftags/index.ts +26 -0
  50. package/src/parser/rules/block/module/iftags/preprocess.ts +140 -0
  51. package/src/parser/rules/block/module/iftags/resolve.ts +73 -0
  52. package/src/parser/rules/block/module/iftags/types.ts +63 -0
  53. package/src/parser/rules/block/module/include/index.ts +20 -0
  54. package/src/parser/rules/block/module/include/resolve.ts +556 -0
  55. package/src/parser/rules/block/module/index.ts +122 -0
  56. package/src/parser/rules/block/module/join/index.ts +34 -0
  57. package/src/parser/rules/block/module/join/types.ts +23 -0
  58. package/src/parser/rules/block/module/listpages/compiler.ts +453 -0
  59. package/src/parser/rules/block/module/listpages/extract.ts +410 -0
  60. package/src/parser/rules/block/module/listpages/index.ts +83 -0
  61. package/src/parser/rules/block/module/listpages/normalize.ts +390 -0
  62. package/src/parser/rules/block/module/listpages/parser.ts +106 -0
  63. package/src/parser/rules/block/module/listpages/resolve.ts +130 -0
  64. package/src/parser/rules/block/module/listpages/types.ts +513 -0
  65. package/src/parser/rules/block/module/listpages/url-resolver.ts +186 -0
  66. package/src/parser/rules/block/module/listusers/compiler.ts +77 -0
  67. package/src/parser/rules/block/module/listusers/extract.ts +45 -0
  68. package/src/parser/rules/block/module/listusers/index.ts +36 -0
  69. package/src/parser/rules/block/module/listusers/parser.ts +54 -0
  70. package/src/parser/rules/block/module/listusers/resolve.ts +58 -0
  71. package/src/parser/rules/block/module/listusers/types.ts +93 -0
  72. package/src/parser/rules/block/module/mapping.ts +61 -0
  73. package/src/parser/rules/block/module/page-tree/index.ts +38 -0
  74. package/src/parser/rules/block/module/page-tree/types.ts +29 -0
  75. package/src/parser/rules/block/module/rate/index.ts +28 -0
  76. package/src/parser/rules/block/module/rate/types.ts +19 -0
  77. package/src/parser/rules/block/module/resolve.ts +411 -0
  78. package/src/parser/rules/block/module/types-common.ts +59 -0
  79. package/src/parser/rules/block/module/types.ts +61 -0
  80. package/src/parser/rules/block/module/utils.ts +43 -0
  81. package/src/parser/rules/block/module/walk.ts +380 -0
  82. package/src/parser/rules/block/module.ts +164 -0
  83. package/src/parser/rules/block/orphan-li.ts +177 -0
  84. package/src/parser/rules/block/paragraph.ts +157 -0
  85. package/src/parser/rules/block/table-block.ts +726 -0
  86. package/src/parser/rules/block/table.ts +441 -0
  87. package/src/parser/rules/block/tabview.ts +331 -0
  88. package/src/parser/rules/block/toc.ts +129 -0
  89. package/src/parser/rules/block/utils.ts +615 -0
  90. package/src/parser/rules/index.ts +49 -0
  91. package/src/parser/rules/inline/anchor-name.ts +154 -0
  92. package/src/parser/rules/inline/anchor.ts +327 -0
  93. package/src/parser/rules/inline/bibcite.ts +153 -0
  94. package/src/parser/rules/inline/bold.ts +86 -0
  95. package/src/parser/rules/inline/color.ts +140 -0
  96. package/src/parser/rules/inline/comment.ts +90 -0
  97. package/src/parser/rules/inline/equation-ref.ts +115 -0
  98. package/src/parser/rules/inline/expr.ts +526 -0
  99. package/src/parser/rules/inline/footnote.ts +223 -0
  100. package/src/parser/rules/inline/guillemet.ts +64 -0
  101. package/src/parser/rules/inline/html.ts +132 -0
  102. package/src/parser/rules/inline/image.ts +328 -0
  103. package/src/parser/rules/inline/index.ts +150 -0
  104. package/src/parser/rules/inline/italic.ts +74 -0
  105. package/src/parser/rules/inline/line-break.ts +326 -0
  106. package/src/parser/rules/inline/link-anchor.ts +147 -0
  107. package/src/parser/rules/inline/link-single.ts +164 -0
  108. package/src/parser/rules/inline/link-star.ts +134 -0
  109. package/src/parser/rules/inline/link-triple.ts +267 -0
  110. package/src/parser/rules/inline/math-inline.ts +126 -0
  111. package/src/parser/rules/inline/monospace.ts +78 -0
  112. package/src/parser/rules/inline/raw.ts +262 -0
  113. package/src/parser/rules/inline/size.ts +244 -0
  114. package/src/parser/rules/inline/span.ts +424 -0
  115. package/src/parser/rules/inline/strikethrough.ts +115 -0
  116. package/src/parser/rules/inline/subscript.ts +84 -0
  117. package/src/parser/rules/inline/superscript.ts +84 -0
  118. package/src/parser/rules/inline/text.ts +84 -0
  119. package/src/parser/rules/inline/underline.ts +127 -0
  120. package/src/parser/rules/inline/user.ts +147 -0
  121. package/src/parser/rules/inline/utils.ts +344 -0
  122. package/src/parser/rules/types.ts +252 -0
  123. package/src/parser/rules/utils.ts +155 -0
  124. package/src/parser/toc.ts +130 -0
package/dist/index.js CHANGED
@@ -9077,6 +9077,8 @@ function parseTagCondition(condition) {
9077
9077
  const required = [];
9078
9078
  const forbidden = [];
9079
9079
  const optional = [];
9080
+ let hasEmptyRequired = false;
9081
+ let hasEmptyForbidden = false;
9080
9082
  const parts = condition.trim().split(/\s+/);
9081
9083
  for (const part of parts) {
9082
9084
  if (!part)
@@ -9085,20 +9087,31 @@ function parseTagCondition(condition) {
9085
9087
  const tag = part.slice(1);
9086
9088
  if (tag)
9087
9089
  required.push(tag);
9090
+ else
9091
+ hasEmptyRequired = true;
9088
9092
  } else if (part.startsWith("-")) {
9089
9093
  const tag = part.slice(1);
9090
9094
  if (tag)
9091
9095
  forbidden.push(tag);
9096
+ else
9097
+ hasEmptyForbidden = true;
9092
9098
  } else {
9093
9099
  optional.push(part);
9094
9100
  }
9095
9101
  }
9096
- return { required, forbidden, optional };
9102
+ return { required, forbidden, optional, hasEmptyRequired, hasEmptyForbidden };
9097
9103
  }
9098
9104
  function evaluateTagCondition(condition, pageTags) {
9099
- if (condition.required.length === 0 && condition.forbidden.length === 0 && condition.optional.length === 0) {
9105
+ const noNamedTokens = condition.required.length === 0 && condition.forbidden.length === 0 && condition.optional.length === 0;
9106
+ if (noNamedTokens && !condition.hasEmptyRequired && !condition.hasEmptyForbidden) {
9100
9107
  return false;
9101
9108
  }
9109
+ if (condition.hasEmptyRequired) {
9110
+ return false;
9111
+ }
9112
+ if (condition.hasEmptyForbidden && noNamedTokens) {
9113
+ return true;
9114
+ }
9102
9115
  const tagSet = new Set(pageTags);
9103
9116
  for (const tag of condition.required) {
9104
9117
  if (!tagSet.has(tag)) {
@@ -9118,19 +9131,10 @@ function evaluateTagCondition(condition, pageTags) {
9118
9131
  return true;
9119
9132
  }
9120
9133
 
9121
- // packages/parser/src/parser/rules/block/module/iftags/preprocess.ts
9122
- var BASE_PLACEHOLDER_OPEN = "";
9123
- var BASE_PLACEHOLDER_CLOSE = "";
9124
- var INNERMOST_IFTAGS_PATTERN = /\[\[\s*iftags\b([^\]]*)\]\]((?:(?!\[\[\s*iftags\b|\[\[\/\s*iftags\s*\]\]).)*)\[\[\/\s*iftags\s*\]\]/gis;
9134
+ // packages/parser/src/parser/preprocess/utils.ts
9135
+ var BASE_PLACEHOLDER_OPEN = "";
9136
+ var BASE_PLACEHOLDER_CLOSE = "";
9125
9137
  var RAW_BLOCK_OPEN_PATTERN = /\[\[\s*(code|html)\b[^\]]*\]\]/iy;
9126
- function preprocessIftags(source, pageTags) {
9127
- if (!source.includes("[["))
9128
- return source;
9129
- const sentinels = makeUniqueSentinels(source);
9130
- const { masked, placeholders } = maskRawRegions(source, sentinels);
9131
- const reduced = reduceIftags(masked, pageTags);
9132
- return restorePlaceholders(reduced, placeholders, sentinels);
9133
- }
9134
9138
  function makeUniqueSentinels(source) {
9135
9139
  let open = BASE_PLACEHOLDER_OPEN;
9136
9140
  let close = BASE_PLACEHOLDER_CLOSE;
@@ -9140,46 +9144,91 @@ function makeUniqueSentinels(source) {
9140
9144
  }
9141
9145
  return { open, close };
9142
9146
  }
9143
- function reduceIftags(source, pageTags) {
9144
- let current = source;
9145
- const maxIterations = source.length + 1;
9146
- const tagSet = pageTags ?? [];
9147
- for (let i = 0;i < maxIterations; i++) {
9148
- const depths = pageTags === null ? computeBracketDepths(current) : null;
9149
- let changed = false;
9150
- const next = current.replace(INNERMOST_IFTAGS_PATTERN, (match, cond, body, offset) => {
9151
- if (depths !== null && depths[offset] === 0) {
9152
- return match;
9147
+ function maskRawRegions(source, sentinels) {
9148
+ const placeholders = [];
9149
+ let masked = "";
9150
+ let i = 0;
9151
+ while (i < source.length) {
9152
+ if (source[i] === "[" && source[i + 1] === "[") {
9153
+ RAW_BLOCK_OPEN_PATTERN.lastIndex = i;
9154
+ const openMatch = RAW_BLOCK_OPEN_PATTERN.exec(source);
9155
+ if (openMatch) {
9156
+ const name = openMatch[1].toLowerCase();
9157
+ const openLen = openMatch[0].length;
9158
+ const closePattern = new RegExp(`\\[\\[\\/\\s*${name}\\s*\\]\\]`, "ig");
9159
+ closePattern.lastIndex = i + openLen;
9160
+ const closeMatch = closePattern.exec(source);
9161
+ if (closeMatch) {
9162
+ const regionEnd = closeMatch.index + closeMatch[0].length;
9163
+ masked += pushPlaceholder(placeholders, source.slice(i, regionEnd), sentinels);
9164
+ i = regionEnd;
9165
+ continue;
9166
+ }
9167
+ if (name === "code") {
9168
+ masked += pushPlaceholder(placeholders, source.slice(i), sentinels);
9169
+ i = source.length;
9170
+ continue;
9171
+ }
9153
9172
  }
9154
- changed = true;
9155
- const condition = parseTagCondition(cond);
9156
- return evaluateTagCondition(condition, tagSet) ? body : "";
9157
- });
9158
- if (!changed)
9159
- return current;
9160
- current = next;
9173
+ }
9174
+ if (source[i] === "@" && source[i + 1] === "<") {
9175
+ const close = source.indexOf(">@", i + 2);
9176
+ const newline = source.indexOf(`
9177
+ `, i + 2);
9178
+ if (close !== -1 && (newline === -1 || close < newline)) {
9179
+ const regionEnd = close + 2;
9180
+ masked += pushPlaceholder(placeholders, source.slice(i, regionEnd), sentinels);
9181
+ i = regionEnd;
9182
+ continue;
9183
+ }
9184
+ }
9185
+ if (source[i] === "@" && source[i + 1] === "@") {
9186
+ const close = source.indexOf("@@", i + 2);
9187
+ const newline = source.indexOf(`
9188
+ `, i + 2);
9189
+ if (close !== -1 && (newline === -1 || close < newline)) {
9190
+ const regionEnd = close + 2;
9191
+ masked += pushPlaceholder(placeholders, source.slice(i, regionEnd), sentinels);
9192
+ i = regionEnd;
9193
+ continue;
9194
+ }
9195
+ }
9196
+ masked += source[i];
9197
+ i++;
9161
9198
  }
9162
- return current;
9199
+ return { masked, placeholders };
9200
+ }
9201
+ function pushPlaceholder(placeholders, text, sentinels) {
9202
+ const idx = placeholders.length;
9203
+ placeholders.push(text);
9204
+ return `${sentinels.open}${idx}${sentinels.close}`;
9205
+ }
9206
+ function escapeRegex(str) {
9207
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
9208
+ }
9209
+ function restorePlaceholders(source, placeholders, sentinels) {
9210
+ const pattern = new RegExp(`${escapeRegex(sentinels.open)}(\\d+)${escapeRegex(sentinels.close)}`, "g");
9211
+ return source.replace(pattern, (_, idx) => placeholders[Number(idx)] ?? "");
9163
9212
  }
9164
- function computeBracketDepths(masked) {
9165
- const n = masked.length;
9213
+ function computeBracketDepths(source) {
9214
+ const n = source.length;
9166
9215
  const depths = new Int32Array(n + 1);
9167
9216
  let depth = 0;
9168
9217
  let i = 0;
9169
9218
  while (i < n) {
9170
9219
  depths[i] = depth;
9171
- const c = masked.charCodeAt(i);
9172
- const c1 = i + 1 < n ? masked.charCodeAt(i + 1) : -1;
9173
- const c2 = i + 2 < n ? masked.charCodeAt(i + 2) : -1;
9174
- if (depth > 0 && c === 34 && precededByEqualsAttr(masked, i)) {
9175
- const end = findQuoteEnd(masked, i + 1);
9220
+ const c = source.charCodeAt(i);
9221
+ const c1 = i + 1 < n ? source.charCodeAt(i + 1) : -1;
9222
+ const c2 = i + 2 < n ? source.charCodeAt(i + 2) : -1;
9223
+ if (depth > 0 && c === 34 && precededByEqualsAttr(source, i)) {
9224
+ const end = findQuoteEnd(source, i + 1);
9176
9225
  for (let k = i;k <= end; k++)
9177
9226
  depths[k] = depth;
9178
9227
  i = end + 1;
9179
9228
  continue;
9180
9229
  }
9181
9230
  if (c === 91 && c1 === 91 && c2 === 91) {
9182
- const end = findTripleLinkEnd(masked, i + 3);
9231
+ const end = findTripleLinkEnd(source, i + 3);
9183
9232
  for (let k = i;k <= end; k++)
9184
9233
  depths[k] = depth;
9185
9234
  i = end + 1;
@@ -9236,71 +9285,198 @@ function findTripleLinkEnd(s, from) {
9236
9285
  }
9237
9286
  return s.length - 1;
9238
9287
  }
9239
- function maskRawRegions(source, sentinels) {
9240
- const placeholders = [];
9241
- let masked = "";
9288
+
9289
+ // packages/parser/src/parser/rules/block/module/iftags/preprocess.ts
9290
+ var INNERMOST_IFTAGS_PATTERN = /\[\[\s*iftags\b([^\]]*)\]\]((?:(?!\[\[\s*iftags\b|\[\[\/\s*iftags\s*\]\]).)*)\[\[\/\s*iftags\s*\]\]/gis;
9291
+ function preprocessIftags(source, pageTags) {
9292
+ if (!source.includes("[["))
9293
+ return source;
9294
+ const sentinels = makeUniqueSentinels(source);
9295
+ const { masked, placeholders } = maskRawRegions(source, sentinels);
9296
+ const reduced = reduceIftags(masked, pageTags);
9297
+ return restorePlaceholders(reduced, placeholders, sentinels);
9298
+ }
9299
+ function reduceIftags(source, pageTags) {
9300
+ let current = source;
9301
+ const maxIterations = source.length + 1;
9302
+ const tagSet = pageTags ?? [];
9303
+ for (let i = 0;i < maxIterations; i++) {
9304
+ const depths = pageTags === null ? computeBracketDepths(current) : null;
9305
+ let changed = false;
9306
+ const next = current.replace(INNERMOST_IFTAGS_PATTERN, (match, cond, body, offset) => {
9307
+ if (depths !== null && depths[offset] === 0) {
9308
+ return match;
9309
+ }
9310
+ changed = true;
9311
+ const condition = parseTagCondition(cond);
9312
+ return evaluateTagCondition(condition, tagSet) ? body : "";
9313
+ });
9314
+ if (!changed)
9315
+ return current;
9316
+ current = next;
9317
+ }
9318
+ return current;
9319
+ }
9320
+
9321
+ // packages/parser/src/parser/preprocess/expr.ts
9322
+ import { evaluateExpression, formatExprValue, isTruthy } from "@wdprlib/ast";
9323
+ function preprocessExpr(source) {
9324
+ if (!source.includes("[[#"))
9325
+ return source;
9326
+ const sentinels = makeUniqueSentinels(source);
9327
+ const { masked, placeholders } = maskRawRegions(source, sentinels);
9328
+ const reduced = reduceExpr(masked);
9329
+ return restorePlaceholders(reduced, placeholders, sentinels);
9330
+ }
9331
+ function reduceExpr(source) {
9332
+ let current = source;
9333
+ const maxIterations = source.length + 1;
9334
+ for (let i = 0;i < maxIterations; i++) {
9335
+ const next = expandInnermost(current);
9336
+ if (next === current)
9337
+ return current;
9338
+ current = next;
9339
+ }
9340
+ return current;
9341
+ }
9342
+ function expandInnermost(source) {
9343
+ const depths = computeBracketDepths(source);
9344
+ let result = "";
9242
9345
  let i = 0;
9346
+ let replaced = false;
9243
9347
  while (i < source.length) {
9244
- if (source[i] === "[" && source[i + 1] === "[") {
9245
- RAW_BLOCK_OPEN_PATTERN.lastIndex = i;
9246
- const openMatch = RAW_BLOCK_OPEN_PATTERN.exec(source);
9247
- if (openMatch) {
9248
- const name = openMatch[1].toLowerCase();
9249
- const openLen = openMatch[0].length;
9250
- const closePattern = new RegExp(`\\[\\[\\/\\s*${name}\\s*\\]\\]`, "ig");
9251
- closePattern.lastIndex = i + openLen;
9252
- const closeMatch = closePattern.exec(source);
9253
- if (closeMatch) {
9254
- const regionEnd = closeMatch.index + closeMatch[0].length;
9255
- masked += pushPlaceholder(placeholders, source.slice(i, regionEnd), sentinels);
9256
- i = regionEnd;
9257
- continue;
9258
- }
9259
- if (name === "code") {
9260
- masked += pushPlaceholder(placeholders, source.slice(i), sentinels);
9261
- i = source.length;
9262
- continue;
9263
- }
9264
- }
9265
- }
9266
- if (source[i] === "@" && source[i + 1] === "<") {
9267
- const close = source.indexOf(">@", i + 2);
9268
- const newline = source.indexOf(`
9269
- `, i + 2);
9270
- if (close !== -1 && (newline === -1 || close < newline)) {
9271
- const regionEnd = close + 2;
9272
- masked += pushPlaceholder(placeholders, source.slice(i, regionEnd), sentinels);
9273
- i = regionEnd;
9348
+ const kind = matchDirectiveKind(source, i);
9349
+ if (kind !== null && depths[i] > 0) {
9350
+ const match = tryParseInnermostDirective(source, i, kind);
9351
+ if (match !== null) {
9352
+ result += evaluateDirective(kind, match);
9353
+ i = match.end;
9354
+ replaced = true;
9274
9355
  continue;
9275
9356
  }
9276
9357
  }
9277
- if (source[i] === "@" && source[i + 1] === "@") {
9278
- const close = source.indexOf("@@", i + 2);
9279
- const newline = source.indexOf(`
9280
- `, i + 2);
9281
- if (close !== -1 && (newline === -1 || close < newline)) {
9282
- const regionEnd = close + 2;
9283
- masked += pushPlaceholder(placeholders, source.slice(i, regionEnd), sentinels);
9284
- i = regionEnd;
9285
- continue;
9358
+ result += source[i];
9359
+ i++;
9360
+ }
9361
+ return replaced ? result : source;
9362
+ }
9363
+ function matchDirectiveKind(source, i) {
9364
+ if (!source.startsWith("[[#", i))
9365
+ return null;
9366
+ if (source.startsWith("ifexpr", i + 3) && !isIdentChar(source[i + 9])) {
9367
+ return "ifexpr";
9368
+ }
9369
+ if (source.startsWith("if", i + 3) && !isIdentChar(source[i + 5])) {
9370
+ return "if";
9371
+ }
9372
+ if (source.startsWith("expr", i + 3) && !isIdentChar(source[i + 7])) {
9373
+ return "expr";
9374
+ }
9375
+ return null;
9376
+ }
9377
+ function tryParseInnermostDirective(source, start, kind) {
9378
+ const keywordLen = kind === "ifexpr" ? 6 : kind === "expr" ? 4 : 2;
9379
+ let pos = start + 3 + keywordLen;
9380
+ while (pos < source.length && isWhitespace(source[pos]))
9381
+ pos++;
9382
+ const headStart = pos;
9383
+ let blockDepth = 0;
9384
+ let linkDepth = 0;
9385
+ const pipes = [];
9386
+ let closeStart = -1;
9387
+ while (pos < source.length) {
9388
+ if (matchDirectiveKind(source, pos) !== null) {
9389
+ return null;
9390
+ }
9391
+ if (source.startsWith("[[[", pos)) {
9392
+ linkDepth++;
9393
+ pos += 3;
9394
+ continue;
9395
+ }
9396
+ if (linkDepth > 0 && source.startsWith("]]]", pos)) {
9397
+ linkDepth--;
9398
+ pos += 3;
9399
+ continue;
9400
+ }
9401
+ if (linkDepth > 0) {
9402
+ pos++;
9403
+ continue;
9404
+ }
9405
+ if (source.startsWith("[[", pos)) {
9406
+ blockDepth++;
9407
+ pos += 2;
9408
+ continue;
9409
+ }
9410
+ if (source.startsWith("]]", pos)) {
9411
+ if (blockDepth === 0) {
9412
+ closeStart = pos;
9413
+ break;
9286
9414
  }
9415
+ blockDepth--;
9416
+ pos += 2;
9417
+ continue;
9287
9418
  }
9288
- masked += source[i];
9289
- i++;
9419
+ if (source[pos] === "|" && blockDepth === 0 && linkDepth === 0) {
9420
+ pipes.push(pos);
9421
+ }
9422
+ pos++;
9290
9423
  }
9291
- return { masked, placeholders };
9424
+ if (closeStart === -1)
9425
+ return null;
9426
+ const hasPipe = pipes.length > 0;
9427
+ if (!hasPipe && (kind === "if" || kind === "ifexpr"))
9428
+ return null;
9429
+ let head;
9430
+ let thenText = "";
9431
+ let elseText = "";
9432
+ if (!hasPipe) {
9433
+ head = source.slice(headStart, closeStart).trim();
9434
+ } else {
9435
+ head = source.slice(headStart, pipes[0]).trim();
9436
+ if (pipes.length >= 2) {
9437
+ thenText = source.slice(pipes[0] + 1, pipes[1]).trim();
9438
+ elseText = source.slice(pipes[1] + 1, closeStart).trim();
9439
+ } else {
9440
+ thenText = source.slice(pipes[0] + 1, closeStart).trim();
9441
+ }
9442
+ }
9443
+ return {
9444
+ end: closeStart + 2,
9445
+ head,
9446
+ thenText,
9447
+ elseText,
9448
+ hasPipe
9449
+ };
9292
9450
  }
9293
- function pushPlaceholder(placeholders, text, sentinels) {
9294
- const idx = placeholders.length;
9295
- placeholders.push(text);
9296
- return `${sentinels.open}${idx}${sentinels.close}`;
9451
+ function evaluateDirective(kind, m) {
9452
+ if (kind === "expr") {
9453
+ const result2 = evaluateExpression(m.head);
9454
+ if (result2.success)
9455
+ return formatExprValue(result2.value);
9456
+ if (result2.error === "empty expression")
9457
+ return "";
9458
+ return "ERROR";
9459
+ }
9460
+ if (kind === "if") {
9461
+ if (!m.hasPipe)
9462
+ return "";
9463
+ return isTruthy(m.head) ? m.thenText : m.elseText;
9464
+ }
9465
+ if (!m.hasPipe)
9466
+ return "";
9467
+ const result = evaluateExpression(m.head);
9468
+ if (!result.success)
9469
+ return "ERROR";
9470
+ return result.value !== 0 && !Number.isNaN(result.value) ? m.thenText : m.elseText;
9297
9471
  }
9298
- function escapeRegex(str) {
9299
- return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
9472
+ function isWhitespace(ch) {
9473
+ return ch === " " || ch === "\t" || ch === `
9474
+ ` || ch === "\r";
9300
9475
  }
9301
- function restorePlaceholders(source, placeholders, sentinels) {
9302
- const pattern = new RegExp(`${escapeRegex(sentinels.open)}(\\d+)${escapeRegex(sentinels.close)}`, "g");
9303
- return source.replace(pattern, (_, idx) => placeholders[Number(idx)] ?? "");
9476
+ function isIdentChar(ch) {
9477
+ if (!ch)
9478
+ return false;
9479
+ return /[a-z0-9_-]/i.test(ch);
9304
9480
  }
9305
9481
 
9306
9482
  // packages/parser/src/parser/parse.ts
@@ -9411,7 +9587,8 @@ class Parser {
9411
9587
  }
9412
9588
  }
9413
9589
  function parse(source, options) {
9414
- const iftagsProcessed = options?.pageTags !== undefined ? preprocessIftags(source, options.pageTags) : source;
9590
+ const ifProcessed = preprocessExpr(source);
9591
+ const iftagsProcessed = options?.pageTags !== undefined ? preprocessIftags(ifProcessed, options.pageTags) : ifProcessed;
9415
9592
  const preprocessed = preprocess(iftagsProcessed);
9416
9593
  const tokens = tokenize(preprocessed, { trackPositions: options?.trackPositions });
9417
9594
  return new Parser(tokens, options).parse();
@@ -10383,15 +10560,23 @@ async function resolveIncludesAsync(source, fetcher, options) {
10383
10560
  return expandIterativeAsync(source, cachedFetcher, maxIterations);
10384
10561
  }
10385
10562
  var INCLUDE_OPEN_PATTERN = /^\[\[include\s/gim;
10563
+ function hasAttributes(innerSoFar) {
10564
+ if (innerSoFar.includes("|"))
10565
+ return true;
10566
+ const trimmed = innerSoFar.trimStart();
10567
+ const ws = trimmed.search(/\s/);
10568
+ if (ws === -1)
10569
+ return false;
10570
+ return trimmed.slice(ws).trim().length > 0;
10571
+ }
10386
10572
  function isRestOfLineBlank(source, pos) {
10387
10573
  for (let i = pos;i < source.length; i++) {
10388
10574
  const ch = source[i];
10389
10575
  if (ch === `
10390
10576
  `)
10391
10577
  return true;
10392
- if (ch === " " || ch === "\t" || ch === "\r" || ch === "]")
10393
- continue;
10394
- return false;
10578
+ if (ch !== " " && ch !== "\t" && ch !== "\r")
10579
+ return false;
10395
10580
  }
10396
10581
  return true;
10397
10582
  }
@@ -10425,6 +10610,12 @@ function scanIncludeDirectives(source) {
10425
10610
  depth--;
10426
10611
  i += 2;
10427
10612
  if (depth <= 0) {
10613
+ const innerSoFar = source.slice(contentStart, closeStart);
10614
+ if (hasAttributes(innerSoFar)) {
10615
+ while (i < source.length && source[i] === "]") {
10616
+ i++;
10617
+ }
10618
+ }
10428
10619
  const onOpenerLine = firstNewline === -1 || closeStart < firstNewline;
10429
10620
  if (onOpenerLine || isRestOfLineBlank(source, i)) {
10430
10621
  closeEnd = i;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wdprlib/parser",
3
- "version": "3.1.1",
3
+ "version": "3.2.0",
4
4
  "description": "Parser for Wikidot markup",
5
5
  "keywords": [
6
6
  "ast",
@@ -15,7 +15,8 @@
15
15
  "directory": "packages/parser"
16
16
  },
17
17
  "files": [
18
- "dist"
18
+ "dist",
19
+ "src"
19
20
  ],
20
21
  "type": "module",
21
22
  "sideEffects": false,
@@ -24,6 +25,7 @@
24
25
  "types": "./dist/index.d.ts",
25
26
  "exports": {
26
27
  ".": {
28
+ "bun": "./src/index.ts",
27
29
  "import": {
28
30
  "types": "./dist/index.d.ts",
29
31
  "default": "./dist/index.js"
@@ -39,6 +41,6 @@
39
41
  },
40
42
  "dependencies": {
41
43
  "@braintree/sanitize-url": "^7.1.1",
42
- "@wdprlib/ast": "2.0.0"
44
+ "@wdprlib/ast": "2.1.0"
43
45
  }
44
46
  }
package/src/index.ts ADDED
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Wikidot markup parser.
3
+ *
4
+ * This package converts Wikidot wikitext source into an abstract syntax
5
+ * tree (AST) defined by `@wdprlib/ast`. It also provides module-resolution
6
+ * utilities for dynamic constructs such as `[[module ListPages]]`,
7
+ * `[[module ListUsers]]`, `[[include]]`, and `[[iftags]]`.
8
+ *
9
+ * Typical usage:
10
+ *
11
+ * ```ts
12
+ * import { parse } from "@wdprlib/parser";
13
+ *
14
+ * const { ast, diagnostics } = parse("**bold** and //italic//");
15
+ * ```
16
+ *
17
+ * For server-side module resolution, see {@link extractDataRequirements},
18
+ * {@link resolveModules}, and {@link resolveIncludes}.
19
+ *
20
+ * @packageDocumentation
21
+ */
22
+
23
+ // Re-export AST types and utilities from @wdprlib/ast
24
+ export type {
25
+ Position,
26
+ Point,
27
+ Version,
28
+ Element,
29
+ SyntaxTree,
30
+ ContainerType,
31
+ ContainerData,
32
+ AttributeMap,
33
+ VariableMap,
34
+ Alignment,
35
+ LinkType,
36
+ LinkLocation,
37
+ LinkLabel,
38
+ PageRef,
39
+ ImageSource,
40
+ FloatAlignment,
41
+ ListType,
42
+ ListItem,
43
+ ListData,
44
+ CodeBlockData,
45
+ TabData,
46
+ TableCell,
47
+ TableRow,
48
+ TableData,
49
+ DefinitionListItem,
50
+ Module,
51
+ CollapsibleData,
52
+ ClearFloat,
53
+ AnchorTarget,
54
+ HeaderType,
55
+ AlignType,
56
+ HeadingLevel,
57
+ Heading,
58
+ DateItem,
59
+ Embed,
60
+ TocEntry,
61
+ // Diagnostics
62
+ Diagnostic,
63
+ DiagnosticSeverity,
64
+ ParseResult,
65
+ } from "@wdprlib/ast";
66
+ export {
67
+ createPoint,
68
+ createPosition,
69
+ text,
70
+ container,
71
+ paragraph,
72
+ bold,
73
+ italics,
74
+ heading,
75
+ lineBreak,
76
+ horizontalRule,
77
+ link,
78
+ list,
79
+ listItemElements,
80
+ listItemSubList,
81
+ } from "@wdprlib/ast";
82
+
83
+ // Wikitext settings (re-exported from @wdprlib/ast)
84
+ export type { WikitextMode, WikitextSettings } from "@wdprlib/ast";
85
+ export { createSettings, DEFAULT_SETTINGS } from "@wdprlib/ast";
86
+
87
+ // Lexer
88
+ export type { TokenType, Token, LexerOptions } from "./lexer";
89
+ export { Lexer, tokenize, createToken } from "./lexer";
90
+
91
+ // Parser
92
+ export type { ParserOptions } from "./parser";
93
+ export { Parser, parse } from "./parser";
94
+
95
+ // Modules (ListPages, ListUsers, IfTags, Include, etc.)
96
+ export type {
97
+ // ListPages query types
98
+ ListPagesQuery,
99
+ ListPagesVariable,
100
+ // Data requirement types
101
+ ListPagesDataRequirement,
102
+ DataRequirements,
103
+ // External data types
104
+ UserInfo,
105
+ PageData,
106
+ SiteContext,
107
+ ListPagesExternalData,
108
+ // Callback types
109
+ ListPagesDataFetcher,
110
+ DataProvider,
111
+ // Template types
112
+ VariableContext,
113
+ CompiledTemplate,
114
+ // Extraction types
115
+ ExtractionResult,
116
+ // Resolution types
117
+ ParseFunction,
118
+ ResolveOptions,
119
+ // Include resolution
120
+ IncludeFetcher,
121
+ AsyncIncludeFetcher,
122
+ ResolveIncludesOptions,
123
+ // ListUsers types
124
+ ListUsersVariable,
125
+ ListUsersUserData,
126
+ ListUsersDataRequirement,
127
+ ListUsersExternalData,
128
+ ListUsersDataFetcher,
129
+ ListUsersVariableContext,
130
+ ListUsersCompiledTemplate,
131
+ // Normalized query types
132
+ NormalizedListPagesQuery,
133
+ NormalizedTags,
134
+ NormalizedCategory,
135
+ NormalizedOrder,
136
+ NormalizedParent,
137
+ NormalizedDateSelector,
138
+ NormalizedNumericSelector,
139
+ } from "./parser/rules/block/module/index";
140
+ export {
141
+ extractDataRequirements,
142
+ resolveModules,
143
+ STYLE_SLOT_PREFIX,
144
+ compileTemplate,
145
+ // Include resolution
146
+ resolveIncludes,
147
+ resolveIncludesAsync,
148
+ // IfTags source-level preprocessing (run between include expansion and parse)
149
+ preprocessIftags,
150
+ // Query normalization (for advanced use cases)
151
+ normalizeQuery,
152
+ parseTags,
153
+ parseCategory,
154
+ parseOrder,
155
+ parseParent,
156
+ parseDateSelector,
157
+ parseNumericSelector,
158
+ // ListUsers
159
+ extractListUsersVariables,
160
+ compileListUsersTemplate,
161
+ isListUsersModule,
162
+ resolveListUsers,
163
+ } from "./parser/rules/block/module/index";