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