@intlayer/core 9.4.4 → 9.5.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 (162) hide show
  1. package/README.md +0 -2
  2. package/dist/cjs/dictionaryManipulator/qualifiedDictionary.cjs.map +1 -1
  3. package/dist/cjs/index.cjs +5 -8
  4. package/dist/cjs/interpreter/getPlural.cjs.map +1 -1
  5. package/dist/cjs/markdown/compiler.cjs +437 -209
  6. package/dist/cjs/markdown/compiler.cjs.map +1 -1
  7. package/dist/cjs/markdown/constants.cjs +32 -50
  8. package/dist/cjs/markdown/constants.cjs.map +1 -1
  9. package/dist/cjs/markdown/elementScanner.cjs +191 -0
  10. package/dist/cjs/markdown/elementScanner.cjs.map +1 -0
  11. package/dist/cjs/markdown/index.cjs +5 -8
  12. package/dist/cjs/markdown/parser.cjs +90 -48
  13. package/dist/cjs/markdown/parser.cjs.map +1 -1
  14. package/dist/cjs/markdown/renderer.cjs +14 -11
  15. package/dist/cjs/markdown/renderer.cjs.map +1 -1
  16. package/dist/cjs/markdown/utils.cjs +123 -34
  17. package/dist/cjs/markdown/utils.cjs.map +1 -1
  18. package/dist/esm/dictionaryManipulator/qualifiedDictionary.mjs.map +1 -1
  19. package/dist/esm/index.mjs +3 -3
  20. package/dist/esm/interpreter/getPlural.mjs.map +1 -1
  21. package/dist/esm/markdown/compiler.mjs +438 -211
  22. package/dist/esm/markdown/compiler.mjs.map +1 -1
  23. package/dist/esm/markdown/constants.mjs +29 -43
  24. package/dist/esm/markdown/constants.mjs.map +1 -1
  25. package/dist/esm/markdown/elementScanner.mjs +183 -0
  26. package/dist/esm/markdown/elementScanner.mjs.map +1 -0
  27. package/dist/esm/markdown/index.mjs +3 -3
  28. package/dist/esm/markdown/parser.mjs +90 -48
  29. package/dist/esm/markdown/parser.mjs.map +1 -1
  30. package/dist/esm/markdown/renderer.mjs +14 -11
  31. package/dist/esm/markdown/renderer.mjs.map +1 -1
  32. package/dist/esm/markdown/utils.mjs +124 -35
  33. package/dist/esm/markdown/utils.mjs.map +1 -1
  34. package/dist/types/dictionaryManipulator/qualifiedDictionary.d.ts +3 -3
  35. package/dist/types/dictionaryManipulator/qualifiedDictionary.d.ts.map +1 -1
  36. package/dist/types/index.d.ts +4 -4
  37. package/dist/types/interpreter/getPlural.d.ts +3 -3
  38. package/dist/types/interpreter/getPlural.d.ts.map +1 -1
  39. package/dist/types/markdown/compiler.d.ts +43 -0
  40. package/dist/types/markdown/compiler.d.ts.map +1 -1
  41. package/dist/types/markdown/constants.d.ts +26 -17
  42. package/dist/types/markdown/constants.d.ts.map +1 -1
  43. package/dist/types/markdown/elementScanner.d.ts +14 -0
  44. package/dist/types/markdown/elementScanner.d.ts.map +1 -0
  45. package/dist/types/markdown/index.d.ts +4 -4
  46. package/dist/types/markdown/parser.d.ts +0 -5
  47. package/dist/types/markdown/parser.d.ts.map +1 -1
  48. package/dist/types/markdown/renderer.d.ts +0 -4
  49. package/dist/types/markdown/renderer.d.ts.map +1 -1
  50. package/dist/types/markdown/types.d.ts +13 -0
  51. package/dist/types/markdown/types.d.ts.map +1 -1
  52. package/dist/types/markdown/utils.d.ts +7 -2
  53. package/dist/types/markdown/utils.d.ts.map +1 -1
  54. package/package.json +7 -7
  55. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/dictionaryManipulator/qualifiedDynamicLoader.d.ts +0 -1
  56. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getDictionaryAsync.d.ts +0 -1
  57. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getEnumeration.d.ts +0 -1
  58. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getGender.d.ts +0 -1
  59. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getInsertion.d.ts +0 -1
  60. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getPlural.d.ts +0 -1
  61. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getSelect.d.ts +0 -1
  62. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/index.d.ts +0 -7
  63. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/interpolableNode.d.ts +0 -1
  64. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/transpiler/index.d.ts +0 -2
  65. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/transpiler/nesting/index.d.ts +0 -1
  66. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/transpiler/nesting/nesting.d.ts +0 -1
  67. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/dictionaryManipulator/qualifiedDynamicLoader.d.ts +0 -1
  68. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getDictionaryAsync.d.ts +0 -1
  69. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getEnumeration.d.ts +0 -1
  70. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getGender.d.ts +0 -1
  71. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getInsertion.d.ts +0 -1
  72. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getPlural.d.ts +0 -1
  73. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/getSelect.d.ts +0 -1
  74. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/index.d.ts +0 -7
  75. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/interpreter/interpolableNode.d.ts +0 -1
  76. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/intlayer/dist/types/index.d.ts +0 -6
  77. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/transpiler/index.d.ts +0 -2
  78. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/transpiler/nesting/index.d.ts +0 -1
  79. package/dist/types/@intlayer/core/dist/types/@intlayer/core/dist/types/transpiler/nesting/nesting.d.ts +0 -1
  80. package/dist/types/@intlayer/core/dist/types/dictionaryManipulator/plainDynamicLoader.d.ts +0 -2
  81. package/dist/types/@intlayer/core/dist/types/dictionaryManipulator/qualifiedDynamicLoader.d.ts +0 -2
  82. package/dist/types/@intlayer/core/dist/types/formatters/compact.d.ts +0 -1
  83. package/dist/types/@intlayer/core/dist/types/formatters/currency.d.ts +0 -1
  84. package/dist/types/@intlayer/core/dist/types/formatters/date.d.ts +0 -1
  85. package/dist/types/@intlayer/core/dist/types/formatters/index.d.ts +0 -9
  86. package/dist/types/@intlayer/core/dist/types/formatters/list.d.ts +0 -1
  87. package/dist/types/@intlayer/core/dist/types/formatters/number.d.ts +0 -1
  88. package/dist/types/@intlayer/core/dist/types/formatters/percentage.d.ts +0 -1
  89. package/dist/types/@intlayer/core/dist/types/formatters/relativeTime.d.ts +0 -1
  90. package/dist/types/@intlayer/core/dist/types/formatters/units.d.ts +0 -1
  91. package/dist/types/@intlayer/core/dist/types/interpreter/dictionaryTransformCache.d.ts +0 -4
  92. package/dist/types/@intlayer/core/dist/types/interpreter/getCondition.d.ts +0 -2
  93. package/dist/types/@intlayer/core/dist/types/interpreter/getContent/deepTransform.d.ts +0 -1
  94. package/dist/types/@intlayer/core/dist/types/interpreter/getContent/getContent.d.ts +0 -3
  95. package/dist/types/@intlayer/core/dist/types/interpreter/getContent/index.d.ts +0 -3
  96. package/dist/types/@intlayer/core/dist/types/interpreter/getContent/plugins.d.ts +0 -7
  97. package/dist/types/@intlayer/core/dist/types/interpreter/getDictionary.d.ts +0 -4
  98. package/dist/types/@intlayer/core/dist/types/interpreter/getDictionaryAsync.d.ts +0 -6
  99. package/dist/types/@intlayer/core/dist/types/interpreter/getEnumeration.d.ts +0 -2
  100. package/dist/types/@intlayer/core/dist/types/interpreter/getGender.d.ts +0 -2
  101. package/dist/types/@intlayer/core/dist/types/interpreter/getInsertion.d.ts +0 -2
  102. package/dist/types/@intlayer/core/dist/types/interpreter/getIntlayer.d.ts +0 -4
  103. package/dist/types/@intlayer/core/dist/types/interpreter/getIntlayerAsync.d.ts +0 -4
  104. package/dist/types/@intlayer/core/dist/types/interpreter/getNesting.d.ts +0 -6
  105. package/dist/types/@intlayer/core/dist/types/interpreter/getPlural.d.ts +0 -3
  106. package/dist/types/@intlayer/core/dist/types/interpreter/getSelect.d.ts +0 -2
  107. package/dist/types/@intlayer/core/dist/types/interpreter/getTranslation.d.ts +0 -1
  108. package/dist/types/@intlayer/core/dist/types/interpreter/index.d.ts +0 -18
  109. package/dist/types/@intlayer/core/dist/types/interpreter/interpolableNode.d.ts +0 -2
  110. package/dist/types/@intlayer/core/dist/types/intlayer/dist/types/index.d.ts +0 -6
  111. package/dist/types/@intlayer/core/dist/types/localization/comparePaths.d.ts +0 -1
  112. package/dist/types/@intlayer/core/dist/types/localization/domainUtils.d.ts +0 -2
  113. package/dist/types/@intlayer/core/dist/types/localization/generateSitemap.d.ts +0 -2
  114. package/dist/types/@intlayer/core/dist/types/localization/getBrowserLocale.d.ts +0 -1
  115. package/dist/types/@intlayer/core/dist/types/localization/getCanonicalPath.d.ts +0 -2
  116. package/dist/types/@intlayer/core/dist/types/localization/getHTMLTextDir.d.ts +0 -1
  117. package/dist/types/@intlayer/core/dist/types/localization/getLocale.d.ts +0 -1
  118. package/dist/types/@intlayer/core/dist/types/localization/getLocaleFromPath.d.ts +0 -2
  119. package/dist/types/@intlayer/core/dist/types/localization/getLocaleLang.d.ts +0 -1
  120. package/dist/types/@intlayer/core/dist/types/localization/getLocaleName.d.ts +0 -1
  121. package/dist/types/@intlayer/core/dist/types/localization/getLocalizedPath.d.ts +0 -2
  122. package/dist/types/@intlayer/core/dist/types/localization/getLocalizedUrl.d.ts +0 -2
  123. package/dist/types/@intlayer/core/dist/types/localization/getMultilingualUrls.d.ts +0 -2
  124. package/dist/types/@intlayer/core/dist/types/localization/getPathWithoutLocale.d.ts +0 -1
  125. package/dist/types/@intlayer/core/dist/types/localization/getPrefix.d.ts +0 -2
  126. package/dist/types/@intlayer/core/dist/types/localization/index.d.ts +0 -22
  127. package/dist/types/@intlayer/core/dist/types/localization/localeDetector.d.ts +0 -2
  128. package/dist/types/@intlayer/core/dist/types/localization/localeMapper.d.ts +0 -1
  129. package/dist/types/@intlayer/core/dist/types/localization/localeResolver.d.ts +0 -1
  130. package/dist/types/@intlayer/core/dist/types/localization/resolveBrowserLocale.d.ts +0 -2
  131. package/dist/types/@intlayer/core/dist/types/localization/rewriteMatch.d.ts +0 -1
  132. package/dist/types/@intlayer/core/dist/types/localization/rewriteUtils.d.ts +0 -2
  133. package/dist/types/@intlayer/core/dist/types/localization/validatePrefix.d.ts +0 -2
  134. package/dist/types/@intlayer/core/dist/types/markdown/index.d.ts +0 -2
  135. package/dist/types/@intlayer/core/dist/types/transpiler/condition/condition.d.ts +0 -1
  136. package/dist/types/@intlayer/core/dist/types/transpiler/condition/index.d.ts +0 -1
  137. package/dist/types/@intlayer/core/dist/types/transpiler/enumeration/enumeration.d.ts +0 -1
  138. package/dist/types/@intlayer/core/dist/types/transpiler/enumeration/index.d.ts +0 -1
  139. package/dist/types/@intlayer/core/dist/types/transpiler/file/file.d.ts +0 -1
  140. package/dist/types/@intlayer/core/dist/types/transpiler/file/index.d.ts +0 -1
  141. package/dist/types/@intlayer/core/dist/types/transpiler/gender/gender.d.ts +0 -1
  142. package/dist/types/@intlayer/core/dist/types/transpiler/gender/index.d.ts +0 -1
  143. package/dist/types/@intlayer/core/dist/types/transpiler/html/html.d.ts +0 -1
  144. package/dist/types/@intlayer/core/dist/types/transpiler/html/index.d.ts +0 -1
  145. package/dist/types/@intlayer/core/dist/types/transpiler/index.d.ts +0 -22
  146. package/dist/types/@intlayer/core/dist/types/transpiler/insertion/index.d.ts +0 -1
  147. package/dist/types/@intlayer/core/dist/types/transpiler/insertion/insertion.d.ts +0 -1
  148. package/dist/types/@intlayer/core/dist/types/transpiler/markdown/index.d.ts +0 -1
  149. package/dist/types/@intlayer/core/dist/types/transpiler/markdown/markdown.d.ts +0 -1
  150. package/dist/types/@intlayer/core/dist/types/transpiler/nesting/index.d.ts +0 -1
  151. package/dist/types/@intlayer/core/dist/types/transpiler/nesting/nesting.d.ts +0 -4
  152. package/dist/types/@intlayer/core/dist/types/transpiler/plural/index.d.ts +0 -1
  153. package/dist/types/@intlayer/core/dist/types/transpiler/plural/plural.d.ts +0 -1
  154. package/dist/types/@intlayer/core/dist/types/transpiler/select/index.d.ts +0 -1
  155. package/dist/types/@intlayer/core/dist/types/transpiler/select/select.d.ts +0 -1
  156. package/dist/types/@intlayer/core/dist/types/transpiler/translation/index.d.ts +0 -1
  157. package/dist/types/@intlayer/core/dist/types/transpiler/translation/translation.d.ts +0 -2
  158. package/dist/types/@intlayer/core/dist/types/utils/index.d.ts +0 -3
  159. package/dist/types/@intlayer/core/dist/types/utils/intl.d.ts +0 -1
  160. package/dist/types/@intlayer/core/dist/types/utils/isSameKeyPath.d.ts +0 -1
  161. package/dist/types/@intlayer/core/dist/types/utils/localeStorage.d.ts +0 -2
  162. package/dist/types/intlayer/dist/types/index.d.ts +0 -14
