@intlayer/core 9.4.1 → 9.4.2

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 (72) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/index.cjs +8 -4
  3. package/dist/cjs/localization/getCanonicalPath.cjs +52 -0
  4. package/dist/cjs/localization/getCanonicalPath.cjs.map +1 -0
  5. package/dist/cjs/localization/getCanonicalPath.test-d.cjs +12 -0
  6. package/dist/cjs/localization/getCanonicalPath.test-d.cjs.map +1 -0
  7. package/dist/cjs/localization/getLocalizedPath.cjs +4 -2
  8. package/dist/cjs/localization/getLocalizedPath.cjs.map +1 -1
  9. package/dist/cjs/localization/index.cjs +4 -2
  10. package/dist/cjs/localization/rewriteMatch.cjs +52 -0
  11. package/dist/cjs/localization/rewriteMatch.cjs.map +1 -0
  12. package/dist/cjs/localization/rewriteUtils.cjs +6 -68
  13. package/dist/cjs/localization/rewriteUtils.cjs.map +1 -1
  14. package/dist/cjs/localization/rewriteUtils.test-d.cjs +0 -5
  15. package/dist/cjs/localization/rewriteUtils.test-d.cjs.map +1 -1
  16. package/dist/cjs/markdown/compiler.cjs +100 -75
  17. package/dist/cjs/markdown/compiler.cjs.map +1 -1
  18. package/dist/cjs/markdown/constants.cjs +14 -5
  19. package/dist/cjs/markdown/constants.cjs.map +1 -1
  20. package/dist/cjs/markdown/index.cjs +4 -2
  21. package/dist/cjs/markdown/parser.cjs +70 -44
  22. package/dist/cjs/markdown/parser.cjs.map +1 -1
  23. package/dist/cjs/markdown/renderer.cjs +7 -19
  24. package/dist/cjs/markdown/renderer.cjs.map +1 -1
  25. package/dist/cjs/markdown/utils.cjs +33 -37
  26. package/dist/cjs/markdown/utils.cjs.map +1 -1
  27. package/dist/esm/index.mjs +5 -3
  28. package/dist/esm/localization/getCanonicalPath.mjs +51 -0
  29. package/dist/esm/localization/getCanonicalPath.mjs.map +1 -0
  30. package/dist/esm/localization/getCanonicalPath.test-d.mjs +12 -0
  31. package/dist/esm/localization/getCanonicalPath.test-d.mjs.map +1 -0
  32. package/dist/esm/localization/getLocalizedPath.mjs +3 -1
  33. package/dist/esm/localization/getLocalizedPath.mjs.map +1 -1
  34. package/dist/esm/localization/index.mjs +3 -1
  35. package/dist/esm/localization/rewriteMatch.mjs +49 -0
  36. package/dist/esm/localization/rewriteMatch.mjs.map +1 -0
  37. package/dist/esm/localization/rewriteUtils.mjs +3 -63
  38. package/dist/esm/localization/rewriteUtils.mjs.map +1 -1
  39. package/dist/esm/localization/rewriteUtils.test-d.mjs +1 -6
  40. package/dist/esm/localization/rewriteUtils.test-d.mjs.map +1 -1
  41. package/dist/esm/markdown/compiler.mjs +101 -55
  42. package/dist/esm/markdown/compiler.mjs.map +1 -1
  43. package/dist/esm/markdown/constants.mjs +11 -4
  44. package/dist/esm/markdown/constants.mjs.map +1 -1
  45. package/dist/esm/markdown/index.mjs +2 -2
  46. package/dist/esm/markdown/parser.mjs +70 -44
  47. package/dist/esm/markdown/parser.mjs.map +1 -1
  48. package/dist/esm/markdown/renderer.mjs +7 -19
  49. package/dist/esm/markdown/renderer.mjs.map +1 -1
  50. package/dist/esm/markdown/utils.mjs +34 -38
  51. package/dist/esm/markdown/utils.mjs.map +1 -1
  52. package/dist/types/index.d.ts +5 -3
  53. package/dist/types/localization/getCanonicalPath.d.ts +19 -0
  54. package/dist/types/localization/getCanonicalPath.d.ts.map +1 -0
  55. package/dist/types/localization/getCanonicalPath.test-d.d.ts +1 -0
  56. package/dist/types/localization/getLocalizedPath.d.ts.map +1 -1
  57. package/dist/types/localization/index.d.ts +3 -1
  58. package/dist/types/localization/rewriteMatch.d.ts +20 -0
  59. package/dist/types/localization/rewriteMatch.d.ts.map +1 -0
  60. package/dist/types/localization/rewriteUtils.d.ts +2 -14
  61. package/dist/types/localization/rewriteUtils.d.ts.map +1 -1
  62. package/dist/types/markdown/compiler.d.ts.map +1 -1
  63. package/dist/types/markdown/constants.d.ts +11 -4
  64. package/dist/types/markdown/constants.d.ts.map +1 -1
  65. package/dist/types/markdown/index.d.ts +2 -2
  66. package/dist/types/markdown/parser.d.ts.map +1 -1
  67. package/dist/types/markdown/renderer.d.ts.map +1 -1
  68. package/dist/types/markdown/types.d.ts +8 -2
  69. package/dist/types/markdown/types.d.ts.map +1 -1
  70. package/dist/types/markdown/utils.d.ts +0 -4
  71. package/dist/types/markdown/utils.d.ts.map +1 -1
  72. package/package.json +7 -7
