@wdprlib/parser 3.1.2 → 4.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 (419) hide show
  1. package/dist/index.cjs +10456 -8230
  2. package/dist/index.d.cts +313 -337
  3. package/dist/index.d.ts +313 -337
  4. package/dist/index.js +10460 -8234
  5. package/package.json +5 -3
  6. package/src/index.ts +170 -0
  7. package/src/lexer/anchor.ts +48 -0
  8. package/src/lexer/index.ts +21 -0
  9. package/src/lexer/lexer.ts +201 -0
  10. package/src/lexer/options.ts +19 -0
  11. package/src/lexer/punctuation.ts +70 -0
  12. package/src/lexer/quoted-string.ts +16 -0
  13. package/src/lexer/runs.ts +85 -0
  14. package/src/lexer/spacing-actions.ts +24 -0
  15. package/src/lexer/state.ts +103 -0
  16. package/src/lexer/syntax-actions.ts +80 -0
  17. package/src/lexer/text-actions.ts +41 -0
  18. package/src/lexer/token-actions.ts +136 -0
  19. package/src/lexer/token-factory.ts +62 -0
  20. package/src/lexer/tokenize.ts +18 -0
  21. package/src/lexer/tokens.ts +141 -0
  22. package/src/parser/constants.ts +175 -0
  23. package/src/parser/depth/index.ts +111 -0
  24. package/src/parser/depth/stack.ts +82 -0
  25. package/src/parser/index.ts +18 -0
  26. package/src/parser/parse/block.ts +42 -0
  27. package/src/parser/parse/context.ts +26 -0
  28. package/src/parser/parse/footnotes.ts +25 -0
  29. package/src/parser/parse/index.ts +42 -0
  30. package/src/parser/parse/options.ts +34 -0
  31. package/src/parser/parse/parser.ts +79 -0
  32. package/src/parser/parse/plain-non-ascii.ts +129 -0
  33. package/src/parser/parse/result.ts +57 -0
  34. package/src/parser/parse/source.ts +11 -0
  35. package/src/parser/postprocess/divAdjacentParagraph.ts +76 -0
  36. package/src/parser/postprocess/index.ts +15 -0
  37. package/src/parser/postprocess/spanStrip/clean-element.ts +168 -0
  38. package/src/parser/postprocess/spanStrip/cleanup.ts +25 -0
  39. package/src/parser/postprocess/spanStrip/empty-spans.ts +36 -0
  40. package/src/parser/postprocess/spanStrip/escaped.ts +78 -0
  41. package/src/parser/postprocess/spanStrip/factory.ts +23 -0
  42. package/src/parser/postprocess/spanStrip/index.ts +8 -0
  43. package/src/parser/postprocess/spanStrip/merge.ts +117 -0
  44. package/src/parser/postprocess/spanStrip/predicates.ts +59 -0
  45. package/src/parser/postprocess/spanStrip/split.ts +67 -0
  46. package/src/parser/preprocess/expr/chars.ts +15 -0
  47. package/src/parser/preprocess/expr/evaluate.ts +22 -0
  48. package/src/parser/preprocess/expr/index.ts +45 -0
  49. package/src/parser/preprocess/expr/kind.ts +19 -0
  50. package/src/parser/preprocess/expr/parse.ts +103 -0
  51. package/src/parser/preprocess/expr/scan.ts +34 -0
  52. package/src/parser/preprocess/expr/types.ts +14 -0
  53. package/src/parser/preprocess/index.ts +38 -0
  54. package/src/parser/preprocess/typography.ts +132 -0
  55. package/src/parser/preprocess/utils/bracket-depths.ts +98 -0
  56. package/src/parser/preprocess/utils/index.ts +13 -0
  57. package/src/parser/preprocess/utils/raw-regions.ts +153 -0
  58. package/src/parser/preprocess/whitespace/detection.ts +39 -0
  59. package/src/parser/preprocess/whitespace/index.ts +79 -0
  60. package/src/parser/preprocess/whitespace/leading-spaces.ts +11 -0
  61. package/src/parser/preprocess/whitespace/patterns.ts +23 -0
  62. package/src/parser/rules/block/align/body.ts +46 -0
  63. package/src/parser/rules/block/align/element.ts +13 -0
  64. package/src/parser/rules/block/align/index.ts +90 -0
  65. package/src/parser/rules/block/align/syntax.ts +113 -0
  66. package/src/parser/rules/block/bibliography/body.ts +81 -0
  67. package/src/parser/rules/block/bibliography/entries.ts +49 -0
  68. package/src/parser/rules/block/bibliography/entry-content.ts +73 -0
  69. package/src/parser/rules/block/bibliography/entry-key.ts +83 -0
  70. package/src/parser/rules/block/bibliography/index.ts +90 -0
  71. package/src/parser/rules/block/bibliography/open.ts +53 -0
  72. package/src/parser/rules/block/block-list/bare-content.ts +105 -0
  73. package/src/parser/rules/block/block-list/bare-paragraph.ts +60 -0
  74. package/src/parser/rules/block/block-list/index.ts +51 -0
  75. package/src/parser/rules/block/block-list/item-content.ts +132 -0
  76. package/src/parser/rules/block/block-list/li-content.ts +107 -0
  77. package/src/parser/rules/block/block-list/li-item.ts +77 -0
  78. package/src/parser/rules/block/block-list/list-block.ts +100 -0
  79. package/src/parser/rules/block/block-list/open.ts +51 -0
  80. package/src/parser/rules/block/block-list/tags.ts +50 -0
  81. package/src/parser/rules/block/blockquote/build.ts +62 -0
  82. package/src/parser/rules/block/blockquote/index.ts +80 -0
  83. package/src/parser/rules/block/blockquote/line.ts +79 -0
  84. package/src/parser/rules/block/blockquote/lines.ts +39 -0
  85. package/src/parser/rules/block/center/index.ts +72 -0
  86. package/src/parser/rules/block/center/open.ts +27 -0
  87. package/src/parser/rules/block/clear-float/index.ts +51 -0
  88. package/src/parser/rules/block/clear-float/syntax.ts +43 -0
  89. package/src/parser/rules/block/code/attributes.ts +30 -0
  90. package/src/parser/rules/block/code/content.ts +57 -0
  91. package/src/parser/rules/block/code/index.ts +100 -0
  92. package/src/parser/rules/block/collapsible/attributes.ts +95 -0
  93. package/src/parser/rules/block/collapsible/body.ts +69 -0
  94. package/src/parser/rules/block/collapsible/index.ts +117 -0
  95. package/src/parser/rules/block/collapsible/open.ts +51 -0
  96. package/src/parser/rules/block/collapsible/orphans.ts +31 -0
  97. package/src/parser/rules/block/collapsible/tags.ts +17 -0
  98. package/src/parser/rules/block/comment/consume.ts +37 -0
  99. package/src/parser/rules/block/comment/index.ts +47 -0
  100. package/src/parser/rules/block/content-separator/index.ts +49 -0
  101. package/src/parser/rules/block/content-separator/syntax.ts +33 -0
  102. package/src/parser/rules/block/definition-list/collect.ts +40 -0
  103. package/src/parser/rules/block/definition-list/index.ts +63 -0
  104. package/src/parser/rules/block/definition-list/item-key.ts +95 -0
  105. package/src/parser/rules/block/definition-list/item-value.ts +56 -0
  106. package/src/parser/rules/block/definition-list/items.ts +54 -0
  107. package/src/parser/rules/block/div/body.ts +41 -0
  108. package/src/parser/rules/block/div/close.ts +41 -0
  109. package/src/parser/rules/block/div/failed.ts +117 -0
  110. package/src/parser/rules/block/div/index.ts +112 -0
  111. package/src/parser/rules/block/div/nesting.ts +37 -0
  112. package/src/parser/rules/block/div/open.ts +59 -0
  113. package/src/parser/rules/block/div/paragraph-strip.ts +44 -0
  114. package/src/parser/rules/block/embed-block/content.ts +53 -0
  115. package/src/parser/rules/block/embed-block/index.ts +91 -0
  116. package/src/parser/rules/block/embed-block/open.ts +52 -0
  117. package/src/parser/rules/block/embed-block/tags.ts +5 -0
  118. package/src/parser/rules/block/footnoteblock/attributes.ts +73 -0
  119. package/src/parser/rules/block/footnoteblock/index.ts +82 -0
  120. package/src/parser/rules/block/footnoteblock/open.ts +53 -0
  121. package/src/parser/rules/block/heading/index.ts +87 -0
  122. package/src/parser/rules/block/heading/open.ts +50 -0
  123. package/src/parser/rules/block/heading/toc-text.ts +26 -0
  124. package/src/parser/rules/block/horizontal-rule/index.ts +44 -0
  125. package/src/parser/rules/block/horizontal-rule/syntax.ts +21 -0
  126. package/src/parser/rules/block/html/body.ts +114 -0
  127. package/src/parser/rules/block/html/diagnostics.ts +11 -0
  128. package/src/parser/rules/block/html/index.ts +95 -0
  129. package/src/parser/rules/block/html/open.ts +36 -0
  130. package/src/parser/rules/block/iframe/attributes.ts +106 -0
  131. package/src/parser/rules/block/iframe/index.ts +73 -0
  132. package/src/parser/rules/block/iframe/open.ts +58 -0
  133. package/src/parser/rules/block/iframe/source.ts +24 -0
  134. package/src/parser/rules/block/iframe/url.ts +38 -0
  135. package/src/parser/rules/block/iftags/body.ts +48 -0
  136. package/src/parser/rules/block/iftags/condition.ts +24 -0
  137. package/src/parser/rules/block/iftags/index.ts +108 -0
  138. package/src/parser/rules/block/include/arguments.ts +48 -0
  139. package/src/parser/rules/block/include/index.ts +75 -0
  140. package/src/parser/rules/block/include/location.ts +24 -0
  141. package/src/parser/rules/block/include/variables.ts +37 -0
  142. package/src/parser/rules/block/index.ts +127 -0
  143. package/src/parser/rules/block/list/index.ts +73 -0
  144. package/src/parser/rules/block/list/line.ts +77 -0
  145. package/src/parser/rules/block/list/native.ts +89 -0
  146. package/src/parser/rules/block/math/content.ts +54 -0
  147. package/src/parser/rules/block/math/index.ts +106 -0
  148. package/src/parser/rules/block/math/name.ts +35 -0
  149. package/src/parser/rules/block/module/backlinks/index.ts +31 -0
  150. package/src/parser/rules/block/module/backlinks/types.ts +21 -0
  151. package/src/parser/rules/block/module/body.ts +92 -0
  152. package/src/parser/rules/block/module/categories/index.ts +34 -0
  153. package/src/parser/rules/block/module/categories/types.ts +21 -0
  154. package/src/parser/rules/block/module/css/index.ts +37 -0
  155. package/src/parser/rules/block/module/element.ts +33 -0
  156. package/src/parser/rules/block/module/iftags/condition.ts +109 -0
  157. package/src/parser/rules/block/module/iftags/index.ts +26 -0
  158. package/src/parser/rules/block/module/iftags/preprocess.ts +140 -0
  159. package/src/parser/rules/block/module/iftags/resolve.ts +73 -0
  160. package/src/parser/rules/block/module/iftags/types.ts +63 -0
  161. package/src/parser/rules/block/module/include/directive.ts +91 -0
  162. package/src/parser/rules/block/module/include/index.ts +29 -0
  163. package/src/parser/rules/block/module/include/references.ts +42 -0
  164. package/src/parser/rules/block/module/include/resolve/cache.ts +44 -0
  165. package/src/parser/rules/block/module/include/resolve/index.ts +106 -0
  166. package/src/parser/rules/block/module/include/resolve/iterate.ts +202 -0
  167. package/src/parser/rules/block/module/include/resolve/replace.ts +31 -0
  168. package/src/parser/rules/block/module/include/resolve/types.ts +105 -0
  169. package/src/parser/rules/block/module/include/scanner.ts +121 -0
  170. package/src/parser/rules/block/module/index.ts +134 -0
  171. package/src/parser/rules/block/module/join/index.ts +34 -0
  172. package/src/parser/rules/block/module/join/types.ts +23 -0
  173. package/src/parser/rules/block/module/listpages/compiler.ts +73 -0
  174. package/src/parser/rules/block/module/listpages/extract.ts +76 -0
  175. package/src/parser/rules/block/module/listpages/extraction/listpages.ts +42 -0
  176. package/src/parser/rules/block/module/listpages/extraction/listusers.ts +30 -0
  177. package/src/parser/rules/block/module/listpages/extraction/query.ts +51 -0
  178. package/src/parser/rules/block/module/listpages/extraction/result.ts +18 -0
  179. package/src/parser/rules/block/module/listpages/extraction/template.ts +96 -0
  180. package/src/parser/rules/block/module/listpages/extraction/variables.ts +58 -0
  181. package/src/parser/rules/block/module/listpages/index.ts +83 -0
  182. package/src/parser/rules/block/module/listpages/normalization/date-selector.ts +53 -0
  183. package/src/parser/rules/block/module/listpages/normalization/numeric-selector.ts +32 -0
  184. package/src/parser/rules/block/module/listpages/normalization/order-parent.ts +82 -0
  185. package/src/parser/rules/block/module/listpages/normalization/selectors.ts +2 -0
  186. package/src/parser/rules/block/module/listpages/normalization/tags-category.ts +86 -0
  187. package/src/parser/rules/block/module/listpages/normalize.ts +74 -0
  188. package/src/parser/rules/block/module/listpages/parser.ts +106 -0
  189. package/src/parser/rules/block/module/listpages/resolution/items.ts +43 -0
  190. package/src/parser/rules/block/module/listpages/resolution/wrapper.ts +42 -0
  191. package/src/parser/rules/block/module/listpages/resolve.ts +60 -0
  192. package/src/parser/rules/block/module/listpages/template/format/content.ts +41 -0
  193. package/src/parser/rules/block/module/listpages/template/format/date.ts +116 -0
  194. package/src/parser/rules/block/module/listpages/template/format/index.ts +4 -0
  195. package/src/parser/rules/block/module/listpages/template/format/tags.ts +7 -0
  196. package/src/parser/rules/block/module/listpages/template/format/user.ts +9 -0
  197. package/src/parser/rules/block/module/listpages/template/getters/index.ts +36 -0
  198. package/src/parser/rules/block/module/listpages/template/getters/parameterized.ts +60 -0
  199. package/src/parser/rules/block/module/listpages/template/getters/simple.ts +65 -0
  200. package/src/parser/rules/block/module/listpages/template/getters/types.ts +3 -0
  201. package/src/parser/rules/block/module/listpages/template/syntax.ts +97 -0
  202. package/src/parser/rules/block/module/listpages/types/data-fetcher.ts +15 -0
  203. package/src/parser/rules/block/module/listpages/types/data-requirements.ts +52 -0
  204. package/src/parser/rules/block/module/listpages/types/external-data.ts +77 -0
  205. package/src/parser/rules/block/module/listpages/types/index.ts +17 -0
  206. package/src/parser/rules/block/module/listpages/types/normalized-query.ts +120 -0
  207. package/src/parser/rules/block/module/listpages/types/query.ts +67 -0
  208. package/src/parser/rules/block/module/listpages/types/template.ts +17 -0
  209. package/src/parser/rules/block/module/listpages/types/variables.ts +69 -0
  210. package/src/parser/rules/block/module/listpages/url-resolution/fields.ts +48 -0
  211. package/src/parser/rules/block/module/listpages/url-resolution/params.ts +19 -0
  212. package/src/parser/rules/block/module/listpages/url-resolution/query.ts +24 -0
  213. package/src/parser/rules/block/module/listpages/url-resolution/resolve.ts +53 -0
  214. package/src/parser/rules/block/module/listpages/url-resolution/value.ts +25 -0
  215. package/src/parser/rules/block/module/listpages/url-resolver.ts +29 -0
  216. package/src/parser/rules/block/module/listusers/compiler.ts +56 -0
  217. package/src/parser/rules/block/module/listusers/extract.ts +40 -0
  218. package/src/parser/rules/block/module/listusers/getters.ts +21 -0
  219. package/src/parser/rules/block/module/listusers/index.ts +36 -0
  220. package/src/parser/rules/block/module/listusers/parser.ts +54 -0
  221. package/src/parser/rules/block/module/listusers/resolve.ts +58 -0
  222. package/src/parser/rules/block/module/listusers/types.ts +93 -0
  223. package/src/parser/rules/block/module/listusers/variables.ts +15 -0
  224. package/src/parser/rules/block/module/mapping.ts +61 -0
  225. package/src/parser/rules/block/module/open.ts +57 -0
  226. package/src/parser/rules/block/module/page-tree/index.ts +38 -0
  227. package/src/parser/rules/block/module/page-tree/types.ts +29 -0
  228. package/src/parser/rules/block/module/rate/index.ts +28 -0
  229. package/src/parser/rules/block/module/rate/types.ts +19 -0
  230. package/src/parser/rules/block/module/resolution/contexts.ts +78 -0
  231. package/src/parser/rules/block/module/resolution/data-maps.ts +39 -0
  232. package/src/parser/rules/block/module/resolution/dynamic-modules.ts +93 -0
  233. package/src/parser/rules/block/module/resolution/styles.ts +53 -0
  234. package/src/parser/rules/block/module/resolution/walk-resolve.ts +107 -0
  235. package/src/parser/rules/block/module/resolve.ts +198 -0
  236. package/src/parser/rules/block/module/rule.ts +56 -0
  237. package/src/parser/rules/block/module/types-common.ts +70 -0
  238. package/src/parser/rules/block/module/types.ts +61 -0
  239. package/src/parser/rules/block/module/utils.ts +43 -0
  240. package/src/parser/rules/block/module/walk/children.ts +35 -0
  241. package/src/parser/rules/block/module/walk/index.ts +9 -0
  242. package/src/parser/rules/block/module/walk/map/index.ts +2 -0
  243. package/src/parser/rules/block/module/walk/map/stateful-definition-list.ts +25 -0
  244. package/src/parser/rules/block/module/walk/map/stateful-list.ts +40 -0
  245. package/src/parser/rules/block/module/walk/map/stateful-table.ts +23 -0
  246. package/src/parser/rules/block/module/walk/map/stateful-tabs.ts +19 -0
  247. package/src/parser/rules/block/module/walk/map/stateful.ts +71 -0
  248. package/src/parser/rules/block/module/walk/map/stateless-definition-list.ts +12 -0
  249. package/src/parser/rules/block/module/walk/map/stateless-list.ts +29 -0
  250. package/src/parser/rules/block/module/walk/map/stateless-table.ts +11 -0
  251. package/src/parser/rules/block/module/walk/map/stateless-tabs.ts +5 -0
  252. package/src/parser/rules/block/module/walk/map/stateless.ts +51 -0
  253. package/src/parser/rules/block/module/walk/map/types.ts +6 -0
  254. package/src/parser/rules/block/module/walk/traverse.ts +65 -0
  255. package/src/parser/rules/block/orphan-li/content.ts +60 -0
  256. package/src/parser/rules/block/orphan-li/index.ts +75 -0
  257. package/src/parser/rules/block/orphan-li/open.ts +25 -0
  258. package/src/parser/rules/block/orphan-li/tags.ts +40 -0
  259. package/src/parser/rules/block/paragraph/content.ts +12 -0
  260. package/src/parser/rules/block/paragraph/index.ts +60 -0
  261. package/src/parser/rules/block/paragraph/normalize.ts +52 -0
  262. package/src/parser/rules/block/paragraph/span-markers.ts +52 -0
  263. package/src/parser/rules/block/parsing/attributes/index.ts +32 -0
  264. package/src/parser/rules/block/parsing/attributes/names.ts +93 -0
  265. package/src/parser/rules/block/parsing/attributes/scanner.ts +75 -0
  266. package/src/parser/rules/block/parsing/attributes/values.ts +26 -0
  267. package/src/parser/rules/block/parsing/block-item.ts +29 -0
  268. package/src/parser/rules/block/parsing/content.ts +127 -0
  269. package/src/parser/rules/block/parsing/end-condition.ts +51 -0
  270. package/src/parser/rules/block/parsing/inline-content.ts +105 -0
  271. package/src/parser/rules/block/parsing/inline-newline.ts +41 -0
  272. package/src/parser/rules/block/parsing/non-boundary.ts +24 -0
  273. package/src/parser/rules/block/parsing/rule-dispatch.ts +44 -0
  274. package/src/parser/rules/block/table/index.ts +80 -0
  275. package/src/parser/rules/block/table/pipe/cell-start.ts +69 -0
  276. package/src/parser/rules/block/table/pipe/cell.ts +106 -0
  277. package/src/parser/rules/block/table/pipe/index.ts +2 -0
  278. package/src/parser/rules/block/table/pipe/row.ts +88 -0
  279. package/src/parser/rules/block/table/pipe/tokens.ts +14 -0
  280. package/src/parser/rules/block/table/pipe/trim.ts +50 -0
  281. package/src/parser/rules/block/table-block/body.ts +79 -0
  282. package/src/parser/rules/block/table-block/cell-attributes.ts +33 -0
  283. package/src/parser/rules/block/table-block/cell-boundary.ts +99 -0
  284. package/src/parser/rules/block/table-block/cell-content/index.ts +88 -0
  285. package/src/parser/rules/block/table-block/cell-content/segments.ts +134 -0
  286. package/src/parser/rules/block/table-block/cell-newline.ts +47 -0
  287. package/src/parser/rules/block/table-block/cell.ts +64 -0
  288. package/src/parser/rules/block/table-block/index.ts +113 -0
  289. package/src/parser/rules/block/table-block/row-boundary.ts +75 -0
  290. package/src/parser/rules/block/table-block/structure.ts +80 -0
  291. package/src/parser/rules/block/tabview/body.ts +64 -0
  292. package/src/parser/rules/block/tabview/index.ts +90 -0
  293. package/src/parser/rules/block/tabview/open.ts +50 -0
  294. package/src/parser/rules/block/tabview/tab.ts +92 -0
  295. package/src/parser/rules/block/tabview/tags.ts +30 -0
  296. package/src/parser/rules/block/toc/element.ts +11 -0
  297. package/src/parser/rules/block/toc/index.ts +44 -0
  298. package/src/parser/rules/block/toc/open.ts +84 -0
  299. package/src/parser/rules/block/utils.ts +15 -0
  300. package/src/parser/rules/common/attribute-safety.ts +109 -0
  301. package/src/parser/rules/common/block-name.ts +33 -0
  302. package/src/parser/rules/common/index.ts +2 -0
  303. package/src/parser/rules/contracts/index.ts +3 -0
  304. package/src/parser/rules/contracts/parse-context.ts +38 -0
  305. package/src/parser/rules/contracts/rule.ts +43 -0
  306. package/src/parser/rules/contracts/scope.ts +31 -0
  307. package/src/parser/rules/index.ts +49 -0
  308. package/src/parser/rules/inline/anchor/attributes.ts +54 -0
  309. package/src/parser/rules/inline/anchor/child.ts +26 -0
  310. package/src/parser/rules/inline/anchor/close.ts +34 -0
  311. package/src/parser/rules/inline/anchor/content.ts +59 -0
  312. package/src/parser/rules/inline/anchor/index.ts +103 -0
  313. package/src/parser/rules/inline/anchor/newline.ts +26 -0
  314. package/src/parser/rules/inline/anchor/open.ts +47 -0
  315. package/src/parser/rules/inline/anchor/paragraph-strip.ts +14 -0
  316. package/src/parser/rules/inline/anchor/syntax.ts +40 -0
  317. package/src/parser/rules/inline/anchor-name/index.ts +38 -0
  318. package/src/parser/rules/inline/anchor-name/name.ts +39 -0
  319. package/src/parser/rules/inline/anchor-name/syntax.ts +46 -0
  320. package/src/parser/rules/inline/bibcite/element.ts +14 -0
  321. package/src/parser/rules/inline/bibcite/index.ts +34 -0
  322. package/src/parser/rules/inline/bibcite/syntax.ts +64 -0
  323. package/src/parser/rules/inline/bold.ts +49 -0
  324. package/src/parser/rules/inline/color/index.ts +35 -0
  325. package/src/parser/rules/inline/color/syntax.ts +69 -0
  326. package/src/parser/rules/inline/comment/consume.ts +31 -0
  327. package/src/parser/rules/inline/comment/index.ts +64 -0
  328. package/src/parser/rules/inline/equation-ref/element.ts +8 -0
  329. package/src/parser/rules/inline/equation-ref/index.ts +34 -0
  330. package/src/parser/rules/inline/equation-ref/syntax.ts +45 -0
  331. package/src/parser/rules/inline/expr/branch.ts +104 -0
  332. package/src/parser/rules/inline/expr/conditional-branch.ts +27 -0
  333. package/src/parser/rules/inline/expr/conditional.ts +80 -0
  334. package/src/parser/rules/inline/expr/depth.ts +25 -0
  335. package/src/parser/rules/inline/expr/elements.ts +39 -0
  336. package/src/parser/rules/inline/expr/index.ts +84 -0
  337. package/src/parser/rules/inline/expr/syntax.ts +45 -0
  338. package/src/parser/rules/inline/footnote/child.ts +22 -0
  339. package/src/parser/rules/inline/footnote/close.ts +33 -0
  340. package/src/parser/rules/inline/footnote/content.ts +54 -0
  341. package/src/parser/rules/inline/footnote/elements.ts +38 -0
  342. package/src/parser/rules/inline/footnote/index.ts +54 -0
  343. package/src/parser/rules/inline/footnote/newline.ts +27 -0
  344. package/src/parser/rules/inline/footnote/open.ts +38 -0
  345. package/src/parser/rules/inline/formatting/container.ts +50 -0
  346. package/src/parser/rules/inline/guillemet/index.ts +56 -0
  347. package/src/parser/rules/inline/guillemet/text.ts +11 -0
  348. package/src/parser/rules/inline/html/gate.ts +64 -0
  349. package/src/parser/rules/inline/html/index.ts +81 -0
  350. package/src/parser/rules/inline/html/open.ts +37 -0
  351. package/src/parser/rules/inline/image/attributes.ts +22 -0
  352. package/src/parser/rules/inline/image/body.ts +36 -0
  353. package/src/parser/rules/inline/image/index.ts +89 -0
  354. package/src/parser/rules/inline/image/open.ts +56 -0
  355. package/src/parser/rules/inline/image/source.ts +62 -0
  356. package/src/parser/rules/inline/image/syntax.ts +76 -0
  357. package/src/parser/rules/inline/index.ts +150 -0
  358. package/src/parser/rules/inline/italic.ts +46 -0
  359. package/src/parser/rules/inline/line-break/backslash.ts +58 -0
  360. package/src/parser/rules/inline/line-break/elements.ts +9 -0
  361. package/src/parser/rules/inline/line-break/index.ts +3 -0
  362. package/src/parser/rules/inline/line-break/newline.ts +82 -0
  363. package/src/parser/rules/inline/line-break/underscore.ts +45 -0
  364. package/src/parser/rules/inline/link-anchor.ts +72 -0
  365. package/src/parser/rules/inline/link-bracket/anchor.ts +3 -0
  366. package/src/parser/rules/inline/link-bracket/direct-url.ts +5 -0
  367. package/src/parser/rules/inline/link-bracket/parsed.ts +81 -0
  368. package/src/parser/rules/inline/link-bracket/parts.ts +64 -0
  369. package/src/parser/rules/inline/link-bracket/prefix.ts +15 -0
  370. package/src/parser/rules/inline/link-single.ts +73 -0
  371. package/src/parser/rules/inline/link-star.ts +72 -0
  372. package/src/parser/rules/inline/link-triple/fallback.ts +10 -0
  373. package/src/parser/rules/inline/link-triple/index.ts +62 -0
  374. package/src/parser/rules/inline/link-triple/interwiki.ts +11 -0
  375. package/src/parser/rules/inline/link-triple/label.ts +35 -0
  376. package/src/parser/rules/inline/link-triple/syntax.ts +72 -0
  377. package/src/parser/rules/inline/link-triple/target.ts +36 -0
  378. package/src/parser/rules/inline/math-inline/index.ts +40 -0
  379. package/src/parser/rules/inline/math-inline/syntax.ts +55 -0
  380. package/src/parser/rules/inline/monospace.ts +50 -0
  381. package/src/parser/rules/inline/parsing/block-boundary.ts +42 -0
  382. package/src/parser/rules/inline/parsing/block-start-predicates.ts +117 -0
  383. package/src/parser/rules/inline/parsing/collect.ts +23 -0
  384. package/src/parser/rules/inline/parsing/inline-content.ts +115 -0
  385. package/src/parser/rules/inline/parsing/paragraph-boundary.ts +47 -0
  386. package/src/parser/rules/inline/parsing/plain-text.ts +69 -0
  387. package/src/parser/rules/inline/parsing/preserved-line-break.ts +11 -0
  388. package/src/parser/rules/inline/parsing/rules.ts +34 -0
  389. package/src/parser/rules/inline/parsing/simple-token.ts +26 -0
  390. package/src/parser/rules/inline/raw/angle.ts +40 -0
  391. package/src/parser/rules/inline/raw/double-at.ts +78 -0
  392. package/src/parser/rules/inline/raw/index.ts +26 -0
  393. package/src/parser/rules/inline/raw/result.ts +26 -0
  394. package/src/parser/rules/inline/size/content.ts +65 -0
  395. package/src/parser/rules/inline/size/index.ts +55 -0
  396. package/src/parser/rules/inline/size/open.ts +43 -0
  397. package/src/parser/rules/inline/size/value.ts +45 -0
  398. package/src/parser/rules/inline/span/content.ts +97 -0
  399. package/src/parser/rules/inline/span/elements.ts +108 -0
  400. package/src/parser/rules/inline/span/index.ts +79 -0
  401. package/src/parser/rules/inline/span/newline.ts +50 -0
  402. package/src/parser/rules/inline/span/syntax.ts +70 -0
  403. package/src/parser/rules/inline/strikethrough/index.ts +60 -0
  404. package/src/parser/rules/inline/strikethrough/parse.ts +14 -0
  405. package/src/parser/rules/inline/strikethrough/syntax.ts +24 -0
  406. package/src/parser/rules/inline/subscript.ts +47 -0
  407. package/src/parser/rules/inline/superscript.ts +49 -0
  408. package/src/parser/rules/inline/text/element.ts +5 -0
  409. package/src/parser/rules/inline/text/index.ts +85 -0
  410. package/src/parser/rules/inline/underline/child.ts +26 -0
  411. package/src/parser/rules/inline/underline/content.ts +29 -0
  412. package/src/parser/rules/inline/underline/index.ts +84 -0
  413. package/src/parser/rules/inline/user/element.ts +11 -0
  414. package/src/parser/rules/inline/user/index.ts +34 -0
  415. package/src/parser/rules/inline/user/syntax.ts +67 -0
  416. package/src/parser/rules/inline/utils.ts +4 -0
  417. package/src/parser/rules/tokens.ts +106 -0
  418. package/src/parser/rules/types.ts +9 -0
  419. package/src/parser/toc.ts +130 -0