@@ -1 +1 @@
1
- {"version":3,"file":"renderer.cjs","names":[],"sources":["../../../src/markdown/renderer.ts"],"sourcesContent":["import type {\n ParserResult,\n ParseState,\n RenderRuleHook,\n RuleOutput,\n Rules,\n} from './types';\n\n/**\n * Creates a renderer for AST nodes.\n * Renamed from `reactFor` to be framework-agnostic.\n *\n * @param render - The render function to call for each node\n * @returns A function that renders AST to output\n */\nexport const renderFor = (\n render: (ast: ParserResult, render: RuleOutput, state: ParseState) => unknown\n) => {\n // Bound once instead of per node: rebuilding the recursive renderer on every\n // visit allocated a closure pair for each AST node.\n const emit = (\n ast: ParserResult | ParserResult[],\n state: ParseState = {}\n ): any => {\n if (!Array.isArray(ast)) {\n return render(ast, emit as RuleOutput, state);\n }\n\n const oldKey = state.key;\n const result: any[] = [];\n\n // map nestedOutput over the ast, except group any text\n // nodes together into a single string output.\n let lastWasString = false;\n let renderedIndex = 0;\n\n for (let i = 0; i < ast.length; i++) {\n // We clone the state to avoid side effects on other nodes in the same level\n // while ensuring each non-null rendered node gets a unique, sequential key.\n const nodeOut = emit(ast[i], { ...state, key: renderedIndex });\n const isString = typeof nodeOut === 'string';\n\n if (isString && lastWasString) {\n result[result.length - 1] =\n (result[result.length - 1] as string) + nodeOut;\n } else if (nodeOut !== null) {\n result.push(nodeOut);\n renderedIndex++;\n }\n\n lastWasString = isString;\n }\n\n state.key = oldKey;\n\n return result;\n };\n\n return emit;\n};\n\n/**\n * Creates a renderer from rules with optional custom render hook.\n *\n * @param rules - The rules object containing _render functions\n * @param userRender - Optional custom render hook for full control\n * @returns A render function for AST nodes\n */\nexport const createRenderer =\n (rules: Rules, userRender?: RenderRuleHook) =>\n (ast: ParserResult, render: RuleOutput, state: ParseState): unknown => {\n const renderer = rules[ast.type]?._render;\n\n return userRender\n ? userRender(() => renderer?.(ast, render, state), ast, render, state)\n : renderer?.(ast, render, state);\n };\n"],"mappings":";;;;;;;;;AAeA,MAAa,aACX,WACG;CAGH,MAAM,QACJ,KACA,QAAoB,CAAC,MACb;EACR,IAAI,CAAC,MAAM,QAAQ,GAAG,GACpB,OAAO,OAAO,KAAK,MAAoB,KAAK;EAG9C,MAAM,SAAS,MAAM;EACrB,MAAM,SAAgB,CAAC;EAIvB,IAAI,gBAAgB;EACpB,IAAI,gBAAgB;EAEpB,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;GAGnC,MAAM,UAAU,KAAK,IAAI,IAAI;IAAE,GAAG;IAAO,KAAK;GAAc,CAAC;GAC7D,MAAM,WAAW,OAAO,YAAY;GAEpC,IAAI,YAAY,eACd,OAAO,OAAO,SAAS,KACpB,OAAO,OAAO,SAAS,KAAgB;QACrC,IAAI,YAAY,MAAM;IAC3B,OAAO,KAAK,OAAO;IACnB;GACF;GAEA,gBAAgB;EAClB;EAEA,MAAM,MAAM;EAEZ,OAAO;CACT;CAEA,OAAO;AACT;;;;;;;;AASA,MAAa,kBACV,OAAc,gBACd,KAAmB,QAAoB,UAA+B;CACrE,MAAM,WAAW,MAAM,IAAI,KAAK,EAAE;CAElC,OAAO,aACH,iBAAiB,WAAW,KAAK,QAAQ,KAAK,GAAG,KAAK,QAAQ,KAAK,IACnE,WAAW,KAAK,QAAQ,KAAK;AACnC"}
1
+ {"version":3,"file":"renderer.cjs","names":[],"sources":["../../../src/markdown/renderer.ts"],"sourcesContent":["import type {\n ParserResult,\n ParseState,\n RenderRuleHook,\n Rule,\n RuleOutput,\n Rules,\n} from './types';\n\n/**\n * Creates a renderer for AST nodes.\n * Renamed from `reactFor` to be framework-agnostic.\n *\n * @param render - The render function to call for each node\n * @returns A function that renders AST to output\n */\n\nexport const renderFor = (\n render: (ast: ParserResult, render: RuleOutput, state: ParseState) => unknown\n) => {\n const emit = (\n ast: ParserResult | ParserResult[],\n state: ParseState = {}\n ): any => {\n if (!Array.isArray(ast)) {\n return render(ast, emit as RuleOutput, state);\n }\n\n const oldKey = state.key;\n const result: any[] = [];\n\n let lastWasString = false;\n let renderedIndex = 0;\n\n for (let i = 0; i < ast.length; i++) {\n state.key = renderedIndex;\n const node = ast[i]!;\n const nodeOut = Array.isArray(node)\n ? emit(node, state)\n : render(node, emit as RuleOutput, state);\n const isString = typeof nodeOut === 'string';\n\n if (isString && lastWasString) {\n result[result.length - 1] =\n (result[result.length - 1] as string) + nodeOut;\n } else if (nodeOut !== null) {\n result.push(nodeOut);\n renderedIndex++;\n }\n\n lastWasString = isString;\n }\n\n state.key = oldKey;\n\n return result;\n };\n\n return emit;\n};\n\n/**\n * Creates a renderer from rules with optional custom render hook.\n */\nexport const createRenderer = (rules: Rules, userRender?: RenderRuleHook) => {\n const renderers: Record<string, Rule<any>['_render']> = {};\n for (const type in rules) renderers[type] = rules[type]?._render;\n\n if (!userRender) {\n return (\n ast: ParserResult,\n render: RuleOutput,\n state: ParseState\n ): unknown => {\n const r = renderers[ast.type];\n return r ? r(ast, render, state) : undefined;\n };\n }\n\n return (\n ast: ParserResult,\n render: RuleOutput,\n state: ParseState\n ): unknown => {\n const renderer = renderers[ast.type];\n\n return userRender(() => renderer?.(ast, render, state), ast, render, state);\n };\n};\n"],"mappings":";;;;;;;;;AAiBA,MAAa,aACX,WACG;CACH,MAAM,QACJ,KACA,QAAoB,CAAC,MACb;EACR,IAAI,CAAC,MAAM,QAAQ,GAAG,GACpB,OAAO,OAAO,KAAK,MAAoB,KAAK;EAG9C,MAAM,SAAS,MAAM;EACrB,MAAM,SAAgB,CAAC;EAEvB,IAAI,gBAAgB;EACpB,IAAI,gBAAgB;EAEpB,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;GACnC,MAAM,MAAM;GACZ,MAAM,OAAO,IAAI;GACjB,MAAM,UAAU,MAAM,QAAQ,IAAI,IAC9B,KAAK,MAAM,KAAK,IAChB,OAAO,MAAM,MAAoB,KAAK;GAC1C,MAAM,WAAW,OAAO,YAAY;GAEpC,IAAI,YAAY,eACd,OAAO,OAAO,SAAS,KACpB,OAAO,OAAO,SAAS,KAAgB;QACrC,IAAI,YAAY,MAAM;IAC3B,OAAO,KAAK,OAAO;IACnB;GACF;GAEA,gBAAgB;EAClB;EAEA,MAAM,MAAM;EAEZ,OAAO;CACT;CAEA,OAAO;AACT;;;;AAKA,MAAa,kBAAkB,OAAc,eAAgC;CAC3E,MAAM,YAAkD,CAAC;CACzD,KAAK,MAAM,QAAQ,OAAO,UAAU,QAAQ,MAAM,KAAK,EAAE;CAEzD,IAAI,CAAC,YACH,QACE,KACA,QACA,UACY;EACZ,MAAM,IAAI,UAAU,IAAI;EACxB,OAAO,IAAI,EAAE,KAAK,QAAQ,KAAK,IAAI;CACrC;CAGF,QACE,KACA,QACA,UACY;EACZ,MAAM,WAAW,UAAU,IAAI;EAE/B,OAAO,iBAAiB,WAAW,KAAK,QAAQ,KAAK,GAAG,KAAK,QAAQ,KAAK;CAC5E;AACF"}
@@ -7,8 +7,8 @@ const require_markdown_constants = require('./constants.cjs');
7
7
  */
8
8
  const trimEnd = (str) => {
9
9
  let end = str.length;
10
- while (end > 0 && str[end - 1] <= " ") end--;
11
- return str.slice(0, end);
10
+ while (end > 0 && str.charCodeAt(end - 1) <= 32) end--;
11
+ return end === str.length ? str : str.slice(0, end);
12
12
  };
13
13
  /**
14
14
  * Check if string starts with prefix.
@@ -27,7 +27,7 @@ const unquote = (str) => {
27
27
  /**
28
28
  * Unescape backslash-escaped characters.
29
29
  */
30
- const unescapeString = (rawString) => rawString ? rawString.replace(require_markdown_constants.UNESCAPE_R, "$1") : rawString;
30
+ const unescapeString = (rawString) => rawString && rawString.indexOf("\\") !== -1 ? rawString.replace(require_markdown_constants.UNESCAPE_R, "$1") : rawString;
31
31
  /**
32
32
  * Join class names, filtering out falsy values.
33
33
  */
@@ -36,6 +36,7 @@ const cx = (...args) => args.filter(Boolean).join(" ");
36
36
  * Get a nested property from an object using dot notation.
37
37
  */