@@ -1 +1 @@
1
- {"version":3,"file":"parser.mjs","names":[],"sources":["../../../src/markdown/parser.ts"],"sourcesContent":["import { DURATION_DELAY_TRIGGER } from './constants';\nimport type { NestedParser, ParserResult, ParseState, Rules } from './types';\nimport { normalizeWhitespace, qualifies } from './utils';\n\n/**\n * Creates a parser for a given set of rules, with the precedence\n * specified as a list of rules.\n *\n * @param rules - An object containing rule type -> {match, order, parse} objects\n * (lower order is higher precedence)\n *\n * @returns The resulting parse function\n */\nexport const parserFor = (\n rules: Rules\n): ((source: string, state: ParseState) => ParserResult[]) => {\n const start = performance.now();\n const ruleList = Object.keys(rules);\n\n if (process.env.NODE_ENV !== 'production') {\n ruleList.forEach((type) => {\n const order = rules[type]?._order;\n if (typeof order !== 'number' || !Number.isFinite(order)) {\n console.warn(`intlayer: Invalid order for rule \\`${type}\\`: ${order}`);\n }\n });\n }\n\n // Sorts rules in order of increasing order, then\n // ascending rule name (numeric) in case of ties.\n // RuleType keys are string numbers — use numeric comparison to preserve\n // intended ordering (e.g. codeFenced '4' must precede headingSetext '10').\n ruleList.sort((a, b) => {\n return rules[a]!._order - rules[b]!._order || +a - +b;\n });\n\n const nestedParse: NestedParser = (\n source: string,\n state: ParseState = {}\n ): ParserResult[] => {\n const parseStart = performance.now();\n const result: ParserResult[] = [];\n state.prevCapture = state.prevCapture || '';\n\n if (source.trim()) {\n while (source) {\n let i = 0;\n while (i < ruleList.length) {\n const ruleType = ruleList[i];\n const rule = rules[ruleType as keyof typeof rules];\n\n if (rule?._qualify && !qualifies(source, state, rule._qualify)) {\n i++;\n continue;\n }\n\n const matchStart = performance.now();\n const capture = rule?._match(source, state);\n\n if (process.env.NODE_ENV !== 'production') {\n const matchDuration = performance.now() - matchStart;\n if (matchDuration > 1) {\n console.log(\n `${ruleType}._match: ${matchDuration.toFixed(3)}ms, source length: ${source.length}`\n );\n }\n }\n\n if (capture?.[0]) {\n source = source.substring(capture[0].length);\n\n const ruleParseStart = performance.now();\n const parsedAny: any = rule?._parse(capture, nestedParse, state);\n\n if (process.env.NODE_ENV !== 'production') {\n const ruleParseDuration = performance.now() - ruleParseStart;\n if (ruleParseDuration > 1) {\n console.log(\n `${ruleType}._parse: ${ruleParseDuration.toFixed(3)}ms, capture length: ${capture[0].length}`\n );\n }\n }\n\n state.prevCapture = (state.prevCapture || '') + capture[0];\n\n if (!parsedAny.type) {\n parsedAny.type = ruleType;\n }\n result.push(parsedAny as ParserResult);\n break;\n }\n i++;\n }\n }\n }\n\n // Tree shakeable log for prod\n if (process.env.NODE_ENV === 'development') {\n const parseDuration = performance.now() - parseStart;\n if (parseDuration > 1) {\n console.log(\n `nestedParse: ${parseDuration.toFixed(3)}ms, source length: ${source.length}, result count: ${result.length}`\n );\n }\n }\n\n return result;\n };\n\n // Tree shakeable log for prod\n if (process.env.NODE_ENV === 'development') {\n const duration = performance.now() - start;\n\n if (duration > DURATION_DELAY_TRIGGER) {\n console.log(\n `parserFor: ${duration.toFixed(3)}ms, rules count: ${ruleList.length}`\n );\n }\n }\n\n return (source: string, state: ParseState) =>\n nestedParse(normalizeWhitespace(source), state);\n};\n"],"mappings":";;;;;;;;;;;;;AAaA,MAAa,aACX,UAC4D;CAC5D,MAAM,QAAQ,YAAY,IAAI;CAC9B,MAAM,WAAW,OAAO,KAAK,KAAK;CAGhC,SAAS,SAAS,SAAS;EACzB,MAAM,QAAQ,MAAM,KAAK,EAAE;EAC3B,IAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,GACrD,QAAQ,KAAK,sCAAsC,KAAK,MAAM,OAAO;CAEzE,CAAC;CAOH,SAAS,MAAM,GAAG,MAAM;EACtB,OAAO,MAAM,EAAE,CAAE,SAAS,MAAM,EAAE,CAAE,UAAU,CAAC,IAAI,CAAC;CACtD,CAAC;CAED,MAAM,eACJ,QACA,QAAoB,CAAC,MACF;EACnB,MAAM,aAAa,YAAY,IAAI;EACnC,MAAM,SAAyB,CAAC;EAChC,MAAM,cAAc,MAAM,eAAe;EAEzC,IAAI,OAAO,KAAK,GACd,OAAO,QAAQ;GACb,IAAI,IAAI;GACR,OAAO,IAAI,SAAS,QAAQ;IAC1B,MAAM,WAAW,SAAS;IAC1B,MAAM,OAAO,MAAM;IAEnB,IAAI,MAAM,YAAY,CAAC,UAAU,QAAQ,OAAO,KAAK,QAAQ,GAAG;KAC9D;KACA;IACF;IAEA,MAAM,aAAa,YAAY,IAAI;IACnC,MAAM,UAAU,MAAM,OAAO,QAAQ,KAAK;IAEC;KACzC,MAAM,gBAAgB,YAAY,IAAI,IAAI;KAC1C,IAAI,gBAAgB,GAClB,QAAQ,IACN,GAAG,SAAS,WAAW,cAAc,QAAQ,CAAC,EAAE,qBAAqB,OAAO,QAC9E;IAEJ;IAEA,IAAI,UAAU,IAAI;KAChB,SAAS,OAAO,UAAU,QAAQ,EAAE,CAAC,MAAM;KAE3C,MAAM,iBAAiB,YAAY,IAAI;KACvC,MAAM,YAAiB,MAAM,OAAO,SAAS,aAAa,KAAK;KAEpB;MACzC,MAAM,oBAAoB,YAAY,IAAI,IAAI;MAC9C,IAAI,oBAAoB,GACtB,QAAQ,IACN,GAAG,SAAS,WAAW,kBAAkB,QAAQ,CAAC,EAAE,sBAAsB,QAAQ,EAAE,CAAC,QACvF;KAEJ;KAEA,MAAM,eAAe,MAAM,eAAe,MAAM,QAAQ;KAExD,IAAI,CAAC,UAAU,MACb,UAAU,OAAO;KAEnB,OAAO,KAAK,SAAyB;KACrC;IACF;IACA;GACF;EACF;EAI0C;GAC1C,MAAM,gBAAgB,YAAY,IAAI,IAAI;GAC1C,IAAI,gBAAgB,GAClB,QAAQ,IACN,gBAAgB,cAAc,QAAQ,CAAC,EAAE,qBAAqB,OAAO,OAAO,kBAAkB,OAAO,QACvG;EAEJ;EAEA,OAAO;CACT;CAG4C;EAC1C,MAAM,WAAW,YAAY,IAAI,IAAI;EAErC,IAAI,eACF,QAAQ,IACN,cAAc,SAAS,QAAQ,CAAC,EAAE,mBAAmB,SAAS,QAChE;CAEJ;CAEA,QAAQ,QAAgB,UACtB,YAAY,oBAAoB,MAAM,GAAG,KAAK;AAClD"}
1
+ {"version":3,"file":"parser.mjs","names":[],"sources":["../../../src/markdown/parser.ts"],"sourcesContent":["import type {\n NestedParser,\n ParserResult,\n ParseState,\n Rule,\n Rules,\n} from './types';\nimport { normalizeWhitespace, qualifies } from './utils';\n\n/**\n * Cached rule precedence, grouped by rule count.\n *\n * A rule's `_order` never varies, so the precedence a rule set sorts into is a\n * pure function of which rules it holds. Rules themselves are rebuilt per\n * document, so the cache is keyed by rule names rather than by object identity,\n * and grouped by count to keep the comparison short.\n */\ntype RuleOrderEntry = { names: string[]; order: string[] };\n\nconst ruleOrderCache = new Map<number, RuleOrderEntry[]>();\n\nconst sameNames = (a: string[], b: string[]): boolean => {\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n\n return true;\n};\n\n/**\n * Resolves `Object.keys(rules)` into precedence order, sorting only the first\n * time a given rule set is seen.\n */\nconst orderRules = (names: string[], rules: Rules): string[] => {\n const candidates = ruleOrderCache.get(names.length);\n\n if (candidates) {\n for (let i = 0; i < candidates.length; i++) {\n if (sameNames(names, candidates[i]!.names)) return candidates[i]!.order;\n }\n }\n\n // Sorts rules in order of increasing order, then\n // ascending rule name (numeric) in case of ties.\n // RuleType keys are string numbers — use numeric comparison to preserve\n // intended ordering (e.g. codeFenced '4' must precede headingSetext '10').\n const order = names\n .slice()\n .sort((a, b) => rules[a]!._order - rules[b]!._order || +a - +b);\n\n const entry: RuleOrderEntry = { names: names.slice(), order };\n\n if (candidates) candidates.push(entry);\n else ruleOrderCache.set(names.length, [entry]);\n\n return order;\n};\n\n/**\n * Advance the incremental lookbehind carried on the parse state.\n *\n * Rules only ever ask two questions about the source already consumed: how far\n * the current line is indented (`prevCaptureIndent`) and whether a blank line\n * has been seen (`prevCaptureHasBlankLine`). Both are maintained in time\n * proportional to the capture rather than by concatenating — and later\n * rescanning — the whole document, which made parsing quadratic.\n */\nconst advanceLookbehind = (state: ParseState, capture: string): void => {\n const newlineIndex = capture.lastIndexOf('\\n');\n // `undefined` marks a lookbehind nothing has been appended to yet, which is\n // the only way to tell an empty lookbehind from one ending on a line break.\n const hasPrevCapture = state.prevCaptureHasBlankLine !== undefined;\n\n if (state.prevCaptureHasBlankLine !== true) {\n state.prevCaptureHasBlankLine =\n capture.indexOf('\\n\\n') !== -1 ||\n // A capture opening on a line break closes a blank line when the\n // lookbehind already ended on one.\n (hasPrevCapture &&\n capture.charCodeAt(0) === 10 /* \\n */ &&\n state.prevCaptureIndent === '');\n }\n\n // Everything after the final newline forms the current line. With no newline\n // in the capture, the current line simply grew by `capture`.\n const tail =\n newlineIndex === -1\n ? state.prevCaptureIndent === undefined\n ? undefined\n : state.prevCaptureIndent + capture\n : capture.slice(newlineIndex + 1);\n\n if (tail === undefined || tail === '') {\n state.prevCaptureIndent = tail;\n return;\n }\n\n for (let i = 0; i < tail.length; i++) {\n if (tail.charCodeAt(i) !== 32 /* space */) {\n state.prevCaptureIndent = undefined;\n return;\n }\n }\n\n state.prevCaptureIndent = tail;\n};\n\n/**\n * Creates a parser for a given set of rules, with the precedence\n * specified as a list of rules.\n *\n * @param rules - An object containing rule type -> {match, order, parse} objects\n * (lower order is higher precedence)\n *\n * @returns The resulting parse function\n */\nexport const parserFor = (\n rules: Rules\n): ((source: string, state: ParseState) => ParserResult[]) => {\n const ruleList = Object.keys(rules);\n\n if (process.env.NODE_ENV !== 'production') {\n ruleList.forEach((type) => {\n const order = rules[type]?._order;\n if (typeof order !== 'number' || !Number.isFinite(order)) {\n console.warn(`intlayer: Invalid order for rule \\`${type}\\`: ${order}`);\n }\n });\n }\n\n const order = orderRules(ruleList, rules);\n const ruleCount = order.length;\n\n // Rule objects have heterogeneous shapes (`_qualify` and `_render` are both\n // optional), so their members are hoisted into parallel arrays: the hot loop\n // then reads monomorphic slots instead of polymorphic properties, and the\n // whole set costs three allocations rather than one wrapper per rule.\n const qualifiers: (Rule<any>['_qualify'] | undefined)[] = new Array(\n ruleCount\n );\n const matchers: Rule<any>['_match'][] = new Array(ruleCount);\n const parsers: Rule<any>['_parse'][] = new Array(ruleCount);\n\n for (let i = 0; i < ruleCount; i++) {\n const rule = rules[order[i]!]!;\n\n qualifiers[i] = rule._qualify;\n matchers[i] = rule._match;\n parsers[i] = rule._parse;\n }\n\n const nestedParse: NestedParser = (\n source: string,\n state: ParseState = {}\n ): ParserResult[] => {\n const result: ParserResult[] = [];\n\n if (state.prevCaptureHasBlankLine === undefined) {\n // Nothing consumed yet: an empty lookbehind sits at the start of a line.\n state.prevCaptureIndent = '';\n }\n\n if (source.trim()) {\n while (source) {\n for (let i = 0; i < ruleCount; i++) {\n const qualify = qualifiers[i];\n\n if (qualify && !qualifies(source, state, qualify)) continue;\n\n const capture = matchers[i]!(source, state);\n\n if (capture?.[0]) {\n source = source.substring(capture[0].length);\n\n const parsed: any = parsers[i]!(capture, nestedParse, state);\n\n advanceLookbehind(state, capture[0]);\n\n if (!parsed.type) {\n parsed.type = order[i];\n }\n\n result.push(parsed as ParserResult);\n break;\n }\n }\n }\n }\n\n return result;\n };\n\n return (source: string, state: ParseState) =>\n nestedParse(normalizeWhitespace(source), state);\n};\n"],"mappings":"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"}
@@ -1,5 +1,3 @@
1
- import { DURATION_DELAY_TRIGGER } from "./constants.mjs";
2
-
3
1
  //#region src/markdown/renderer.ts
