@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,109 @@
1
+ // Event handler attributes (on*) are blocked entirely.
2
+ const SAFE_ATTRIBUTES = new Set([
3
+ "accept",
4
+ "align",
5
+ "alt",
6
+ "autocapitalize",
7
+ "autoplay",
8
+ "background",
9
+ "bgcolor",
10
+ "border",
11
+ "buffered",
12
+ "checked",
13
+ "cite",
14
+ "class",
15
+ "cols",
16
+ "colspan",
17
+ "contenteditable",
18
+ "controls",
19
+ "coords",
20
+ "datetime",
21
+ "decoding",
22
+ "default",
23
+ "dir",
24
+ "dirname",
25
+ "disabled",
26
+ "download",
27
+ "draggable",
28
+ "for",
29
+ "form",
30
+ "headers",
31
+ "height",
32
+ "hidden",
33
+ "high",
34
+ "href",
35
+ "hreflang",
36
+ "id",
37
+ "inputmode",
38
+ "ismap",
39
+ "itemprop",
40
+ "kind",
41
+ "label",
42
+ "lang",
43
+ "list",
44
+ "loop",
45
+ "low",
46
+ "max",
47
+ "maxlength",
48
+ "min",
49
+ "minlength",
50
+ "multiple",
51
+ "muted",
52
+ "name",
53
+ "optimum",
54
+ "pattern",
55
+ "placeholder",
56
+ "poster",
57
+ "preload",
58
+ "readonly",
59
+ "required",
60
+ "reversed",
61
+ "role",
62
+ "rows",
63
+ "rowspan",
64
+ "scope",
65
+ "selected",
66
+ "shape",
67
+ "size",
68
+ "sizes",
69
+ "span",
70
+ "spellcheck",
71
+ "src",
72
+ "srclang",
73
+ "srcset",
74
+ "start",
75
+ "step",
76
+ "style",
77
+ "tabindex",
78
+ "target",
79
+ "title",
80
+ "translate",
81
+ "type",
82
+ "usemap",
83
+ "value",
84
+ "width",
85
+ "wrap",
86
+ ]);
87
+
88
+ /**
89
+ * Filter unsafe HTML attributes (blocks event handlers, allows safe attributes + aria-* / data-*).
90
+ */
91
+ export function filterUnsafeAttributes(attrs: Record<string, string>): Record<string, string> {
92
+ const result: Record<string, string> = {};
93
+ for (const [key, value] of Object.entries(attrs)) {
94
+ const lower = key.toLowerCase();
95
+ if (lower.startsWith("on")) continue;
96
+ if (lower.startsWith("aria-") || lower.startsWith("data-")) {
97
+ result[key] = value;
98
+ continue;
99
+ }
100
+ if (!SAFE_ATTRIBUTES.has(lower)) continue;
101
+ // Wikidot prefixes user-set IDs with "u-".
102
+ if (lower === "id") {
103
+ result[key] = value.startsWith("u-") ? value : `u-${value}`;
104
+ continue;
105
+ }
106
+ result[key] = value;
107
+ }
108
+ return result;
109
+ }
@@ -0,0 +1,33 @@
1
+ import type { ParseContext } from "../types";
2
+
3
+ /**
4
+ * Parse block name from tokens (handles [[name or [[/name).
5
+ *
6
+ * Handles underscore suffix like "div_" which may be tokenized as
7
+ * [IDENTIFIER "div"] [UNDERSCORE "_"].
8
+ */
9
+ export function parseBlockName(
10
+ ctx: ParseContext,
11
+ startPos: number,
12
+ ): { name: string; consumed: number } | null {
13
+ let pos = startPos;
14
+ let consumed = 0;
15
+
16
+ // Wikidot does NOT allow whitespace between [[ and block name.
17
+ // e.g. [[ code ]] is treated as plain text, not a code block.
18
+ const token = ctx.tokens[pos];
19
+ if (!token || (token.type !== "TEXT" && token.type !== "IDENTIFIER")) {
20
+ return null;
21
+ }
22
+
23
+ let name = token.value.toLowerCase();
24
+ consumed++;
25
+ pos++;
26
+
27
+ if (ctx.tokens[pos]?.type === "UNDERSCORE") {
28
+ name += "_";
29
+ consumed++;
30
+ }
31
+
32
+ return { name, consumed };
33
+ }
@@ -0,0 +1,2 @@
1
+ export { filterUnsafeAttributes } from "./attribute-safety";
2
+ export { parseBlockName } from "./block-name";
@@ -0,0 +1,3 @@
1
+ export type { ParseContext } from "./parse-context";
2
+ export type { BlockRule, InlineRule, RuleResult } from "./rule";
3
+ export type { ScopeContext } from "./scope";
@@ -0,0 +1,38 @@
1
+ import type { Token } from "../../../lexer";
2
+ import type {
3
+ CodeBlockData,
4
+ Diagnostic,
5
+ Element,
6
+ TocEntry,
7
+ Version,
8
+ WikitextSettings,
9
+ } from "@wdprlib/ast";
10
+ import type { BlockRule, InlineRule } from "./rule";
11
+ import type { ScopeContext } from "./scope";
12
+
13
+ /**
14
+ * Parser context passed to rules.
15
+ *
16
+ * Fields are grouped by lifecycle:
17
+ * - Static config (`tokens`, `version`, `trackPositions`, `settings`, rule arrays).
18
+ * - `pos`: per-scope cursor; kept top-level because every rule spread overrides it.
19
+ * - Accumulators (`footnotes`, `tocEntries`, ..., `diagnostics`): shared by array identity.
20
+ * - `scope`: per-scope state with immutable-replace semantics.
21
+ */
22
+ export interface ParseContext {
23
+ tokens: Token[];
24
+ pos: number;
25
+ version: Version;
26
+ trackPositions: boolean;
27
+ settings: WikitextSettings;
28
+ footnotes: Element[][];
29
+ tocEntries: TocEntry[];
30
+ codeBlocks: CodeBlockData[];
31
+ htmlBlocks: string[];
32
+ bibcites: string[];
33
+ blockRules: BlockRule[];
34
+ blockFallbackRule: BlockRule;
35
+ inlineRules: InlineRule[];
36
+ diagnostics: Diagnostic[];
37
+ scope: ScopeContext;
38
+ }
@@ -0,0 +1,43 @@
1
+ import type { Element } from "@wdprlib/ast";
2
+ import type { TokenType } from "../../../lexer";
3
+ import type { ParseContext } from "./parse-context";
4
+
5
+ /**
6
+ * Result of a rule attempt.
7
+ */
8
+ export type RuleResult<T> = { success: true; elements: T[]; consumed: number } | { success: false };
9
+
10
+ /**
11
+ * Block rule interface.
12
+ */
13
+ export interface BlockRule {
14
+ /** Rule name for debugging. */
15
+ name: string;
16
+ /** Token types that can start this rule. */
17
+ startTokens: TokenType[];
18
+ /** Whether this rule requires line start. */
19
+ requiresLineStart: boolean;
20
+ /** Try to parse this block. */
21
+ parse(ctx: ParseContext): RuleResult<Element>;
22
+ /**
23
+ * Check if tokens at the given position match this rule's start pattern.
24
+ * Used by inline parser to determine behavior before a block boundary.
25
+ */
26
+ isStartPattern?(ctx: ParseContext, pos: number): boolean;
27
+ /**
28
+ * When true, a single newline before this block becomes a line-break.
29
+ */
30
+ preservesPrecedingLineBreak?: boolean;
31
+ }
32
+
33
+ /**
34
+ * Inline rule interface.
35
+ */
36
+ export interface InlineRule {
37
+ /** Rule name for debugging. */
38
+ name: string;
39
+ /** Token types that can start this rule. */
40
+ startTokens: TokenType[];
41
+ /** Try to parse this inline element. */
42
+ parse(ctx: ParseContext): RuleResult<Element>;
43
+ }
@@ -0,0 +1,31 @@
1
+ import type { ParseContext } from "./parse-context";
2
+
3
+ /**
4
+ * Per-scope state propagated by spread + override semantics.
5
+ *
6
+ * Every field is `readonly` so a rule cannot accidentally mutate the
7
+ * parent scope by writing through a shared reference. Updates must be
8
+ * expressed as a replacement: `ctx.scope = { ...ctx.scope, X: ... }`.
9
+ */
10
+ export interface ScopeContext {
11
+ /**
12
+ * Close condition for the current block. The paragraph parser calls
13
+ * it to decide when to stop collecting inline content.
14
+ */
15
+ readonly blockCloseCondition?: (ctx: ParseContext) => boolean;
16
+ /**
17
+ * Block names excluded from paragraph-boundary detection.
18
+ */
19
+ readonly excludedBlockNames?: ReadonlySet<string>;
20
+ /**
21
+ * Budget for div nesting: tracks how many more nested divs can open.
22
+ */
23
+ readonly divClosesBudget?: number;
24
+ /**
25
+ * Used by the footnote-block rule to reject duplicate top-level occurrences.
26
+ *
27
+ * Scope is per spread copy of `ParseContext`, not document-global. This keeps
28
+ * the original primitive semantics while avoiding rollback-unsafe shared state.
29
+ */
30
+ readonly footnoteBlockParsed: boolean;
31
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ *
3
+ * Aggregated exports for all parser rules (block and inline).
4
+ *
5
+ * The parser uses a rule-based architecture where each syntactic construct
6
+ * (heading, list, bold, link, etc.) is defined as a separate rule. Rules are
7
+ * organized into two categories:
8
+ *
9
+ * - **Block rules**: Match constructs that occupy one or more full lines
10
+ * (headings, lists, blockquotes, horizontal rules, paragraphs, etc.)
11
+ * - **Inline rules**: Match constructs within a line of text
12
+ * (bold, italic, links, raw/code spans, etc.)
13
+ *
14
+ * Each category also has a fallback rule that is used when no other rule matches,
15
+ * ensuring that all input is consumed.
16
+ *
17
+ * @module
18
+ */
19
+
20
+ // Types
21
+ export type { ParseContext, ScopeContext, RuleResult, BlockRule, InlineRule } from "./types";
22
+ export {
23
+ currentToken,
24
+ peekToken,
25
+ checkToken,
26
+ isAtEnd,
27
+ hasClosingMarkerBeforeNewline,
28
+ } from "./types";
29
+
30
+ // Block rules
31
+ export { blockRules, blockFallbackRule } from "./block";
32
+ export { headingRule, horizontalRuleRule, listRule, blockquoteRule, paragraphRule } from "./block"; // TODO: exportが足りているのか確認
33
+
34
+ // Inline rules
35
+ export { inlineRules, inlineFallbackRule } from "./inline";
36
+ export {
37
+ boldRule,
38
+ italicRule,
39
+ underlineRule,
40
+ strikethroughRule,
41
+ superscriptRule,
42
+ subscriptRule,
43
+ monospaceRule,
44
+ linkTripleRule,
45
+ linkSingleRule,
46
+ linkAnchorRule,
47
+ rawRule,
48
+ textRule,
49
+ } from "./inline"; // TODO: exportが足りているのか確認
@@ -0,0 +1,54 @@
1
+ import { sanitizeUrl as braintreeSanitizeUrl } from "@braintree/sanitize-url";
2
+
3
+ export type AnchorTarget = "new-tab" | "parent" | "top" | "same";
4
+
5
+ export interface AnchorAttributes {
6
+ target: AnchorTarget | null;
7
+ attributes: Record<string, string>;
8
+ }
9
+
10
+ export function buildAnchorAttributes(attrs: Record<string, string>): AnchorAttributes {
11
+ const target = parseAnchorTarget(attrs.target);
12
+ const { target: _target, ...cleanAttrs } = attrs;
13
+
14
+ if (cleanAttrs.href) {
15
+ cleanAttrs.href = sanitizeUrl(cleanAttrs.href);
16
+ }
17
+
18
+ return { target, attributes: cleanAttrs };
19
+ }
20
+
21
+ function parseAnchorTarget(targetAttr: string | undefined): AnchorTarget | null {
22
+ if (targetAttr === "_blank") return "new-tab";
23
+ if (targetAttr === "_parent") return "parent";
24
+ if (targetAttr === "_top") return "top";
25
+ if (targetAttr === "_self") return "same";
26
+ return null;
27
+ }
28
+
29
+ function sanitizeUrl(url: string): string {
30
+ const normalizedForCheck = stripControlAndWhitespace(url).toLowerCase();
31
+ const dangerousSchemes = ["javascript:", "data:", "vbscript:"];
32
+ for (const scheme of dangerousSchemes) {
33
+ if (normalizedForCheck.startsWith(scheme)) {
34
+ return "#invalid-url";
35
+ }
36
+ }
37
+
38
+ const sanitized = braintreeSanitizeUrl(url);
39
+ return sanitized === "about:blank" ? "#invalid-url" : url;
40
+ }
41
+
42
+ const WHITESPACE = /\s/;
43
+
44
+ function stripControlAndWhitespace(value: string): string {
45
+ let result = "";
46
+ for (const char of value) {
47
+ const code = char.charCodeAt(0);
48
+ if (WHITESPACE.test(char) || code <= 0x1f) {
49
+ continue;
50
+ }
51
+ result += char;
52
+ }
53
+ return result;
54
+ }
@@ -0,0 +1,26 @@
1
+ import type { Element } from "@wdprlib/ast";
2
+ import type { ParseContext } from "../../types";
3
+ import { inlineRules } from "../index";
4
+ import { getCandidateInlineRules } from "../utils";
5
+
6
+ export interface AnchorChildResult {
7
+ elements: Element[];
8
+ consumed: number;
9
+ }
10
+
11
+ export function parseAnchorChild(ctx: ParseContext, pos: number): AnchorChildResult {
12
+ const token = ctx.tokens[pos];
13
+ if (!token) {
14
+ return { elements: [], consumed: 0 };
15
+ }
16
+
17
+ const inlineCtx: ParseContext = { ...ctx, pos };
18
+ for (const rule of getCandidateInlineRules(inlineRules, token.type)) {
19
+ const result = rule.parse(inlineCtx);
20
+ if (result.success) {
21
+ return { elements: result.elements, consumed: result.consumed };
22
+ }
23
+ }
24
+
25
+ return { elements: [{ element: "text", data: token.value }], consumed: 1 };
26
+ }
@@ -0,0 +1,34 @@
1
+ import type { ParseContext } from "../../types";
2
+ import { isAnchorBlockName, parseAnchorBlockName } from "./syntax";
3
+
4
+ export function tryConsumeAnchorClose(
5
+ ctx: ParseContext,
6
+ pos: number,
7
+ paragraphStrip: boolean,
8
+ ): { consumed: number } | null {
9
+ if (ctx.tokens[pos]?.type !== "BLOCK_END_OPEN") {
10
+ return null;
11
+ }
12
+
13
+ const closeNameResult = parseAnchorBlockName(ctx, pos + 1);
14
+ if (!closeNameResult || !isAnchorBlockName(closeNameResult.name)) {
15
+ return null;
16
+ }
17
+
18
+ let consumed = 1 + closeNameResult.consumed;
19
+ let closePos = pos + consumed;
20
+ if (ctx.tokens[closePos]?.type === "BLOCK_CLOSE") {
21
+ closePos++;
22
+ consumed++;
23
+ }
24
+
25
+ if (
26
+ paragraphStrip &&
27
+ ctx.tokens[closePos]?.type === "NEWLINE" &&
28
+ ctx.tokens[closePos + 1]?.type !== "NEWLINE"
29
+ ) {
30
+ consumed++;
31
+ }
32
+
33
+ return { consumed };
34
+ }
@@ -0,0 +1,59 @@
1
+ import type { Element } from "@wdprlib/ast";
2
+ import type { ParseContext } from "../../types";
3
+ import { parseAnchorChild } from "./child";
4
+ import { tryConsumeAnchorClose } from "./close";
5
+ import { consumeAnchorNewline } from "./newline";
6
+ import { trimParagraphStripLineBreaks } from "./paragraph-strip";
7
+
8
+ export interface AnchorContentResult {
9
+ children: Element[];
10
+ consumed: number;
11
+ foundClose: boolean;
12
+ }
13
+
14
+ export function parseAnchorContent(
15
+ ctx: ParseContext,
16
+ startPos: number,
17
+ paragraphStrip: boolean,
18
+ ): AnchorContentResult {
19
+ const children: Element[] = [];
20
+ let pos = startPos;
21
+ let consumed = 0;
22
+
23
+ while (pos < ctx.tokens.length) {
24
+ const token = ctx.tokens[pos];
25
+ if (!token || token.type === "EOF") {
26
+ break;
27
+ }
28
+
29
+ const closeResult = tryConsumeAnchorClose(ctx, pos, paragraphStrip);
30
+ if (closeResult !== null) {
31
+ trimParagraphStripLineBreaks(children, paragraphStrip);
32
+ return {
33
+ children,
34
+ consumed: consumed + closeResult.consumed,
35
+ foundClose: true,
36
+ };
37
+ }
38
+
39
+ if (token.type === "NEWLINE") {
40
+ const newline = consumeAnchorNewline(ctx, pos, paragraphStrip, children);
41
+ pos += newline.consumed;
42
+ consumed += newline.consumed;
43
+ continue;
44
+ }
45
+
46
+ if (token.type === "WHITESPACE" && token.lineStart) {
47
+ pos++;
48
+ consumed++;
49
+ continue;
50
+ }
51
+
52
+ const child = parseAnchorChild(ctx, pos);
53
+ children.push(...child.elements);
54
+ pos += child.consumed;
55
+ consumed += child.consumed;
56
+ }
57
+
58
+ return { children, consumed, foundClose: false };
59
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ *
3
+ * Parses the Wikidot anchor inline block syntax: `[[a]]...[[/a]]`.
4
+ *
5
+ * An anchor wraps inline content in an HTML `<a>` element, allowing
6
+ * href, target, and other HTML attributes to be specified.
7
+ *
8
+ * Wikidot syntax variants:
9
+ * - `[[a href="url"]]text[[/a]]` -- basic anchor with href
10
+ * - `[[a_ href="url"]]text[[/a]]` -- paragraph strip mode (trailing underscore)
11
+ *
12
+ * Paragraph strip mode (`[[a_]]`) suppresses newlines within the anchor
13
+ * body and strips at most one trailing newline after the closing tag
14
+ * (preserving double newlines as paragraph breaks). This prevents
15
+ * unwanted `<br>` elements when consecutive anchor blocks are placed on
16
+ * separate lines.
17
+ *
18
+ * The `target` attribute is extracted and mapped to a semantic enum value
19
+ * (`"new-tab"`, `"parent"`, `"top"`, `"same"`), while the remaining
20
+ * attributes (including `href`) are passed through after URL sanitization.
21
+ *
22
+ * @module
23
+ */
24
+ import type { Element } from "@wdprlib/ast";
25
+ import type { InlineRule, ParseContext, RuleResult } from "../../types";
26
+ import { currentToken } from "../../types";
27
+ import { buildAnchorAttributes } from "./attributes";
28
+ import { parseAnchorContent } from "./content";
29
+ import { parseAnchorOpen } from "./open";
30
+
31
+ /**
32
+ * Inline rule for parsing `[[a]]...[[/a]]` blocks.
33
+ *
34
+ * Triggered by a `BLOCK_OPEN` (`[[`) token. The rule verifies the block name
35
+ * is `a` or `anchor` (optionally with `_` suffix), parses HTML attributes,
36
+ * then recursively parses inline content until the matching closing tag.
37
+ *
38
+ * Produces an `"anchor"` AST element containing the parsed children, a
39
+ * semantic `target` value, and the sanitized attribute map.
40
+ *
41
+ * Edge cases:
42
+ * - If no matching closing tag is found, the rule fails (returns `{ success: false }`),
43
+ * allowing the tokens to fall through to other rules or the text fallback.
44
+ * - In paragraph strip mode, newlines within the body are consumed silently
45
+ * rather than converted to line-break elements. After the closing tag,
46
+ * at most one trailing newline is consumed to prevent a line-break between
47
+ * consecutive `[[a_]]` blocks, but double newlines are preserved as
48
+ * paragraph breaks.
49
+ * - The `href` attribute is sanitized to block `javascript:`, `data:`, and
50
+ * `vbscript:` schemes.
51
+ */
52
+ export const anchorRule: InlineRule = {
53
+ name: "anchor",
54
+ startTokens: ["BLOCK_OPEN"],
55
+
56
+ /**
57
+ * Attempts to parse an anchor block starting at the current position.
58
+ *
59
+ * @param ctx - Parse context with token stream and current position
60
+ * @returns A successful result with an `"anchor"` element, or `{ success: false }`
61
+ */
62
+ parse(ctx: ParseContext): RuleResult<Element> {
63
+ const openToken = currentToken(ctx);
64
+ if (openToken.type !== "BLOCK_OPEN") {
65
+ return { success: false };
66
+ }
67
+
68
+ const openResult = parseAnchorOpen(ctx);
69
+ if (!openResult) {
70
+ return { success: false };
71
+ }
72
+
73
+ const contentResult = parseAnchorContent(ctx, openResult.bodyStart, openResult.paragraphStrip);
74
+ const consumed = openResult.consumed + contentResult.consumed;
75
+
76
+ if (!contentResult.foundClose) {
77
+ ctx.diagnostics.push({
78
+ severity: "warning",
79
+ code: "unclosed-block",
80
+ message: `Missing closing tag [[/a]] for [[${openResult.name}]]`,
81
+ position: openToken.position,
82
+ });
83
+ return { success: false };
84
+ }
85
+
86
+ const anchorAttrs = buildAnchorAttributes(openResult.attributes);
87
+
88
+ return {
89
+ success: true,
90
+ elements: [
91
+ {
92
+ element: "anchor",
93
+ data: {
94
+ target: anchorAttrs.target,
95
+ attributes: anchorAttrs.attributes,
96
+ elements: contentResult.children,
97
+ },
98
+ },
99
+ ],
100
+ consumed,
101
+ };
102
+ },
103
+ };
@@ -0,0 +1,26 @@
1
+ import type { Element } from "@wdprlib/ast";
2
+ import type { ParseContext } from "../../types";
3
+
4
+ export interface AnchorNewlineResult {
5
+ consumed: number;
6
+ }
7
+
8
+ export function consumeAnchorNewline(
9
+ ctx: ParseContext,
10
+ pos: number,
11
+ paragraphStrip: boolean,
12
+ children: Element[],
13
+ ): AnchorNewlineResult {
14
+ let consumed = 1;
15
+ let nextPos = pos + 1;
16
+
17
+ if (!paragraphStrip) {
18
+ children.push({ element: "line-break" });
19
+ while (ctx.tokens[nextPos]?.type === "WHITESPACE" && ctx.tokens[nextPos]?.lineStart) {
20
+ nextPos++;
21
+ consumed++;
22
+ }
23
+ }
24
+
25
+ return { consumed };
26
+ }
@@ -0,0 +1,47 @@
1
+ import type { ParseContext } from "../../types";
2
+ import { parseAttributes } from "../../block/utils";
3
+ import { isAnchorBlockName, parseAnchorBlockName } from "./syntax";
4
+
5
+ export interface AnchorOpenResult {
6
+ name: string;
7
+ paragraphStrip: boolean;
8
+ attributes: Record<string, string>;
9
+ bodyStart: number;
10
+ consumed: number;
11
+ }
12
+
13
+ export function parseAnchorOpen(ctx: ParseContext): AnchorOpenResult | null {
14
+ if (ctx.tokens[ctx.pos]?.type !== "BLOCK_OPEN") {
15
+ return null;
16
+ }
17
+
18
+ let pos = ctx.pos + 1;
19
+ let consumed = 1;
20
+
21
+ const nameResult = parseAnchorBlockName(ctx, pos);
22
+ if (!nameResult || !isAnchorBlockName(nameResult.name)) {
23
+ return null;
24
+ }
25
+
26
+ pos += nameResult.consumed;
27
+ consumed += nameResult.consumed;
28
+
29
+ const attrResult = parseAttributes(ctx, pos);
30
+ pos += attrResult.consumed;
31
+ consumed += attrResult.consumed;
32
+
33
+ if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
34
+ return null;
35
+ }
36
+
37
+ pos++;
38
+ consumed++;
39
+
40
+ return {
41
+ name: nameResult.name,
42
+ paragraphStrip: nameResult.paragraphStrip,
43
+ attributes: attrResult.attrs,
44
+ bodyStart: pos,
45
+ consumed,
46
+ };
47
+ }
@@ -0,0 +1,14 @@
1
+ import type { Element } from "@wdprlib/ast";
2
+
3
+ export function trimParagraphStripLineBreaks(children: Element[], paragraphStrip: boolean): void {
4
+ if (!paragraphStrip) {
5
+ return;
6
+ }
7
+
8
+ while (children.length > 0 && children[0]?.element === "line-break") {
9
+ children.shift();
10
+ }
11
+ while (children.length > 0 && children[children.length - 1]?.element === "line-break") {
12
+ children.pop();
13
+ }
14
+ }