@@ -0,0 +1,92 @@
1
+ import type { ParseContext } from "../../types";
2
+ import { currentToken } from "../../types";
3
+ import { parseBlockName } from "../utils";
4
+
5
+ export interface ModuleBodyResult {
6
+ body?: string;
7
+ pos: number;
8
+ consumed: number;
9
+ }
10
+
11
+ export function parseModuleBody(
12
+ ctx: ParseContext,
13
+ startPos: number,
14
+ moduleHasBody: boolean,
15
+ ): ModuleBodyResult {
16
+ if (!moduleHasBody || ctx.tokens[startPos]?.type !== "NEWLINE") {
17
+ return { pos: startPos, consumed: 0 };
18
+ }
19
+
20
+ let pos = startPos + 1;
21
+ let consumed = 1;
22
+ const bodyParts: string[] = [];
23
+ let foundClose = false;
24
+
25
+ while (pos < ctx.tokens.length) {
26
+ const token = ctx.tokens[pos];
27
+ if (!token || token.type === "EOF") {
28
+ break;
29
+ }
30
+
31
+ if (token.type === "BLOCK_END_OPEN") {
32
+ const closeResult = parseModuleClose(ctx, pos);
33
+ if (closeResult) {
34
+ foundClose = true;
35
+ pos = closeResult.pos;
36
+ consumed += closeResult.consumed;
37
+ break;
38
+ }
39
+ }
40
+
41
+ bodyParts.push(token.value);
42
+ pos++;
43
+ consumed++;
44
+ }
45
+
46
+ if (!foundClose) {
47
+ ctx.diagnostics.push({
48
+ severity: "warning",
49
+ code: "unclosed-block",
50
+ message: "Missing closing tag [[/module]] for [[module]]",
51
+ position: currentToken(ctx).position,
52
+ });
53
+ }
54
+
55
+ const trimmed = bodyParts.join("").trim();
56
+ return {
57
+ body: trimmed ? trimmed : undefined,
58
+ pos,
59
+ consumed,
60
+ };
61
+ }
62
+
63
+ function parseModuleClose(
64
+ ctx: ParseContext,
65
+ startPos: number,
66
+ ): { pos: number; consumed: number } | null {
67
+ if (ctx.tokens[startPos]?.type !== "BLOCK_END_OPEN") {
68
+ return null;
69
+ }
70
+
71
+ const closeNameResult = parseBlockName(ctx, startPos + 1);
72
+ if (
73
+ !closeNameResult ||
74
+ (closeNameResult.name !== "module" && closeNameResult.name !== "module654")
75
+ ) {
76
+ return null;
77
+ }
78
+
79
+ let pos = startPos + 1 + closeNameResult.consumed;
80
+ let consumed = 1 + closeNameResult.consumed;
81
+
82
+ if (ctx.tokens[pos]?.type === "BLOCK_CLOSE") {
83
+ pos++;
84
+ consumed++;
85
+ }
86
+ if (ctx.tokens[pos]?.type === "NEWLINE") {
87
+ pos++;
88
+ consumed++;
89
+ }
90
+
91
+ return { pos, consumed };
92
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ *
3
+ * Parser rule for the Wikidot `[[module Categories]]` block.
4
+ *
5
+ * Displays a list of page categories on the site. Accepts an optional
6
+ * `include-hidden` boolean attribute to control whether hidden categories
7
+ * (those prefixed with `_`) are shown.
8
+ *
9
+ * @module
10
+ */
11
+
12
+ import type { ModuleRule } from "../types";
13
+ import { parseBool } from "../utils";
14
+ import type { CategoriesModuleData } from "./types";
15
+
16
+ /**
17
+ * Module rule for `[[module Categories]]`.
18
+ *
19
+ * Parses the `include-hidden` attribute (accepts both `include-hidden` and
20
+ * `includehidden` forms, since Wikidot lowercases all attribute names).
21
+ * Defaults to false.
22
+ */
23
+ export const categoriesModuleRule: ModuleRule = {
24
+ name: "module-categories",
25
+ acceptsNames: ["categories"],
26
+ hasBody: false,
27
+
28
+ parse(_ctx, _pos, args): CategoriesModuleData {
29
+ return {
30
+ module: "categories",
31
+ "include-hidden": parseBool(args["include-hidden"] ?? args.includehidden, false),
32
+ };
33
+ },
34
+ };
@@ -0,0 +1,21 @@
1
+ /**
2
+ *
3
+ * Type definitions for the Categories module.
4
+ *
5
+ * The `[[module Categories]]` block displays a list of page categories
6
+ * on the current site.
7
+ *
8
+ * @module
9
+ */
10
+
11
+ /**
12
+ * AST data for a `[[module Categories]]` element.
13
+ *
14
+ * The rendering application should query the site's category list and
15
+ * display them, optionally including hidden categories.
16
+ */
17
+ export interface CategoriesModuleData {
18
+ module: "categories";
19
+ /** Whether to include hidden categories (prefixed with `_`) in the listing */
20
+ "include-hidden": boolean;
21
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ *
3
+ * Parser rule for the Wikidot `[[module CSS]]` block.
4
+ *
5
+ * Allows embedding custom CSS styles within a Wikidot page. The module body
6
+ * contains raw CSS text that is emitted as a `style` element in the AST.
7
+ *
8
+ * @example
9
+ * ```
10
+ * [[module CSS]]
11
+ * .custom-class { color: red; }
12
+ * [[/module]]
13
+ * ```
14
+ *
15
+ * @module
16
+ */
17
+
18
+ import type { Element } from "@wdprlib/ast";
19
+ import type { ModuleRule } from "../types";
20
+
21
+ /**
22
+ * Module rule for `[[module CSS]]`.
23
+ *
24
+ * Unlike most modules that produce a `Module` AST node, the CSS module
25
+ * produces a direct `style` Element. This is because CSS content is not
26
+ * a Wikidot module that needs external data resolution -- it can be
27
+ * rendered immediately as a `<style>` tag.
28
+ */
29
+ export const cssModuleRule: ModuleRule = {
30
+ name: "module-css",
31
+ acceptsNames: ["css"],
32
+ hasBody: true,
33
+
34
+ parse(_ctx, _pos, _args, body): Element {
35
+ return { element: "style", data: body ?? "" };
36
+ },
37
+ };
@@ -0,0 +1,33 @@
1
+ import type { Element, Module } from "@wdprlib/ast";
2
+
3
+ export function moduleParseResultToElement(result: Module | Element): Element {
4
+ if (isElement(result)) {
5
+ return result;
6
+ }
7
+
8
+ return createModuleElement(result);
9
+ }
10
+
11
+ export function createUnknownModuleElement(
12
+ name: string,
13
+ args: Record<string, string>,
14
+ body: string | undefined,
15
+ ): Element {
16
+ return createModuleElement({
17
+ module: "unknown",
18
+ name,
19
+ arguments: args,
20
+ body,
21
+ });
22
+ }
23
+
24
+ function createModuleElement(data: Module): Element {
25
+ return {
26
+ element: "module",
27
+ data,
28
+ };
29
+ }
30
+
31
+ function isElement(value: Module | Element): value is Element {
32
+ return "element" in value;
33
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ *
3
+ * Parsing and evaluation of `[[iftags]]` condition strings.
4
+ *
5
+ * The condition string uses a simple syntax where each whitespace-separated
6
+ * token is a tag name with an optional prefix:
7
+ * - `+tag` - Tag must be present (AND condition)
8
+ * - `-tag` - Tag must be absent (NOT condition)
9
+ * - `tag` - At least one bare tag must be present (OR condition)
10
+ *
11
+ * All three categories must independently be satisfied:
12
+ * required (AND) + forbidden (AND) + optional (OR).
13
+ *
14
+ * @module
15
+ */
16
+
17
+ import type { TagCondition } from "./types";
18
+
19
+ /**
20
+ * Parse iftags condition string into structured format
21
+ *
22
+ * @param condition - Raw condition string like "+fruit -admin component"
23
+ * @returns Parsed condition with required, forbidden, and optional tags
24
+ */
25
+ export function parseTagCondition(condition: string): TagCondition {
26
+ const required: string[] = [];
27
+ const forbidden: string[] = [];
28
+ const optional: string[] = [];
29
+ let hasEmptyRequired = false;
30
+ let hasEmptyForbidden = false;
31
+
32
+ const parts = condition.trim().split(/\s+/);
33
+
34
+ for (const part of parts) {
35
+ if (!part) continue;
36
+
37
+ if (part.startsWith("+")) {
38
+ const tag = part.slice(1);
39
+ if (tag) required.push(tag);
40
+ else hasEmptyRequired = true;
41
+ } else if (part.startsWith("-")) {
42
+ const tag = part.slice(1);
43
+ if (tag) forbidden.push(tag);
44
+ else hasEmptyForbidden = true;
45
+ } else {
46
+ optional.push(part);
47
+ }
48
+ }
49
+
50
+ return { required, forbidden, optional, hasEmptyRequired, hasEmptyForbidden };
51
+ }
52
+
53
+ /**
54
+ * Evaluate if a tag condition matches the given tags
55
+ *
56
+ * @param condition - Parsed tag condition
57
+ * @param pageTags - Actual tags on the page
58
+ * @returns true if condition is satisfied
59
+ */
60
+ export function evaluateTagCondition(condition: TagCondition, pageTags: string[]): boolean {
61
+ const noNamedTokens =
62
+ condition.required.length === 0 &&
63
+ condition.forbidden.length === 0 &&
64
+ condition.optional.length === 0;
65
+
66
+ // No tokens at all = supercommentout (`[[iftags]]`) → never match.
67
+ if (noNamedTokens && !condition.hasEmptyRequired && !condition.hasEmptyForbidden) {
68
+ return false;
69
+ }
70
+
71
+ // A bare `+` (with or without other tokens) means "require an unnamed
72
+ // tag", which can never be satisfied — the whole condition is Hide.
73
+ if (condition.hasEmptyRequired) {
74
+ return false;
75
+ }
76
+
77
+ // `[[iftags - ]]` — bare `-` token alone means "forbid nothing", which
78
+ // is trivially true, so this is Show Always. When combined with other
79
+ // tokens the bare `-` adds no constraint, so we fall through to the
80
+ // standard evaluation below.
81
+ if (condition.hasEmptyForbidden && noNamedTokens) {
82
+ return true;
83
+ }
84
+
85
+ const tagSet = new Set(pageTags);
86
+
87
+ // All required tags must be present
88
+ for (const tag of condition.required) {
89
+ if (!tagSet.has(tag)) {
90
+ return false;
91
+ }
92
+ }
93
+
94
+ // All forbidden tags must be absent
95
+ for (const tag of condition.forbidden) {
96
+ if (tagSet.has(tag)) {
97
+ return false;
98
+ }
99
+ }
100
+
101
+ // At least one optional tag must be present (if any specified)
102
+ if (condition.optional.length > 0) {
103
+ if (!condition.optional.some((tag) => tagSet.has(tag))) {
104
+ return false;
105
+ }
106
+ }
107
+
108
+ return true;
109
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ *
3
+ * IfTags conditional rendering module for Wikidot's `[[iftags]]` block.
4
+ *
5
+ * Enables conditional content display based on the current page's tags.
6
+ * The condition syntax supports required tags (`+tag` or bare `tag`) and
7
+ * forbidden tags (`-tag`). Content inside the block is only rendered when
8
+ * all conditions are satisfied.
9
+ *
10
+ * Exports condition parsing, evaluation, and AST resolution functions.
11
+ *
12
+ * @module
13
+ */
14
+
15
+ // Types
16
+ export type { TagCondition, IfTagsResolver } from "./types";
17
+
18
+ // Condition parsing and evaluation
19
+ export { parseTagCondition, evaluateTagCondition } from "./condition";
20
+
21
+ // Resolution
22
+ export type { IfTagsData, IfTagsResolveResult } from "./resolve";
23
+ export { isIfTagsElement, resolveIfTags } from "./resolve";
24
+
25
+ // Source-level preprocessing (must run after include expansion, before parse)
26
+ export { preprocessIftags } from "./preprocess";
@@ -0,0 +1,140 @@
1
+ /**
2
+ *
3
+ * Text-level expansion of `[[iftags]]` blocks before parsing.
4
+ *
5
+ * Unlike the AST-level {@link resolveIfTags} resolver, which evaluates
6
+ * `if-tags` nodes after parsing, this pass operates directly on the
7
+ * raw wikitext source. The difference matters for templates that embed
8
+ * an `[[iftags]]` block inside another construct's attribute string,
9
+ * for example:
10
+ *
11
+ * ```wikitext
12
+ * [[div_ class="x" [[iftags +foo]]style="display:none;"[[/iftags]]]]
13
+ * ```
14
+ *
15
+ * The block-level tokenizer cannot recover a well-formed opener from
16
+ * that input — the inner `[[iftags ...]]X[[/iftags]]` has to collapse
17
+ * to either `X` or the empty string *before* the parser sees the outer
18
+ * tag, so the attribute string becomes plain text again.
19
+ *
20
+ * `pageTags` semantics:
21
+ *
22
+ * - `string[]`: full pass. Every `[[iftags]]` is evaluated against the
23
+ * given tag set and collapses to either its body or the empty string.
24
+ * The AST will contain no `if-tags` nodes after parsing.
25
+ * - `null`: tags are unknown (e.g. draft preview, fixture test).
26
+ * The pass still runs but only collapses `[[iftags]]` blocks that are
27
+ * embedded inside another block's opener (`[[name ... [[iftags ...]]X[[/iftags]] ... ]]`).
28
+ * Block-level `[[iftags]]` are left alone for {@link resolveIfTags}
29
+ * to evaluate later when real tags are supplied via `getPageTags`.
30
+ * Opener-embedded iftags are collapsed using an empty-tag assumption
31
+ * (i.e. `+tag` conditions fail, `-tag` conditions pass). This is a
32
+ * lossy fallback that keeps the outer block parseable; callers that
33
+ * need accurate rendering should pass the real tags as `string[]`.
34
+ *
35
+ * Pipeline order (when invoked via `parse()`):
36
+ *
37
+ * ```
38
+ * getPageTags → resolveIncludes → parse({ pageTags }) → resolveModules
39
+ * ```
40
+ *
41
+ * Running this after include expansion is intentional: an included
42
+ * page may itself embed `[[iftags]]`, and the condition is evaluated
43
+ * against the *including* page's tags, not the included page's.
44
+ *
45
+ * @module
46
+ */
47
+
48
+ import { parseTagCondition, evaluateTagCondition } from "./condition";
49
+ import {
50
+ computeBracketDepths,
51
+ makeUniqueSentinels,
52
+ maskRawRegions,
53
+ restorePlaceholders,
54
+ } from "../../../../preprocess/utils";
55
+
56
+ /**
57
+ * Matches one `[[iftags ...]]X[[/iftags]]` where `X` contains no further
58
+ * `[[iftags]]` opener or closer. Used for innermost-first reduction.
59
+ *
60
+ * - `g` (global): a single `replace` pass rewrites every innermost block,
61
+ * so sibling blocks collapse together and the reduction loop runs once
62
+ * per nesting level rather than once per block.
63
+ * - `i` (case-insensitive): Wikidot block names are case-insensitive
64
+ * - `s` (dot matches newline): bodies can span multiple lines
65
+ * - `[^\]]*` for the condition: tag-condition tokens (`+tag`, `-tag`,
66
+ * bare names) never contain `]`, and stopping at the first `]` keeps
67
+ * the regex linear in body length even on degenerate input.
68
+ */
69
+ const INNERMOST_IFTAGS_PATTERN =
70
+ /\[\[\s*iftags\b([^\]]*)\]\]((?:(?!\[\[\s*iftags\b|\[\[\/\s*iftags\s*\]\]).)*)\[\[\/\s*iftags\s*\]\]/gis;
71
+
72
+ /**
73
+ * Expand `[[iftags ...]]X[[/iftags]]` directives in `source` against the
74
+ * current page's tags.
75
+ *
76
+ * Behaviour:
77
+ * - Raw regions (`[[code]]`, `[[html]]`, `@@...@@`, `@<...>@`) are
78
+ * protected: literal `[[iftags]]` tokens inside them are not expanded.
79
+ * - Nested `[[iftags]]` are processed innermost-first, so an outer
80
+ * block can re-process the now-flattened inner body uniformly.
81
+ * - `pageTags === null`: only `[[iftags]]` blocks embedded inside
82
+ * another block's opener are collapsed (using an empty-tag fallback
83
+ * so `+tag` conditions fail and `-tag` conditions pass). Block-level
84
+ * iftags are left intact for the AST-level resolver.
85
+ *
86
+ * @param source Raw wikitext (typically after include expansion).
87
+ * @param pageTags Tags of the page being rendered, or `null` for the
88
+ * opener-embedded-only fallback mode.
89
+ * @returns Source with matching iftags replaced by their bodies and
90
+ * unmatched iftags removed entirely.
91
+ */
92
+ export function preprocessIftags(source: string, pageTags: string[] | null): string {
93
+ if (!source.includes("[[")) return source; // fast path
94
+
95
+ const sentinels = makeUniqueSentinels(source);
96
+ const { masked, placeholders } = maskRawRegions(source, sentinels);
97
+ const reduced = reduceIftags(masked, pageTags);
98
+ return restorePlaceholders(reduced, placeholders, sentinels);
99
+ }
100
+
101
+ /**
102
+ * Replace `[[iftags ...]]X[[/iftags]]` blocks innermost-first until no
103
+ * iftags pair remains.
104
+ *
105
+ * Behaviour by `pageTags`:
106
+ * - `string[]`: every match collapses to body / empty based on tag membership.
107
+ * - `null`: only matches whose start offset has `bracketDepth > 0`
108
+ * (i.e. embedded inside an outer block opener) are collapsed, using
109
+ * an empty-tag assumption. Block-level matches are returned verbatim
110
+ * so {@link resolveIfTags} can evaluate them later.
111
+ *
112
+ * The loop terminates when a pass changes nothing.
113
+ */
114
+ function reduceIftags(source: string, pageTags: string[] | null): string {
115
+ let current = source;
116
+ // Worst-case bound: one pass eliminates at least one nesting level, and
117
+ // nesting depth is at most `source.length`. The explicit cap stops a
118
+ // runaway regex (e.g. pathological zero-width match) from looping forever.
119
+ const maxIterations = source.length + 1;
120
+ const tagSet: string[] = pageTags ?? [];
121
+ for (let i = 0; i < maxIterations; i++) {
122
+ const depths = pageTags === null ? computeBracketDepths(current) : null;
123
+ let changed = false;
124
+ const next = current.replace(
125
+ INNERMOST_IFTAGS_PATTERN,
126
+ (match, cond: string, body: string, offset: number) => {
127
+ if (depths !== null && depths[offset] === 0) {
128
+ // block-level iftags in `null` mode → leave for AST resolver
129
+ return match;
130
+ }
131
+ changed = true;
132
+ const condition = parseTagCondition(cond);
133
+ return evaluateTagCondition(condition, tagSet) ? body : "";
134
+ },
135
+ );
136
+ if (!changed) return current;
137
+ current = next;
138
+ }
139
+ return current;
140
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ *
3
+ * Resolution of `[[iftags]]` conditional blocks.
4
+ *
5
+ * During the resolve phase, each `[[iftags]]` element is evaluated against
6
+ * the current page's tags. If the condition matches, the element's children
7
+ * are included in the output; otherwise, they are omitted. If page tags are
8
+ * not available (null), the element is kept as-is for later resolution.
9
+ *
10
+ * @module
11
+ */
12
+
13
+ import type { Element } from "@wdprlib/ast";
14
+ import { parseTagCondition, evaluateTagCondition } from "./condition";
15
+
16
+ /**
17
+ * Data structure for an `[[iftags]]` element in the AST.
18
+ */
19
+ export interface IfTagsData {
20
+ condition: string;
21
+ elements: Element[];
22
+ }
23
+
24
+ /**
25
+ * Result of attempting to resolve an `[[iftags]]` element.
26
+ *
27
+ * The `evaluated` flag indicates whether the condition was actually tested.
28
+ * When `pageTags` is null (tags not available), the condition is not evaluated
29
+ * and the element should be kept in the AST for later resolution.
30
+ */
31
+ export interface IfTagsResolveResult {
32
+ /**
33
+ * Whether the condition was evaluated
34
+ * - true: condition was evaluated (pageTags provided)
35
+ * - false: pageTags was null, element kept as-is
36
+ */
37
+ evaluated: boolean;
38
+
39
+ /**
40
+ * Whether the condition matched (only meaningful if evaluated=true)
41
+ */
42
+ matched: boolean;
43
+ }
44
+
45
+ /**
46
+ * Type guard to check if an element is an `[[iftags]]` element.
47
+ *
48
+ * @param element - The element to check
49
+ * @returns true if the element has `element: "if-tags"` and appropriate data structure
50
+ */
51
+ export function isIfTagsElement(
52
+ element: Element,
53
+ ): element is Element & { element: "if-tags"; data: IfTagsData } {
54
+ return element.element === "if-tags";
55
+ }
56
+
57
+ /**
58
+ * Evaluate an iftags condition against page tags
59
+ *
60
+ * @param data - IfTags element data with condition and child elements
61
+ * @param pageTags - Current page's tags, or null if not available
62
+ * @returns Result indicating if evaluated and if matched
63
+ */
64
+ export function resolveIfTags(data: IfTagsData, pageTags: string[] | null): IfTagsResolveResult {
65
+ if (pageTags === null) {
66
+ return { evaluated: false, matched: false };
67
+ }
68
+
69
+ const condition = parseTagCondition(data.condition);
70
+ const matched = evaluateTagCondition(condition, pageTags);
71
+
72
+ return { evaluated: true, matched };
73
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ *
3
+ * Type definitions for the IfTags conditional rendering module.
4
+ *
5
+ * `[[iftags]]` is a Wikidot block that conditionally renders its content
6
+ * based on the current page's tags. The condition syntax supports required
7
+ * tags (`+tag`), forbidden tags (`-tag`), and optional tags (bare `tag`).
8
+ *
9
+ * @module
10
+ */
11
+
12
+ /**
13
+ * Parsed representation of an `[[iftags +tag -tag ...]]` condition.
14
+ *
15
+ * The condition string is parsed into three arrays:
16
+ * - `required` tags must ALL be present on the page (AND logic, `+tag` syntax)
17
+ * - `forbidden` tags must ALL be absent from the page (AND logic, `-tag` syntax)
18
+ * - `optional` tags require at least ONE to be present (OR logic, bare `tag` syntax)
19
+ *
20
+ * All three categories must independently be satisfied.
21
+ *
22
+ * @example
23
+ * `[[iftags +fruit -admin component template]]` parses to:
24
+ * ```
25
+ * { required: ["fruit"], forbidden: ["admin"], optional: ["component", "template"] }
26
+ * ```
27
+ */
28
+ export interface TagCondition {
29
+ /** Tags that must all be present on the page (`+tag` syntax) */
30
+ required: string[];
31
+
32
+ /** Tags that must all be absent from the page (`-tag` syntax) */
33
+ forbidden: string[];
34
+
35
+ /** Tags where at least one must be present (bare `tag` syntax, OR logic) */
36
+ optional: string[];
37
+
38
+ /**
39
+ * `true` when the condition contained a bare `+` token (a `+` prefix with
40
+ * no tag name). Wikidot treats `+` alone as "require an unnamed tag",
41
+ * which can never be satisfied, so a `+`-only condition evaluates to
42
+ * `false` (Hide Always).
43
+ */
44
+ hasEmptyRequired?: boolean;
45
+
46
+ /**
47
+ * `true` when the condition contained a bare `-` token (a `-` prefix with
48
+ * no tag name). Wikidot treats `-` alone as "forbid nothing", which is
49
+ * trivially satisfied — so a `-`-only condition evaluates to `true`
50
+ * (Show Always).
51
+ */
52
+ hasEmptyForbidden?: boolean;
53
+ }
54
+
55
+ /**
56
+ * Callback to retrieve the current page's tags during the resolve phase.
57
+ *
58
+ * Called when evaluating `[[iftags]]` conditions. The application must provide
59
+ * this callback with access to the current page's tag list.
60
+ *
61
+ * @returns Array of tag names for the current page
62
+ */
63
+ export type IfTagsResolver = () => string[];