4
2
  /**
5
3
  * Creates a renderer for AST nodes.
@@ -8,16 +6,15 @@ import { DURATION_DELAY_TRIGGER } from "./constants.mjs";
8
6
  * @param render - The render function to call for each node
9
7
  * @returns A function that renders AST to output
10
8
  */
11
- const renderFor = (render) => (ast, state = {}) => {
12
- const start = performance.now();
13
- const patchedRender = (ast, state = {}) => renderFor(render)(ast, state);
14
- if (Array.isArray(ast)) {
9
+ const renderFor = (render) => {
10
+ const emit = (ast, state = {}) => {
11
+ if (!Array.isArray(ast)) return render(ast, emit, state);
15
12
  const oldKey = state.key;
16
13
  const result = [];
17
14
  let lastWasString = false;
18
15
  let renderedIndex = 0;
19
16
  for (let i = 0; i < ast.length; i++) {
20
- const nodeOut = patchedRender(ast[i], {
17
+ const nodeOut = emit(ast[i], {
21
18
  ...state,
22
19
  key: renderedIndex
23
20
  });
@@ -30,14 +27,9 @@ const renderFor = (render) => (ast, state = {}) => {
30
27
  lastWasString = isString;
31
28
  }
32
29
  state.key = oldKey;
33
- const duration = performance.now() - start;
34
- if (duration > 20) console.log(`renderFor (array): ${duration.toFixed(3)}ms, ast length: ${ast.length}`);
35
30
  return result;
36
- }
37
- const result = render(ast, patchedRender, state);
38
- const duration = performance.now() - start;
39
- if (duration > 20) console.log(`renderFor (single): ${duration.toFixed(3)}ms, ast type: ${ast.type}`);
40
- return result;
31
+ };
32
+ return emit;
41
33
  };
42
34
  /**
43
35
  * Creates a renderer from rules with optional custom render hook.
@@ -47,12 +39,8 @@ const renderFor = (render) => (ast, state = {}) => {
47
39
  * @returns A render function for AST nodes
48
40
  */
49
41
  const createRenderer = (rules, userRender) => (ast, render, state) => {
50
- const start = performance.now();
51
42
  const renderer = rules[ast.type]?._render;
52
- const result = userRender ? userRender(() => renderer?.(ast, render, state), ast, render, state) : renderer?.(ast, render, state);
53
- const duration = performance.now() - start;
54
- if (duration > 20) console.log(`createRenderer: ${duration.toFixed(3)}ms, ast type: ${ast.type}, hasUserRender: ${!!userRender}`);
55
- return result;
43
+ return userRender ? userRender(() => renderer?.(ast, render, state), ast, render, state) : renderer?.(ast, render, state);
56
44
  };
57
45
 
58
46
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"renderer.mjs","names":[],"sources":["../../../src/markdown/renderer.ts"],"sourcesContent":["import { DURATION_DELAY_TRIGGER } from './constants';\nimport 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 (\n render: (\n ast: ParserResult,\n render: RuleOutput,\n state: ParseState\n ) => unknown\n ) =>\n (ast: ParserResult | ParserResult[], state: ParseState = {}): any => {\n const start = performance.now();\n\n const patchedRender = (\n ast: ParserResult | ParserResult[],\n state: ParseState = {}\n ): any => renderFor(render)(ast, state);\n\n if (Array.isArray(ast)) {\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 = patchedRender(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 const duration = performance.now() - start;\n if (duration > DURATION_DELAY_TRIGGER) {\n console.log(\n `renderFor (array): ${duration.toFixed(3)}ms, ast length: ${ast.length}`\n );\n }\n\n return result;\n }\n\n const result = render(ast, patchedRender as RuleOutput, state);\n\n const duration = performance.now() - start;\n if (duration > DURATION_DELAY_TRIGGER) {\n console.log(\n `renderFor (single): ${duration.toFixed(3)}ms, ast type: ${(ast as ParserResult).type}`\n );\n }\n\n return result;\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 start = performance.now();\n const renderer = rules[ast.type]?._render;\n\n const result = userRender\n ? userRender(() => renderer?.(ast, render, state), ast, render, state)\n : renderer?.(ast, render, state);\n\n const duration = performance.now() - start;\n if (duration > DURATION_DELAY_TRIGGER) {\n console.log(\n `createRenderer: ${duration.toFixed(3)}ms, ast type: ${ast.type}, hasUserRender: ${!!userRender}`\n );\n }\n\n return result;\n };\n"],"mappings":";;;;;;;;;;AAgBA,MAAa,aAET,YAMD,KAAoC,QAAoB,CAAC,MAAW;CACnE,MAAM,QAAQ,YAAY,IAAI;CAE9B,MAAM,iBACJ,KACA,QAAoB,CAAC,MACb,UAAU,MAAM,CAAC,CAAC,KAAK,KAAK;CAEtC,IAAI,MAAM,QAAQ,GAAG,GAAG;EACtB,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,cAAc,IAAI,IAAI;IAAE,GAAG;IAAO,KAAK;GAAc,CAAC;GACtE,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,MAAM,WAAW,YAAY,IAAI,IAAI;EACrC,IAAI,eACF,QAAQ,IACN,sBAAsB,SAAS,QAAQ,CAAC,EAAE,kBAAkB,IAAI,QAClE;EAGF,OAAO;CACT;CAEA,MAAM,SAAS,OAAO,KAAK,eAA6B,KAAK;CAE7D,MAAM,WAAW,YAAY,IAAI,IAAI;CACrC,IAAI,eACF,QAAQ,IACN,uBAAuB,SAAS,QAAQ,CAAC,EAAE,gBAAiB,IAAqB,MACnF;CAGF,OAAO;AACT;;;;;;;;AASF,MAAa,kBACV,OAAc,gBACd,KAAmB,QAAoB,UAA+B;CACrE,MAAM,QAAQ,YAAY,IAAI;CAC9B,MAAM,WAAW,MAAM,IAAI,KAAK,EAAE;CAElC,MAAM,SAAS,aACX,iBAAiB,WAAW,KAAK,QAAQ,KAAK,GAAG,KAAK,QAAQ,KAAK,IACnE,WAAW,KAAK,QAAQ,KAAK;CAEjC,MAAM,WAAW,YAAY,IAAI,IAAI;CACrC,IAAI,eACF,QAAQ,IACN,mBAAmB,SAAS,QAAQ,CAAC,EAAE,gBAAgB,IAAI,KAAK,mBAAmB,CAAC,CAAC,YACvF;CAGF,OAAO;AACT"}
1
+ {"version":3,"file":"renderer.mjs","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,4 +1,4 @@
1
- import { ATTRIBUTES_TO_SANITIZE, CAPTURE_LETTER_AFTER_HYPHEN, CR_NEWLINE_R, DURATION_DELAY_TRIGGER, FENCE_DELIMITER_R, FORMFEED_R, HTML_CUSTOM_ATTR_R, INTERPOLATION_R, TABLE_CENTER_ALIGN, TABLE_LEFT_ALIGN, TABLE_RIGHT_ALIGN, TABLE_TRIM_PIPES, TAB_R, UNESCAPE_R } from "./constants.mjs";
1
+ import { ATTRIBUTES_TO_SANITIZE, CAPTURE_LETTER_AFTER_HYPHEN, FENCE_DELIMITER_R, HTML_CUSTOM_ATTR_R, INTERPOLATION_R, NORMALIZE_WHITESPACE_R, TABLE_CENTER_ALIGN, TABLE_LEFT_ALIGN, TABLE_RIGHT_ALIGN, TABLE_TRIM_PIPES, UNESCAPE_R } from "./constants.mjs";
2
2
 
3
3
  //#region src/markdown/utils.ts
4
4
  /**
@@ -48,15 +48,37 @@ const get = (src, path, fb) => {
48
48
  * Convert a string to a URL-safe slug.
49
49
  * Based on https://stackoverflow.com/a/18123682/1141611
50
50
  */
51
- const slugify = (str) => str.replace(/[ÀÁÂÃÄÅàáâãä忯]/g, "a").replace(/[çÇ]/g, "c").replace(/[ðÐ]/g, "d").replace(/[ÈÉÊËéèêë]/g, "e").replace(/[ÏïÎîÍíÌì]/g, "i").replace(/[Ññ]/g, "n").replace(/[øØœŒÕõÔôÓóÒò]/g, "o").replace(/[ÜüÛûÚúÙù]/g, "u").replace(/[ŸÿÝý]/g, "y").replace(/[^a-z0-9- ]/gi, "").replace(/ /gi, "-").toLowerCase();
51
+ const SLUGIFY_REPLACEMENTS = [
52
+ [/[ÀÁÂÃÄÅàáâãä忯]/g, "a"],
53
+ [/[çÇ]/g, "c"],
54
+ [/[ðÐ]/g, "d"],
55
+ [/[ÈÉÊËéèêë]/g, "e"],
56
+ [/[ÏïÎîÍíÌì]/g, "i"],
57
+ [/[Ññ]/g, "n"],
58
+ [/[øØœŒÕõÔôÓóÒò]/g, "o"],
59
+ [/[ÜüÛûÚúÙù]/g, "u"],
60
+ [/[ŸÿÝý]/g, "y"],
61
+ [/[^a-z0-9- ]/gi, ""],
62
+ [/ /gi, "-"]
63
+ ];
64
+ const slugify = (str) => {
65
+ let result = str;
66
+ for (let i = 0; i < SLUGIFY_REPLACEMENTS.length; i++) {
67
+ const [pattern, replacement] = SLUGIFY_REPLACEMENTS[i];
68
+ result = result.replace(pattern, replacement);
69
+ }
70
+ return result.toLowerCase();
71
+ };
52
72
  const SANITIZE_R = /(javascript|vbscript|data(?!:image)):/i;
73
+ const SANITIZE_STRIP_R = /[^A-Za-z0-9/:]/g;
53
74
  /**
54
75
  * Sanitize URLs to prevent XSS attacks.
55
76
  * Returns null if the URL is unsafe.
56
77
  */
57
78
  const sanitizer = (input) => {
79
+ if (input.indexOf(":") === -1 && input.indexOf("%") === -1) return input;
58
80
  try {
59
- const decoded = decodeURIComponent(input).replace(/[^A-Za-z0-9/:]/g, "");
81
+ const decoded = decodeURIComponent(input).replace(SANITIZE_STRIP_R, "");
60
82
  if (SANITIZE_R.test(decoded)) {
61
83
  console.warn("Input contains an unsafe JavaScript/VBScript/data expression, it will not be rendered.", decoded);
62
84
  return null;
@@ -70,13 +92,11 @@ const sanitizer = (input) => {
70
92
  /**
71
93
  * Normalize whitespace in source string.
72
94
  */
73
- const normalizeWhitespace = (source) => {
74
- const start = performance.now();
75
- const result = source.replace(CR_NEWLINE_R, "\n").replace(FORMFEED_R, "").replace(TAB_R, " ");
76
- const duration = performance.now() - start;
77
- if (duration > 20) console.log(`normalizeWhitespace: ${duration.toFixed(3)}ms, source length: ${source.length}`);
78
- return result;
79
- };
95
+ const normalizeWhitespace = (source) => source.replace(NORMALIZE_WHITESPACE_R, (match) => {
96
+ if (match === " ") return " ";
97
+ if (match === "\f") return "";
98
+ return "\n";
99
+ });
80
100
  /**
81
101
  * Safely remove a uniform leading indentation from lines, but do NOT touch
82
102
  * the content inside fenced code blocks (``` or ~~~).
@@ -88,13 +108,12 @@ const normalizeWhitespace = (source) => {
88
108
  * verbatim, instead of losing the first level of indentation.
89
109
  */
90
110
  const trimLeadingWhitespaceOutsideFences = (text, whitespace) => {
91
- const start = performance.now();
92
111
  if (!whitespace) return text;
93
112
  const lines = text.split("\n");
94
113
  /** Indentation and marker of the fence currently open, if any. */
95
114
  let openFence = null;
96
115
  const removePrefix = (line, prefix) => prefix && line.startsWith(prefix) ? line.slice(prefix.length) : line;
97
- const result = lines.map((line) => {
116
+ return lines.map((line) => {
98
117
  const fenceMatch = line.match(FENCE_DELIMITER_R);
99
118
  if (!openFence) {
100
119
  if (fenceMatch) openFence = {
@@ -103,14 +122,11 @@ const trimLeadingWhitespaceOutsideFences = (text, whitespace) => {
103
122
  };
104
123
  return removePrefix(line, whitespace);
105
124
  }
106
- const isClosingFence = fenceMatch?.[2].startsWith(openFence.marker);
125
+ const isClosingFence = fenceMatch?.[2]?.startsWith(openFence.marker);
107
126
  const { indentation } = openFence;
108
127
  if (isClosingFence) openFence = null;
109
128
  return removePrefix(line, indentation);
110
129
  }).join("\n");
111
- const duration = performance.now() - start;
112
- if (duration > 20) console.log(`trimLeadingWhitespaceOutsideFences: ${duration.toFixed(3)}ms, text length: ${text.length}, lines count: ${lines.length}`);
113
- return result;
114
130
  };
115
131
  /**
116
132
  * Normalize HTML attribute key to JSX prop name.
@@ -125,7 +141,6 @@ const normalizeAttributeKey = (key) => {
125
141
  * Parse a CSS style string into an array of [key, value] tuples.
126
142
  */
127
143
  const parseStyleAttribute = (styleString) => {
128
- const start = performance.now();
129
144
  const styles = [];
130
145
  let buffer = "";
131
146
  let inUrl = false;
@@ -167,8 +182,6 @@ const parseStyleAttribute = (styleString) => {
167
182
  styles.push([key, value]);
168
183
  }
169
184
  }
170
- const duration = performance.now() - start;
171
- if (duration > 20) console.log(`parseStyleAttribute: ${duration.toFixed(3)}ms, styleString length: ${styleString.length}, styles count: ${styles.length}`);
172
185
  return styles;
173
186
  };
174
187
  /**
@@ -205,7 +218,6 @@ const parseTableAlign = (source) => {
205
218
  * Parse a single table row.
206
219
  */
207
220
  const parseTableRow = (source, parse, state, tableOutput) => {
208
- const start = performance.now();
209
221
  const prevInTable = state.inTable;
210
222
  state.inTable = true;
211
223
  const cells = [[]];
@@ -228,20 +240,13 @@ const parseTableRow = (source, parse, state, tableOutput) => {
228
240
  });
229
241
  flush();
230
242
  state.inTable = prevInTable;
231
- const duration = performance.now() - start;
232
- if (duration > 20) console.log(`parseTableRow: ${duration.toFixed(3)}ms, source length: ${source.length}, cells count: ${cells.length}`);
233
243
  return cells;
234
244
  };
235
245
  /**
236
246
  * Parse table cells (multiple rows).
237
247
  */
238
248
  const parseTableCells = (source, parse, state) => {
239
- const start = performance.now();
240
- const rowsText = source.trim().split("\n");
241
- const result = rowsText.map((rowText) => parseTableRow(rowText, parse, state, true));
242
- const duration = performance.now() - start;
243
- if (duration > 20) console.log(`parseTableCells: ${duration.toFixed(3)}ms, source length: ${source.length}, rows count: ${rowsText.length}`);
244
- return result;
249
+ return source.trim().split("\n").map((rowText) => parseTableRow(rowText, parse, state, true));
245
250
  };
246
251
  /**
247
252
  * Check if a rule qualifies for the current source and state.
@@ -292,7 +297,6 @@ const anyScopeRegex = (fn) => allowInline((source, state) => {
292
297
  * Parse inline content (including links).
293
298
  */
294
299
  const parseInline = (parse, children, state) => {
295
- const start = performance.now();
296
300
  const isCurrentlyInline = state.inline ?? false;
297
301
  const isCurrentlySimple = state.simple ?? false;
298
302
  state.inline = true;
@@ -300,15 +304,12 @@ const parseInline = (parse, children, state) => {
300
304
  const result = parse(children, state);
301
305
  state.inline = isCurrentlyInline;
302
306
  state.simple = isCurrentlySimple;
303
- const duration = performance.now() - start;
304
- if (duration > 20) console.log(`parseInline: ${duration.toFixed(3)}ms, children length: ${children.length}, result count: ${result.length}`);
305
307
  return result;
306
308
  };
307
309
  /**
308
310
  * Parse simple inline content (no links).
309
311
  */
310
312
  const parseSimpleInline = (parse, children, state) => {
311
- const start = performance.now();
312
313
  const isCurrentlyInline = state.inline ?? false;
313
314
  const isCurrentlySimple = state.simple ?? false;
314
315
  state.inline = false;
@@ -316,29 +317,24 @@ const parseSimpleInline = (parse, children, state) => {
316
317
  const result = parse(children, state);
317
318
  state.inline = isCurrentlyInline;
318
319
  state.simple = isCurrentlySimple;
319
- const duration = performance.now() - start;
320
- if (duration > 20) console.log(`parseSimpleInline: ${duration.toFixed(3)}ms, children length: ${children.length}, result count: ${result.length}`);
321
320
  return result;
322
321
  };
323
322
  /**
324
323
  * Parse block content.
325
324
  */
326
325
  const parseBlock = (parse, children, state = {}) => {
327
- const start = performance.now();
328
326
  const isCurrentlyInline = state.inline || false;
329
327
  state.inline = false;
330
328
  const normalizedChildren = trimEnd(children);
331
329
  const result = parse(/\n\n$/.test(normalizedChildren) === false ? normalizedChildren.endsWith("\n") ? `${normalizedChildren}\n` : `${normalizedChildren}\n\n` : normalizedChildren, state);
332
330
  state.inline = isCurrentlyInline;
333
- const duration = performance.now() - start;
334
- if (duration > 20) console.log(`parseBlock: ${duration.toFixed(3)}ms, children length: ${children.length}, result count: ${result.length}`);
335
331
  return result;
336
332
  };
337
333
  /**
338
334
  * Helper to parse capture group 2 as inline content.
339
335
  */
340
336
  const parseCaptureInline = (capture, parse, state) => {
341
- return { children: parseInline(parse, capture[2], state) };
337
+ return { children: parseInline(parse, capture[2] ?? "", state) };
342
338
  };
343
339
  /**
344
340
  * Helper that captures nothing (empty object).
@@ -1 +1 @@
1
- {"version":3,"file":"utils.mjs","names":[],"sources":["../../../src/markdown/utils.ts"],"sourcesContent":["import {\n ATTRIBUTES_TO_SANITIZE,\n CAPTURE_LETTER_AFTER_HYPHEN,\n CR_NEWLINE_R,\n DURATION_DELAY_TRIGGER,\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[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 */\nexport const slugify = (str: string): string =>\n str\n .replace(/[ÀÁÂÃÄÅàáâãä忯]/g, 'a')\n .replace(/[çÇ]/g, 'c')\n .replace(/[ðÐ]/g, 'd')\n .replace(/[ÈÉÊËéèêë]/g, 'e')\n .replace(/[ÏïÎîÍíÌì]/g, 'i')\n .replace(/[Ññ]/g, 'n')\n .replace(/[øØœŒÕõÔôÓóÒò]/g, 'o')\n .replace(/[ÜüÛûÚúÙù]/g, 'u')\n .replace(/[ŸÿÝý]/g, 'y')\n .replace(/[^a-z0-9- ]/gi, '')\n .replace(/ /gi, '-')\n .toLowerCase();\n\n// ============================================================================\n// SANITIZER\n// ============================================================================\n\nconst SANITIZE_R = /(javascript|vbscript|data(?!:image)):/i;\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 try {\n const decoded = decodeURIComponent(input).replace(/[^A-Za-z0-9/:]/g, '');\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 const start = performance.now();\n const result = source\n .replace(CR_NEWLINE_R, '\\n')\n .replace(FORMFEED_R, '')\n .replace(TAB_R, ' ');\n\n const duration = performance.now() - start;\n\n if (duration > DURATION_DELAY_TRIGGER) {\n console.log(\n `normalizeWhitespace: ${duration.toFixed(3)}ms, source length: ${source.length}`\n );\n }\n\n return result;\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 const start = performance.now();\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 const duration = performance.now() - start;\n if (duration > DURATION_DELAY_TRIGGER) {\n console.log(\n `trimLeadingWhitespaceOutsideFences: ${duration.toFixed(3)}ms, text length: ${text.length}, lines count: ${lines.length}`\n );\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 start = performance.now();\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 const duration = performance.now() - start;\n\n if (duration > DURATION_DELAY_TRIGGER) {\n console.log(\n `parseStyleAttribute: ${duration.toFixed(3)}ms, styleString length: ${styleString.length}, styles count: ${styles.length}`\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 start = performance.now();\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 const duration = performance.now() - start;\n if (duration > DURATION_DELAY_TRIGGER) {\n console.log(\n `parseTableRow: ${duration.toFixed(3)}ms, source length: ${source.length}, cells count: ${cells.length}`\n );\n }\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 start = performance.now();\n const rowsText = source.trim().split('\\n');\n\n const result = rowsText.map((rowText) =>\n parseTableRow(rowText, parse, state, true)\n );\n\n const duration = performance.now() - start;\n if (duration > DURATION_DELAY_TRIGGER) {\n console.log(\n `parseTableCells: ${duration.toFixed(3)}ms, source length: ${source.length}, rows count: ${rowsText.length}`\n );\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 start = performance.now();\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 const duration = performance.now() - start;\n if (duration > DURATION_DELAY_TRIGGER) {\n console.log(\n `parseInline: ${duration.toFixed(3)}ms, children length: ${children.length}, result count: ${result.length}`\n );\n }\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 start = performance.now();\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 const duration = performance.now() - start;\n if (duration > DURATION_DELAY_TRIGGER) {\n console.log(\n `parseSimpleInline: ${duration.toFixed(3)}ms, children length: ${children.length}, result count: ${result.length}`\n );\n }\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 start = performance.now();\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 const duration = performance.now() - start;\n if (duration > DURATION_DELAY_TRIGGER) {\n console.log(\n `parseBlock: ${duration.toFixed(3)}ms, children length: ${children.length}, result count: ${result.length}`\n );\n }\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.mjs","names":[],"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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"}
@@ -81,6 +81,7 @@ import { comparePaths, normalizePath } from "./localization/comparePaths.js";
81
81
  import { LocaleDomainMap, getDomainHostname, getDomainOrigin, getLocaleFromDomain, isLocaleExclusiveOnDomain } from "./localization/domainUtils.js";
82
82
  import { GenerateSitemapOptions, SitemapUrlEntry, generateSitemap, generateSitemapUrl } from "./localization/generateSitemap.js";
83
83
  import { getBrowserLocale } from "./localization/getBrowserLocale.js";
84
+ import { getCanonicalPath } from "./localization/getCanonicalPath.js";
84
85
  import { getHTMLTextDir } from "./localization/getHTMLTextDir.js";
85
86
  import { getLocale } from "./localization/getLocale.js";
86
87
  import { GetPrefixOptions, GetPrefixResult, getPrefix, isDeclaredLocale } from "./localization/getPrefix.js";
@@ -96,12 +97,13 @@ import { localeFlatMap, localeMap, localeRecord } from "./localization/localeMap
96
97
  import { localeResolver } from "./localization/localeResolver.js";
97
98
  import { ProxyMode, formatProxyEnabledMessage, isProxyStorageLocaleEnabled, resolveProxyMode } from "./localization/proxyMode.js";
98
99
  import { getPreloadLocale, resolveBrowserLocale } from "./localization/resolveBrowserLocale.js";
99
- import { LocalizedPathResult, getCanonicalPath, getInternalPath, getRewritePath, getRewriteRules, resolveLocalizedPath } from "./localization/rewriteUtils.js";
100
+ import { getRewriteRules } from "./localization/rewriteMatch.js";
101
+ import { LocalizedPathResult, getInternalPath, getRewritePath, resolveLocalizedPath } from "./localization/rewriteUtils.js";
100
102
  import { validatePrefix } from "./localization/validatePrefix.js";
101
103
  import "./localization/index.js";
102
104
  import { BlockQuoteNode, BoldTextNode, BreakLineNode, BreakThematicNode, CodeBlockNode, CodeFencedNode, CodeInlineNode, CompileOptions, ComponentOverrides, CustomComponentNode, ElementType, EscapedTextNode, FootnoteNode, FootnoteReferenceNode, GFMTaskNode, HTMLCommentNode, HTMLNode, HTMLSelfClosingNode, HTMLTag, HeadingNode, HeadingSetextNode, ImageNode, ItalicTextNode, LinkAngleBraceNode, LinkBareURLNode, LinkNode, MarkdownContext, MarkdownOptions, MarkdownRuntime, MarkedTextNode, NestedParser, NewlineNode, OrderedListNode, ParagraphNode, ParseState, Parser, ParserResult, ReferenceImageNode, ReferenceLinkNode, ReferenceNode, RenderRuleHook, Rule, RuleOutput, Rules, StrikethroughTextNode, TableNode, TableSeparatorNode, TextNode, UnorderedListNode } from "./markdown/types.js";
103
105
  import { MarkdownReferences, ParsedMarkdown, compile, compileWithOptions, createCompiler, parseMarkdown, renderMarkdownAst } from "./markdown/compiler.js";
104
- 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_R, DO_NOT_PROCESS_HTML_ELEMENTS, DURATION_DELAY_TRIGGER, 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_CHAR_CODE_R, HTML_COMMENT_R, HTML_CUSTOM_ATTR_R, HTML_LEFT_TRIM_AMOUNT_R, HTML_SELF_CLOSING_ELEMENT_R, INLINE_SKIP_R, INTERPOLATION_R, LINK_AUTOLINK_BARE_URL_R, LINK_AUTOLINK_R, LIST_LOOKBEHIND_R, LOOKAHEAD, ListType, 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, PriorityValue, REFERENCE_IMAGE_OR_LINK, REFERENCE_IMAGE_R, REFERENCE_LINK_R, RuleType, RuleTypeValue, 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.js";
106
+ 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, ListType, 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, PriorityValue, REFERENCE_IMAGE_OR_LINK, REFERENCE_IMAGE_R, REFERENCE_LINK_R, RuleType, RuleTypeValue, 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.js";
105
107
  import { parserFor } from "./markdown/parser.js";
106
108
  import { createRenderer, renderFor } from "./markdown/renderer.js";
107
109
  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.js";
@@ -121,4 +123,4 @@ import { CookieBuildAttributes, LocaleStorage, LocaleStorageClient, LocaleStorag
121
123
  import { YamlRecord, YamlValue, parseYaml } from "./utils/parseYaml.js";
122
124
  import { stringifyYaml } from "./utils/stringifyYaml.js";
123
125
  import "./utils/index.js";
124
- 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, type BlockQuoteNode, type BoldTextNode, type BreakLineNode, type BreakThematicNode, 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_R, CachedIntl, CachedIntl as Intl, type CodeBlockNode, type CodeFencedNode, type CodeInlineNode, type CompileOptions, type ComponentOverrides, ConditionCond, ConditionContent, ConditionContentStates, CookieBuildAttributes, type CustomComponentNode, DEFAULT_VARIANT_ID, DO_NOT_PROCESS_HTML_ELEMENTS, DURATION_DELAY_TRIGGER, DateTimePreset, DeepTransformContent, DotPath, DynamicDictionaryLoader, type ElementType, EnterFormat, EnumerationCond, EnumerationContent, EnumerationContentState, type EscapedTextNode, FENCE_DELIMITER_R, FOOTNOTE_R, FOOTNOTE_REFERENCE_R, FORMFEED_R, FRONT_MATTER_R, FileCond, type FileContent, type FileContentConstructor, type FootnoteNode, type FootnoteReferenceNode, type GFMTaskNode, GFM_TASK_R, Gender, GenderCond, GenderContent, GenderContentStates, type GenerateSitemapOptions, GetNestingResult, type GetPrefixOptions, type GetPrefixResult, HEADING_ATX_COMPLIANT_R, HEADING_R, HEADING_SETEXT_R, type HTMLCommentNode, HTMLContent, HTMLContentConstructor, type HTMLNode, type HTMLSelfClosingNode, type HTMLTag, HTMLTagsType, HTMLValidationIssue, HTMLValidationResult, HTML_BLOCK_ELEMENT_R, HTML_CHAR_CODE_R, HTML_COMMENT_R, HTML_CUSTOM_ATTR_R, HTML_LEFT_TRIM_AMOUNT_R, HTML_SELF_CLOSING_ELEMENT_R, HTML_TAGS, type HeadingNode, type HeadingSetextNode, IInterpreterPlugin, IInterpreterPluginState, INLINE_SKIP_R, INTERPOLATION_R, type ImageNode, InsertionCond, InsertionContent, InsertionContentConstructor, InterpolableNode, IntlConstructorName, IsAny, type ItalicTextNode, JsonValue, LINK_AUTOLINK_BARE_URL_R, LINK_AUTOLINK_R, LIST_LOOKBEHIND_R, LOOKAHEAD, type LinkAngleBraceNode, type LinkBareURLNode, type LinkNode, ListType, type LocaleDomainMap, LocaleStorage, LocaleStorageClient, LocaleStorageClientOptions, LocaleStorageOptions, LocaleStorageServer, LocaleStorageServerOptions, type LocalizedPathResult, MarkdownContent, MarkdownContentConstructor, type MarkdownContext, type MarkdownOptions, MarkdownReferences, type MarkdownRuntime, type HTMLValidationIssue as MarkdownValidationIssue, MarkdownValidationResult, type MarkedTextNode, MessageFormatDialect, MessageValues, NAMED_CODES_TO_UNICODE, NP_TABLE_R, NestedCond, NestedContent, NestedContentState, NestedFieldReferences, type NestedParser, type NewlineNode, NodeProps, ORDERED, ORDERED_LIST_BULLET, ORDERED_LIST_ITEM_PREFIX, ORDERED_LIST_ITEM_PREFIX_R, ORDERED_LIST_ITEM_R, ORDERED_LIST_R, type OrderedListNode, PARAGRAPH_R, PRELOADED_DYNAMIC_KEY, type ParagraphNode, type ParseState, ParsedMarkdown, type Parser, type ParserResult, PlainDynamicLoaderMap, Plugins, PluralCategory, PluralCond, PluralContent, PluralContentState, PortableObject, PreloadableDynamicLoaderMap, PreloadedDynamicEntry, Priority, PriorityValue, type ProxyMode, QUALIFIER_DYNAMIC_TYPES_KEY, QUALIFIER_ORDER, QualifiedDynamicLoaderMap, QualifiedDynamicLoaderTree, REFERENCE_IMAGE_OR_LINK, REFERENCE_IMAGE_R, REFERENCE_LINK_R, type ReferenceImageNode, type ReferenceLinkNode, type ReferenceNode, type RenderRuleHook, ResolvedDynamicLoader, type Rule, type RuleOutput, RuleType, RuleTypeValue, type Rules, SHORTCODE_R, SHOULD_RENDER_AS_BLOCK_R, SelectCond, SelectContent, SelectContentStates, SelectSelector, type SitemapUrlEntry, type StrikethroughTextNode, 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, type TableNode, type TableSeparatorNode, TaggedMessageToken, type TextNode, TranslationCond, TranslationContent, UNESCAPE_R, UNORDERED, UNORDERED_LIST_BULLET, UNORDERED_LIST_ITEM_PREFIX, UNORDERED_LIST_ITEM_PREFIX_R, UNORDERED_LIST_ITEM_R, UNORDERED_LIST_R, UnionKeys, type UnorderedListNode, VOID_HTML_ELEMENTS, ValidDotPathsFor, ValueAtKey, WrappedIntl, YamlRecord, YamlValue, 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, type file, type fileContent, 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 };
126
+ 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, type BlockQuoteNode, type BoldTextNode, type BreakLineNode, type BreakThematicNode, 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, type CodeBlockNode, type CodeFencedNode, type CodeInlineNode, type CompileOptions, type ComponentOverrides, ConditionCond, ConditionContent, ConditionContentStates, CookieBuildAttributes, type CustomComponentNode, DEFAULT_VARIANT_ID, DO_NOT_PROCESS_HTML_ELEMENTS, DateTimePreset, DeepTransformContent, DotPath, DynamicDictionaryLoader, type ElementType, EnterFormat, EnumerationCond, EnumerationContent, EnumerationContentState, type EscapedTextNode, FENCE_DELIMITER_R, FOOTNOTE_R, FOOTNOTE_REFERENCE_R, FORMFEED_R, FRONT_MATTER_R, FileCond, type FileContent, type FileContentConstructor, type FootnoteNode, type FootnoteReferenceNode, type GFMTaskNode, GFM_TASK_R, Gender, GenderCond, GenderContent, GenderContentStates, type GenerateSitemapOptions, GetNestingResult, type GetPrefixOptions, type GetPrefixResult, HEADING_ATX_COMPLIANT_R, HEADING_R, HEADING_SETEXT_R, type HTMLCommentNode, HTMLContent, HTMLContentConstructor, type HTMLNode, type HTMLSelfClosingNode, type HTMLTag, HTMLTagsType, HTMLValidationIssue, HTMLValidationResult, 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, type HeadingNode, type HeadingSetextNode, IInterpreterPlugin, IInterpreterPluginState, INLINE_SKIP_R, INTERPOLATION_R, type ImageNode, InsertionCond, InsertionContent, InsertionContentConstructor, InterpolableNode, IntlConstructorName, IsAny, type ItalicTextNode, JsonValue, LINK_AUTOLINK_BARE_URL_R, LINK_AUTOLINK_R, LOOKAHEAD, type LinkAngleBraceNode, type LinkBareURLNode, type LinkNode, ListType, type LocaleDomainMap, LocaleStorage, LocaleStorageClient, LocaleStorageClientOptions, LocaleStorageOptions, LocaleStorageServer, LocaleStorageServerOptions, type LocalizedPathResult, MarkdownContent, MarkdownContentConstructor, type MarkdownContext, type MarkdownOptions, MarkdownReferences, type MarkdownRuntime, type HTMLValidationIssue as MarkdownValidationIssue, MarkdownValidationResult, type MarkedTextNode, MessageFormatDialect, MessageValues, NAMED_CODES_TO_UNICODE, NORMALIZE_WHITESPACE_R, NP_TABLE_R, NestedCond, NestedContent, NestedContentState, NestedFieldReferences, type NestedParser, type NewlineNode, NodeProps, ORDERED, ORDERED_LIST_BULLET, ORDERED_LIST_ITEM_PREFIX, ORDERED_LIST_ITEM_PREFIX_R, ORDERED_LIST_ITEM_R, ORDERED_LIST_R, type OrderedListNode, PARAGRAPH_R, PRELOADED_DYNAMIC_KEY, type ParagraphNode, type ParseState, ParsedMarkdown, type Parser, type ParserResult, PlainDynamicLoaderMap, Plugins, PluralCategory, PluralCond, PluralContent, PluralContentState, PortableObject, PreloadableDynamicLoaderMap, PreloadedDynamicEntry, Priority, PriorityValue, type ProxyMode, QUALIFIER_DYNAMIC_TYPES_KEY, QUALIFIER_ORDER, QualifiedDynamicLoaderMap, QualifiedDynamicLoaderTree, REFERENCE_IMAGE_OR_LINK, REFERENCE_IMAGE_R, REFERENCE_LINK_R, type ReferenceImageNode, type ReferenceLinkNode, type ReferenceNode, type RenderRuleHook, ResolvedDynamicLoader, type Rule, type RuleOutput, RuleType, RuleTypeValue, type Rules, SHORTCODE_R, SHOULD_RENDER_AS_BLOCK_R, SelectCond, SelectContent, SelectContentStates, SelectSelector, type SitemapUrlEntry, type StrikethroughTextNode, 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, type TableNode, type TableSeparatorNode, TaggedMessageToken, type TextNode, TranslationCond, TranslationContent, UNESCAPE_R, UNORDERED, UNORDERED_LIST_BULLET, UNORDERED_LIST_ITEM_PREFIX, UNORDERED_LIST_ITEM_PREFIX_R, UNORDERED_LIST_ITEM_R, UNORDERED_LIST_R, UnionKeys, type UnorderedListNode, VOID_HTML_ELEMENTS, ValidDotPathsFor, ValueAtKey, WrappedIntl, YamlRecord, YamlValue, 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, type file, type fileContent, 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 };
@@ -0,0 +1,19 @@
1
+ import { LocalesValues } from "@intlayer/types/module_augmentation";
2
+ import { RewriteRules, RoutingConfig } from "@intlayer/types/config";
3
+ //#region src/localization/getCanonicalPath.d.ts
4
+ /**
5
+ * Given a localized URL (e.g., "/produits/123"), finds the canonical internal path (e.g., "/products/123").
6
+ * If locale is provided, only check for that locale. Otherwise, check for all locales.
7
+ *
8
+ * The lookup is encoding-insensitive: a percent-encoded path matches a pattern
9
+ * written with literal (non-ASCII) characters.
10
+ *
11
+ * @param localizedPath - The path as seen in the browser, without locale prefix.
12
+ * @param locale - Restricts the lookup to that locale's rules. Defaults to every locale.
13
+ * @param rewriteRules - Raw `routing.rewrite` configuration or already-normalized
14
+ * rules. Defaults to the project configuration's `routing.rewrite`.
15
+ */
16
+ declare const getCanonicalPath: <const Path extends string, const Locale extends LocalesValues = LocalesValues>(localizedPath: Path, locale?: Locale, rewriteRules?: RoutingConfig['rewrite'] | RewriteRules) => string;
17
+ //#endregion
18
+ export { getCanonicalPath };
19
+ //# sourceMappingURL=getCanonicalPath.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getCanonicalPath.d.ts","names":[],"sources":["../../../src/localization/getCanonicalPath.ts"],"mappings":";;;;;;;;;;;;;;;cAkCa,yBACL,2BACA,eAAe,gBAAgB,eAAa,eAEnC,MAAI,SACV,QAAM,eACA,2BAA2B"}
@@ -1 +1 @@
1
- {"version":3,"file":"getLocalizedPath.d.ts","names":[],"sources":["../../../src/localization/getLocalizedPath.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA8Da,yBACL,oCACA,sBAAsB,gBAAgB,uBAAqB,eAElD,eAAa,SACpB,eAAa,UACZ,mBACR,kBAAkB,eAAe"}
1
+ {"version":3,"file":"getLocalizedPath.d.ts","names":[],"sources":["../../../src/localization/getLocalizedPath.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA4Da,yBACL,oCACA,sBAAsB,gBAAgB,uBAAqB,eAElD,eAAa,SACpB,eAAa,UACZ,mBACR,kBAAkB,eAAe"}
@@ -2,6 +2,7 @@ import { comparePaths, normalizePath } from "./comparePaths.js";
2
2
  import { LocaleDomainMap, getDomainHostname, getDomainOrigin, getLocaleFromDomain, isLocaleExclusiveOnDomain } from "./domainUtils.js";
3
3
  import { GenerateSitemapOptions, SitemapUrlEntry, generateSitemap, generateSitemapUrl } from "./generateSitemap.js";
4
4
  import { getBrowserLocale } from "./getBrowserLocale.js";
5
+ import { getCanonicalPath } from "./getCanonicalPath.js";
5
6
  import { getHTMLTextDir } from "./getHTMLTextDir.js";
6
7
  import { getLocale } from "./getLocale.js";
7
8
  import { GetPrefixOptions, GetPrefixResult, getPrefix, isDeclaredLocale } from "./getPrefix.js";
@@ -17,6 +18,7 @@ import { localeFlatMap, localeMap, localeRecord } from "./localeMapper.js";
17
18
  import { localeResolver } from "./localeResolver.js";
18
19
  import { ProxyMode, formatProxyEnabledMessage, isProxyStorageLocaleEnabled, resolveProxyMode } from "./proxyMode.js";
19
20
  import { getPreloadLocale, resolveBrowserLocale } from "./resolveBrowserLocale.js";
20
- import { LocalizedPathResult, getCanonicalPath, getInternalPath, getRewritePath, getRewriteRules, resolveLocalizedPath } from "./rewriteUtils.js";
21
+ import { getRewriteRules } from "./rewriteMatch.js";
22
+ import { LocalizedPathResult, getInternalPath, getRewritePath, resolveLocalizedPath } from "./rewriteUtils.js";
21
23
  import { validatePrefix } from "./validatePrefix.js";
22
24
  export { type GenerateSitemapOptions, type GetPrefixOptions, type GetPrefixResult, type LocaleDomainMap, type LocalizedPathResult, type ProxyMode, type SitemapUrlEntry, comparePaths, formatProxyEnabledMessage, generateSitemap, generateSitemapUrl, getBrowserLocale, getCanonicalPath, getDomainHostname, getDomainOrigin, getHTMLTextDir, getInternalPath, getLocale, getLocaleFromDomain, getLocaleFromPath, getLocaleLang, getLocaleName, getLocalizedPath, getLocalizedUrl, getMultilingualUrls, getPathWithoutLocale, getPrefix, getPreloadLocale, getRewritePath, getRewriteRules, isDeclaredLocale, isLocaleExclusiveOnDomain, isProxyStorageLocaleEnabled, localeDetector, localeFlatMap, localeMap, localeRecord, localeResolver, normalizePath, resolveBrowserLocale, resolveLocalizedPath, resolveProxyMode, validatePrefix };
@@ -0,0 +1,20 @@
1
+ import { RewriteObject, RewriteRules, RoutingConfig } from "@intlayer/types/config";
2
+ //#region src/localization/rewriteMatch.d.ts
3
+ /**
4
+ * Normalizes legacy Record format or extracts specialized rules from RewriteObject.
5
+ *
6
+ * Idempotent: already-normalized rules are handed back untouched, so a caller
7
+ * holding resolved rules can pass them on without re-normalizing.
8
+ */
9
+ declare const getRewriteRules: <const Context extends keyof RewriteObject = 'url'>(rewrite: RoutingConfig['rewrite'] | RewriteRules, context?: Context) => RewriteRules | undefined;
10
+ /**
11
+ * Replaces route parameters in a path with provided values.
12
+ */
13
+ declare const fillPath: (pattern: string, params: string[]) => string;
14
+ /**
15
+ * Extract values from a URL based on a pattern.
16
+ */
17
+ declare const extractParams: (url: string, pattern: string) => string[] | null;
18
+ //#endregion
19
+ export { extractParams, fillPath, getRewriteRules };
20
+ //# sourceMappingURL=rewriteMatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rewriteMatch.d.ts","names":[],"sources":["../../../src/localization/rewriteMatch.ts"],"mappings":";;;;;;;;cAYa,wBACL,sBAAsB,uBAAqB,SAExC,2BAA2B,cAAY,UACvC,YACR;;;;cA+DU,WAAQ,iBAAmB;;;;cAa3B,gBAAa,aACb"}