38
38
  const get = (src, path, fb) => {
39
+ if (path.indexOf(".") === -1) return src?.[path] ?? fb;
39
40
  let ptr = src;
40
41
  const frags = path.split(".");
41
42
  while (frags.length) {
@@ -58,16 +59,18 @@ const SLUGIFY_REPLACEMENTS = [
58
59
  [/[Ññ]/g, "n"],
59
60
  [/[øØœŒÕõÔôÓóÒò]/g, "o"],
60
61
  [/[ÜüÛûÚúÙù]/g, "u"],
61
- [/[ŸÿÝý]/g, "y"],
62
- [/[^a-z0-9- ]/gi, ""],
63
- [/ /gi, "-"]
62
+ [/[ŸÿÝý]/g, "y"]
64
63
  ];
64
+ const NON_ASCII_R = /[\u0080-\uffff]/;
65
+ const SLUGIFY_STRIP_R = /[^a-z0-9- ]/gi;
66
+ const SLUGIFY_SPACE_R = / /g;
65
67
  const slugify = (str) => {
66
68
  let result = str;
67
- for (let i = 0; i < SLUGIFY_REPLACEMENTS.length; i++) {
69
+ if (NON_ASCII_R.test(result)) for (let i = 0; i < SLUGIFY_REPLACEMENTS.length; i++) {
68
70
  const [pattern, replacement] = SLUGIFY_REPLACEMENTS[i];
69
71
  result = result.replace(pattern, replacement);
70
72
  }
73
+ result = result.replace(SLUGIFY_STRIP_R, "").replace(SLUGIFY_SPACE_R, "-");
71
74
  return result.toLowerCase();
72
75
  };
73
76
  const SANITIZE_R = /(javascript|vbscript|data(?!:image)):/i;
@@ -93,11 +96,13 @@ const sanitizer = (input) => {
93
96
  /**
94
97
  * Normalize whitespace in source string.
95
98
  */
96
- const normalizeWhitespace = (source) => source.replace(require_markdown_constants.NORMALIZE_WHITESPACE_R, (match) => {
97
- if (match === " ") return " ";
98
- if (match === "\f") return "";
99
- return "\n";
100
- });
99
+ const normalizeWhitespace = (source) => {
100
+ let normalized = source;
101
+ if (normalized.indexOf(" ") !== -1) normalized = normalized.replace(require_markdown_constants.TAB_R, " ");
102
+ if (normalized.indexOf("\r") !== -1) normalized = normalized.replace(require_markdown_constants.CR_NEWLINE_R, "\n");
103
+ if (normalized.indexOf("\f") !== -1) normalized = normalized.replace(require_markdown_constants.FORMFEED_R, "");
104
+ return normalized;
105
+ };
101
106
  /**
102
107
  * Safely remove a uniform leading indentation from lines, but do NOT touch
103
108
  * the content inside fenced code blocks (``` or ~~~).
@@ -204,6 +209,7 @@ const attributeValueToNodePropValue = (tag, key, value, sanitizeUrlFn) => {
204
209
  * Parse table alignment from a separator row.
205
210
  */
206
211
  const parseTableAlignCapture = (alignCapture) => {
212
+ if (alignCapture.indexOf(":") === -1) return "left";
207
213
  if (require_markdown_constants.TABLE_RIGHT_ALIGN.test(alignCapture)) return "right";
208
214
  else if (require_markdown_constants.TABLE_CENTER_ALIGN.test(alignCapture)) return "center";
209
215
  else if (require_markdown_constants.TABLE_LEFT_ALIGN.test(alignCapture)) return "left";
@@ -221,25 +227,88 @@ const parseTableAlign = (source) => {
221
227
  const parseTableRow = (source, parse, state, tableOutput) => {
222
228
  const prevInTable = state.inTable;
223
229
  state.inTable = true;
224
- const cells = [[]];
225
- let acc = "";
226
- const flush = () => {
227
- if (!acc) return;
228
- const cell = cells[cells.length - 1];
229
- cell.push.apply(cell, parse(acc, state));
230
- acc = "";
230
+ let rStart = 0;
231
+ let rEnd = source.length;
232
+ while (rStart < rEnd) {
233
+ const c = source.charCodeAt(rStart);
234
+ if (c === 32 || c === 9) rStart++;
235
+ else break;
236
+ }
237
+ while (rEnd > rStart) {
238
+ const c = source.charCodeAt(rEnd - 1);
239
+ if (c === 32 || c === 9) rEnd--;
240
+ else break;
241
+ }
242
+ if (rStart < rEnd && source.charCodeAt(rStart) === 124) rStart++;
243
+ if (rEnd > rStart && source.charCodeAt(rEnd - 1) === 124 && (rEnd - 2 < rStart || source.charCodeAt(rEnd - 2) !== 92)) rEnd--;
244
+ if (!tableOutput) {
245
+ const cell = parse(source.slice(rStart, rEnd), state);
246
+ state.inTable = prevInTable;
247
+ return [cell];
248
+ }
249
+ const cells = [];
250
+ const len = source.length;
251
+ let cellStart = rStart;
252
+ let i = rStart;
253
+ /**
254
+ * Parse one cell, dropping the padding an aligned table pads it with.
255
+ *
256
+ * GFM strips a cell's surrounding whitespace, so trimming is both the correct
257
+ * output and the cheap one: the padding of a column-aligned table is a large
258
+ * share of its bytes, and every space handed to `parse` costs a rule dispatch,
259
+ * a capture and a lookbehind update. `trim` is a native scan, and walking the
260
+ * padding back from the delimiter in JavaScript instead costs half as much
261
+ * again as the whole row scan.
262
+ */
263
+ const parseCell = (from, to) => {
264
+ const cell = source.slice(from, to).trim();
265
+ return cell === "" ? [] : parse(cell, state);
231
266
  };
232
- source.trim().split(/(`[^`]*`|\\\||\|)/).filter(Boolean).forEach((fragment, i, arr) => {
233
- if (fragment.trim() === "|") {
234
- flush();
235
- if (tableOutput) {
236
- if (i !== 0 && i !== arr.length - 1) cells.push([]);
237
- return;
267
+ let pipeAt = -1;
268
+ let escapeAt = -1;
269
+ let backtickAt = -1;
270
+ while (i < rEnd) {
271
+ if (pipeAt < i) {
272
+ const at = source.indexOf("|", i);
273
+ pipeAt = at === -1 ? len : at;
274
+ }
275
+ if (escapeAt < i) {
276
+ const at = source.indexOf("\\", i);
277
+ escapeAt = at === -1 ? len : at;
278
+ }
279
+ if (backtickAt < i) {
280
+ const at = source.indexOf("`", i);
281
+ backtickAt = at === -1 ? len : at;
282
+ }
283
+ i = Math.min(pipeAt, escapeAt, backtickAt);
284
+ if (i >= rEnd) break;
285
+ if (i === escapeAt) {
286
+ i += i + 1 < rEnd ? 2 : 1;
287
+ continue;
288
+ }
289
+ if (i === backtickAt) {
290
+ let backticks = 0;
291
+ while (i < rEnd && source.charCodeAt(i) === 96) {
292
+ backticks++;
293
+ i++;
294
+ }
295
+ let found = false;
296
+ while (i < rEnd && !found) {
297
+ let closeBackticks = 0;
298
+ while (i < rEnd && source.charCodeAt(i) === 96) {
299
+ closeBackticks++;
300
+ i++;
301
+ }
302
+ if (closeBackticks === backticks) found = true;
303
+ else if (closeBackticks === 0) i++;
238
304
  }
305
+ continue;
239
306
  }
240
- acc += fragment;
241
- });
242
- flush();
307
+ cells.push(parseCell(cellStart, i));
308
+ i++;
309
+ cellStart = i;
310
+ }
311
+ cells.push(parseCell(cellStart, rEnd));
243
312
  state.inTable = prevInTable;
244
313
  return cells;
245
314
  };
@@ -247,7 +316,27 @@ const parseTableRow = (source, parse, state, tableOutput) => {
247
316
  * Parse table cells (multiple rows).
248
317
  */
249
318
  const parseTableCells = (source, parse, state) => {
250
- return source.trim().split("\n").map((rowText) => parseTableRow(rowText, parse, state, true));
319
+ if (!source) return [];
320
+ const result = [];
321
+ let start = 0;
322
+ const len = source.length;
323
+ while (start < len) {
324
+ let nextNewline = source.indexOf("\n", start);
325
+ if (nextNewline === -1) nextNewline = len;
326
+ const rowText = source.slice(start, nextNewline);
327
+ start = nextNewline + 1;
328
+ let hasContent = false;
329
+ for (let j = 0; j < rowText.length; j++) {
330
+ const c = rowText.charCodeAt(j);
331
+ if (c !== 32 && c !== 9 && c !== 13) {
332
+ hasContent = true;
333
+ break;
334
+ }
335
+ }
336
+ if (!hasContent) continue;
337
+ result.push(parseTableRow(rowText, parse, state, true));
338
+ }
339
+ return result;
251
340
  };
252
341
  /**
253
342
  * Check if a rule qualifies for the current source and state.
@@ -345,11 +434,11 @@ const captureNothing = () => ({});
345
434
  * Helper that renders nothing (null).
346
435
  */
347
436
  const renderNothing = () => null;
348
- /**
349
- * Check if any regex in a list matches the input.
350
- */
351
- const some = (regexes, input) => {
352
- for (let i = 0; i < regexes.length; i++) if (regexes[i].test(input)) return true;
437
+ const some = (probes, input) => {
438
+ for (let i = 0; i < probes.length; i++) {
439
+ const probe = probes[i];
440
+ if (typeof probe === "function" ? probe(input) : probe.test(input)) return true;
441
+ }
353
442
  return false;
354
443
  };
355
444
 
@@ -1 +1 @@
1
- {"version":3,"file":"utils.cjs","names":["UNESCAPE_R","NORMALIZE_WHITESPACE_R","FENCE_DELIMITER_R","HTML_CUSTOM_ATTR_R","CAPTURE_LETTER_AFTER_HYPHEN","ATTRIBUTES_TO_SANITIZE","INTERPOLATION_R","TABLE_RIGHT_ALIGN","TABLE_CENTER_ALIGN","TABLE_LEFT_ALIGN","TABLE_TRIM_PIPES"],"sources":["../../../src/markdown/utils.ts"],"sourcesContent":["import {\n ATTRIBUTES_TO_SANITIZE,\n CAPTURE_LETTER_AFTER_HYPHEN,\n FENCE_DELIMITER_R,\n HTML_CUSTOM_ATTR_R,\n INTERPOLATION_R,\n NORMALIZE_WHITESPACE_R,\n TABLE_CENTER_ALIGN,\n TABLE_LEFT_ALIGN,\n TABLE_RIGHT_ALIGN,\n TABLE_TRIM_PIPES,\n UNESCAPE_R,\n} from './constants';\nimport type { NestedParser, ParserResult, ParseState, Rule } from './types';\n\n// ============================================================================\n// STRING UTILITIES\n// ============================================================================\n\n/**\n * Trim trailing whitespace from a string.\n */\nexport const trimEnd = (str: string): string => {\n let end = str.length;\n\n while (end > 0 && str[end - 1]! <= ' ') end--;\n\n return str.slice(0, end);\n};\n\n/**\n * Check if string starts with prefix.\n */\nexport const startsWith = (str: string, prefix: string): boolean => {\n return str.startsWith(prefix);\n};\n\n/**\n * Remove symmetrical leading and trailing quotes.\n */\nexport const unquote = (str: string): string => {\n const first = str[0];\n\n if (\n (first === '\"' || first === \"'\") &&\n str.length >= 2 &&\n str[str.length - 1] === first\n ) {\n return str.slice(1, -1);\n }\n\n return str;\n};\n\n/**\n * Unescape backslash-escaped characters.\n */\nexport const unescapeString = (rawString: string): string =>\n rawString ? rawString.replace(UNESCAPE_R, '$1') : rawString;\n\n/**\n * Join class names, filtering out falsy values.\n */\nexport const cx = (...args: any[]): string => args.filter(Boolean).join(' ');\n\n/**\n * Get a nested property from an object using dot notation.\n */\nexport const get = (src: any, path: string, fb?: any): any => {\n let ptr = src;\n const frags = path.split('.');\n\n while (frags.length) {\n ptr = ptr[frags[0]!];\n\n if (ptr === undefined) break;\n else frags.shift();\n }\n\n return ptr ?? fb;\n};\n\n// ============================================================================\n// SLUGIFY\n// ============================================================================\n\n/**\n * Convert a string to a URL-safe slug.\n * Based on https://stackoverflow.com/a/18123682/1141611\n */\nconst SLUGIFY_REPLACEMENTS: [pattern: RegExp, replacement: string][] = [\n [/[ÀÁÂÃÄÅàáâãä忯]/g, 'a'],\n [/[çÇ]/g, 'c'],\n [/[ðÐ]/g, 'd'],\n [/[ÈÉÊËéèêë]/g, 'e'],\n [/[ÏïÎîÍíÌì]/g, 'i'],\n [/[Ññ]/g, 'n'],\n [/[øØœŒÕõÔôÓóÒò]/g, 'o'],\n [/[ÜüÛûÚúÙù]/g, 'u'],\n [/[ŸÿÝý]/g, 'y'],\n [/[^a-z0-9- ]/gi, ''],\n [/ /gi, '-'],\n];\n\nexport const slugify = (str: string): string => {\n let result = str;\n\n for (let i = 0; i < SLUGIFY_REPLACEMENTS.length; i++) {\n const [pattern, replacement] = SLUGIFY_REPLACEMENTS[i]!;\n result = result.replace(pattern, replacement);\n }\n\n return result.toLowerCase();\n};\n\n// ============================================================================\n// SANITIZER\n// ============================================================================\n\nconst SANITIZE_R = /(javascript|vbscript|data(?!:image)):/i;\nconst SANITIZE_STRIP_R = /[^A-Za-z0-9/:]/g;\n\n/**\n * Sanitize URLs to prevent XSS attacks.\n * Returns null if the URL is unsafe.\n */\nexport const sanitizer = (input: string): string | null => {\n // `SANITIZE_R` only ever matches on a colon, and decoding can introduce one\n // solely through a percent escape — so a value with neither is safe as-is,\n // and skipping the decode avoids the bulk of the work on ordinary links.\n if (input.indexOf(':') === -1 && input.indexOf('%') === -1) return input;\n\n try {\n const decoded = decodeURIComponent(input).replace(SANITIZE_STRIP_R, '');\n\n if (SANITIZE_R.test(decoded)) {\n console.warn(\n 'Input contains an unsafe JavaScript/VBScript/data expression, it will not be rendered.',\n decoded\n );\n\n return null;\n }\n } catch (_e) {\n console.warn(\n 'Input could not be decoded due to malformed syntax or characters, it will not be rendered.',\n input\n );\n\n // decodeURIComponent sometimes throws a URIError\n return null;\n }\n\n return input;\n};\n\n// ============================================================================\n// WHITESPACE NORMALIZATION\n// ============================================================================\n\n/**\n * Normalize whitespace in source string.\n */\nexport const normalizeWhitespace = (source: string): string =>\n // `\\r`, `\\f` and `\\t` are disjoint, so one pass over the source replaces\n // what used to take three full scans.\n source.replace(NORMALIZE_WHITESPACE_R, (match) => {\n if (match === '\\t') return ' ';\n if (match === '\\f') return '';\n\n return '\\n';\n });\n\n/**\n * Safely remove a uniform leading indentation from lines, but do NOT touch\n * the content inside fenced code blocks (``` or ~~~).\n *\n * Inside a fenced block the structural indentation is irrelevant: what matters\n * is the indentation of the opening fence itself, which is the only amount\n * CommonMark strips from the code lines. A block nested in an indented HTML\n * element but written with its fence at column 0 therefore keeps its code\n * verbatim, instead of losing the first level of indentation.\n */\nexport const trimLeadingWhitespaceOutsideFences = (\n text: string,\n whitespace: string\n): string => {\n if (!whitespace) return text;\n\n const lines = text.split('\\n');\n\n /** Indentation and marker of the fence currently open, if any. */\n let openFence: { indentation: string; marker: string } | null = null;\n\n const removePrefix = (line: string, prefix: string): string =>\n prefix && line.startsWith(prefix) ? line.slice(prefix.length) : line;\n\n // Outside fences, strip the base structural indentation from every line.\n // Lines that don't start with the whitespace prefix are left as-is.\n const result = lines\n .map((line) => {\n const fenceMatch = line.match(FENCE_DELIMITER_R);\n\n if (!openFence) {\n if (fenceMatch)\n openFence = { indentation: fenceMatch[1]!, marker: fenceMatch[2]! };\n return removePrefix(line, whitespace);\n }\n\n const isClosingFence = fenceMatch?.[2]?.startsWith(openFence.marker);\n const { indentation } = openFence;\n\n if (isClosingFence) openFence = null;\n\n return removePrefix(line, indentation);\n })\n .join('\\n');\n\n return result;\n};\n\n/**\n * Normalize HTML attribute key to JSX prop name.\n */\nexport const normalizeAttributeKey = (key: string): string => {\n const hyphenIndex = key.indexOf('-');\n\n if (hyphenIndex !== -1 && key.match(HTML_CUSTOM_ATTR_R) === null) {\n key = key.replace(CAPTURE_LETTER_AFTER_HYPHEN, (_, letter) => {\n return letter.toUpperCase();\n });\n }\n\n return key;\n};\n\ntype StyleTuple = [key: string, value: string];\n\n/**\n * Parse a CSS style string into an array of [key, value] tuples.\n */\nexport const parseStyleAttribute = (styleString: string): StyleTuple[] => {\n const styles: StyleTuple[] = [];\n let buffer = '';\n let inUrl = false;\n let inQuotes = false;\n let quoteChar: '\"' | \"'\" | '' = '';\n\n if (!styleString) return styles;\n\n for (let i = 0; i < styleString.length; i++) {\n const char = styleString[i];\n\n if ((char === '\"' || char === \"'\") && !inUrl) {\n if (!inQuotes) {\n inQuotes = true;\n quoteChar = char;\n } else if (char === quoteChar) {\n inQuotes = false;\n quoteChar = '';\n }\n }\n\n if (char === '(' && buffer.endsWith('url')) {\n inUrl = true;\n } else if (char === ')' && inUrl) {\n inUrl = false;\n }\n\n if (char === ';' && !inQuotes && !inUrl) {\n const declaration = buffer.trim();\n\n if (declaration) {\n const colonIndex = declaration.indexOf(':');\n\n if (colonIndex > 0) {\n const key = declaration.slice(0, colonIndex).trim();\n const value = declaration.slice(colonIndex + 1).trim();\n styles.push([key, value]);\n }\n }\n buffer = '';\n } else {\n buffer += char;\n }\n }\n\n const declaration = buffer.trim();\n\n if (declaration) {\n const colonIndex = declaration.indexOf(':');\n if (colonIndex > 0) {\n const key = declaration.slice(0, colonIndex).trim();\n const value = declaration.slice(colonIndex + 1).trim();\n styles.push([key, value]);\n }\n }\n\n return styles;\n};\n\n/**\n * Convert an attribute value to a Node prop value.\n */\nexport const attributeValueToNodePropValue = (\n tag: string,\n key: string,\n value: string,\n sanitizeUrlFn: (\n value: string,\n tag: string,\n attribute: string\n ) => string | null\n): any => {\n if (key === 'style') {\n return parseStyleAttribute(value).reduce(\n (styles, [styleKey, styleValue]) => {\n const camelCasedKey = styleKey.replace(/(-[a-z])/g, (substr) =>\n substr[1]!.toUpperCase()\n );\n\n (styles as Record<string, any>)[camelCasedKey] = sanitizeUrlFn(\n styleValue,\n tag,\n styleKey\n );\n\n return styles;\n },\n {} as Record<string, any>\n );\n } else if (ATTRIBUTES_TO_SANITIZE.indexOf(key) !== -1) {\n return sanitizeUrlFn(unescapeString(value), tag, key);\n } else if (value.match(INTERPOLATION_R)) {\n value = unescapeString(value.slice(1, value.length - 1));\n }\n\n if (value === 'true') {\n return true;\n } else if (value === 'false') {\n return false;\n }\n\n return value;\n};\n\n// ============================================================================\n// TABLE PARSING\n// ============================================================================\n\n/**\n * Parse table alignment from a separator row.\n */\nexport const parseTableAlignCapture = (\n alignCapture: string\n): 'left' | 'right' | 'center' => {\n if (TABLE_RIGHT_ALIGN.test(alignCapture)) {\n return 'right';\n } else if (TABLE_CENTER_ALIGN.test(alignCapture)) {\n return 'center';\n } else if (TABLE_LEFT_ALIGN.test(alignCapture)) {\n return 'left';\n }\n\n return 'left';\n};\n\n/**\n * Parse table alignment row.\n */\nexport const parseTableAlign = (\n source: string\n): ('left' | 'right' | 'center')[] => {\n const alignText = source.replace(TABLE_TRIM_PIPES, '').split('|');\n return alignText.map(parseTableAlignCapture);\n};\n\n/**\n * Parse a single table row.\n */\nexport const parseTableRow = (\n source: string,\n parse: NestedParser,\n state: ParseState,\n tableOutput: boolean\n): ParserResult[][] => {\n const prevInTable = state.inTable;\n\n state.inTable = true;\n\n const cells: ParserResult[][] = [[]];\n let acc = '';\n\n const flush = (): void => {\n if (!acc) return;\n\n const cell = cells[cells.length - 1]!;\n cell.push.apply(cell, parse(acc, state));\n acc = '';\n };\n\n source\n .trim()\n .split(/(`[^`]*`|\\\\\\||\\|)/)\n .filter(Boolean)\n .forEach((fragment, i, arr) => {\n if (fragment.trim() === '|') {\n flush();\n\n if (tableOutput) {\n if (i !== 0 && i !== arr.length - 1) {\n cells.push([]);\n }\n\n return;\n }\n }\n\n acc += fragment;\n });\n\n flush();\n\n state.inTable = prevInTable;\n\n return cells;\n};\n\n/**\n * Parse table cells (multiple rows).\n */\nexport const parseTableCells = (\n source: string,\n parse: NestedParser,\n state: ParseState\n): ParserResult[][][] => {\n const rowsText = source.trim().split('\\n');\n\n const result = rowsText.map((rowText) =>\n parseTableRow(rowText, parse, state, true)\n );\n\n return result;\n};\n\n// ============================================================================\n// PARSING HELPERS\n// ============================================================================\n\n/**\n * Check if a rule qualifies for the current source and state.\n */\nexport const qualifies = (\n source: string,\n state: ParseState,\n qualify: NonNullable<Rule<any>['_qualify']>\n): boolean => {\n if (Array.isArray(qualify)) {\n for (let i = 0; i < qualify.length; i++) {\n if (startsWith(source, qualify[i]!)) return true;\n }\n\n return false;\n }\n\n return (qualify as (source: string, state: ParseState) => boolean)(\n source,\n state\n );\n};\n\n/**\n * Marks a matcher function as eligible for being run inside an inline context.\n */\nexport const allowInline = <T extends (...args: any[]) => any>(\n fn: T\n): T & { inline: 1 } => {\n (fn as any).inline = 1;\n return fn as T & { inline: 1 };\n};\n\n/**\n * Creates a match function for an inline scoped element from a regex.\n */\nexport const inlineRegex = (regex: RegExp) =>\n allowInline((source: string, state: ParseState): RegExpMatchArray | null => {\n if (state.inline) {\n return regex.exec(source);\n } else {\n return null;\n }\n });\n\n/**\n * Creates a match function for inline elements except links.\n */\nexport const simpleInlineRegex = (regex: RegExp) =>\n allowInline((source: string, state: ParseState): RegExpMatchArray | null => {\n if (state.inline || state.simple) {\n return regex.exec(source);\n } else {\n return null;\n }\n });\n\n/**\n * Creates a match function for a block scoped element from a regex.\n */\nexport const blockRegex =\n (regex: RegExp) =>\n (source: string, state: ParseState): RegExpMatchArray | null => {\n if (state.inline || state.simple) {\n return null;\n } else {\n return regex.exec(source);\n }\n };\n\n/**\n * Creates a match function from a regex, ignoring block/inline scope.\n */\nexport const anyScopeRegex = (\n fn: RegExp | ((source: string, state: ParseState) => RegExpMatchArray | null)\n) =>\n allowInline((source: string, state: ParseState): RegExpMatchArray | null => {\n if (typeof fn === 'function') {\n return fn(source, state);\n }\n return fn.exec(source);\n });\n\n/**\n * Parse inline content (including links).\n */\nexport const parseInline = (\n parse: NestedParser,\n children: string,\n state: ParseState\n): ParserResult[] => {\n const isCurrentlyInline = state.inline ?? false;\n const isCurrentlySimple = state.simple ?? false;\n state.inline = true;\n state.simple = true;\n const result = parse(children, state);\n state.inline = isCurrentlyInline;\n state.simple = isCurrentlySimple;\n\n return result;\n};\n\n/**\n * Parse simple inline content (no links).\n */\nexport const parseSimpleInline = (\n parse: NestedParser,\n children: string,\n state: ParseState\n): ParserResult[] => {\n const isCurrentlyInline = state.inline ?? false;\n const isCurrentlySimple = state.simple ?? false;\n\n state.inline = false;\n state.simple = true;\n const result = parse(children, state);\n state.inline = isCurrentlyInline;\n state.simple = isCurrentlySimple;\n\n return result;\n};\n\n/**\n * Parse block content.\n */\nexport const parseBlock = (\n parse: NestedParser,\n children: string,\n state: ParseState = {}\n): ParserResult[] => {\n const isCurrentlyInline = state.inline || false;\n state.inline = false;\n const normalizedChildren = trimEnd(children);\n const needsTerminator = /\\n\\n$/.test(normalizedChildren) === false;\n const blockInput = needsTerminator\n ? normalizedChildren.endsWith('\\n')\n ? `${normalizedChildren}\\n`\n : `${normalizedChildren}\\n\\n`\n : normalizedChildren;\n\n const result = parse(blockInput, state);\n state.inline = isCurrentlyInline;\n\n return result;\n};\n\n/**\n * Helper to parse capture group 2 as inline content.\n */\nexport const parseCaptureInline = (\n capture: RegExpMatchArray,\n parse: NestedParser,\n state: ParseState\n): { children: ParserResult[] } => {\n return {\n children: parseInline(parse, capture[2] ?? '', state),\n };\n};\n\n/**\n * Helper that captures nothing (empty object).\n */\nexport const captureNothing = (): Record<string, never> => ({});\n\n/**\n * Helper that renders nothing (null).\n */\nexport const renderNothing = (): null => null;\n\n/**\n * Check if any regex in a list matches the input.\n */\nexport const some = (regexes: RegExp[], input: string): boolean => {\n for (let i = 0; i < regexes.length; i++) {\n if (regexes[i]!.test(input)) {\n return true;\n }\n }\n return false;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"utils.cjs","names":["UNESCAPE_R","TAB_R","CR_NEWLINE_R","FORMFEED_R","FENCE_DELIMITER_R","HTML_CUSTOM_ATTR_R","CAPTURE_LETTER_AFTER_HYPHEN","ATTRIBUTES_TO_SANITIZE","INTERPOLATION_R","TABLE_RIGHT_ALIGN","TABLE_CENTER_ALIGN","TABLE_LEFT_ALIGN","TABLE_TRIM_PIPES"],"sources":["../../../src/markdown/utils.ts"],"sourcesContent":["import {\n ATTRIBUTES_TO_SANITIZE,\n CAPTURE_LETTER_AFTER_HYPHEN,\n CR_NEWLINE_R,\n FENCE_DELIMITER_R,\n FORMFEED_R,\n HTML_CUSTOM_ATTR_R,\n INTERPOLATION_R,\n TAB_R,\n TABLE_CENTER_ALIGN,\n TABLE_LEFT_ALIGN,\n TABLE_RIGHT_ALIGN,\n TABLE_TRIM_PIPES,\n UNESCAPE_R,\n} from './constants';\nimport type { NestedParser, ParserResult, ParseState, Rule } from './types';\n\n// ============================================================================\n// STRING UTILITIES\n// ============================================================================\n\n/**\n * Trim trailing whitespace from a string.\n */\nexport const trimEnd = (str: string): string => {\n let end = str.length;\n\n while (end > 0 && str.charCodeAt(end - 1) <= 32) end--;\n\n return end === str.length ? str : str.slice(0, end);\n};\n\n/**\n * Check if string starts with prefix.\n */\nexport const startsWith = (str: string, prefix: string): boolean => {\n return str.startsWith(prefix);\n};\n\n/**\n * Remove symmetrical leading and trailing quotes.\n */\nexport const unquote = (str: string): string => {\n const first = str[0];\n\n if (\n (first === '\"' || first === \"'\") &&\n str.length >= 2 &&\n str[str.length - 1] === first\n ) {\n return str.slice(1, -1);\n }\n\n return str;\n};\n\n/**\n * Unescape backslash-escaped characters.\n */\nexport const unescapeString = <T extends string | undefined>(rawString: T): T =>\n (rawString && rawString.indexOf('\\\\') !== -1\n ? rawString.replace(UNESCAPE_R, '$1')\n : rawString) as T;\n\n/**\n * Join class names, filtering out falsy values.\n */\nexport const cx = (...args: any[]): string => args.filter(Boolean).join(' ');\n\n/**\n * Get a nested property from an object using dot notation.\n */\nexport const get = (src: any, path: string, fb?: any): any => {\n // The overwhelming majority of paths are a single segment (a tag name), and\n // splitting those allocated two arrays per lookup.\n if (path.indexOf('.') === -1) return src?.[path] ?? fb;\n\n let ptr = src;\n const frags = path.split('.');\n\n while (frags.length) {\n ptr = ptr[frags[0]!];\n\n if (ptr === undefined) break;\n else frags.shift();\n }\n\n return ptr ?? fb;\n};\n\n// ============================================================================\n// SLUGIFY\n// ============================================================================\n\n/**\n * Convert a string to a URL-safe slug.\n * Based on https://stackoverflow.com/a/18123682/1141611\n */\nconst SLUGIFY_REPLACEMENTS: [pattern: RegExp, replacement: string][] = [\n [/[ÀÁÂÃÄÅàáâãä忯]/g, 'a'],\n [/[çÇ]/g, 'c'],\n [/[ðÐ]/g, 'd'],\n [/[ÈÉÊËéèêë]/g, 'e'],\n [/[ÏïÎîÍíÌì]/g, 'i'],\n [/[Ññ]/g, 'n'],\n [/[øØœŒÕõÔôÓóÒò]/g, 'o'],\n [/[ÜüÛûÚúÙù]/g, 'u'],\n [/[ŸÿÝý]/g, 'y'],\n];\nconst NON_ASCII_R = /[\\u0080-\\uffff]/;\nconst SLUGIFY_STRIP_R = /[^a-z0-9- ]/gi;\nconst SLUGIFY_SPACE_R = / /g;\n\nexport const slugify = (str: string): string => {\n let result = str;\n\n // Every transliteration matches non-ASCII only, so an ASCII heading — the\n // overwhelmingly common case — skips nine full passes over the string.\n if (NON_ASCII_R.test(result)) {\n for (let i = 0; i < SLUGIFY_REPLACEMENTS.length; i++) {\n const [pattern, replacement] = SLUGIFY_REPLACEMENTS[i]!;\n result = result.replace(pattern, replacement);\n }\n }\n\n result = result.replace(SLUGIFY_STRIP_R, '').replace(SLUGIFY_SPACE_R, '-');\n\n return result.toLowerCase();\n};\n\n// ============================================================================\n// SANITIZER\n// ============================================================================\n\nconst SANITIZE_R = /(javascript|vbscript|data(?!:image)):/i;\nconst SANITIZE_STRIP_R = /[^A-Za-z0-9/:]/g;\n\n/**\n * Sanitize URLs to prevent XSS attacks.\n * Returns null if the URL is unsafe.\n */\nexport const sanitizer = (input: string): string | null => {\n // `SANITIZE_R` only ever matches on a colon, and decoding can introduce one\n // solely through a percent escape — so a value with neither is safe as-is,\n // and skipping the decode avoids the bulk of the work on ordinary links.\n if (input.indexOf(':') === -1 && input.indexOf('%') === -1) return input;\n\n try {\n const decoded = decodeURIComponent(input).replace(SANITIZE_STRIP_R, '');\n\n if (SANITIZE_R.test(decoded)) {\n console.warn(\n 'Input contains an unsafe JavaScript/VBScript/data expression, it will not be rendered.',\n decoded\n );\n\n return null;\n }\n } catch (_e) {\n console.warn(\n 'Input could not be decoded due to malformed syntax or characters, it will not be rendered.',\n input\n );\n\n // decodeURIComponent sometimes throws a URIError\n return null;\n }\n\n return input;\n};\n\n// ============================================================================\n// WHITESPACE NORMALIZATION\n// ============================================================================\n\n/**\n * Normalize whitespace in source string.\n */\nexport const normalizeWhitespace = (source: string): string => {\n // Three `indexOf` probes beat one character-class regex here by a wide margin:\n // each is a native memchr over the source, and none of them normally match.\n // Each probe guards its own replacement too, so a source carrying only tabs —\n // the usual case — never rewrites for the other two, and every replacement is\n // a constant string instead of a callback the engine has to re-enter.\n let normalized = source;\n\n if (normalized.indexOf('\\t') !== -1) {\n normalized = normalized.replace(TAB_R, ' ');\n }\n if (normalized.indexOf('\\r') !== -1) {\n normalized = normalized.replace(CR_NEWLINE_R, '\\n');\n }\n if (normalized.indexOf('\\f') !== -1) {\n normalized = normalized.replace(FORMFEED_R, '');\n }\n\n return normalized;\n};\n\n/**\n * Safely remove a uniform leading indentation from lines, but do NOT touch\n * the content inside fenced code blocks (``` or ~~~).\n *\n * Inside a fenced block the structural indentation is irrelevant: what matters\n * is the indentation of the opening fence itself, which is the only amount\n * CommonMark strips from the code lines. A block nested in an indented HTML\n * element but written with its fence at column 0 therefore keeps its code\n * verbatim, instead of losing the first level of indentation.\n */\nexport const trimLeadingWhitespaceOutsideFences = (\n text: string,\n whitespace: string\n): string => {\n if (!whitespace) return text;\n\n const lines = text.split('\\n');\n\n /** Indentation and marker of the fence currently open, if any. */\n let openFence: { indentation: string; marker: string } | null = null;\n\n const removePrefix = (line: string, prefix: string): string =>\n prefix && line.startsWith(prefix) ? line.slice(prefix.length) : line;\n\n // Outside fences, strip the base structural indentation from every line.\n // Lines that don't start with the whitespace prefix are left as-is.\n const result = lines\n .map((line) => {\n const fenceMatch = line.match(FENCE_DELIMITER_R);\n\n if (!openFence) {\n if (fenceMatch)\n openFence = { indentation: fenceMatch[1]!, marker: fenceMatch[2]! };\n return removePrefix(line, whitespace);\n }\n\n const isClosingFence = fenceMatch?.[2]?.startsWith(openFence.marker);\n const { indentation } = openFence;\n\n if (isClosingFence) openFence = null;\n\n return removePrefix(line, indentation);\n })\n .join('\\n');\n\n return result;\n};\n\n/**\n * Normalize HTML attribute key to JSX prop name.\n */\nexport const normalizeAttributeKey = (key: string): string => {\n const hyphenIndex = key.indexOf('-');\n\n if (hyphenIndex !== -1 && key.match(HTML_CUSTOM_ATTR_R) === null) {\n key = key.replace(CAPTURE_LETTER_AFTER_HYPHEN, (_, letter) => {\n return letter.toUpperCase();\n });\n }\n\n return key;\n};\n\ntype StyleTuple = [key: string, value: string];\n\n/**\n * Parse a CSS style string into an array of [key, value] tuples.\n */\nexport const parseStyleAttribute = (styleString: string): StyleTuple[] => {\n const styles: StyleTuple[] = [];\n let buffer = '';\n let inUrl = false;\n let inQuotes = false;\n let quoteChar: '\"' | \"'\" | '' = '';\n\n if (!styleString) return styles;\n\n for (let i = 0; i < styleString.length; i++) {\n const char = styleString[i];\n\n if ((char === '\"' || char === \"'\") && !inUrl) {\n if (!inQuotes) {\n inQuotes = true;\n quoteChar = char;\n } else if (char === quoteChar) {\n inQuotes = false;\n quoteChar = '';\n }\n }\n\n if (char === '(' && buffer.endsWith('url')) {\n inUrl = true;\n } else if (char === ')' && inUrl) {\n inUrl = false;\n }\n\n if (char === ';' && !inQuotes && !inUrl) {\n const declaration = buffer.trim();\n\n if (declaration) {\n const colonIndex = declaration.indexOf(':');\n\n if (colonIndex > 0) {\n const key = declaration.slice(0, colonIndex).trim();\n const value = declaration.slice(colonIndex + 1).trim();\n styles.push([key, value]);\n }\n }\n buffer = '';\n } else {\n buffer += char;\n }\n }\n\n const declaration = buffer.trim();\n\n if (declaration) {\n const colonIndex = declaration.indexOf(':');\n if (colonIndex > 0) {\n const key = declaration.slice(0, colonIndex).trim();\n const value = declaration.slice(colonIndex + 1).trim();\n styles.push([key, value]);\n }\n }\n\n return styles;\n};\n\n/**\n * Convert an attribute value to a Node prop value.\n */\nexport const attributeValueToNodePropValue = (\n tag: string,\n key: string,\n value: string,\n sanitizeUrlFn: (\n value: string,\n tag: string,\n attribute: string\n ) => string | null\n): any => {\n if (key === 'style') {\n return parseStyleAttribute(value).reduce(\n (styles, [styleKey, styleValue]) => {\n const camelCasedKey = styleKey.replace(/(-[a-z])/g, (substr) =>\n substr[1]!.toUpperCase()\n );\n\n (styles as Record<string, any>)[camelCasedKey] = sanitizeUrlFn(\n styleValue,\n tag,\n styleKey\n );\n\n return styles;\n },\n {} as Record<string, any>\n );\n } else if (ATTRIBUTES_TO_SANITIZE.indexOf(key) !== -1) {\n return sanitizeUrlFn(unescapeString(value), tag, key);\n } else if (value.match(INTERPOLATION_R)) {\n value = unescapeString(value.slice(1, value.length - 1));\n }\n\n if (value === 'true') {\n return true;\n } else if (value === 'false') {\n return false;\n }\n\n return value;\n};\n\n// ============================================================================\n// TABLE PARSING\n// ============================================================================\n\n/**\n * Parse table alignment from a separator row.\n */\nexport const parseTableAlignCapture = (\n alignCapture: string\n): 'left' | 'right' | 'center' => {\n // All three patterns need a colon, and the right-align one backtracks across\n // the whole cell before failing — so reject colon-free cells up front.\n if (alignCapture.indexOf(':') === -1) return 'left';\n\n if (TABLE_RIGHT_ALIGN.test(alignCapture)) {\n return 'right';\n } else if (TABLE_CENTER_ALIGN.test(alignCapture)) {\n return 'center';\n } else if (TABLE_LEFT_ALIGN.test(alignCapture)) {\n return 'left';\n }\n\n return 'left';\n};\n\n/**\n * Parse table alignment row.\n */\nexport const parseTableAlign = (\n source: string\n): ('left' | 'right' | 'center')[] => {\n const alignText = source.replace(TABLE_TRIM_PIPES, '').split('|');\n return alignText.map(parseTableAlignCapture);\n};\n\n/**\n * Parse a single table row.\n */\nexport const parseTableRow = (\n source: string,\n parse: NestedParser,\n state: ParseState,\n tableOutput: boolean\n): ParserResult[][] => {\n const prevInTable = state.inTable;\n state.inTable = true;\n\n let rStart = 0;\n let rEnd = source.length;\n\n while (rStart < rEnd) {\n const c = source.charCodeAt(rStart);\n if (c === 32 || c === 9) rStart++;\n else break;\n }\n while (rEnd > rStart) {\n const c = source.charCodeAt(rEnd - 1);\n if (c === 32 || c === 9) rEnd--;\n else break;\n }\n\n // Trim leading pipe if present\n if (rStart < rEnd && source.charCodeAt(rStart) === 124 /* | */) {\n rStart++;\n }\n // Trim trailing pipe if present (and not escaped)\n if (\n rEnd > rStart &&\n source.charCodeAt(rEnd - 1) === 124 /* | */ &&\n (rEnd - 2 < rStart || source.charCodeAt(rEnd - 2) !== 92) /* \\ */\n ) {\n rEnd--;\n }\n\n if (!tableOutput) {\n const cell = parse(source.slice(rStart, rEnd), state);\n state.inTable = prevInTable;\n return [cell];\n }\n\n const cells: ParserResult[][] = [];\n const len = source.length;\n let cellStart = rStart;\n let i = rStart;\n\n /**\n * Parse one cell, dropping the padding an aligned table pads it with.\n *\n * GFM strips a cell's surrounding whitespace, so trimming is both the correct\n * output and the cheap one: the padding of a column-aligned table is a large\n * share of its bytes, and every space handed to `parse` costs a rule dispatch,\n * a capture and a lookbehind update. `trim` is a native scan, and walking the\n * padding back from the delimiter in JavaScript instead costs half as much\n * again as the whole row scan.\n */\n const parseCell = (from: number, to: number): ParserResult[] => {\n const cell = source.slice(from, to).trim();\n return cell === '' ? [] : parse(cell, state);\n };\n\n // Cursors on the only three characters that can end a run of ordinary cell\n // text. Each moves forward only, so the row is swept three times in total by\n // `indexOf` — cheaper than re-entering the regex engine once per character\n // found, which is what an anchored run scanner costs on a wide row.\n let pipeAt = -1;\n let escapeAt = -1;\n let backtickAt = -1;\n\n while (i < rEnd) {\n if (pipeAt < i) {\n const at = source.indexOf('|', i);\n pipeAt = at === -1 ? len : at;\n }\n if (escapeAt < i) {\n const at = source.indexOf('\\\\', i);\n escapeAt = at === -1 ? len : at;\n }\n if (backtickAt < i) {\n const at = source.indexOf('`', i);\n backtickAt = at === -1 ? len : at;\n }\n\n i = Math.min(pipeAt, escapeAt, backtickAt);\n if (i >= rEnd) break;\n\n // Escape: \\|\n if (i === escapeAt) {\n i += i + 1 < rEnd ? 2 : 1;\n continue;\n }\n // Code span: `...`\n if (i === backtickAt) {\n let backticks = 0;\n while (i < rEnd && source.charCodeAt(i) === 96) {\n backticks++;\n i++;\n }\n let found = false;\n while (i < rEnd && !found) {\n let closeBackticks = 0;\n while (i < rEnd && source.charCodeAt(i) === 96) {\n closeBackticks++;\n i++;\n }\n if (closeBackticks === backticks) {\n found = true;\n } else if (closeBackticks === 0) {\n i++;\n }\n }\n continue;\n }\n // Cell delimiter: |\n cells.push(parseCell(cellStart, i));\n i++;\n cellStart = i;\n }\n\n cells.push(parseCell(cellStart, rEnd));\n\n state.inTable = prevInTable;\n return cells;\n};\n\n/**\n * Parse table cells (multiple rows).\n */\nexport const parseTableCells = (\n source: string,\n parse: NestedParser,\n state: ParseState\n): ParserResult[][][] => {\n if (!source) return [];\n\n const result: ParserResult[][][] = [];\n let start = 0;\n const len = source.length;\n\n while (start < len) {\n let nextNewline = source.indexOf('\\n', start);\n if (nextNewline === -1) nextNewline = len;\n const rowText = source.slice(start, nextNewline);\n start = nextNewline + 1;\n\n // Check if row has non-whitespace content\n let hasContent = false;\n for (let j = 0; j < rowText.length; j++) {\n const c = rowText.charCodeAt(j);\n if (c !== 32 && c !== 9 && c !== 13) {\n hasContent = true;\n break;\n }\n }\n if (!hasContent) continue;\n\n result.push(parseTableRow(rowText, parse, state, true));\n }\n\n return result;\n};\n\n// ============================================================================\n// PARSING HELPERS\n// ============================================================================\n\n/**\n * Check if a rule qualifies for the current source and state.\n */\nexport const qualifies = (\n source: string,\n state: ParseState,\n qualify: NonNullable<Rule<any>['_qualify']>\n): boolean => {\n if (Array.isArray(qualify)) {\n for (let i = 0; i < qualify.length; i++) {\n if (startsWith(source, qualify[i]!)) return true;\n }\n\n return false;\n }\n\n return (qualify as (source: string, state: ParseState) => boolean)(\n source,\n state\n );\n};\n\n/**\n * Marks a matcher function as eligible for being run inside an inline context.\n */\nexport const allowInline = <T extends (...args: any[]) => any>(\n fn: T\n): T & { inline: 1 } => {\n (fn as any).inline = 1;\n return fn as T & { inline: 1 };\n};\n\n/**\n * Creates a match function for an inline scoped element from a regex.\n */\nexport const inlineRegex = (regex: RegExp) =>\n allowInline((source: string, state: ParseState): RegExpMatchArray | null => {\n if (state.inline) {\n return regex.exec(source);\n } else {\n return null;\n }\n });\n\n/**\n * Creates a match function for inline elements except links.\n */\nexport const simpleInlineRegex = (regex: RegExp) =>\n allowInline((source: string, state: ParseState): RegExpMatchArray | null => {\n if (state.inline || state.simple) {\n return regex.exec(source);\n } else {\n return null;\n }\n });\n\n/**\n * Creates a match function for a block scoped element from a regex.\n */\nexport const blockRegex =\n (regex: RegExp) =>\n (source: string, state: ParseState): RegExpMatchArray | null => {\n if (state.inline || state.simple) {\n return null;\n } else {\n return regex.exec(source);\n }\n };\n\n/**\n * Creates a match function from a regex, ignoring block/inline scope.\n */\nexport const anyScopeRegex = (\n fn: RegExp | ((source: string, state: ParseState) => RegExpMatchArray | null)\n) =>\n allowInline((source: string, state: ParseState): RegExpMatchArray | null => {\n if (typeof fn === 'function') {\n return fn(source, state);\n }\n return fn.exec(source);\n });\n\n/**\n * Parse inline content (including links).\n */\nexport const parseInline = (\n parse: NestedParser,\n children: string,\n state: ParseState\n): ParserResult[] => {\n const isCurrentlyInline = state.inline ?? false;\n const isCurrentlySimple = state.simple ?? false;\n state.inline = true;\n state.simple = true;\n const result = parse(children, state);\n state.inline = isCurrentlyInline;\n state.simple = isCurrentlySimple;\n\n return result;\n};\n\n/**\n * Parse simple inline content (no links).\n */\nexport const parseSimpleInline = (\n parse: NestedParser,\n children: string,\n state: ParseState\n): ParserResult[] => {\n const isCurrentlyInline = state.inline ?? false;\n const isCurrentlySimple = state.simple ?? false;\n\n state.inline = false;\n state.simple = true;\n const result = parse(children, state);\n state.inline = isCurrentlyInline;\n state.simple = isCurrentlySimple;\n\n return result;\n};\n\n/**\n * Parse block content.\n */\nexport const parseBlock = (\n parse: NestedParser,\n children: string,\n state: ParseState = {}\n): ParserResult[] => {\n const isCurrentlyInline = state.inline || false;\n state.inline = false;\n const normalizedChildren = trimEnd(children);\n const needsTerminator = /\\n\\n$/.test(normalizedChildren) === false;\n const blockInput = needsTerminator\n ? normalizedChildren.endsWith('\\n')\n ? `${normalizedChildren}\\n`\n : `${normalizedChildren}\\n\\n`\n : normalizedChildren;\n\n const result = parse(blockInput, state);\n state.inline = isCurrentlyInline;\n\n return result;\n};\n\n/**\n * Helper to parse capture group 2 as inline content.\n */\nexport const parseCaptureInline = (\n capture: RegExpMatchArray,\n parse: NestedParser,\n state: ParseState\n): { children: ParserResult[] } => {\n return {\n children: parseInline(parse, capture[2] ?? '', state),\n };\n};\n\n/**\n * Helper that captures nothing (empty object).\n */\nexport const captureNothing = (): Record<string, never> => ({});\n\n/**\n * Helper that renders nothing (null).\n */\nexport const renderNothing = (): null => null;\n\n/**\n * Check if any regex in a list matches the input.\n */\n/**\n * A block-syntax probe: either a regex to test, or a predicate for the rules\n * whose regex was replaced by a linear scanner.\n */\nexport type BlockSyntaxProbe = RegExp | ((input: string) => boolean);\n\nexport const some = (probes: BlockSyntaxProbe[], input: string): boolean => {\n for (let i = 0; i < probes.length; i++) {\n const probe = probes[i]!;\n if (typeof probe === 'function' ? probe(input) : probe.test(input)) {\n return true;\n }\n }\n return false;\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"qualifiedDictionary.mjs","names":[],"sources":["../../../src/dictionaryManipulator/qualifiedDictionary.ts"],"sourcesContent":["import type {\n Dictionary,\n DictionaryQualifierType,\n DictionarySelector,\n DictionaryVariantChain,\n DictionaryVariantValue,\n ProviderVariant,\n ProviderVariantMap,\n QualifiedDictionaryGroup,\n} from '@intlayer/types/dictionary';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\n\n/**\n * Separator joining per-dimension ids into a composite entry id. Also used as\n * the chunk path separator in dynamic mode.\n */\nexport const COMPOSITE_ID_SEPARATOR = '/';\n\n/**\n * Identity of the implicit fallback variant. A selector that pins no variant\n * resolves to it, and a variant that declares no entry of its own falls back to\n * it — so a key only has to ship the entries that actually differ.\n */\nexport const DEFAULT_VARIANT_ID = 'default';\n\n/**\n * Characters kept verbatim in an encoded qualifier segment. Everything else is\n * percent-encoded so a segment can never contain the composite-id separator\n * (`/`), path-hostile characters (`\\` `:` `*` `?` `\"` `<` `>` `|`, control\n * chars), or characters that would break the generated loader modules (`'`).\n */\nconst SEGMENT_UNSAFE_CHARS = /[^A-Za-z0-9._&=-]/g;\n\n/**\n * Stricter set for the components of an object variant: also encodes `&` and\n * `=` so the `field=value&field=value` serialization stays unambiguous.\n */\nconst COMPONENT_UNSAFE_CHARS = /[^A-Za-z0-9._-]/g;\n\n/** Percent-encodes one UTF-16 code unit as a fixed-width `%XXXX` run. */\nconst percentEncodeChar = (char: string): string =>\n `%${char.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0')}`;\n\nconst encodeSegmentText = (raw: string, unsafeChars: RegExp): string => {\n // Bare '%' cannot be produced by encoding (every encoded run is %XXXX),\n // so it is a safe stand-in for the empty string.\n if (raw === '') return '%';\n\n const encoded = raw.replace(unsafeChars, percentEncodeChar);\n\n // '.' and '..' are path navigation on every filesystem — encode the dots.\n if (encoded === '.' || encoded === '..') {\n return encoded.replace(/\\./g, '%002E');\n }\n\n return encoded;\n};\n\n/**\n * Canonical serialization of a single variant value into its identity string —\n * the variant segment of a composite id, the chunk directory name in dynamic\n * mode, and the runtime matching key.\n *\n * - `undefined` → `'default'` (the implicit fallback variant)\n * - a string → the string itself (a named variant)\n * - an object → its sorted `key=value` pairs joined by `&`\n * (e.g. `{ userId: '123', id: 'abc' }` → `'id=abc&userId=123'`)\n *\n * Characters that are unsafe in file paths or generated code are\n * percent-encoded (fixed-width `%XXXX` runs, injective). Common names —\n * letters, digits, `-` `_` `.` — are left untouched. Both the declaration and\n * the selector go through this function, so encoding never affects matching.\n *\n * Two variants resolve to the same entry iff their serializations are equal, so\n * an object variant in a selector must equal the one declared on the dictionary.\n */\nexport const serializeVariant = (\n variant: DictionaryVariantValue | undefined\n): string => {\n if (variant === undefined) return DEFAULT_VARIANT_ID;\n if (typeof variant === 'string') {\n return encodeSegmentText(variant, SEGMENT_UNSAFE_CHARS);\n }\n\n return Object.keys(variant)\n .sort()\n .map(\n (field) =>\n `${encodeSegmentText(field, COMPONENT_UNSAFE_CHARS)}=${encodeSegmentText(String(variant[field]), COMPONENT_UNSAFE_CHARS)}`\n )\n .join('&');\n};\n\n/**\n * Serializes the variant coordinate of a selector into the ordered list of\n * candidate ids to try, so a single value and a preference chain share one code\n * path downstream.\n *\n * - `undefined` → `['default']`\n * - a single value → its one serialization\n * - a chain → one serialization per entry, order preserved\n *\n * An empty chain is treated as \"no variant pinned\" (`['default']`) rather than\n * as an unsatisfiable request.\n */\nexport const serializeVariantChain = (\n variant: DictionaryVariantChain | undefined\n): string[] => {\n if (!Array.isArray(variant)) {\n return [serializeVariant(variant as DictionaryVariantValue | undefined)];\n }\n\n if (variant.length === 0) return [DEFAULT_VARIANT_ID];\n\n return variant.map(serializeVariant);\n};\n\n/**\n * Resolves the variant id a selector actually targets among the ids a key\n * declares — the sparse-override fallback.\n *\n * Candidates are tried in order and the first one the key declares wins.\n * Otherwise the key falls back to its `default` entry, so a variant only has to\n * be declared where its wording differs. When the key declares no `default`\n * either, the first candidate is returned unchanged and the caller resolves to\n * `null` / `[]`.\n *\n * @param requestedVariantIds - The serialized ids the selector asks for, in\n * preference order (a single value is a 1-element\n * list).\n * @param isVariantIdDeclared - Whether the key declares an entry for an id.\n */\nexport const resolveEffectiveVariantId = (\n requestedVariantIds: string[],\n isVariantIdDeclared: (variantId: string) => boolean\n): string => {\n for (const requestedVariantId of requestedVariantIds) {\n if (isVariantIdDeclared(requestedVariantId)) return requestedVariantId;\n }\n\n return isVariantIdDeclared(DEFAULT_VARIANT_ID)\n ? DEFAULT_VARIANT_ID\n : (requestedVariantIds[0] ?? DEFAULT_VARIANT_ID);\n};\n\n/**\n * Tests whether a composite entry id matches a selector across every declared\n * dimension. Segments are compared in their encoded form (both the stored id\n * and the selector go through {@link serializeVariant}). The `item` dimension\n * matches any value when the selector does not provide one (open collection\n * axis); the `variant` dimension is compared against the already-resolved\n * effective id, so the fallback is applied consistently across dimensions.\n */\nconst compositeIdMatchesSelector = (\n compositeId: string,\n qualifierTypes: DictionaryQualifierType[],\n selector: DictionarySelector | undefined,\n effectiveVariantId: string\n): boolean => {\n const segments = compositeId.split(COMPOSITE_ID_SEPARATOR);\n\n return qualifierTypes.every((qualifierType, index) => {\n if (qualifierType === 'variant') {\n return segments[index] === effectiveVariantId;\n }\n\n // qualifierType === 'item'\n return (\n selector?.item === undefined || segments[index] === String(selector.item)\n );\n });\n};\n\n/**\n * Type guard discriminating a `QualifiedDictionaryGroup` (merge output of a\n * qualified key) from a plain `Dictionary`. Both carry a `content` field; only\n * the group declares `qualifierTypes`, which is therefore the discriminator.\n */\nexport const isQualifiedDictionaryGroup = (\n value: unknown\n): value is QualifiedDictionaryGroup =>\n typeof value === 'object' &&\n value !== null &&\n 'qualifierTypes' in value &&\n Array.isArray((value as { qualifierTypes: unknown }).qualifierTypes) &&\n 'content' in value;\n\n/**\n * Reconstructs a resolvable {@link Dictionary} from a single entry of a\n * qualified group: the content node stored under its composite id, plus the\n * qualifier coordinates decoded from that id (`variant`, `item`).\n *\n * This keeps the resolver's transform code unchanged: it still sees a\n * `{ key, content, variant?, item? }` shape, even though the stored format no\n * longer duplicates those fields per entry. The `variant` coordinate stays in\n * its serialized (encoded) form, e.g. `'id=abc&userId=123'` — matching happens\n * on the composite id segments, never on this reconstructed field.\n */\nexport const reconstructQualifiedEntry = (\n group: QualifiedDictionaryGroup,\n compositeId: string\n): Dictionary => {\n const segments = compositeId.split(COMPOSITE_ID_SEPARATOR);\n\n const entry = {\n key: group.key,\n content: group.content[compositeId],\n } as Dictionary;\n\n group.qualifierTypes.forEach((qualifierType, index) => {\n if (qualifierType === 'variant') {\n entry.variant = segments[index];\n } else if (qualifierType === 'item') {\n entry.item = Number(segments[index]);\n }\n });\n\n return entry;\n};\n\n/**\n * Resolves a dictionary (or qualified dictionary group) against a selector,\n * across every declared dimension.\n *\n * - Plain dictionary → returned as-is (selector ignored)\n * - `item` declared but not selected → every matching entry ordered by index\n * - `item` selected → the matching entry or null\n * - `variant` defaults to the `default` entry when not selected, and falls back\n * to it when the selected variant declares no entry of its own; an object\n * variant resolves only when the selector provides an equal object (or, again,\n * through the `default` fallback)\n *\n * Dimensions compose: e.g. a variant × item key with `{ variant: 'promo' }`\n * returns every promo item as an array; adding `{ item: 2 }` narrows to one.\n */\nexport const resolveQualifiedDictionary = (\n dictionaryOrGroup: Dictionary | QualifiedDictionaryGroup,\n selector?: DictionarySelector\n): Dictionary | Dictionary[] | null => {\n if (!isQualifiedDictionaryGroup(dictionaryOrGroup)) {\n return dictionaryOrGroup;\n }\n\n const { qualifierTypes, content } = dictionaryOrGroup;\n\n const itemAxisOpen =\n qualifierTypes.includes('item') && selector?.item === undefined;\n\n const compositeIds = Object.keys(content);\n const variantIndex = qualifierTypes.indexOf('variant');\n\n const effectiveVariantId =\n variantIndex === -1\n ? DEFAULT_VARIANT_ID\n : resolveEffectiveVariantId(\n serializeVariantChain(selector?.variant),\n (variantId) =>\n compositeIds.some(\n (compositeId) =>\n compositeId.split(COMPOSITE_ID_SEPARATOR)[variantIndex] ===\n variantId\n )\n );\n\n const matchedEntries = compositeIds\n .filter((compositeId) =>\n compositeIdMatchesSelector(\n compositeId,\n qualifierTypes,\n selector,\n effectiveVariantId\n )\n )\n .map((compositeId) =>\n reconstructQualifiedEntry(dictionaryOrGroup, compositeId)\n );\n\n if (itemAxisOpen) {\n return matchedEntries.sort(\n (left, right) => (left.item ?? 0) - (right.item ?? 0)\n );\n }\n\n return matchedEntries[0] ?? null;\n};\n\n/**\n * Splits the second argument of `getIntlayer` / `getDictionary` into the\n * effective locale and the selector object (if any).\n */\nexport const parseDictionarySelector = <L extends LocalesValues>(\n localeOrSelector?: L | DictionarySelector\n): { locale?: L; selector?: DictionarySelector } => {\n if (typeof localeOrSelector === 'object' && localeOrSelector !== null) {\n return {\n locale: localeOrSelector.locale as L | undefined,\n selector: localeOrSelector,\n };\n }\n\n return { locale: localeOrSelector };\n};\n\n/**\n * Resolves the variant a provider pins for one dictionary key.\n *\n * A string or a chain applies to every key as-is. A plain object is the per-key\n * map: the entry for `dictionaryKey` wins, falling back to the reserved\n * `default` entry, and `undefined` when neither is present (the key then\n * resolves to its own `default` variant, i.e. the behaviour without a provider\n * variant at all).\n *\n * A plain object is **always** the map here — never a structured variant value,\n * which is why a structured variant has to be nested (`{ default: { id } }`).\n *\n * @param providerVariant - The `variant` prop of the surrounding provider.\n * @param dictionaryKey - The key being read.\n */\nexport const resolveProviderVariant = (\n providerVariant: ProviderVariant | undefined,\n dictionaryKey: string\n): DictionaryVariantChain | undefined => {\n if (providerVariant === undefined) return undefined;\n\n if (typeof providerVariant === 'string' || Array.isArray(providerVariant)) {\n return providerVariant as DictionaryVariantChain;\n }\n\n const variantMap = providerVariant as ProviderVariantMap;\n\n return variantMap[dictionaryKey] ?? variantMap[DEFAULT_VARIANT_ID];\n};\n\n/**\n * Builds the effective second argument of a dictionary read by layering the\n * provider defaults under the call-site one — the single place the `locale` and\n * `variant` context defaults are applied, shared by every framework binding.\n *\n * Precedence, per dimension independently:\n * - a call-site selector always wins; `{ variant: 'x' }` **replaces** the\n * provider chain rather than extending it\n * - otherwise the provider value applies\n *\n * Returns a bare locale (not a selector object) whenever no variant is in play,\n * so the existing fast path — and the cache keys built from it — are unchanged\n * for projects that never use variants.\n */\nexport const resolveDictionaryArgument = (params: {\n localeOrSelector?: LocalesValues | DictionarySelector;\n contextLocale?: LocalesValues;\n contextVariant?: ProviderVariant;\n dictionaryKey: string;\n}): LocalesValues | DictionarySelector | undefined => {\n const { localeOrSelector, contextLocale, contextVariant, dictionaryKey } =\n params;\n\n const callSelector =\n typeof localeOrSelector === 'object' && localeOrSelector !== null\n ? localeOrSelector\n : undefined;\n\n const callLocale = callSelector\n ? callSelector.locale\n : (localeOrSelector as LocalesValues | undefined);\n\n // The context locale is typed as widely as the runtime allows, while a\n // selector narrows `locale` to the declared ones — the value is the same.\n const locale = (callLocale ?? contextLocale) as DictionarySelector['locale'];\n\n // A call-site variant is authoritative; the provider only fills the gap.\n const variant =\n callSelector?.variant ??\n resolveProviderVariant(contextVariant, dictionaryKey);\n\n if (variant === undefined) {\n // Nothing to add: keep the argument in its original shape so the identity\n // built from it stays byte-identical to the pre-variant behaviour.\n return callSelector\n ? { ...callSelector, locale }\n : (locale as LocalesValues | undefined);\n }\n\n return { ...callSelector, locale, variant };\n};\n\n/**\n * Builds a stable string identity of a selector (excluding `locale`), suitable\n * for cache keys and memoization dependencies.\n */\nexport const getDictionarySelectorCacheKey = (\n selector?: DictionarySelector\n): string => {\n if (!selector) return '';\n\n return Object.keys(selector)\n .filter((selectorKey) => selectorKey !== 'locale')\n .sort()\n .map((selectorKey) => {\n const value = selector[selectorKey as keyof DictionarySelector];\n const serialized =\n selectorKey === 'variant'\n ? serializeVariantChain(\n value as DictionaryVariantChain | undefined\n ).join(',')\n : String(value);\n return `${selectorKey}:${serialized}`;\n })\n .join('|');\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"qualifiedDictionary.mjs","names":[],"sources":["../../../src/dictionaryManipulator/qualifiedDictionary.ts"],"sourcesContent":["import type {\n Dictionary,\n DictionaryQualifierType,\n DictionarySelector,\n DictionaryVariantChain,\n DictionaryVariantValue,\n ProviderVariantBase,\n ProviderVariantMap,\n QualifiedDictionaryGroup,\n} from '@intlayer/types/dictionary';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\n\n/**\n * Separator joining per-dimension ids into a composite entry id. Also used as\n * the chunk path separator in dynamic mode.\n */\nexport const COMPOSITE_ID_SEPARATOR = '/';\n\n/**\n * Identity of the implicit fallback variant. A selector that pins no variant\n * resolves to it, and a variant that declares no entry of its own falls back to\n * it — so a key only has to ship the entries that actually differ.\n */\nexport const DEFAULT_VARIANT_ID = 'default';\n\n/**\n * Characters kept verbatim in an encoded qualifier segment. Everything else is\n * percent-encoded so a segment can never contain the composite-id separator\n * (`/`), path-hostile characters (`\\` `:` `*` `?` `\"` `<` `>` `|`, control\n * chars), or characters that would break the generated loader modules (`'`).\n */\nconst SEGMENT_UNSAFE_CHARS = /[^A-Za-z0-9._&=-]/g;\n\n/**\n * Stricter set for the components of an object variant: also encodes `&` and\n * `=` so the `field=value&field=value` serialization stays unambiguous.\n */\nconst COMPONENT_UNSAFE_CHARS = /[^A-Za-z0-9._-]/g;\n\n/** Percent-encodes one UTF-16 code unit as a fixed-width `%XXXX` run. */\nconst percentEncodeChar = (char: string): string =>\n `%${char.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0')}`;\n\nconst encodeSegmentText = (raw: string, unsafeChars: RegExp): string => {\n // Bare '%' cannot be produced by encoding (every encoded run is %XXXX),\n // so it is a safe stand-in for the empty string.\n if (raw === '') return '%';\n\n const encoded = raw.replace(unsafeChars, percentEncodeChar);\n\n // '.' and '..' are path navigation on every filesystem — encode the dots.\n if (encoded === '.' || encoded === '..') {\n return encoded.replace(/\\./g, '%002E');\n }\n\n return encoded;\n};\n\n/**\n * Canonical serialization of a single variant value into its identity string —\n * the variant segment of a composite id, the chunk directory name in dynamic\n * mode, and the runtime matching key.\n *\n * - `undefined` → `'default'` (the implicit fallback variant)\n * - a string → the string itself (a named variant)\n * - an object → its sorted `key=value` pairs joined by `&`\n * (e.g. `{ userId: '123', id: 'abc' }` → `'id=abc&userId=123'`)\n *\n * Characters that are unsafe in file paths or generated code are\n * percent-encoded (fixed-width `%XXXX` runs, injective). Common names —\n * letters, digits, `-` `_` `.` — are left untouched. Both the declaration and\n * the selector go through this function, so encoding never affects matching.\n *\n * Two variants resolve to the same entry iff their serializations are equal, so\n * an object variant in a selector must equal the one declared on the dictionary.\n */\nexport const serializeVariant = (\n variant: DictionaryVariantValue | undefined\n): string => {\n if (variant === undefined) return DEFAULT_VARIANT_ID;\n if (typeof variant === 'string') {\n return encodeSegmentText(variant, SEGMENT_UNSAFE_CHARS);\n }\n\n return Object.keys(variant)\n .sort()\n .map(\n (field) =>\n `${encodeSegmentText(field, COMPONENT_UNSAFE_CHARS)}=${encodeSegmentText(String(variant[field]), COMPONENT_UNSAFE_CHARS)}`\n )\n .join('&');\n};\n\n/**\n * Serializes the variant coordinate of a selector into the ordered list of\n * candidate ids to try, so a single value and a preference chain share one code\n * path downstream.\n *\n * - `undefined` → `['default']`\n * - a single value → its one serialization\n * - a chain → one serialization per entry, order preserved\n *\n * An empty chain is treated as \"no variant pinned\" (`['default']`) rather than\n * as an unsatisfiable request.\n */\nexport const serializeVariantChain = (\n variant: DictionaryVariantChain | undefined\n): string[] => {\n if (!Array.isArray(variant)) {\n return [serializeVariant(variant as DictionaryVariantValue | undefined)];\n }\n\n if (variant.length === 0) return [DEFAULT_VARIANT_ID];\n\n return variant.map(serializeVariant);\n};\n\n/**\n * Resolves the variant id a selector actually targets among the ids a key\n * declares — the sparse-override fallback.\n *\n * Candidates are tried in order and the first one the key declares wins.\n * Otherwise the key falls back to its `default` entry, so a variant only has to\n * be declared where its wording differs. When the key declares no `default`\n * either, the first candidate is returned unchanged and the caller resolves to\n * `null` / `[]`.\n *\n * @param requestedVariantIds - The serialized ids the selector asks for, in\n * preference order (a single value is a 1-element\n * list).\n * @param isVariantIdDeclared - Whether the key declares an entry for an id.\n */\nexport const resolveEffectiveVariantId = (\n requestedVariantIds: string[],\n isVariantIdDeclared: (variantId: string) => boolean\n): string => {\n for (const requestedVariantId of requestedVariantIds) {\n if (isVariantIdDeclared(requestedVariantId)) return requestedVariantId;\n }\n\n return isVariantIdDeclared(DEFAULT_VARIANT_ID)\n ? DEFAULT_VARIANT_ID\n : (requestedVariantIds[0] ?? DEFAULT_VARIANT_ID);\n};\n\n/**\n * Tests whether a composite entry id matches a selector across every declared\n * dimension. Segments are compared in their encoded form (both the stored id\n * and the selector go through {@link serializeVariant}). The `item` dimension\n * matches any value when the selector does not provide one (open collection\n * axis); the `variant` dimension is compared against the already-resolved\n * effective id, so the fallback is applied consistently across dimensions.\n */\nconst compositeIdMatchesSelector = (\n compositeId: string,\n qualifierTypes: DictionaryQualifierType[],\n selector: DictionarySelector | undefined,\n effectiveVariantId: string\n): boolean => {\n const segments = compositeId.split(COMPOSITE_ID_SEPARATOR);\n\n return qualifierTypes.every((qualifierType, index) => {\n if (qualifierType === 'variant') {\n return segments[index] === effectiveVariantId;\n }\n\n // qualifierType === 'item'\n return (\n selector?.item === undefined || segments[index] === String(selector.item)\n );\n });\n};\n\n/**\n * Type guard discriminating a `QualifiedDictionaryGroup` (merge output of a\n * qualified key) from a plain `Dictionary`. Both carry a `content` field; only\n * the group declares `qualifierTypes`, which is therefore the discriminator.\n */\nexport const isQualifiedDictionaryGroup = (\n value: unknown\n): value is QualifiedDictionaryGroup =>\n typeof value === 'object' &&\n value !== null &&\n 'qualifierTypes' in value &&\n Array.isArray((value as { qualifierTypes: unknown }).qualifierTypes) &&\n 'content' in value;\n\n/**\n * Reconstructs a resolvable {@link Dictionary} from a single entry of a\n * qualified group: the content node stored under its composite id, plus the\n * qualifier coordinates decoded from that id (`variant`, `item`).\n *\n * This keeps the resolver's transform code unchanged: it still sees a\n * `{ key, content, variant?, item? }` shape, even though the stored format no\n * longer duplicates those fields per entry. The `variant` coordinate stays in\n * its serialized (encoded) form, e.g. `'id=abc&userId=123'` — matching happens\n * on the composite id segments, never on this reconstructed field.\n */\nexport const reconstructQualifiedEntry = (\n group: QualifiedDictionaryGroup,\n compositeId: string\n): Dictionary => {\n const segments = compositeId.split(COMPOSITE_ID_SEPARATOR);\n\n const entry = {\n key: group.key,\n content: group.content[compositeId],\n } as Dictionary;\n\n group.qualifierTypes.forEach((qualifierType, index) => {\n if (qualifierType === 'variant') {\n entry.variant = segments[index];\n } else if (qualifierType === 'item') {\n entry.item = Number(segments[index]);\n }\n });\n\n return entry;\n};\n\n/**\n * Resolves a dictionary (or qualified dictionary group) against a selector,\n * across every declared dimension.\n *\n * - Plain dictionary → returned as-is (selector ignored)\n * - `item` declared but not selected → every matching entry ordered by index\n * - `item` selected → the matching entry or null\n * - `variant` defaults to the `default` entry when not selected, and falls back\n * to it when the selected variant declares no entry of its own; an object\n * variant resolves only when the selector provides an equal object (or, again,\n * through the `default` fallback)\n *\n * Dimensions compose: e.g. a variant × item key with `{ variant: 'promo' }`\n * returns every promo item as an array; adding `{ item: 2 }` narrows to one.\n */\nexport const resolveQualifiedDictionary = (\n dictionaryOrGroup: Dictionary | QualifiedDictionaryGroup,\n selector?: DictionarySelector\n): Dictionary | Dictionary[] | null => {\n if (!isQualifiedDictionaryGroup(dictionaryOrGroup)) {\n return dictionaryOrGroup;\n }\n\n const { qualifierTypes, content } = dictionaryOrGroup;\n\n const itemAxisOpen =\n qualifierTypes.includes('item') && selector?.item === undefined;\n\n const compositeIds = Object.keys(content);\n const variantIndex = qualifierTypes.indexOf('variant');\n\n const effectiveVariantId =\n variantIndex === -1\n ? DEFAULT_VARIANT_ID\n : resolveEffectiveVariantId(\n serializeVariantChain(selector?.variant),\n (variantId) =>\n compositeIds.some(\n (compositeId) =>\n compositeId.split(COMPOSITE_ID_SEPARATOR)[variantIndex] ===\n variantId\n )\n );\n\n const matchedEntries = compositeIds\n .filter((compositeId) =>\n compositeIdMatchesSelector(\n compositeId,\n qualifierTypes,\n selector,\n effectiveVariantId\n )\n )\n .map((compositeId) =>\n reconstructQualifiedEntry(dictionaryOrGroup, compositeId)\n );\n\n if (itemAxisOpen) {\n return matchedEntries.sort(\n (left, right) => (left.item ?? 0) - (right.item ?? 0)\n );\n }\n\n return matchedEntries[0] ?? null;\n};\n\n/**\n * Splits the second argument of `getIntlayer` / `getDictionary` into the\n * effective locale and the selector object (if any).\n */\nexport const parseDictionarySelector = <L extends LocalesValues>(\n localeOrSelector?: L | DictionarySelector\n): { locale?: L; selector?: DictionarySelector } => {\n if (typeof localeOrSelector === 'object' && localeOrSelector !== null) {\n return {\n locale: localeOrSelector.locale as L | undefined,\n selector: localeOrSelector,\n };\n }\n\n return { locale: localeOrSelector };\n};\n\n/**\n * Resolves the variant a provider pins for one dictionary key.\n *\n * A string or a chain applies to every key as-is. A plain object is the per-key\n * map: the entry for `dictionaryKey` wins, falling back to the reserved\n * `default` entry, and `undefined` when neither is present (the key then\n * resolves to its own `default` variant, i.e. the behaviour without a provider\n * variant at all).\n *\n * A plain object is **always** the map here — never a structured variant value,\n * which is why a structured variant has to be nested (`{ default: { id } }`).\n *\n * @param providerVariant - The `variant` prop of the surrounding provider.\n * @param dictionaryKey - The key being read.\n */\nexport const resolveProviderVariant = (\n providerVariant: ProviderVariantBase | undefined,\n dictionaryKey: string\n): DictionaryVariantChain | undefined => {\n if (providerVariant === undefined) return undefined;\n\n if (typeof providerVariant === 'string' || Array.isArray(providerVariant)) {\n return providerVariant as DictionaryVariantChain;\n }\n\n const variantMap = providerVariant as ProviderVariantMap;\n\n return variantMap[dictionaryKey] ?? variantMap[DEFAULT_VARIANT_ID];\n};\n\n/**\n * Builds the effective second argument of a dictionary read by layering the\n * provider defaults under the call-site one — the single place the `locale` and\n * `variant` context defaults are applied, shared by every framework binding.\n *\n * Precedence, per dimension independently:\n * - a call-site selector always wins; `{ variant: 'x' }` **replaces** the\n * provider chain rather than extending it\n * - otherwise the provider value applies\n *\n * Returns a bare locale (not a selector object) whenever no variant is in play,\n * so the existing fast path — and the cache keys built from it — are unchanged\n * for projects that never use variants.\n */\nexport const resolveDictionaryArgument = (params: {\n localeOrSelector?: LocalesValues | DictionarySelector;\n contextLocale?: LocalesValues;\n contextVariant?: ProviderVariantBase;\n dictionaryKey: string;\n}): LocalesValues | DictionarySelector | undefined => {\n const { localeOrSelector, contextLocale, contextVariant, dictionaryKey } =\n params;\n\n const callSelector =\n typeof localeOrSelector === 'object' && localeOrSelector !== null\n ? localeOrSelector\n : undefined;\n\n const callLocale = callSelector\n ? callSelector.locale\n : (localeOrSelector as LocalesValues | undefined);\n\n // The context locale is typed as widely as the runtime allows, while a\n // selector narrows `locale` to the declared ones — the value is the same.\n const locale = (callLocale ?? contextLocale) as DictionarySelector['locale'];\n\n // A call-site variant is authoritative; the provider only fills the gap.\n const variant =\n callSelector?.variant ??\n resolveProviderVariant(contextVariant, dictionaryKey);\n\n if (variant === undefined) {\n // Nothing to add: keep the argument in its original shape so the identity\n // built from it stays byte-identical to the pre-variant behaviour.\n return callSelector\n ? { ...callSelector, locale }\n : (locale as LocalesValues | undefined);\n }\n\n return { ...callSelector, locale, variant };\n};\n\n/**\n * Builds a stable string identity of a selector (excluding `locale`), suitable\n * for cache keys and memoization dependencies.\n */\nexport const getDictionarySelectorCacheKey = (\n selector?: DictionarySelector\n): string => {\n if (!selector) return '';\n\n return Object.keys(selector)\n .filter((selectorKey) => selectorKey !== 'locale')\n .sort()\n .map((selectorKey) => {\n const value = selector[selectorKey as keyof DictionarySelector];\n const serialized =\n selectorKey === 'variant'\n ? serializeVariantChain(\n value as DictionaryVariantChain | undefined\n ).join(',')\n : String(value);\n return `${selectorKey}:${serialized}`;\n })\n .join('|');\n};\n"],"mappings":"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"}
@@ -99,11 +99,11 @@ import { localeFlatMap, localeMap, localeRecord } from "./localization/localeMap
99
99
  import { formatProxyEnabledMessage, isProxyStorageLocaleEnabled, resolveProxyMode } from "./localization/proxyMode.mjs";
100
100
  import { getPreloadLocale, resolveBrowserLocale } from "./localization/resolveBrowserLocale.mjs";
101
101
  import { validatePrefix } from "./localization/validatePrefix.mjs";
102
- import { ATTRIBUTES_TO_SANITIZE, ATTRIBUTE_TO_NODE_PROP_MAP, ATTR_EXTRACTOR_R, BLOCKQUOTE_ALERT_R, BLOCKQUOTE_R, BLOCKQUOTE_TRIM_LEFT_MULTILINE_R, BLOCK_END_R, BREAK_LINE_R, BREAK_THEMATIC_R, CAPTURE_LETTER_AFTER_HYPHEN, CODE_BLOCK_FENCED_R, CODE_BLOCK_R, CODE_INLINE_R, CONSECUTIVE_NEWLINE_R, CR_NEWLINE_R, CUSTOM_COMPONENT_OPENING_TAG_R, CUSTOM_COMPONENT_R, DO_NOT_PROCESS_HTML_ELEMENTS, FENCE_DELIMITER_R, FOOTNOTE_R, FOOTNOTE_REFERENCE_R, FORMFEED_R, FRONT_MATTER_R, GFM_TASK_R, HEADING_ATX_COMPLIANT_R, HEADING_R, HEADING_SETEXT_R, HTML_BLOCK_ELEMENT_R, HTML_BLOCK_OPENING_TAG_R, HTML_CHAR_CODE_R, HTML_COMMENT_R, HTML_CUSTOM_ATTR_R, HTML_LEFT_TRIM_AMOUNT_R, HTML_SELF_CLOSING_ELEMENT_R, HTML_SELF_CLOSING_OPENING_TAG_R, INLINE_SKIP_R, INTERPOLATION_R, LINK_AUTOLINK_BARE_URL_R, LINK_AUTOLINK_R, LOOKAHEAD, NAMED_CODES_TO_UNICODE, NORMALIZE_WHITESPACE_R, NP_TABLE_R, ORDERED, ORDERED_LIST_BULLET, ORDERED_LIST_ITEM_PREFIX, ORDERED_LIST_ITEM_PREFIX_R, ORDERED_LIST_ITEM_R, ORDERED_LIST_R, PARAGRAPH_R, Priority, REFERENCE_IMAGE_OR_LINK, REFERENCE_IMAGE_R, REFERENCE_LINK_R, RuleType, SHORTCODE_R, SHOULD_RENDER_AS_BLOCK_R, TABLE_CENTER_ALIGN, TABLE_LEFT_ALIGN, TABLE_RIGHT_ALIGN, TABLE_TRIM_PIPES, TAB_R, TEXT_BOLD_R, TEXT_EMPHASIZED_R, TEXT_ESCAPED_R, TEXT_MARKED_R, TEXT_PLAIN_R, TEXT_STRIKETHROUGHED_R, TRIM_STARTING_NEWLINES, UNESCAPE_R, UNORDERED, UNORDERED_LIST_BULLET, UNORDERED_LIST_ITEM_PREFIX, UNORDERED_LIST_ITEM_PREFIX_R, UNORDERED_LIST_ITEM_R, UNORDERED_LIST_R, generateListItemPrefix, generateListItemPrefixRegex, generateListItemRegex, generateListRegex } from "./markdown/constants.mjs";
102
+ import { ATTRIBUTES_TO_SANITIZE, ATTRIBUTE_TO_NODE_PROP_MAP, ATTR_EXTRACTOR_R, BLOCKQUOTE_ALERT_R, BLOCKQUOTE_R, BLOCKQUOTE_TRIM_LEFT_MULTILINE_R, BLOCK_END_R, BREAK_LINE_R, BREAK_THEMATIC_R, CAPTURE_LETTER_AFTER_HYPHEN, CODE_BLOCK_FENCED_R, CODE_BLOCK_R, CODE_INLINE_R, CONSECUTIVE_NEWLINE_R, CR_NEWLINE_R, DO_NOT_PROCESS_HTML_ELEMENTS, FENCE_DELIMITER_R, FOOTNOTE_R, FOOTNOTE_REFERENCE_R, FORMFEED_R, FRONT_MATTER_R, GFM_TASK_R, HEADING_ATX_COMPLIANT_R, HEADING_R, HEADING_SETEXT_R, HTML_CHAR_CODE_R, HTML_COMMENT_R, HTML_CUSTOM_ATTR_R, HTML_LEFT_TRIM_AMOUNT_R, INLINE_SKIP_R, INTERPOLATION_R, LINK_AUTOLINK_BARE_URL_R, LINK_AUTOLINK_R, LOOKAHEAD, NAMED_CODES_TO_UNICODE, NP_TABLE_R, ORDERED, ORDERED_LIST_BULLET, ORDERED_LIST_ITEM_PREFIX, ORDERED_LIST_ITEM_PREFIX_R, ORDERED_LIST_ITEM_R, ORDERED_LIST_R, PARAGRAPH_R, Priority, REFERENCE_IMAGE_OR_LINK, REFERENCE_IMAGE_R, REFERENCE_LINK_R, RuleScope, RuleType, SCOPE_BLOCK, SCOPE_BOTH, SCOPE_INLINE, SHORTCODE_R, SHOULD_RENDER_AS_BLOCK_R, TABLE_CENTER_ALIGN, TABLE_LEFT_ALIGN, TABLE_RIGHT_ALIGN, TABLE_TRIM_PIPES, TAB_R, TEXT_BOLD_R, TEXT_EMPHASIZED_R, TEXT_ESCAPED_R, TEXT_MARKED_R, TEXT_STRIKETHROUGHED_R, TRIM_STARTING_NEWLINES, UNESCAPE_R, UNORDERED, UNORDERED_LIST_BULLET, UNORDERED_LIST_ITEM_PREFIX, UNORDERED_LIST_ITEM_PREFIX_R, UNORDERED_LIST_ITEM_R, UNORDERED_LIST_R, generateListItemPrefix, generateListItemPrefixRegex, generateListItemRegex, generateListRegex } from "./markdown/constants.mjs";
103
103
  import { allowInline, anyScopeRegex, attributeValueToNodePropValue, blockRegex, captureNothing, cx, get, inlineRegex, normalizeAttributeKey, normalizeWhitespace, parseBlock, parseCaptureInline, parseInline, parseSimpleInline, parseStyleAttribute, parseTableAlign, parseTableAlignCapture, parseTableCells, parseTableRow, qualifies, renderNothing, sanitizer, simpleInlineRegex, slugify, some, startsWith, trimEnd, trimLeadingWhitespaceOutsideFences, unescapeString, unquote } from "./markdown/utils.mjs";
104
104
  import { parserFor } from "./markdown/parser.mjs";
105
105
  import { createRenderer, renderFor } from "./markdown/renderer.mjs";
106
- import { compile, compileWithOptions, createCompiler, parseMarkdown, renderMarkdownAst } from "./markdown/compiler.mjs";
106
+ import { compile, compileWithOptions, createCompiler, getRuleSet, parseMarkdown, renderMarkdownAst } from "./markdown/compiler.mjs";
107
107
  import { icuToIntlayerFormatter, intlayerToICUFormatter } from "./messageFormat/ICU.mjs";
108
108
  import { i18nextToIntlayerFormatter, intlayerToI18nextFormatter } from "./messageFormat/i18next.mjs";
109
109
  import { navigatePath } from "./messageFormat/navigatePath.mjs";
@@ -113,4 +113,4 @@ import { interpolateMessage, parseTaggedMessage, resolveMessage, resolveMessageN
113
113
  import { isSameKeyPath } from "./utils/isSameKeyPath.mjs";
114
114
  import { stringifyYaml } from "./utils/stringifyYaml.mjs";
115
115
 
116
- export { ATTRIBUTES_TO_SANITIZE, ATTRIBUTE_TO_NODE_PROP_MAP, ATTR_EXTRACTOR_R, BLOCKQUOTE_ALERT_R, BLOCKQUOTE_R, BLOCKQUOTE_TRIM_LEFT_MULTILINE_R, BLOCK_END_R, BREAK_LINE_R, BREAK_THEMATIC_R, CAPTURE_LETTER_AFTER_HYPHEN, CODE_BLOCK_FENCED_R, CODE_BLOCK_R, CODE_INLINE_R, COMPOSITE_ID_SEPARATOR, CONSECUTIVE_NEWLINE_R, CR_NEWLINE_R, CUSTOM_COMPONENT_OPENING_TAG_R, CUSTOM_COMPONENT_R, CachedIntl, CachedIntl as Intl, DEFAULT_VARIANT_ID, DO_NOT_PROCESS_HTML_ELEMENTS, FENCE_DELIMITER_R, FOOTNOTE_R, FOOTNOTE_REFERENCE_R, FORMFEED_R, FRONT_MATTER_R, GFM_TASK_R, HEADING_ATX_COMPLIANT_R, HEADING_R, HEADING_SETEXT_R, HTML_BLOCK_ELEMENT_R, HTML_BLOCK_OPENING_TAG_R, HTML_CHAR_CODE_R, HTML_COMMENT_R, HTML_CUSTOM_ATTR_R, HTML_LEFT_TRIM_AMOUNT_R, HTML_SELF_CLOSING_ELEMENT_R, HTML_SELF_CLOSING_OPENING_TAG_R, HTML_TAGS, INLINE_SKIP_R, INTERPOLATION_R, LINK_AUTOLINK_BARE_URL_R, LINK_AUTOLINK_R, LOOKAHEAD, LocaleStorage, LocaleStorageClient, LocaleStorageServer, NAMED_CODES_TO_UNICODE, NORMALIZE_WHITESPACE_R, NP_TABLE_R, ORDERED, ORDERED_LIST_BULLET, ORDERED_LIST_ITEM_PREFIX, ORDERED_LIST_ITEM_PREFIX_R, ORDERED_LIST_ITEM_R, ORDERED_LIST_R, PARAGRAPH_R, PRELOADED_DYNAMIC_KEY, Priority, QUALIFIER_DYNAMIC_TYPES_KEY, QUALIFIER_ORDER, REFERENCE_IMAGE_OR_LINK, REFERENCE_IMAGE_R, REFERENCE_LINK_R, RuleType, SHORTCODE_R, SHOULD_RENDER_AS_BLOCK_R, TABLE_CENTER_ALIGN, TABLE_LEFT_ALIGN, TABLE_RIGHT_ALIGN, TABLE_TRIM_PIPES, TAB_R, TEXT_BOLD_R, TEXT_EMPHASIZED_R, TEXT_ESCAPED_R, TEXT_MARKED_R, TEXT_PLAIN_R, TEXT_STRIKETHROUGHED_R, TRIM_STARTING_NEWLINES, UNESCAPE_R, UNORDERED, UNORDERED_LIST_BULLET, UNORDERED_LIST_ITEM_PREFIX, UNORDERED_LIST_ITEM_PREFIX_R, UNORDERED_LIST_ITEM_R, UNORDERED_LIST_R, VOID_HTML_ELEMENTS, allowInline, anyScopeRegex, attributeValueToNodePropValue, bindIntl, blockRegex, buildMaskPlugin, captureNothing, checkIsURLAbsolute, checkMissingLocalesPlugin, compact, comparePaths, compile, compileWithOptions, condition as cond, conditionPlugin, createCompiler, createRenderer, currency, cx, date, deepTransformNode, editDictionaryByKeyPath, enumeration as enu, enumerationPlugin, fallbackPlugin, filePlugin, filterMissingTranslationsOnlyPlugin, filterTranslationsOnlyPlugin, findMatchingCondition, findPlainDynamicLoader, formatProxyEnabledMessage, gender, genderPlugin, generateListItemPrefix, generateListItemPrefixRegex, generateListItemRegex, generateListRegex, generateSitemap, generateSitemapUrl, get, getBasePlugins, getBrowserLocale, getCachedIntl, getCanonicalPath, getCondition, getContent, getContentNodeByKeyPath, getCookie, getDefaultNode, getDictionary, getDictionaryAsync, getDictionaryCompositeIds, getDictionaryQualifierIds, getDictionaryQualifierTypes, getDictionarySelectorCacheKey, getDictionaryTransformCacheKey, getDomainHostname, getDomainOrigin, getEditedContent, getEditedDictionary, getEmptyNode, getEnumeration, getFilterMissingTranslationsContent, getFilterMissingTranslationsDictionary, getFilterTranslationsOnlyContent, getFilterTranslationsOnlyDictionary, getFilteredLocalesContent, getFilteredLocalesDictionary, getGender, getHTML, getHTMLTextDir, getInsertion, getInsertionValues, getInternalPath, getInterpolableContent, getIntlayer, getIntlayerAsync, getLocale, getLocaleFromDomain, getLocaleFromPath, getLocaleFromStorage, getLocaleFromStorageClient, getLocaleFromStorageServer, getLocaleLang, getLocaleName, getLocalizedContent, getLocalizedPath, getLocalizedUrl, getMarkdownMetadata, getMaskContent, getMissingLocalesContent, getMissingLocalesContentFromDictionary, getMultilingualDictionary, getMultilingualUrls, getNestedDictionaryGraph, getNestedDictionaryKeys, getNestedDictionaryReferences, getNesting, getNodeChildren, getNodeType, getPathWithoutLocale, getPerLocaleDictionary, getPluginsCacheKey, getPlural, getPrefix, getPreloadLocale, getPreloadedDictionary, getReplacedValuesContent, getRewritePath, getRewriteRules, getSelect, getSplittedContent, getSplittedDictionaryContent, getTranslation, getVariantIds, html, i18nextToIntlayerFormatter, icuToIntlayerFormatter, inlineRegex, insertion as insert, insertContentInDictionary, insertionPlugin, interpolateMessage, intlayerToI18nextFormatter, intlayerToICUFormatter, intlayerToPortableObjectFormatter, intlayerToVueI18nFormatter, isDeclaredLocale, isInterpolableWrapperNode, isLocaleExclusiveOnDomain, isProxyStorageLocaleEnabled, isQualifiedDictionaryGroup, isQualifiedDynamicLoaderMap, isSameKeyPath, isValidElement, list, localeDetector, localeFlatMap, localeMap, localeRecord, localeResolver, localeStorageOptions, markdown as md, mergeDictionaries, mergeQualifiedDictionaries, navigatePath, nesting as nest, nestedPlugin, normalizeAttributeKey, normalizeDictionaries, normalizeDictionary, normalizePath, normalizeWhitespace, number, orderDictionaries, parseBlock, parseCaptureInline, parseDictionarySelector, parseInline, parseMarkdown, parseSimpleInline, parseStyleAttribute, parseTableAlign, parseTableAlignCapture, parseTableCells, parseTableRow, parseTaggedMessage, parseYaml, parserFor, percentage, plural, pluralPlugin, portableObjectToIntlayerFormatter, presets, qualifies, readTransformCache, rebuildInterpolableContent, reconstructQualifiedEntry, relativeTime, removeContentNodeByKeyPath, renameContentNodeByKeyPath, renderFor, renderMarkdownAst, renderNothing, resolveBrowserLocale, resolveDictionaryArgument, resolveEffectiveVariantId, resolveLocalizedPath, resolveMessage, resolveMessageNode, resolvePlainDynamicLoader, resolveProviderVariant, resolveProxyMode, resolveQualifiedDictionary, resolveQualifiedDynamicContent, resolveQualifiedDynamicContentAsync, sanitizer, select, selectPlugin, serializeVariant, serializeVariantChain, setLocaleInStorage, setLocaleInStorageClient, setLocaleInStorageServer, simpleInlineRegex, slugify, some, splitInsertionTemplate, startsWith, stringifyYaml, translation as t, transformInterpolableNode, translationPlugin, trimEnd, trimLeadingWhitespaceOutsideFences, unescapeString, units, unquote, updateNodeChildren, validateHTML, validateMarkdown, validatePrefix, vueI18nToIntlayerFormatter, writeTransformCache };
116
+ export { ATTRIBUTES_TO_SANITIZE, ATTRIBUTE_TO_NODE_PROP_MAP, ATTR_EXTRACTOR_R, BLOCKQUOTE_ALERT_R, BLOCKQUOTE_R, BLOCKQUOTE_TRIM_LEFT_MULTILINE_R, BLOCK_END_R, BREAK_LINE_R, BREAK_THEMATIC_R, CAPTURE_LETTER_AFTER_HYPHEN, CODE_BLOCK_FENCED_R, CODE_BLOCK_R, CODE_INLINE_R, COMPOSITE_ID_SEPARATOR, CONSECUTIVE_NEWLINE_R, CR_NEWLINE_R, CachedIntl, CachedIntl as Intl, DEFAULT_VARIANT_ID, DO_NOT_PROCESS_HTML_ELEMENTS, FENCE_DELIMITER_R, FOOTNOTE_R, FOOTNOTE_REFERENCE_R, FORMFEED_R, FRONT_MATTER_R, GFM_TASK_R, HEADING_ATX_COMPLIANT_R, HEADING_R, HEADING_SETEXT_R, HTML_CHAR_CODE_R, HTML_COMMENT_R, HTML_CUSTOM_ATTR_R, HTML_LEFT_TRIM_AMOUNT_R, HTML_TAGS, INLINE_SKIP_R, INTERPOLATION_R, LINK_AUTOLINK_BARE_URL_R, LINK_AUTOLINK_R, LOOKAHEAD, LocaleStorage, LocaleStorageClient, LocaleStorageServer, NAMED_CODES_TO_UNICODE, NP_TABLE_R, ORDERED, ORDERED_LIST_BULLET, ORDERED_LIST_ITEM_PREFIX, ORDERED_LIST_ITEM_PREFIX_R, ORDERED_LIST_ITEM_R, ORDERED_LIST_R, PARAGRAPH_R, PRELOADED_DYNAMIC_KEY, Priority, QUALIFIER_DYNAMIC_TYPES_KEY, QUALIFIER_ORDER, REFERENCE_IMAGE_OR_LINK, REFERENCE_IMAGE_R, REFERENCE_LINK_R, RuleScope, RuleType, SCOPE_BLOCK, SCOPE_BOTH, SCOPE_INLINE, SHORTCODE_R, SHOULD_RENDER_AS_BLOCK_R, TABLE_CENTER_ALIGN, TABLE_LEFT_ALIGN, TABLE_RIGHT_ALIGN, TABLE_TRIM_PIPES, TAB_R, TEXT_BOLD_R, TEXT_EMPHASIZED_R, TEXT_ESCAPED_R, TEXT_MARKED_R, TEXT_STRIKETHROUGHED_R, TRIM_STARTING_NEWLINES, UNESCAPE_R, UNORDERED, UNORDERED_LIST_BULLET, UNORDERED_LIST_ITEM_PREFIX, UNORDERED_LIST_ITEM_PREFIX_R, UNORDERED_LIST_ITEM_R, UNORDERED_LIST_R, VOID_HTML_ELEMENTS, allowInline, anyScopeRegex, attributeValueToNodePropValue, bindIntl, blockRegex, buildMaskPlugin, captureNothing, checkIsURLAbsolute, checkMissingLocalesPlugin, compact, comparePaths, compile, compileWithOptions, condition as cond, conditionPlugin, createCompiler, createRenderer, currency, cx, date, deepTransformNode, editDictionaryByKeyPath, enumeration as enu, enumerationPlugin, fallbackPlugin, filePlugin, filterMissingTranslationsOnlyPlugin, filterTranslationsOnlyPlugin, findMatchingCondition, findPlainDynamicLoader, formatProxyEnabledMessage, gender, genderPlugin, generateListItemPrefix, generateListItemPrefixRegex, generateListItemRegex, generateListRegex, generateSitemap, generateSitemapUrl, get, getBasePlugins, getBrowserLocale, getCachedIntl, getCanonicalPath, getCondition, getContent, getContentNodeByKeyPath, getCookie, getDefaultNode, getDictionary, getDictionaryAsync, getDictionaryCompositeIds, getDictionaryQualifierIds, getDictionaryQualifierTypes, getDictionarySelectorCacheKey, getDictionaryTransformCacheKey, getDomainHostname, getDomainOrigin, getEditedContent, getEditedDictionary, getEmptyNode, getEnumeration, getFilterMissingTranslationsContent, getFilterMissingTranslationsDictionary, getFilterTranslationsOnlyContent, getFilterTranslationsOnlyDictionary, getFilteredLocalesContent, getFilteredLocalesDictionary, getGender, getHTML, getHTMLTextDir, getInsertion, getInsertionValues, getInternalPath, getInterpolableContent, getIntlayer, getIntlayerAsync, getLocale, getLocaleFromDomain, getLocaleFromPath, getLocaleFromStorage, getLocaleFromStorageClient, getLocaleFromStorageServer, getLocaleLang, getLocaleName, getLocalizedContent, getLocalizedPath, getLocalizedUrl, getMarkdownMetadata, getMaskContent, getMissingLocalesContent, getMissingLocalesContentFromDictionary, getMultilingualDictionary, getMultilingualUrls, getNestedDictionaryGraph, getNestedDictionaryKeys, getNestedDictionaryReferences, getNesting, getNodeChildren, getNodeType, getPathWithoutLocale, getPerLocaleDictionary, getPluginsCacheKey, getPlural, getPrefix, getPreloadLocale, getPreloadedDictionary, getReplacedValuesContent, getRewritePath, getRewriteRules, getRuleSet, getSelect, getSplittedContent, getSplittedDictionaryContent, getTranslation, getVariantIds, html, i18nextToIntlayerFormatter, icuToIntlayerFormatter, inlineRegex, insertion as insert, insertContentInDictionary, insertionPlugin, interpolateMessage, intlayerToI18nextFormatter, intlayerToICUFormatter, intlayerToPortableObjectFormatter, intlayerToVueI18nFormatter, isDeclaredLocale, isInterpolableWrapperNode, isLocaleExclusiveOnDomain, isProxyStorageLocaleEnabled, isQualifiedDictionaryGroup, isQualifiedDynamicLoaderMap, isSameKeyPath, isValidElement, list, localeDetector, localeFlatMap, localeMap, localeRecord, localeResolver, localeStorageOptions, markdown as md, mergeDictionaries, mergeQualifiedDictionaries, navigatePath, nesting as nest, nestedPlugin, normalizeAttributeKey, normalizeDictionaries, normalizeDictionary, normalizePath, normalizeWhitespace, number, orderDictionaries, parseBlock, parseCaptureInline, parseDictionarySelector, parseInline, parseMarkdown, parseSimpleInline, parseStyleAttribute, parseTableAlign, parseTableAlignCapture, parseTableCells, parseTableRow, parseTaggedMessage, parseYaml, parserFor, percentage, plural, pluralPlugin, portableObjectToIntlayerFormatter, presets, qualifies, readTransformCache, rebuildInterpolableContent, reconstructQualifiedEntry, relativeTime, removeContentNodeByKeyPath, renameContentNodeByKeyPath, renderFor, renderMarkdownAst, renderNothing, resolveBrowserLocale, resolveDictionaryArgument, resolveEffectiveVariantId, resolveLocalizedPath, resolveMessage, resolveMessageNode, resolvePlainDynamicLoader, resolveProviderVariant, resolveProxyMode, resolveQualifiedDictionary, resolveQualifiedDynamicContent, resolveQualifiedDynamicContentAsync, sanitizer, select, selectPlugin, serializeVariant, serializeVariantChain, setLocaleInStorage, setLocaleInStorageClient, setLocaleInStorageServer, simpleInlineRegex, slugify, some, splitInsertionTemplate, startsWith, stringifyYaml, translation as t, transformInterpolableNode, translationPlugin, trimEnd, trimLeadingWhitespaceOutsideFences, unescapeString, units, unquote, updateNodeChildren, validateHTML, validateMarkdown, validatePrefix, vueI18nToIntlayerFormatter, writeTransformCache };
@@ -1 +1 @@
1
- {"version":3,"file":"getPlural.mjs","names":[],"sources":["../../../src/interpreter/getPlural.ts"],"sourcesContent":["import type { DeclaredLocales } from '@intlayer/types';\nimport type { LocalesValues } from 'intlayer';\nimport { getCachedIntl } from '../formatters';\nimport type {\n PluralCategory,\n PluralContentState,\n} from '../transpiler/plural/plural';\n\n/**\n * Picks content from a plural map based on a count and locale, using CLDR\n * pluralization rules (`Intl.PluralRules`).\n *\n * Falls back to the `other` category when no specific category matches.\n *\n * @example\n * ```ts\n * getPlural({\n * one: 'one item',\n * other: '{{count}} items',\n * }, 5, 'en');\n * // '{{count}} items'\n * ```\n */\nexport const getPlural = <const L extends LocalesValues = DeclaredLocales>(\n pluralContent: PluralContentState<string>,\n count: number,\n locale: L\n): string => {\n const category = getCachedIntl('PluralRules', locale).select(\n count\n ) as PluralCategory;\n\n return pluralContent[category] ?? pluralContent.other;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAuBA,MAAa,aACX,eACA,OACA,WACW;CAKX,OAAO,cAJU,cAAc,eAAe,MAAM,CAAC,CAAC,OACpD,KAG0B,MAAM,cAAc;AAClD"}
1
+ {"version":3,"file":"getPlural.mjs","names":[],"sources":["../../../src/interpreter/getPlural.ts"],"sourcesContent":["import type { DeclaredLocales } from '@intlayer/types';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport { getCachedIntl } from '../formatters';\nimport type {\n PluralCategory,\n PluralContentState,\n} from '../transpiler/plural/plural';\n\n/**\n * Picks content from a plural map based on a count and locale, using CLDR\n * pluralization rules (`Intl.PluralRules`).\n *\n * Falls back to the `other` category when no specific category matches.\n *\n * @example\n * ```ts\n * getPlural({\n * one: 'one item',\n * other: '{{count}} items',\n * }, 5, 'en');\n * // '{{count}} items'\n * ```\n */\nexport const getPlural = <const L extends LocalesValues = DeclaredLocales>(\n pluralContent: PluralContentState<string>,\n count: number,\n locale: L\n): string => {\n const category = getCachedIntl('PluralRules', locale).select(\n count\n ) as PluralCategory;\n\n return pluralContent[category] ?? pluralContent.other;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAuBA,MAAa,aACX,eACA,OACA,WACW;CAKX,OAAO,cAJU,cAAc,eAAe,MAAM,CAAC,CAAC,OACpD,KAG0B,MAAM,cAAc;AAClD"}