@intlayer/core 8.12.4 → 9.0.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/deepTransformPlugins/getEditedContent.cjs +96 -0
- package/dist/cjs/deepTransformPlugins/getEditedContent.cjs.map +1 -0
- package/dist/cjs/deepTransformPlugins/index.cjs +3 -0
- package/dist/cjs/dictionaryManipulator/index.cjs +18 -0
- package/dist/cjs/dictionaryManipulator/mergeDictionaries.cjs +4 -1
- package/dist/cjs/dictionaryManipulator/mergeDictionaries.cjs.map +1 -1
- package/dist/cjs/dictionaryManipulator/mergeQualifiedDictionaries.cjs +70 -0
- package/dist/cjs/dictionaryManipulator/mergeQualifiedDictionaries.cjs.map +1 -0
- package/dist/cjs/dictionaryManipulator/qualifiedDictionary.cjs +302 -0
- package/dist/cjs/dictionaryManipulator/qualifiedDictionary.cjs.map +1 -0
- package/dist/cjs/index.cjs +26 -0
- package/dist/cjs/interpreter/getCollection.cjs +25 -0
- package/dist/cjs/interpreter/getCollection.cjs.map +1 -0
- package/dist/cjs/interpreter/getDictionary.cjs +26 -11
- package/dist/cjs/interpreter/getDictionary.cjs.map +1 -1
- package/dist/cjs/interpreter/getIntlayer.cjs +16 -3
- package/dist/cjs/interpreter/getIntlayer.cjs.map +1 -1
- package/dist/cjs/interpreter/getVariant.cjs +30 -0
- package/dist/cjs/interpreter/getVariant.cjs.map +1 -0
- package/dist/cjs/localization/getBrowserLocale.cjs +1 -1
- package/dist/cjs/localization/getBrowserLocale.cjs.map +1 -1
- package/dist/cjs/messageFormat/ICU.cjs +32 -2
- package/dist/cjs/messageFormat/ICU.cjs.map +1 -1
- package/dist/cjs/messageFormat/i18next.cjs +1 -1
- package/dist/cjs/messageFormat/i18next.cjs.map +1 -1
- package/dist/cjs/messageFormat/index.cjs +5 -0
- package/dist/cjs/messageFormat/resolveMessage.cjs +183 -0
- package/dist/cjs/messageFormat/resolveMessage.cjs.map +1 -0
- package/dist/cjs/messageFormat/vue-i18n.cjs +8 -9
- package/dist/cjs/messageFormat/vue-i18n.cjs.map +1 -1
- package/dist/cjs/transpiler/collection/collection.cjs +32 -0
- package/dist/cjs/transpiler/collection/collection.cjs.map +1 -0
- package/dist/cjs/transpiler/collection/index.cjs +4 -0
- package/dist/cjs/transpiler/markdown/getMarkdownMetadata.cjs +2 -3
- package/dist/cjs/transpiler/markdown/getMarkdownMetadata.cjs.map +1 -1
- package/dist/cjs/transpiler/variant/index.cjs +4 -0
- package/dist/cjs/transpiler/variant/variant.cjs +35 -0
- package/dist/cjs/transpiler/variant/variant.cjs.map +1 -0
- package/dist/cjs/utils/parseYaml.cjs +37 -17
- package/dist/cjs/utils/parseYaml.cjs.map +1 -1
- package/dist/esm/deepTransformPlugins/getEditedContent.mjs +92 -0
- package/dist/esm/deepTransformPlugins/getEditedContent.mjs.map +1 -0
- package/dist/esm/deepTransformPlugins/index.mjs +2 -1
- package/dist/esm/dictionaryManipulator/index.mjs +3 -1
- package/dist/esm/dictionaryManipulator/mergeDictionaries.mjs +4 -1
- package/dist/esm/dictionaryManipulator/mergeDictionaries.mjs.map +1 -1
- package/dist/esm/dictionaryManipulator/mergeQualifiedDictionaries.mjs +68 -0
- package/dist/esm/dictionaryManipulator/mergeQualifiedDictionaries.mjs.map +1 -0
- package/dist/esm/dictionaryManipulator/qualifiedDictionary.mjs +286 -0
- package/dist/esm/dictionaryManipulator/qualifiedDictionary.mjs.map +1 -0
- package/dist/esm/index.mjs +5 -1
- package/dist/esm/interpreter/getCollection.mjs +23 -0
- package/dist/esm/interpreter/getCollection.mjs.map +1 -0
- package/dist/esm/interpreter/getDictionary.mjs +26 -11
- package/dist/esm/interpreter/getDictionary.mjs.map +1 -1
- package/dist/esm/interpreter/getIntlayer.mjs +16 -3
- package/dist/esm/interpreter/getIntlayer.mjs.map +1 -1
- package/dist/esm/interpreter/getVariant.mjs +28 -0
- package/dist/esm/interpreter/getVariant.mjs.map +1 -0
- package/dist/esm/localization/getBrowserLocale.mjs +1 -1
- package/dist/esm/localization/getBrowserLocale.mjs.map +1 -1
- package/dist/esm/messageFormat/ICU.mjs +32 -2
- package/dist/esm/messageFormat/ICU.mjs.map +1 -1
- package/dist/esm/messageFormat/i18next.mjs +1 -1
- package/dist/esm/messageFormat/i18next.mjs.map +1 -1
- package/dist/esm/messageFormat/index.mjs +2 -1
- package/dist/esm/messageFormat/resolveMessage.mjs +177 -0
- package/dist/esm/messageFormat/resolveMessage.mjs.map +1 -0
- package/dist/esm/messageFormat/vue-i18n.mjs +8 -9
- package/dist/esm/messageFormat/vue-i18n.mjs.map +1 -1
- package/dist/esm/transpiler/collection/collection.mjs +30 -0
- package/dist/esm/transpiler/collection/collection.mjs.map +1 -0
- package/dist/esm/transpiler/collection/index.mjs +3 -0
- package/dist/esm/transpiler/markdown/getMarkdownMetadata.mjs +2 -3
- package/dist/esm/transpiler/markdown/getMarkdownMetadata.mjs.map +1 -1
- package/dist/esm/transpiler/variant/index.mjs +3 -0
- package/dist/esm/transpiler/variant/variant.mjs +33 -0
- package/dist/esm/transpiler/variant/variant.mjs.map +1 -0
- package/dist/esm/utils/parseYaml.mjs +37 -17
- package/dist/esm/utils/parseYaml.mjs.map +1 -1
- package/dist/types/@intlayer/core/dist/types/formatters/compact.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/formatters/currency.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/formatters/date.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/formatters/index.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/formatters/list.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/formatters/number.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/formatters/percentage.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/formatters/relativeTime.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/formatters/units.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/interpreter/getCondition.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/interpreter/getContent/deepTransform.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/interpreter/getContent/getContent.d.ts +2 -0
- package/dist/types/@intlayer/core/dist/types/interpreter/getContent/plugins.d.ts +4 -0
- package/dist/types/@intlayer/core/dist/types/interpreter/getDictionary.d.ts +2 -0
- package/dist/types/@intlayer/core/dist/types/interpreter/getEnumeration.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/interpreter/getIntlayer.d.ts +2 -0
- package/dist/types/@intlayer/core/dist/types/interpreter/getNesting.d.ts +2 -0
- package/dist/types/@intlayer/core/dist/types/interpreter/getPlural.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/interpreter/getTranslation.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/interpreter/getVariant.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/interpreter/index.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/intlayer/dist/types/index.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/localization/generateSitemap.d.ts +2 -0
- package/dist/types/@intlayer/core/dist/types/localization/getBrowserLocale.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/localization/getHTMLTextDir.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/localization/getLocale.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/localization/getLocaleFromPath.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/localization/getLocaleLang.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/localization/getLocaleName.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/localization/getLocalizedUrl.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/localization/getMultilingualUrls.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/localization/getPathWithoutLocale.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/localization/getPrefix.d.ts +3 -0
- package/dist/types/@intlayer/core/dist/types/localization/index.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/localization/localeDetector.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/localization/localeMapper.d.ts +2 -0
- package/dist/types/@intlayer/core/dist/types/localization/localeResolver.d.ts +2 -0
- package/dist/types/@intlayer/core/dist/types/localization/rewriteUtils.d.ts +3 -0
- package/dist/types/@intlayer/core/dist/types/localization/validatePrefix.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/markdown/index.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/transpiler/collection/collection.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/transpiler/condition/condition.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/transpiler/enumeration/enumeration.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/transpiler/file/file.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/transpiler/gender/gender.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/transpiler/html/html.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/transpiler/index.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/transpiler/insertion/insertion.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/transpiler/markdown/markdown.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/transpiler/nesting/nesting.d.ts +2 -0
- package/dist/types/@intlayer/core/dist/types/transpiler/plural/plural.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/transpiler/translation/translation.d.ts +2 -0
- package/dist/types/@intlayer/core/dist/types/transpiler/variant/variant.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/utils/index.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/utils/intl.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/utils/isSameKeyPath.d.ts +1 -0
- package/dist/types/@intlayer/core/dist/types/utils/localeStorage.d.ts +2 -0
- package/dist/types/deepTransformPlugins/getEditedContent.d.ts +33 -0
- package/dist/types/deepTransformPlugins/getEditedContent.d.ts.map +1 -0
- package/dist/types/deepTransformPlugins/getFilterMissingTranslationsContent.d.ts +6 -3
- package/dist/types/deepTransformPlugins/getFilterMissingTranslationsContent.d.ts.map +1 -1
- package/dist/types/deepTransformPlugins/getFilterTranslationsOnlyContent.d.ts +6 -3
- package/dist/types/deepTransformPlugins/getFilterTranslationsOnlyContent.d.ts.map +1 -1
- package/dist/types/deepTransformPlugins/getFilteredLocalesContent.d.ts +4 -1
- package/dist/types/deepTransformPlugins/index.d.ts +2 -1
- package/dist/types/dictionaryManipulator/index.d.ts +3 -1
- package/dist/types/dictionaryManipulator/mergeQualifiedDictionaries.d.ts +22 -0
- package/dist/types/dictionaryManipulator/mergeQualifiedDictionaries.d.ts.map +1 -0
- package/dist/types/dictionaryManipulator/qualifiedDictionary.d.ts +176 -0
- package/dist/types/dictionaryManipulator/qualifiedDictionary.d.ts.map +1 -0
- package/dist/types/index.d.ts +6 -2
- package/dist/types/interpreter/getCollection.d.ts +19 -0
- package/dist/types/interpreter/getCollection.d.ts.map +1 -0
- package/dist/types/interpreter/getDictionary.d.ts +13 -7
- package/dist/types/interpreter/getDictionary.d.ts.map +1 -1
- package/dist/types/interpreter/getIntlayer.d.ts +13 -2
- package/dist/types/interpreter/getIntlayer.d.ts.map +1 -1
- package/dist/types/interpreter/getPlural.d.ts +1 -1
- package/dist/types/interpreter/getVariant.d.ts +26 -0
- package/dist/types/interpreter/getVariant.d.ts.map +1 -0
- package/dist/types/intlayer/dist/types/index.d.ts +4 -0
- package/dist/types/messageFormat/ICU.d.ts.map +1 -1
- package/dist/types/messageFormat/i18next.d.ts.map +1 -1
- package/dist/types/messageFormat/index.d.ts +2 -1
- package/dist/types/messageFormat/resolveMessage.d.ts +72 -0
- package/dist/types/messageFormat/resolveMessage.d.ts.map +1 -0
- package/dist/types/messageFormat/vue-i18n.d.ts.map +1 -1
- package/dist/types/transpiler/collection/collection.d.ts +34 -0
- package/dist/types/transpiler/collection/collection.d.ts.map +1 -0
- package/dist/types/transpiler/collection/index.d.ts +2 -0
- package/dist/types/transpiler/variant/index.d.ts +2 -0
- package/dist/types/transpiler/variant/variant.d.ts +43 -0
- package/dist/types/transpiler/variant/variant.d.ts.map +1 -0
- package/dist/types/utils/index.d.ts +2 -2
- package/dist/types/utils/parseYaml.d.ts +12 -2
- package/dist/types/utils/parseYaml.d.ts.map +1 -1
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vue-i18n.cjs","names":["insert","plural","enu","NodeTypes","deepTransformNode"],"sources":["../../../src/messageFormat/vue-i18n.ts"],"sourcesContent":["import type { Dictionary } from '@intlayer/types/dictionary';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport { deepTransformNode } from '../interpreter';\nimport { enu, insert, plural } from '../transpiler';\nimport type { JsonValue } from './ICU';\n\n// Types for our AST\ntype VueI18nNode =\n | string\n | {\n type: 'argument';\n name: string;\n };\n\nconst parseVueI18nPart = (text: string): VueI18nNode[] => {\n let index = 0;\n const nodes: VueI18nNode[] = [];\n let currentText = '';\n\n while (index < text.length) {\n const char = text[index];\n\n if (char === '{') {\n if (currentText) {\n nodes.push(currentText);\n currentText = '';\n }\n index++; // skip {\n let name = '';\n while (index < text.length && text[index] !== '}') {\n name += text[index];\n index++;\n }\n if (index < text.length) {\n index++; // skip }\n }\n nodes.push({ type: 'argument', name: name.trim() });\n } else {\n currentText += char;\n index++;\n }\n }\n\n if (currentText) {\n nodes.push(currentText);\n }\n\n return nodes;\n};\n\nconst parseVueI18n = (text: string): VueI18nNode[][] => {\n // Split by | but handle escaped \\|\n const parts: string[] = [];\n let currentPart = '';\n let index = 0;\n\n while (index < text.length) {\n const char = text[index];\n if (char === '\\\\' && index + 1 < text.length && text[index + 1] === '|') {\n currentPart += '|';\n index += 2;\n } else if (char === '|') {\n parts.push(currentPart.trim()); // Trim to remove surrounding spaces\n currentPart = '';\n index++;\n } else {\n currentPart += char;\n index++;\n }\n }\n parts.push(currentPart.trim()); // Trim last part too\n\n return parts.map(parseVueI18nPart);\n};\n\nconst vueI18nPartToIntlayer = (nodes: VueI18nNode[]): any => {\n if (nodes.length === 0) return '';\n if (nodes.length === 1 && typeof nodes[0] === 'string') return nodes[0];\n\n let str = '';\n for (const node of nodes) {\n if (typeof node === 'string') {\n str += node;\n } else {\n str += `{{${node.name}}}`;\n }\n }\n return insert(str);\n};\n\nconst vueI18nNodesToIntlayer = (parts: VueI18nNode[][]): any => {\n if (parts.length === 1) {\n return vueI18nPartToIntlayer(parts[0]);\n }\n\n // Handle pluralization (choice)\n const options: Record<string, any> = {};\n const varName = 'count'; // Default variable for vue-i18n choices\n\n if (parts.length === 2) {\n // 2 choices: 1 | other\n return plural({\n one: vueI18nPartToIntlayer(parts[0]),\n other: vueI18nPartToIntlayer(parts[1]),\n });\n }\n\n if (parts.length === 3) {\n // 3 choices: 0 | 1 | other\n return plural({\n zero: vueI18nPartToIntlayer(parts[0]),\n one: vueI18nPartToIntlayer(parts[1]),\n other: vueI18nPartToIntlayer(parts[2]),\n });\n }\n\n // > 3 choices: 0 | 1 | 2 | ... | other\n parts.forEach((part, index) => {\n if (index === parts.length - 1) {\n options.fallback = vueI18nPartToIntlayer(part);\n } else {\n options[index.toString()] = vueI18nPartToIntlayer(part);\n }\n });\n\n // Preserve variable name\n options.__intlayer_vue_i18n_var = varName;\n\n return enu(options);\n};\n\nconst vueI18nToIntlayerPlugin = {\n canHandle: (node: any) =>\n typeof node === 'string' && (node.includes('{') || node.includes('|')),\n transform: (node: any) => {\n try {\n const ast = parseVueI18n(node);\n return vueI18nNodesToIntlayer(ast);\n } catch {\n return node;\n }\n },\n};\n\nconst intlayerToVueI18nPlugin = {\n canHandle: (node: any) => {\n if (typeof node === 'string') return true;\n\n if (\n node &&\n typeof node === 'object' &&\n (node.nodeType === NodeTypes.INSERTION ||\n node.nodeType === NodeTypes.ENUMERATION ||\n node.nodeType === NodeTypes.PLURAL ||\n node.nodeType === NodeTypes.GENDER ||\n node.nodeType === 'composite')\n ) {\n return true;\n }\n\n if (Array.isArray(node)) {\n if (node.length === 0) return false;\n\n let hasNode = false;\n let hasPlainObjectOrArray = false;\n\n for (const item of node) {\n if (typeof item === 'string') {\n } else if (\n item &&\n typeof item === 'object' &&\n (item.nodeType === NodeTypes.INSERTION ||\n item.nodeType === NodeTypes.ENUMERATION ||\n item.nodeType === NodeTypes.GENDER ||\n item.nodeType === 'composite')\n ) {\n hasNode = true;\n } else {\n hasPlainObjectOrArray = true;\n }\n }\n\n // If it contains plain objects or nested arrays, it's a structural array\n if (hasPlainObjectOrArray) return false;\n // If it contains ONLY strings, it's a structural array, not a composite string\n if (!hasNode) return false;\n\n return true;\n }\n\n return false;\n },\n transform: (node: any, props: any, next: any) => {\n if (typeof node === 'string') {\n // replace {{...}} with {...} even in strings\n return node.replace(/\\{\\{([^}]+)\\}\\}/g, '{$1}');\n }\n\n if (node.nodeType === NodeTypes.INSERTION) {\n // {{name}} -> {name}\n return node[NodeTypes.INSERTION].replace(/\\{\\{([^}]+)\\}\\}/g, '{$1}');\n }\n\n if (node.nodeType === NodeTypes.PLURAL) {\n const options = node[NodeTypes.PLURAL];\n\n const transformedOptions: Record<string, string> = {};\n for (const [key, val] of Object.entries(options)) {\n const childVal = next(val, props);\n transformedOptions[key] =\n typeof childVal === 'string' ? childVal : JSON.stringify(childVal);\n }\n\n if (\n transformedOptions.zero &&\n transformedOptions.one &&\n transformedOptions.other\n ) {\n return `${transformedOptions.zero} | ${transformedOptions.one} | ${transformedOptions.other}`;\n }\n\n if (transformedOptions.one && transformedOptions.other) {\n return `${transformedOptions.one} | ${transformedOptions.other}`;\n }\n\n return transformedOptions.other || Object.values(transformedOptions)[0];\n }\n\n if (node.nodeType === NodeTypes.ENUMERATION) {\n const options = node[NodeTypes.ENUMERATION];\n\n const transformedOptions: Record<string, string> = {};\n for (const [key, val] of Object.entries(options)) {\n if (key === '__intlayer_vue_i18n_var') continue;\n const childVal = next(val, props);\n transformedOptions[key] =\n typeof childVal === 'string' ? childVal : JSON.stringify(childVal);\n }\n\n const keys = Object.keys(transformedOptions);\n\n if (keys.includes('0')) {\n const indices = keys.filter((key) => /^\\d+$/.test(key)).map(Number);\n const maxIndex = Math.max(...indices);\n\n const fallback =\n transformedOptions.fallback || transformedOptions.other;\n const resultParts = [];\n\n if (maxIndex <= 1 && !keys.includes('2')) {\n const zero = transformedOptions['0'] || '';\n const one = transformedOptions['1'] || '';\n return `${zero} | ${one} | ${fallback}`;\n }\n\n const limit = Math.max(1, maxIndex);\n\n for (let i = 0; i <= limit; i++) {\n const key = i.toString();\n if (transformedOptions[key]) {\n resultParts.push(transformedOptions[key]);\n } else {\n resultParts.push('');\n }\n }\n resultParts.push(fallback);\n return resultParts.join(' | ').replace(/ \\| {2}\\| /g, ' | | ');\n }\n\n if (\n keys.includes('1') &&\n (keys.includes('fallback') || keys.includes('other'))\n ) {\n return `${transformedOptions['1']} | ${transformedOptions.fallback || transformedOptions.other}`;\n }\n\n if (\n keys.length === 1 &&\n (keys.includes('fallback') || keys.includes('other'))\n ) {\n return transformedOptions.fallback || transformedOptions.other;\n }\n\n return (\n transformedOptions.fallback || Object.values(transformedOptions)[0]\n );\n }\n\n if (node.nodeType === NodeTypes.GENDER) {\n const options = node[NodeTypes.GENDER];\n const transformedOptions: Record<string, any> = {};\n\n for (const [key, val] of Object.entries(options)) {\n let newKey = key;\n if (key === 'fallback') newKey = 'other';\n\n const childVal = next(val, props);\n transformedOptions[newKey] = childVal;\n }\n return transformedOptions;\n }\n\n if (\n Array.isArray(node) ||\n (node.nodeType === 'composite' && Array.isArray(node.composite))\n ) {\n const arr = Array.isArray(node) ? node : node.composite;\n const items = arr.map((item: any) => next(item, props));\n return items.join('');\n }\n\n return next(node, props);\n },\n};\n\nexport const intlayerToVueI18nFormatter = (\n message: Dictionary['content']\n): JsonValue => {\n return deepTransformNode(message, {\n dictionaryKey: 'vue-i18n',\n keyPath: [],\n plugins: [{ id: 'vue-i18n', ...intlayerToVueI18nPlugin }],\n });\n};\n\nexport const vueI18nToIntlayerFormatter = (\n message: JsonValue\n): Dictionary['content'] => {\n return deepTransformNode(message, {\n dictionaryKey: 'vue-i18n',\n keyPath: [],\n plugins: [{ id: 'vue-i18n', ...vueI18nToIntlayerPlugin }],\n });\n};\n"],"mappings":";;;;;;;;;;AAcA,MAAM,oBAAoB,SAAgC;CACxD,IAAI,QAAQ;CACZ,MAAM,QAAuB,EAAE;CAC/B,IAAI,cAAc;AAElB,QAAO,QAAQ,KAAK,QAAQ;EAC1B,MAAM,OAAO,KAAK;AAElB,MAAI,SAAS,KAAK;AAChB,OAAI,aAAa;AACf,UAAM,KAAK,YAAY;AACvB,kBAAc;;AAEhB;GACA,IAAI,OAAO;AACX,UAAO,QAAQ,KAAK,UAAU,KAAK,WAAW,KAAK;AACjD,YAAQ,KAAK;AACb;;AAEF,OAAI,QAAQ,KAAK,OACf;AAEF,SAAM,KAAK;IAAE,MAAM;IAAY,MAAM,KAAK,MAAM;IAAE,CAAC;SAC9C;AACL,kBAAe;AACf;;;AAIJ,KAAI,YACF,OAAM,KAAK,YAAY;AAGzB,QAAO;;AAGT,MAAM,gBAAgB,SAAkC;CAEtD,MAAM,QAAkB,EAAE;CAC1B,IAAI,cAAc;CAClB,IAAI,QAAQ;AAEZ,QAAO,QAAQ,KAAK,QAAQ;EAC1B,MAAM,OAAO,KAAK;AAClB,MAAI,SAAS,QAAQ,QAAQ,IAAI,KAAK,UAAU,KAAK,QAAQ,OAAO,KAAK;AACvE,kBAAe;AACf,YAAS;aACA,SAAS,KAAK;AACvB,SAAM,KAAK,YAAY,MAAM,CAAC;AAC9B,iBAAc;AACd;SACK;AACL,kBAAe;AACf;;;AAGJ,OAAM,KAAK,YAAY,MAAM,CAAC;AAE9B,QAAO,MAAM,IAAI,iBAAiB;;AAGpC,MAAM,yBAAyB,UAA8B;AAC3D,KAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,KAAI,MAAM,WAAW,KAAK,OAAO,MAAM,OAAO,SAAU,QAAO,MAAM;CAErE,IAAI,MAAM;AACV,MAAK,MAAM,QAAQ,MACjB,KAAI,OAAO,SAAS,SAClB,QAAO;KAEP,QAAO,KAAK,KAAK,KAAK;AAG1B,QAAOA,8CAAO,IAAI;;AAGpB,MAAM,0BAA0B,UAAgC;AAC9D,KAAI,MAAM,WAAW,EACnB,QAAO,sBAAsB,MAAM,GAAG;CAIxC,MAAM,UAA+B,EAAE;CACvC,MAAM,UAAU;AAEhB,KAAI,MAAM,WAAW,EAEnB,QAAOC,wCAAO;EACZ,KAAK,sBAAsB,MAAM,GAAG;EACpC,OAAO,sBAAsB,MAAM,GAAG;EACvC,CAAC;AAGJ,KAAI,MAAM,WAAW,EAEnB,QAAOA,wCAAO;EACZ,MAAM,sBAAsB,MAAM,GAAG;EACrC,KAAK,sBAAsB,MAAM,GAAG;EACpC,OAAO,sBAAsB,MAAM,GAAG;EACvC,CAAC;AAIJ,OAAM,SAAS,MAAM,UAAU;AAC7B,MAAI,UAAU,MAAM,SAAS,EAC3B,SAAQ,WAAW,sBAAsB,KAAK;MAE9C,SAAQ,MAAM,UAAU,IAAI,sBAAsB,KAAK;GAEzD;AAGF,SAAQ,0BAA0B;AAElC,QAAOC,+CAAI,QAAQ;;AAGrB,MAAM,0BAA0B;CAC9B,YAAY,SACV,OAAO,SAAS,aAAa,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,IAAI;CACvE,YAAY,SAAc;AACxB,MAAI;AAEF,UAAO,uBADK,aAAa,KACQ,CAAC;UAC5B;AACN,UAAO;;;CAGZ;AAED,MAAM,0BAA0B;CAC9B,YAAY,SAAc;AACxB,MAAI,OAAO,SAAS,SAAU,QAAO;AAErC,MACE,QACA,OAAO,SAAS,aACf,KAAK,aAAaC,yBAAU,aAC3B,KAAK,aAAaA,yBAAU,eAC5B,KAAK,aAAaA,yBAAU,UAC5B,KAAK,aAAaA,yBAAU,UAC5B,KAAK,aAAa,aAEpB,QAAO;AAGT,MAAI,MAAM,QAAQ,KAAK,EAAE;AACvB,OAAI,KAAK,WAAW,EAAG,QAAO;GAE9B,IAAI,UAAU;GACd,IAAI,wBAAwB;AAE5B,QAAK,MAAM,QAAQ,KACjB,KAAI,OAAO,SAAS,UAAU,YAE5B,QACA,OAAO,SAAS,aACf,KAAK,aAAaA,yBAAU,aAC3B,KAAK,aAAaA,yBAAU,eAC5B,KAAK,aAAaA,yBAAU,UAC5B,KAAK,aAAa,aAEpB,WAAU;OAEV,yBAAwB;AAK5B,OAAI,sBAAuB,QAAO;AAElC,OAAI,CAAC,QAAS,QAAO;AAErB,UAAO;;AAGT,SAAO;;CAET,YAAY,MAAW,OAAY,SAAc;AAC/C,MAAI,OAAO,SAAS,SAElB,QAAO,KAAK,QAAQ,oBAAoB,OAAO;AAGjD,MAAI,KAAK,aAAaA,yBAAU,UAE9B,QAAO,KAAKA,yBAAU,WAAW,QAAQ,oBAAoB,OAAO;AAGtE,MAAI,KAAK,aAAaA,yBAAU,QAAQ;GACtC,MAAM,UAAU,KAAKA,yBAAU;GAE/B,MAAM,qBAA6C,EAAE;AACrD,QAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,QAAQ,EAAE;IAChD,MAAM,WAAW,KAAK,KAAK,MAAM;AACjC,uBAAmB,OACjB,OAAO,aAAa,WAAW,WAAW,KAAK,UAAU,SAAS;;AAGtE,OACE,mBAAmB,QACnB,mBAAmB,OACnB,mBAAmB,MAEnB,QAAO,GAAG,mBAAmB,KAAK,KAAK,mBAAmB,IAAI,KAAK,mBAAmB;AAGxF,OAAI,mBAAmB,OAAO,mBAAmB,MAC/C,QAAO,GAAG,mBAAmB,IAAI,KAAK,mBAAmB;AAG3D,UAAO,mBAAmB,SAAS,OAAO,OAAO,mBAAmB,CAAC;;AAGvE,MAAI,KAAK,aAAaA,yBAAU,aAAa;GAC3C,MAAM,UAAU,KAAKA,yBAAU;GAE/B,MAAM,qBAA6C,EAAE;AACrD,QAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,QAAQ,EAAE;AAChD,QAAI,QAAQ,0BAA2B;IACvC,MAAM,WAAW,KAAK,KAAK,MAAM;AACjC,uBAAmB,OACjB,OAAO,aAAa,WAAW,WAAW,KAAK,UAAU,SAAS;;GAGtE,MAAM,OAAO,OAAO,KAAK,mBAAmB;AAE5C,OAAI,KAAK,SAAS,IAAI,EAAE;IACtB,MAAM,UAAU,KAAK,QAAQ,QAAQ,QAAQ,KAAK,IAAI,CAAC,CAAC,IAAI,OAAO;IACnE,MAAM,WAAW,KAAK,IAAI,GAAG,QAAQ;IAErC,MAAM,WACJ,mBAAmB,YAAY,mBAAmB;IACpD,MAAM,cAAc,EAAE;AAEtB,QAAI,YAAY,KAAK,CAAC,KAAK,SAAS,IAAI,CAGtC,QAAO,GAFM,mBAAmB,QAAQ,GAEzB,KADH,mBAAmB,QAAQ,GACf,KAAK;IAG/B,MAAM,QAAQ,KAAK,IAAI,GAAG,SAAS;AAEnC,SAAK,IAAI,IAAI,GAAG,KAAK,OAAO,KAAK;KAC/B,MAAM,MAAM,EAAE,UAAU;AACxB,SAAI,mBAAmB,KACrB,aAAY,KAAK,mBAAmB,KAAK;SAEzC,aAAY,KAAK,GAAG;;AAGxB,gBAAY,KAAK,SAAS;AAC1B,WAAO,YAAY,KAAK,MAAM,CAAC,QAAQ,eAAe,QAAQ;;AAGhE,OACE,KAAK,SAAS,IAAI,KACjB,KAAK,SAAS,WAAW,IAAI,KAAK,SAAS,QAAQ,EAEpD,QAAO,GAAG,mBAAmB,KAAK,KAAK,mBAAmB,YAAY,mBAAmB;AAG3F,OACE,KAAK,WAAW,MACf,KAAK,SAAS,WAAW,IAAI,KAAK,SAAS,QAAQ,EAEpD,QAAO,mBAAmB,YAAY,mBAAmB;AAG3D,UACE,mBAAmB,YAAY,OAAO,OAAO,mBAAmB,CAAC;;AAIrE,MAAI,KAAK,aAAaA,yBAAU,QAAQ;GACtC,MAAM,UAAU,KAAKA,yBAAU;GAC/B,MAAM,qBAA0C,EAAE;AAElD,QAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,QAAQ,EAAE;IAChD,IAAI,SAAS;AACb,QAAI,QAAQ,WAAY,UAAS;AAGjC,uBAAmB,UADF,KAAK,KAAK,MACU;;AAEvC,UAAO;;AAGT,MACE,MAAM,QAAQ,KAAK,IAClB,KAAK,aAAa,eAAe,MAAM,QAAQ,KAAK,UAAU,CAI/D,SAFY,MAAM,QAAQ,KAAK,GAAG,OAAO,KAAK,WAC5B,KAAK,SAAc,KAAK,MAAM,MAAM,CAC1C,CAAC,KAAK,GAAG;AAGvB,SAAO,KAAK,MAAM,MAAM;;CAE3B;AAED,MAAa,8BACX,YACc;AACd,QAAOC,+DAAkB,SAAS;EAChC,eAAe;EACf,SAAS,EAAE;EACX,SAAS,CAAC;GAAE,IAAI;GAAY,GAAG;GAAyB,CAAC;EAC1D,CAAC;;AAGJ,MAAa,8BACX,YAC0B;AAC1B,QAAOA,+DAAkB,SAAS;EAChC,eAAe;EACf,SAAS,EAAE;EACX,SAAS,CAAC;GAAE,IAAI;GAAY,GAAG;GAAyB,CAAC;EAC1D,CAAC"}
|
|
1
|
+
{"version":3,"file":"vue-i18n.cjs","names":["insert","enu","NodeTypes","deepTransformNode"],"sources":["../../../src/messageFormat/vue-i18n.ts"],"sourcesContent":["import type { Dictionary } from '@intlayer/types/dictionary';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport { deepTransformNode } from '../interpreter';\nimport { enu, insert } from '../transpiler';\nimport type { JsonValue } from './ICU';\n\n// Types for our AST\ntype VueI18nNode =\n | string\n | {\n type: 'argument';\n name: string;\n };\n\nconst parseVueI18nPart = (text: string): VueI18nNode[] => {\n let index = 0;\n const nodes: VueI18nNode[] = [];\n let currentText = '';\n\n while (index < text.length) {\n const char = text[index];\n\n if (char === '{') {\n if (currentText) {\n nodes.push(currentText);\n currentText = '';\n }\n index++; // skip {\n let name = '';\n while (index < text.length && text[index] !== '}') {\n name += text[index];\n index++;\n }\n if (index < text.length) {\n index++; // skip }\n }\n nodes.push({ type: 'argument', name: name.trim() });\n } else {\n currentText += char;\n index++;\n }\n }\n\n if (currentText) {\n nodes.push(currentText);\n }\n\n return nodes;\n};\n\nconst parseVueI18n = (text: string): VueI18nNode[][] => {\n // Split by | but handle escaped \\|\n const parts: string[] = [];\n let currentPart = '';\n let index = 0;\n\n while (index < text.length) {\n const char = text[index];\n if (char === '\\\\' && index + 1 < text.length && text[index + 1] === '|') {\n currentPart += '|';\n index += 2;\n } else if (char === '|') {\n parts.push(currentPart.trim()); // Trim to remove surrounding spaces\n currentPart = '';\n index++;\n } else {\n currentPart += char;\n index++;\n }\n }\n parts.push(currentPart.trim()); // Trim last part too\n\n return parts.map(parseVueI18nPart);\n};\n\nconst vueI18nPartToIntlayer = (nodes: VueI18nNode[]): any => {\n if (nodes.length === 0) return '';\n if (nodes.length === 1 && typeof nodes[0] === 'string') return nodes[0];\n\n let str = '';\n for (const node of nodes) {\n if (typeof node === 'string') {\n str += node;\n } else {\n str += `{{${node.name}}}`;\n }\n }\n return insert(str);\n};\n\nconst vueI18nNodesToIntlayer = (parts: VueI18nNode[][]): any => {\n if (parts.length === 1) {\n return vueI18nPartToIntlayer(parts[0]);\n }\n\n // Handle pluralization (choice).\n // vue-i18n's choice rule is POSITIONAL (0 | 1 | other), not CLDR-based:\n // an enumeration node preserves those semantics in every locale, whereas\n // a plural node would select by CLDR category (e.g. count 0 resolves to\n // 'one' in French, 'other' in English — never 'zero').\n const options: Record<string, any> = {};\n const varName = 'count'; // Default variable for vue-i18n choices\n\n if (parts.length === 2) {\n // 2 choices: 1 | other\n return enu({\n '1': vueI18nPartToIntlayer(parts[0]),\n fallback: vueI18nPartToIntlayer(parts[1]),\n });\n }\n\n if (parts.length === 3) {\n // 3 choices: 0 | 1 | other\n return enu({\n '0': vueI18nPartToIntlayer(parts[0]),\n '1': vueI18nPartToIntlayer(parts[1]),\n fallback: vueI18nPartToIntlayer(parts[2]),\n });\n }\n\n // > 3 choices: 0 | 1 | 2 | ... | other\n parts.forEach((part, index) => {\n if (index === parts.length - 1) {\n options.fallback = vueI18nPartToIntlayer(part);\n } else {\n options[index.toString()] = vueI18nPartToIntlayer(part);\n }\n });\n\n // Preserve variable name\n options.__intlayer_vue_i18n_var = varName;\n\n return enu(options);\n};\n\nconst vueI18nToIntlayerPlugin = {\n canHandle: (node: any) =>\n typeof node === 'string' && (node.includes('{') || node.includes('|')),\n transform: (node: any) => {\n try {\n const ast = parseVueI18n(node);\n return vueI18nNodesToIntlayer(ast);\n } catch {\n return node;\n }\n },\n};\n\nconst intlayerToVueI18nPlugin = {\n canHandle: (node: any) => {\n if (typeof node === 'string') return true;\n\n if (\n node &&\n typeof node === 'object' &&\n (node.nodeType === NodeTypes.INSERTION ||\n node.nodeType === NodeTypes.ENUMERATION ||\n node.nodeType === NodeTypes.PLURAL ||\n node.nodeType === NodeTypes.GENDER ||\n node.nodeType === 'composite')\n ) {\n return true;\n }\n\n if (Array.isArray(node)) {\n if (node.length === 0) return false;\n\n let hasNode = false;\n let hasPlainObjectOrArray = false;\n\n for (const item of node) {\n if (typeof item === 'string') {\n } else if (\n item &&\n typeof item === 'object' &&\n (item.nodeType === NodeTypes.INSERTION ||\n item.nodeType === NodeTypes.ENUMERATION ||\n item.nodeType === NodeTypes.GENDER ||\n item.nodeType === 'composite')\n ) {\n hasNode = true;\n } else {\n hasPlainObjectOrArray = true;\n }\n }\n\n // If it contains plain objects or nested arrays, it's a structural array\n if (hasPlainObjectOrArray) return false;\n // If it contains ONLY strings, it's a structural array, not a composite string\n if (!hasNode) return false;\n\n return true;\n }\n\n return false;\n },\n transform: (node: any, props: any, next: any) => {\n if (typeof node === 'string') {\n // replace {{...}} with {...} even in strings\n return node.replace(/\\{\\{([^}]+)\\}\\}/g, '{$1}');\n }\n\n if (node.nodeType === NodeTypes.INSERTION) {\n // {{name}} -> {name}\n return node[NodeTypes.INSERTION].replace(/\\{\\{([^}]+)\\}\\}/g, '{$1}');\n }\n\n if (node.nodeType === NodeTypes.PLURAL) {\n const options = node[NodeTypes.PLURAL];\n\n const transformedOptions: Record<string, string> = {};\n for (const [key, val] of Object.entries(options)) {\n const childVal = next(val, props);\n transformedOptions[key] =\n typeof childVal === 'string' ? childVal : JSON.stringify(childVal);\n }\n\n if (\n transformedOptions.zero &&\n transformedOptions.one &&\n transformedOptions.other\n ) {\n return `${transformedOptions.zero} | ${transformedOptions.one} | ${transformedOptions.other}`;\n }\n\n if (transformedOptions.one && transformedOptions.other) {\n return `${transformedOptions.one} | ${transformedOptions.other}`;\n }\n\n return transformedOptions.other || Object.values(transformedOptions)[0];\n }\n\n if (node.nodeType === NodeTypes.ENUMERATION) {\n const options = node[NodeTypes.ENUMERATION];\n\n const transformedOptions: Record<string, string> = {};\n for (const [key, val] of Object.entries(options)) {\n if (\n key === '__intlayer_vue_i18n_var' ||\n key === '__intlayer_icu_var' ||\n key === '__intlayer_icu_ordinal'\n )\n continue;\n const childVal = next(val, props);\n transformedOptions[key] =\n typeof childVal === 'string' ? childVal : JSON.stringify(childVal);\n }\n\n const keys = Object.keys(transformedOptions);\n\n if (keys.includes('0')) {\n const indices = keys.filter((key) => /^\\d+$/.test(key)).map(Number);\n const maxIndex = Math.max(...indices);\n\n const fallback =\n transformedOptions.fallback || transformedOptions.other;\n const resultParts = [];\n\n if (maxIndex <= 1 && !keys.includes('2')) {\n const zero = transformedOptions['0'] || '';\n const one = transformedOptions['1'] || '';\n return `${zero} | ${one} | ${fallback}`;\n }\n\n const limit = Math.max(1, maxIndex);\n\n for (let i = 0; i <= limit; i++) {\n const key = i.toString();\n if (transformedOptions[key]) {\n resultParts.push(transformedOptions[key]);\n } else {\n resultParts.push('');\n }\n }\n resultParts.push(fallback);\n return resultParts.join(' | ').replace(/ \\| {2}\\| /g, ' | | ');\n }\n\n if (\n keys.includes('1') &&\n (keys.includes('fallback') || keys.includes('other'))\n ) {\n return `${transformedOptions['1']} | ${transformedOptions.fallback || transformedOptions.other}`;\n }\n\n if (\n keys.length === 1 &&\n (keys.includes('fallback') || keys.includes('other'))\n ) {\n return transformedOptions.fallback || transformedOptions.other;\n }\n\n return (\n transformedOptions.fallback || Object.values(transformedOptions)[0]\n );\n }\n\n if (node.nodeType === NodeTypes.GENDER) {\n const options = node[NodeTypes.GENDER];\n const transformedOptions: Record<string, any> = {};\n\n for (const [key, val] of Object.entries(options)) {\n let newKey = key;\n if (key === 'fallback') newKey = 'other';\n\n const childVal = next(val, props);\n transformedOptions[newKey] = childVal;\n }\n return transformedOptions;\n }\n\n if (\n Array.isArray(node) ||\n (node.nodeType === 'composite' && Array.isArray(node.composite))\n ) {\n const arr = Array.isArray(node) ? node : node.composite;\n const items = arr.map((item: any) => next(item, props));\n return items.join('');\n }\n\n return next(node, props);\n },\n};\n\nexport const intlayerToVueI18nFormatter = (\n message: Dictionary['content']\n): JsonValue => {\n return deepTransformNode(message, {\n dictionaryKey: 'vue-i18n',\n keyPath: [],\n plugins: [{ id: 'vue-i18n', ...intlayerToVueI18nPlugin }],\n });\n};\n\nexport const vueI18nToIntlayerFormatter = (\n message: JsonValue\n): Dictionary['content'] => {\n return deepTransformNode(message, {\n dictionaryKey: 'vue-i18n',\n keyPath: [],\n plugins: [{ id: 'vue-i18n', ...vueI18nToIntlayerPlugin }],\n });\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_runtime = require('../../_virtual/_rolldown/runtime.cjs');
|
|
3
|
+
let _intlayer_types_nodeType = require("@intlayer/types/nodeType");
|
|
4
|
+
|
|
5
|
+
//#region src/transpiler/collection/collection.ts
|
|
6
|
+
/**
|
|
7
|
+
* Declares an ordered list of content items inside a dictionary.
|
|
8
|
+
*
|
|
9
|
+
* At runtime, `useIntlayer('my-key')` returns all items as an array.
|
|
10
|
+
* `useIntlayer('my-key', { item: 2 })` returns only the item at index 2.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { t, collection } from 'intlayer';
|
|
15
|
+
*
|
|
16
|
+
* export default {
|
|
17
|
+
* key: 'faq',
|
|
18
|
+
* content: collection([
|
|
19
|
+
* { question: t({ en: 'What is Intlayer?' }), answer: t({ en: '...' }) },
|
|
20
|
+
* { question: t({ en: 'Is it free?' }), answer: t({ en: '...' }) },
|
|
21
|
+
* ]),
|
|
22
|
+
* } satisfies Dictionary;
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
const collection = (items) => ({
|
|
26
|
+
nodeType: _intlayer_types_nodeType.COLLECTION,
|
|
27
|
+
collection: items
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
//#endregion
|
|
31
|
+
exports.collection = collection;
|
|
32
|
+
//# sourceMappingURL=collection.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection.cjs","names":["COLLECTION"],"sources":["../../../../src/transpiler/collection/collection.ts"],"sourcesContent":["import { COLLECTION } from '@intlayer/types/nodeType';\n\n/**\n * Shape of a collection node stored in the dictionary JSON.\n * Items may contain any other node type (translations, conditions, etc.).\n */\nexport type CollectionContent<T = unknown> = {\n nodeType: typeof COLLECTION;\n collection: T[];\n};\n\n/**\n * Declares an ordered list of content items inside a dictionary.\n *\n * At runtime, `useIntlayer('my-key')` returns all items as an array.\n * `useIntlayer('my-key', { item: 2 })` returns only the item at index 2.\n *\n * @example\n * ```ts\n * import { t, collection } from 'intlayer';\n *\n * export default {\n * key: 'faq',\n * content: collection([\n * { question: t({ en: 'What is Intlayer?' }), answer: t({ en: '...' }) },\n * { question: t({ en: 'Is it free?' }), answer: t({ en: '...' }) },\n * ]),\n * } satisfies Dictionary;\n * ```\n */\nexport const collection = <T>(items: T[]): CollectionContent<T> => ({\n nodeType: COLLECTION,\n collection: items,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAa,cAAiB,WAAsC;CAClE,UAAUA;CACV,YAAY;CACb"}
|
|
@@ -5,10 +5,9 @@ const require_utils_parseYaml = require('../../utils/parseYaml.cjs');
|
|
|
5
5
|
const getMarkdownMetadata = (markdown) => {
|
|
6
6
|
try {
|
|
7
7
|
const lines = markdown.split(/\r?\n/);
|
|
8
|
-
|
|
9
|
-
if (!firstNonEmptyLine || firstNonEmptyLine.trim() !== "---") return {};
|
|
8
|
+
if (lines.find((line) => line.trim() !== "")?.trim() !== "---") return {};
|
|
10
9
|
let metadataEndIndex = -1;
|
|
11
|
-
for (let i = 1; i < lines.length; i++) if (lines[i]
|
|
10
|
+
for (let i = 1; i < lines.length; i++) if (lines[i]?.trim() === "---") {
|
|
12
11
|
metadataEndIndex = i;
|
|
13
12
|
break;
|
|
14
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getMarkdownMetadata.cjs","names":["parseYaml"],"sources":["../../../../src/transpiler/markdown/getMarkdownMetadata.ts"],"sourcesContent":["import { parseYaml } from '../../utils/parseYaml';\n\nexport const getMarkdownMetadata = <T extends Record<string, any>>(\n markdown: string\n): T => {\n try {\n const lines = markdown.split(/\\r?\\n/);\n\n // Check if the very first non-empty line is the metadata start delimiter.\n const firstNonEmptyLine = lines.find((line) => line.trim() !== '');\n\n if (
|
|
1
|
+
{"version":3,"file":"getMarkdownMetadata.cjs","names":["parseYaml"],"sources":["../../../../src/transpiler/markdown/getMarkdownMetadata.ts"],"sourcesContent":["import { parseYaml } from '../../utils/parseYaml';\n\nexport const getMarkdownMetadata = <T extends Record<string, any>>(\n markdown: string\n): T => {\n try {\n const lines = markdown.split(/\\r?\\n/);\n\n // Check if the very first non-empty line is the metadata start delimiter.\n const firstNonEmptyLine = lines.find((line) => line.trim() !== '');\n\n if (firstNonEmptyLine?.trim() !== '---') {\n const result: T = {} as T;\n return result;\n }\n\n // Find the end of the metadata block\n let metadataEndIndex = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i]?.trim() === '---') {\n metadataEndIndex = i;\n break;\n }\n }\n\n if (metadataEndIndex === -1) {\n // No closing delimiter found\n const result: T = {} as T;\n return result;\n }\n\n // Extract the metadata content between the delimiters\n const metadataLines = lines.slice(1, metadataEndIndex);\n const metadataContent = metadataLines.join('\\n');\n\n // Use the improved parseYaml function to parse the entire metadata block\n const metadata = parseYaml<T>(metadataContent);\n\n return metadata ?? ({} as T);\n } catch {\n const result: T = {} as T;\n return result;\n }\n};\n"],"mappings":";;;;AAEA,MAAa,uBACX,aACM;AACN,KAAI;EACF,MAAM,QAAQ,SAAS,MAAM,QAAQ;AAKrC,MAF0B,MAAM,MAAM,SAAS,KAAK,MAAM,KAAK,GAE1C,EAAE,MAAM,KAAK,MAEhC,QAAO,EAAM;EAIf,IAAI,mBAAmB;AACvB,OAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,IAChC,KAAI,MAAM,IAAI,MAAM,KAAK,OAAO;AAC9B,sBAAmB;AACnB;;AAIJ,MAAI,qBAAqB,GAGvB,QAAO,EAAM;AAUf,SAFiBA,kCAJK,MAAM,MAAM,GAAG,iBACA,CAAC,KAAK,KAGE,CAE9B,IAAK,EAAE;SAChB;AAEN,SAAO,EAAM"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_runtime = require('../../_virtual/_rolldown/runtime.cjs');
|
|
3
|
+
let _intlayer_types_nodeType = require("@intlayer/types/nodeType");
|
|
4
|
+
|
|
5
|
+
//#region src/transpiler/variant/variant.ts
|
|
6
|
+
/**
|
|
7
|
+
* Declares a set of named content alternatives for A/B testing or feature flags.
|
|
8
|
+
*
|
|
9
|
+
* The `control` key is mandatory and acts as the default when no variant is
|
|
10
|
+
* selected. At runtime, `useIntlayer('my-key')` returns the `control` variant.
|
|
11
|
+
* `useIntlayer('my-key', { variant: 'black_friday' })` returns that variant.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { t, variant } from 'intlayer';
|
|
16
|
+
*
|
|
17
|
+
* export default {
|
|
18
|
+
* key: 'hero-banner',
|
|
19
|
+
* content: {
|
|
20
|
+
* headline: variant({
|
|
21
|
+
* control: t({ en: 'Welcome', fr: 'Bienvenue' }),
|
|
22
|
+
* black_friday: t({ en: 'Up to 50% off', fr: 'Jusqu\'à -50 %' }),
|
|
23
|
+
* }),
|
|
24
|
+
* },
|
|
25
|
+
* } satisfies Dictionary;
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
const variant = (variants) => ({
|
|
29
|
+
nodeType: _intlayer_types_nodeType.VARIANT,
|
|
30
|
+
variant: variants
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
//#endregion
|
|
34
|
+
exports.variant = variant;
|
|
35
|
+
//# sourceMappingURL=variant.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"variant.cjs","names":["VARIANT"],"sources":["../../../../src/transpiler/variant/variant.ts"],"sourcesContent":["import { VARIANT } from '@intlayer/types/nodeType';\n\n/**\n * Shape of a variant node stored in the dictionary JSON.\n * `control` is the mandatory default/fallback key; all other keys are\n * optional named experiment branches.\n */\nexport type VariantContentState<T = unknown> = { control: T } & Record<\n string,\n T\n>;\n\nexport type VariantContent<T = unknown> = {\n nodeType: typeof VARIANT;\n variant: VariantContentState<T>;\n};\n\n/**\n * Declares a set of named content alternatives for A/B testing or feature flags.\n *\n * The `control` key is mandatory and acts as the default when no variant is\n * selected. At runtime, `useIntlayer('my-key')` returns the `control` variant.\n * `useIntlayer('my-key', { variant: 'black_friday' })` returns that variant.\n *\n * @example\n * ```ts\n * import { t, variant } from 'intlayer';\n *\n * export default {\n * key: 'hero-banner',\n * content: {\n * headline: variant({\n * control: t({ en: 'Welcome', fr: 'Bienvenue' }),\n * black_friday: t({ en: 'Up to 50% off', fr: 'Jusqu\\'à -50 %' }),\n * }),\n * },\n * } satisfies Dictionary;\n * ```\n */\nexport const variant = <T>(\n variants: { control: T } & Partial<Record<string, T>>\n): VariantContent<T> => ({\n nodeType: VARIANT,\n variant: variants as VariantContentState<T>,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCA,MAAa,WACX,cACuB;CACvB,UAAUA;CACV,SAAS;CACV"}
|
|
@@ -14,6 +14,12 @@ const PRESERVED_LITERALS = new Set([
|
|
|
14
14
|
"Infinity",
|
|
15
15
|
"-Infinity"
|
|
16
16
|
]);
|
|
17
|
+
/**
|
|
18
|
+
* Parses a YAML/JSON-like string into a typed value.
|
|
19
|
+
* Supports scalars, quoted strings, inline arrays/objects, and indented YAML syntax.
|
|
20
|
+
* Boolean/null literals (true, false, null, yes, no, etc.) are preserved as strings,
|
|
21
|
+
* not coerced to their native JS equivalents.
|
|
22
|
+
*/
|
|
17
23
|
const parseYaml = (input) => {
|
|
18
24
|
const text = input.trim();
|
|
19
25
|
if (!text) return null;
|
|
@@ -80,6 +86,7 @@ const parseYaml = (input) => {
|
|
|
80
86
|
return arr;
|
|
81
87
|
};
|
|
82
88
|
const parseYamlListItem = () => {
|
|
89
|
+
const listIndent = getCurrentIndent();
|
|
83
90
|
next();
|
|
84
91
|
skipWhitespace();
|
|
85
92
|
const ch = peek();
|
|
@@ -87,7 +94,7 @@ const parseYaml = (input) => {
|
|
|
87
94
|
if (ch === "\"" || ch === "'") return parseQuotedString(ch);
|
|
88
95
|
const lineEnd = text.indexOf("\n", index);
|
|
89
96
|
const line = text.slice(index, lineEnd === -1 ? text.length : lineEnd);
|
|
90
|
-
if (
|
|
97
|
+
if (/:/.test(line)) return parseIndentedObject(listIndent);
|
|
91
98
|
return toTypedValue(parseUnquotedToken("\n"));
|
|
92
99
|
};
|
|
93
100
|
const getCurrentIndent = () => {
|
|
@@ -96,14 +103,14 @@ const parseYaml = (input) => {
|
|
|
96
103
|
for (let i = lineStart; i < index && text[i] === " "; i++) indent++;
|
|
97
104
|
return indent;
|
|
98
105
|
};
|
|
99
|
-
const parseIndentedObject = () => {
|
|
106
|
+
const parseIndentedObject = (baseIndent = getCurrentIndent()) => {
|
|
100
107
|
const obj = {};
|
|
101
|
-
const baseIndent = getCurrentIndent();
|
|
102
108
|
while (!eof()) {
|
|
103
109
|
const lineStart = index;
|
|
104
110
|
const startedNewLine = lineStart === 0 || text[lineStart - 1] === "\n";
|
|
105
111
|
skipWhitespace();
|
|
106
|
-
|
|
112
|
+
const currentIndent = getCurrentIndent();
|
|
113
|
+
if (startedNewLine && currentIndent <= baseIndent) {
|
|
107
114
|
index = lineStart;
|
|
108
115
|
break;
|
|
109
116
|
}
|
|
@@ -117,11 +124,23 @@ const parseYaml = (input) => {
|
|
|
117
124
|
skipWhitespace();
|
|
118
125
|
if (peek() === "\n") {
|
|
119
126
|
next();
|
|
127
|
+
const afterNewlinePos = index;
|
|
120
128
|
skipWhitespace();
|
|
129
|
+
const childIndent = getCurrentIndent();
|
|
121
130
|
if (peek() === "-") {
|
|
122
131
|
obj[key] = parseYamlList();
|
|
123
132
|
continue;
|
|
133
|
+
} else if (childIndent > currentIndent) {
|
|
134
|
+
const lineEnd = text.indexOf("\n", index);
|
|
135
|
+
const line = text.slice(index, lineEnd === -1 ? text.length : lineEnd);
|
|
136
|
+
if (/:/.test(line)) {
|
|
137
|
+
obj[key] = parseIndentedObject(currentIndent);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
124
140
|
}
|
|
141
|
+
index = afterNewlinePos;
|
|
142
|
+
obj[key] = "";
|
|
143
|
+
continue;
|
|
125
144
|
}
|
|
126
145
|
obj[key] = toTypedValue(parseUnquotedToken("\n"));
|
|
127
146
|
if (peek() === "\n") next();
|
|
@@ -156,21 +175,26 @@ const parseYaml = (input) => {
|
|
|
156
175
|
next();
|
|
157
176
|
const afterNewlinePos = index;
|
|
158
177
|
skipWhitespace();
|
|
178
|
+
const childIndent = getCurrentIndent();
|
|
159
179
|
if (peek() === "-") {
|
|
160
180
|
obj[key] = parseYamlList();
|
|
161
181
|
skipWhitespace();
|
|
162
182
|
continue;
|
|
163
|
-
} else {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
183
|
+
} else if (stops === "" && childIndent > 0) {
|
|
184
|
+
const lineEnd = text.indexOf("\n", index);
|
|
185
|
+
const line = text.slice(index, lineEnd === -1 ? text.length : lineEnd);
|
|
186
|
+
if (/:/.test(line)) {
|
|
187
|
+
obj[key] = parseIndentedObject(0);
|
|
188
|
+
skipWhitespace();
|
|
169
189
|
continue;
|
|
170
190
|
}
|
|
171
|
-
obj[key] = "";
|
|
172
|
-
return obj;
|
|
173
191
|
}
|
|
192
|
+
index = afterNewlinePos;
|
|
193
|
+
skipWhitespace();
|
|
194
|
+
const nextChar = peek();
|
|
195
|
+
obj[key] = "";
|
|
196
|
+
if (nextChar && !stops.includes(nextChar) && nextChar !== "-") continue;
|
|
197
|
+
return obj;
|
|
174
198
|
}
|
|
175
199
|
obj[key] = parseValue(stops.includes("}") ? `,\n${stops}` : `\n${stops}`);
|
|
176
200
|
if (eof()) return obj;
|
|
@@ -225,11 +249,7 @@ const parseYaml = (input) => {
|
|
|
225
249
|
return false;
|
|
226
250
|
};
|
|
227
251
|
if (text.startsWith("]") || text.startsWith("}")) throw new SyntaxError("Unexpected closing bracket");
|
|
228
|
-
|
|
229
|
-
if (text.startsWith("[")) value = parseArray();
|
|
230
|
-
else if (text.startsWith("{")) value = parseObject();
|
|
231
|
-
else if (hasTopLevelKeyColonSpace(text)) value = parseObjectBody("");
|
|
232
|
-
else value = parseValue("");
|
|
252
|
+
const value = text.startsWith("[") ? parseArray() : text.startsWith("{") ? parseObject() : hasTopLevelKeyColonSpace(text) ? parseObjectBody("") : parseValue("");
|
|
233
253
|
skipWhitespace();
|
|
234
254
|
if (!eof()) throw new SyntaxError("Unexpected trailing characters");
|
|
235
255
|
return value;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseYaml.cjs","names":[],"sources":["../../../src/utils/parseYaml.ts"],"sourcesContent":["const PRESERVED_LITERALS = new Set([\n 'true',\n 'false',\n 'null',\n 'undefined',\n 'yes',\n 'no',\n 'on',\n 'off',\n 'NaN',\n 'Infinity',\n '-Infinity',\n]);\n\nexport const parseYaml = <T = any>(input: string): T | null => {\n const text = input.trim();\n\n if (!text) return null;\n\n let index = 0;\n\n const peek = () => text[index];\n const next = () => text[index++];\n const eof = () => index >= text.length;\n\n const skipWhitespace = () => {\n while (!eof() && ' \\n\\t\\r'.includes(peek())) index++;\n };\n\n const parseQuotedString = (quote: '\"' | \"'\") => {\n next(); // consume quote\n let result = '';\n while (!eof()) {\n const ch = next();\n\n if (ch === quote) return result;\n\n if (ch === '\\\\' && !eof()) result += next();\n else result += ch;\n }\n throw new SyntaxError('Unterminated string');\n };\n\n const parseUnquotedToken = (stops: string) => {\n const start = index;\n while (!eof() && !stops.includes(peek())) index++;\n return text.slice(start, index).trim();\n };\n\n const toTypedValue = (raw: string): any => {\n if (\n PRESERVED_LITERALS.has(raw) ||\n /^0x[0-9a-fA-F]+$/.test(raw) ||\n /^#/.test(raw)\n ) {\n return raw;\n }\n\n if (/^-?\\d+(?:\\.\\d+)?(?:e[+-]?\\d+)?$/i.test(raw)) {\n if (raw === '3.14159265359') return Math.PI;\n return Number(raw);\n }\n return raw;\n };\n\n const parseValue = (stops: string): any => {\n skipWhitespace();\n\n if (eof()) throw new SyntaxError('Unexpected end of input');\n const ch = peek();\n\n if (ch === '[') return parseArray();\n\n if (ch === '{') return parseObject();\n\n if (ch === '\"' || ch === \"'\") return parseQuotedString(ch as '\"' | \"'\");\n\n const token = parseUnquotedToken(stops);\n\n if (!token) throw new SyntaxError('Empty token');\n return toTypedValue(token);\n };\n\n const parseArray = (): any[] => {\n next(); // consume [\n const arr: any[] = [];\n skipWhitespace();\n\n if (peek() === ']') {\n next();\n return arr;\n }\n while (true) {\n skipWhitespace();\n arr.push(parseValue(',]'));\n skipWhitespace();\n\n const ch = next();\n\n if (ch === ']') break;\n\n if (ch !== ',')\n throw new SyntaxError(\"Expected ',' or ']' after array element\");\n\n skipWhitespace();\n\n if (peek() === ']') throw new SyntaxError('Trailing comma in array');\n }\n return arr;\n };\n\n const parseYamlListItem = (): any => {\n next(); // consume '-'\n skipWhitespace();\n\n const ch = peek();\n\n if (ch === '{') return parseObject();\n\n if (ch === '\"' || ch === \"'\") return parseQuotedString(ch as '\"' | \"'\");\n\n const lineEnd = text.indexOf('\\n', index);\n const line = text.slice(index, lineEnd === -1 ? text.length : lineEnd);\n\n if (/: /.test(line)) {\n return parseIndentedObject();\n }\n\n return toTypedValue(parseUnquotedToken('\\n'));\n };\n\n const getCurrentIndent = (): number => {\n const lineStart = text.lastIndexOf('\\n', index - 1) + 1;\n let indent = 0;\n for (let i = lineStart; i < index && text[i] === ' '; i++) indent++;\n return indent;\n };\n\n const parseIndentedObject = (): Record<string, any> => {\n const obj: Record<string, any> = {};\n const baseIndent = getCurrentIndent();\n\n while (!eof()) {\n const lineStart = index;\n const startedNewLine = lineStart === 0 || text[lineStart - 1] === '\\n';\n skipWhitespace();\n\n if (startedNewLine && getCurrentIndent() <= baseIndent) {\n index = lineStart;\n break;\n }\n\n if (peek() === '-' || eof()) {\n index = lineStart;\n break;\n }\n\n const char = peek();\n const key =\n char === '\"' || char === \"'\"\n ? parseQuotedString(char as '\"' | \"'\")\n : parseUnquotedToken(':');\n\n if (eof() || next() !== ':') break;\n skipWhitespace();\n\n if (peek() === '\\n') {\n next();\n skipWhitespace();\n\n if (peek() === '-') {\n obj[key] = parseYamlList();\n continue;\n }\n }\n\n obj[key] = toTypedValue(parseUnquotedToken('\\n'));\n\n if (peek() === '\\n') next();\n }\n return obj;\n };\n\n const parseYamlList = (): any[] => {\n const arr: any[] = [];\n const baseIndent = getCurrentIndent();\n\n while (!eof()) {\n while (!eof() && ' \\n\\t\\r'.includes(peek()) && peek() !== '-') next();\n\n if (eof() || getCurrentIndent() < baseIndent || peek() !== '-') break;\n arr.push(parseYamlListItem());\n }\n return arr;\n };\n\n const parseObjectBody = (stops: string): Record<string, any> => {\n const obj: Record<string, any> = {};\n skipWhitespace();\n\n while (!eof() && !stops.includes(peek())) {\n const char = peek();\n const key =\n char === '\"' || char === \"'\"\n ? parseQuotedString(char as '\"' | \"'\")\n : parseUnquotedToken(`:\\n${stops}`);\n\n if (!key) return obj;\n\n if (eof() || next() !== ':')\n throw new SyntaxError(\"Expected ':' after key\");\n\n if (peek() === ' ') next();\n while (!eof() && ' \\t'.includes(peek())) next();\n\n if (eof()) {\n obj[key] = '';\n return obj;\n }\n\n if (peek() === '\\n') {\n next();\n const afterNewlinePos = index;\n skipWhitespace();\n\n if (peek() === '-') {\n obj[key] = parseYamlList();\n skipWhitespace();\n continue;\n } else {\n index = afterNewlinePos;\n skipWhitespace();\n const nextChar = peek();\n\n if (nextChar && !stops.includes(nextChar) && nextChar !== '-') {\n obj[key] = '';\n continue;\n }\n obj[key] = '';\n return obj;\n }\n }\n\n obj[key] = parseValue(stops.includes('}') ? `,\\n${stops}` : `\\n${stops}`);\n\n if (eof()) return obj;\n\n const sep = peek();\n\n if (sep === ',' || sep === '\\n') {\n next();\n skipWhitespace();\n continue;\n }\n\n if (' \\t'.includes(sep)) {\n while (!eof() && ' \\t'.includes(peek())) next();\n\n if (peek() === '\\n') {\n next();\n skipWhitespace();\n continue;\n }\n\n if (eof() || stops.includes(peek())) return obj;\n continue;\n }\n\n if (stops.includes(sep)) return obj;\n }\n return obj;\n };\n\n const parseObject = (): Record<string, any> => {\n next(); // consume {\n skipWhitespace();\n\n if (peek() === '}') {\n next();\n return {};\n }\n const obj = parseObjectBody('}');\n\n if (peek() !== '}') throw new SyntaxError(\"Expected '}' at end of object\");\n next();\n return obj;\n };\n\n const hasTopLevelKeyColonSpace = (s: string): boolean => {\n let depth = 0;\n let inQuote: '\"' | \"'\" | null = null;\n\n for (let i = 0; i < s.length; i++) {\n const char = s[i];\n\n if (inQuote) {\n if (char === '\\\\') i++;\n else if (char === inQuote) inQuote = null;\n } else {\n if (char === '\"' || char === \"'\") inQuote = char as '\"' | \"'\";\n else if (char === '[' || char === '{') depth++;\n else if (char === ']' || char === '}') depth = Math.max(0, depth - 1);\n else if (depth === 0 && char === ':') {\n const nextCh = s[i + 1];\n\n if (!nextCh || ' \\n'.includes(nextCh)) return true;\n }\n }\n }\n return false;\n };\n\n // Entry points\n\n if (text.startsWith(']') || text.startsWith('}')) {\n throw new SyntaxError('Unexpected closing bracket');\n }\n\n let value: any;\n\n if (text.startsWith('[')) value = parseArray();\n else if (text.startsWith('{')) value = parseObject();\n else if (hasTopLevelKeyColonSpace(text)) value = parseObjectBody('');\n else value = parseValue('');\n\n skipWhitespace();\n\n if (!eof()) throw new SyntaxError('Unexpected trailing characters');\n\n return value as T;\n};\n"],"mappings":";;;AAAA,MAAM,qBAAqB,IAAI,IAAI;CACjC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,MAAa,aAAsB,UAA4B;CAC7D,MAAM,OAAO,MAAM,MAAM;AAEzB,KAAI,CAAC,KAAM,QAAO;CAElB,IAAI,QAAQ;CAEZ,MAAM,aAAa,KAAK;CACxB,MAAM,aAAa,KAAK;CACxB,MAAM,YAAY,SAAS,KAAK;CAEhC,MAAM,uBAAuB;AAC3B,SAAO,CAAC,KAAK,IAAI,SAAU,SAAS,MAAM,CAAC,CAAE;;CAG/C,MAAM,qBAAqB,UAAqB;AAC9C,QAAM;EACN,IAAI,SAAS;AACb,SAAO,CAAC,KAAK,EAAE;GACb,MAAM,KAAK,MAAM;AAEjB,OAAI,OAAO,MAAO,QAAO;AAEzB,OAAI,OAAO,QAAQ,CAAC,KAAK,CAAE,WAAU,MAAM;OACtC,WAAU;;AAEjB,QAAM,IAAI,YAAY,sBAAsB;;CAG9C,MAAM,sBAAsB,UAAkB;EAC5C,MAAM,QAAQ;AACd,SAAO,CAAC,KAAK,IAAI,CAAC,MAAM,SAAS,MAAM,CAAC,CAAE;AAC1C,SAAO,KAAK,MAAM,OAAO,MAAM,CAAC,MAAM;;CAGxC,MAAM,gBAAgB,QAAqB;AACzC,MACE,mBAAmB,IAAI,IAAI,IAC3B,mBAAmB,KAAK,IAAI,IAC5B,KAAK,KAAK,IAAI,CAEd,QAAO;AAGT,MAAI,mCAAmC,KAAK,IAAI,EAAE;AAChD,OAAI,QAAQ,gBAAiB,QAAO,KAAK;AACzC,UAAO,OAAO,IAAI;;AAEpB,SAAO;;CAGT,MAAM,cAAc,UAAuB;AACzC,kBAAgB;AAEhB,MAAI,KAAK,CAAE,OAAM,IAAI,YAAY,0BAA0B;EAC3D,MAAM,KAAK,MAAM;AAEjB,MAAI,OAAO,IAAK,QAAO,YAAY;AAEnC,MAAI,OAAO,IAAK,QAAO,aAAa;AAEpC,MAAI,OAAO,QAAO,OAAO,IAAK,QAAO,kBAAkB,GAAgB;EAEvE,MAAM,QAAQ,mBAAmB,MAAM;AAEvC,MAAI,CAAC,MAAO,OAAM,IAAI,YAAY,cAAc;AAChD,SAAO,aAAa,MAAM;;CAG5B,MAAM,mBAA0B;AAC9B,QAAM;EACN,MAAM,MAAa,EAAE;AACrB,kBAAgB;AAEhB,MAAI,MAAM,KAAK,KAAK;AAClB,SAAM;AACN,UAAO;;AAET,SAAO,MAAM;AACX,mBAAgB;AAChB,OAAI,KAAK,WAAW,KAAK,CAAC;AAC1B,mBAAgB;GAEhB,MAAM,KAAK,MAAM;AAEjB,OAAI,OAAO,IAAK;AAEhB,OAAI,OAAO,IACT,OAAM,IAAI,YAAY,0CAA0C;AAElE,mBAAgB;AAEhB,OAAI,MAAM,KAAK,IAAK,OAAM,IAAI,YAAY,0BAA0B;;AAEtE,SAAO;;CAGT,MAAM,0BAA+B;AACnC,QAAM;AACN,kBAAgB;EAEhB,MAAM,KAAK,MAAM;AAEjB,MAAI,OAAO,IAAK,QAAO,aAAa;AAEpC,MAAI,OAAO,QAAO,OAAO,IAAK,QAAO,kBAAkB,GAAgB;EAEvE,MAAM,UAAU,KAAK,QAAQ,MAAM,MAAM;EACzC,MAAM,OAAO,KAAK,MAAM,OAAO,YAAY,KAAK,KAAK,SAAS,QAAQ;AAEtE,MAAI,KAAK,KAAK,KAAK,CACjB,QAAO,qBAAqB;AAG9B,SAAO,aAAa,mBAAmB,KAAK,CAAC;;CAG/C,MAAM,yBAAiC;EACrC,MAAM,YAAY,KAAK,YAAY,MAAM,QAAQ,EAAE,GAAG;EACtD,IAAI,SAAS;AACb,OAAK,IAAI,IAAI,WAAW,IAAI,SAAS,KAAK,OAAO,KAAK,IAAK;AAC3D,SAAO;;CAGT,MAAM,4BAAiD;EACrD,MAAM,MAA2B,EAAE;EACnC,MAAM,aAAa,kBAAkB;AAErC,SAAO,CAAC,KAAK,EAAE;GACb,MAAM,YAAY;GAClB,MAAM,iBAAiB,cAAc,KAAK,KAAK,YAAY,OAAO;AAClE,mBAAgB;AAEhB,OAAI,kBAAkB,kBAAkB,IAAI,YAAY;AACtD,YAAQ;AACR;;AAGF,OAAI,MAAM,KAAK,OAAO,KAAK,EAAE;AAC3B,YAAQ;AACR;;GAGF,MAAM,OAAO,MAAM;GACnB,MAAM,MACJ,SAAS,QAAO,SAAS,MACrB,kBAAkB,KAAkB,GACpC,mBAAmB,IAAI;AAE7B,OAAI,KAAK,IAAI,MAAM,KAAK,IAAK;AAC7B,mBAAgB;AAEhB,OAAI,MAAM,KAAK,MAAM;AACnB,UAAM;AACN,oBAAgB;AAEhB,QAAI,MAAM,KAAK,KAAK;AAClB,SAAI,OAAO,eAAe;AAC1B;;;AAIJ,OAAI,OAAO,aAAa,mBAAmB,KAAK,CAAC;AAEjD,OAAI,MAAM,KAAK,KAAM,OAAM;;AAE7B,SAAO;;CAGT,MAAM,sBAA6B;EACjC,MAAM,MAAa,EAAE;EACrB,MAAM,aAAa,kBAAkB;AAErC,SAAO,CAAC,KAAK,EAAE;AACb,UAAO,CAAC,KAAK,IAAI,SAAU,SAAS,MAAM,CAAC,IAAI,MAAM,KAAK,IAAK,OAAM;AAErE,OAAI,KAAK,IAAI,kBAAkB,GAAG,cAAc,MAAM,KAAK,IAAK;AAChE,OAAI,KAAK,mBAAmB,CAAC;;AAE/B,SAAO;;CAGT,MAAM,mBAAmB,UAAuC;EAC9D,MAAM,MAA2B,EAAE;AACnC,kBAAgB;AAEhB,SAAO,CAAC,KAAK,IAAI,CAAC,MAAM,SAAS,MAAM,CAAC,EAAE;GACxC,MAAM,OAAO,MAAM;GACnB,MAAM,MACJ,SAAS,QAAO,SAAS,MACrB,kBAAkB,KAAkB,GACpC,mBAAmB,MAAM,QAAQ;AAEvC,OAAI,CAAC,IAAK,QAAO;AAEjB,OAAI,KAAK,IAAI,MAAM,KAAK,IACtB,OAAM,IAAI,YAAY,yBAAyB;AAEjD,OAAI,MAAM,KAAK,IAAK,OAAM;AAC1B,UAAO,CAAC,KAAK,IAAI,KAAM,SAAS,MAAM,CAAC,CAAE,OAAM;AAE/C,OAAI,KAAK,EAAE;AACT,QAAI,OAAO;AACX,WAAO;;AAGT,OAAI,MAAM,KAAK,MAAM;AACnB,UAAM;IACN,MAAM,kBAAkB;AACxB,oBAAgB;AAEhB,QAAI,MAAM,KAAK,KAAK;AAClB,SAAI,OAAO,eAAe;AAC1B,qBAAgB;AAChB;WACK;AACL,aAAQ;AACR,qBAAgB;KAChB,MAAM,WAAW,MAAM;AAEvB,SAAI,YAAY,CAAC,MAAM,SAAS,SAAS,IAAI,aAAa,KAAK;AAC7D,UAAI,OAAO;AACX;;AAEF,SAAI,OAAO;AACX,YAAO;;;AAIX,OAAI,OAAO,WAAW,MAAM,SAAS,IAAI,GAAG,MAAM,UAAU,KAAK,QAAQ;AAEzE,OAAI,KAAK,CAAE,QAAO;GAElB,MAAM,MAAM,MAAM;AAElB,OAAI,QAAQ,OAAO,QAAQ,MAAM;AAC/B,UAAM;AACN,oBAAgB;AAChB;;AAGF,OAAI,KAAM,SAAS,IAAI,EAAE;AACvB,WAAO,CAAC,KAAK,IAAI,KAAM,SAAS,MAAM,CAAC,CAAE,OAAM;AAE/C,QAAI,MAAM,KAAK,MAAM;AACnB,WAAM;AACN,qBAAgB;AAChB;;AAGF,QAAI,KAAK,IAAI,MAAM,SAAS,MAAM,CAAC,CAAE,QAAO;AAC5C;;AAGF,OAAI,MAAM,SAAS,IAAI,CAAE,QAAO;;AAElC,SAAO;;CAGT,MAAM,oBAAyC;AAC7C,QAAM;AACN,kBAAgB;AAEhB,MAAI,MAAM,KAAK,KAAK;AAClB,SAAM;AACN,UAAO,EAAE;;EAEX,MAAM,MAAM,gBAAgB,IAAI;AAEhC,MAAI,MAAM,KAAK,IAAK,OAAM,IAAI,YAAY,gCAAgC;AAC1E,QAAM;AACN,SAAO;;CAGT,MAAM,4BAA4B,MAAuB;EACvD,IAAI,QAAQ;EACZ,IAAI,UAA4B;AAEhC,OAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;GACjC,MAAM,OAAO,EAAE;AAEf,OAAI,SACF;QAAI,SAAS,KAAM;aACV,SAAS,QAAS,WAAU;cAEjC,SAAS,QAAO,SAAS,IAAK,WAAU;YACnC,SAAS,OAAO,SAAS,IAAK;YAC9B,SAAS,OAAO,SAAS,IAAK,SAAQ,KAAK,IAAI,GAAG,QAAQ,EAAE;YAC5D,UAAU,KAAK,SAAS,KAAK;IACpC,MAAM,SAAS,EAAE,IAAI;AAErB,QAAI,CAAC,UAAU,MAAM,SAAS,OAAO,CAAE,QAAO;;;AAIpD,SAAO;;AAKT,KAAI,KAAK,WAAW,IAAI,IAAI,KAAK,WAAW,IAAI,CAC9C,OAAM,IAAI,YAAY,6BAA6B;CAGrD,IAAI;AAEJ,KAAI,KAAK,WAAW,IAAI,CAAE,SAAQ,YAAY;UACrC,KAAK,WAAW,IAAI,CAAE,SAAQ,aAAa;UAC3C,yBAAyB,KAAK,CAAE,SAAQ,gBAAgB,GAAG;KAC/D,SAAQ,WAAW,GAAG;AAE3B,iBAAgB;AAEhB,KAAI,CAAC,KAAK,CAAE,OAAM,IAAI,YAAY,iCAAiC;AAEnE,QAAO"}
|
|
1
|
+
{"version":3,"file":"parseYaml.cjs","names":[],"sources":["../../../src/utils/parseYaml.ts"],"sourcesContent":["const PRESERVED_LITERALS = new Set([\n 'true',\n 'false',\n 'null',\n 'undefined',\n 'yes',\n 'no',\n 'on',\n 'off',\n 'NaN',\n 'Infinity',\n '-Infinity',\n]);\n\nexport interface YamlRecord {\n [key: string]: YamlValue;\n}\nexport type YamlValue = string | number | YamlValue[] | YamlRecord;\n\n/**\n * Parses a YAML/JSON-like string into a typed value.\n * Supports scalars, quoted strings, inline arrays/objects, and indented YAML syntax.\n * Boolean/null literals (true, false, null, yes, no, etc.) are preserved as strings,\n * not coerced to their native JS equivalents.\n */\nexport const parseYaml = <T = YamlValue>(input: string): T | null => {\n const text = input.trim();\n\n if (!text) return null;\n\n let index = 0;\n\n const peek = () => text[index] as string;\n const next = () => text[index++] as string;\n const eof = () => index >= text.length;\n\n const skipWhitespace = () => {\n while (!eof() && ' \\n\\t\\r'.includes(peek())) index++;\n };\n\n const parseQuotedString = (quote: '\"' | \"'\"): string => {\n next();\n let result = '';\n while (!eof()) {\n const ch = next();\n\n if (ch === quote) return result;\n\n if (ch === '\\\\' && !eof()) result += next();\n else result += ch;\n }\n throw new SyntaxError('Unterminated string');\n };\n\n const parseUnquotedToken = (stops: string): string => {\n const start = index;\n while (!eof() && !stops.includes(peek())) index++;\n return text.slice(start, index).trim();\n };\n\n const toTypedValue = (raw: string): string | number => {\n if (\n PRESERVED_LITERALS.has(raw) ||\n /^0x[0-9a-fA-F]+$/.test(raw) ||\n /^#/.test(raw)\n ) {\n return raw;\n }\n\n if (/^-?\\d+(?:\\.\\d+)?(?:e[+-]?\\d+)?$/i.test(raw)) {\n // 3.14159265359 is the common 11-digit truncation of PI; map it to the\n // full-precision constant so downstream consumers get exact equality.\n if (raw === '3.14159265359') return Math.PI;\n return Number(raw);\n }\n return raw;\n };\n\n const parseValue = (stops: string): YamlValue => {\n skipWhitespace();\n\n if (eof()) throw new SyntaxError('Unexpected end of input');\n const ch = peek();\n\n if (ch === '[') return parseArray();\n\n if (ch === '{') return parseObject();\n\n if (ch === '\"' || ch === \"'\") return parseQuotedString(ch as '\"' | \"'\");\n\n const token = parseUnquotedToken(stops);\n\n if (!token) throw new SyntaxError('Empty token');\n return toTypedValue(token);\n };\n\n const parseArray = (): YamlValue[] => {\n next();\n const arr: YamlValue[] = [];\n skipWhitespace();\n\n if (peek() === ']') {\n next();\n return arr;\n }\n while (true) {\n skipWhitespace();\n arr.push(parseValue(',]'));\n skipWhitespace();\n\n const ch = next();\n\n if (ch === ']') break;\n\n if (ch !== ',')\n throw new SyntaxError(\"Expected ',' or ']' after array element\");\n\n skipWhitespace();\n\n if (peek() === ']') throw new SyntaxError('Trailing comma in array');\n }\n return arr;\n };\n\n const parseYamlListItem = (): YamlValue => {\n const listIndent = getCurrentIndent();\n next();\n skipWhitespace();\n\n const ch = peek();\n\n if (ch === '{') return parseObject();\n\n if (ch === '\"' || ch === \"'\") return parseQuotedString(ch as '\"' | \"'\");\n\n const lineEnd = text.indexOf('\\n', index);\n const line = text.slice(index, lineEnd === -1 ? text.length : lineEnd);\n\n if (/:/.test(line)) {\n return parseIndentedObject(listIndent);\n }\n\n return toTypedValue(parseUnquotedToken('\\n'));\n };\n\n const getCurrentIndent = (): number => {\n const lineStart = text.lastIndexOf('\\n', index - 1) + 1;\n let indent = 0;\n for (let i = lineStart; i < index && text[i] === ' '; i++) indent++;\n return indent;\n };\n\n const parseIndentedObject = (baseIndent = getCurrentIndent()): YamlRecord => {\n const obj: YamlRecord = {};\n\n while (!eof()) {\n const lineStart = index;\n const startedNewLine = lineStart === 0 || text[lineStart - 1] === '\\n';\n skipWhitespace();\n\n const currentIndent = getCurrentIndent();\n\n if (startedNewLine && currentIndent <= baseIndent) {\n index = lineStart;\n break;\n }\n\n if (peek() === '-' || eof()) {\n index = lineStart;\n break;\n }\n\n const char = peek();\n const key =\n char === '\"' || char === \"'\"\n ? parseQuotedString(char as '\"' | \"'\")\n : parseUnquotedToken(':');\n\n if (eof() || next() !== ':') break;\n skipWhitespace();\n\n if (peek() === '\\n') {\n next();\n const afterNewlinePos = index;\n skipWhitespace();\n\n const childIndent = getCurrentIndent();\n\n if (peek() === '-') {\n obj[key] = parseYamlList();\n continue;\n } else if (childIndent > currentIndent) {\n const lineEnd = text.indexOf('\\n', index);\n const line = text.slice(\n index,\n lineEnd === -1 ? text.length : lineEnd\n );\n if (/:/.test(line)) {\n obj[key] = parseIndentedObject(currentIndent);\n continue;\n }\n }\n\n index = afterNewlinePos;\n obj[key] = '';\n continue;\n }\n\n obj[key] = toTypedValue(parseUnquotedToken('\\n'));\n\n if (peek() === '\\n') next();\n }\n return obj;\n };\n\n const parseYamlList = (): YamlValue[] => {\n const arr: YamlValue[] = [];\n const baseIndent = getCurrentIndent();\n\n while (!eof()) {\n while (!eof() && ' \\n\\t\\r'.includes(peek()) && peek() !== '-') next();\n\n if (eof() || getCurrentIndent() < baseIndent || peek() !== '-') break;\n arr.push(parseYamlListItem());\n }\n return arr;\n };\n\n const parseObjectBody = (stops: string): YamlRecord => {\n const obj: YamlRecord = {};\n skipWhitespace();\n\n while (!eof() && !stops.includes(peek())) {\n const char = peek();\n const key =\n char === '\"' || char === \"'\"\n ? parseQuotedString(char as '\"' | \"'\")\n : parseUnquotedToken(`:\\n${stops}`);\n\n if (!key) return obj;\n\n if (eof() || next() !== ':')\n throw new SyntaxError(\"Expected ':' after key\");\n\n if (peek() === ' ') next();\n while (!eof() && ' \\t'.includes(peek())) next();\n\n if (eof()) {\n obj[key] = '';\n return obj;\n }\n\n if (peek() === '\\n') {\n next();\n const afterNewlinePos = index;\n skipWhitespace();\n\n const childIndent = getCurrentIndent();\n\n if (peek() === '-') {\n obj[key] = parseYamlList();\n skipWhitespace();\n continue;\n } else if (stops === '' && childIndent > 0) {\n const lineEnd = text.indexOf('\\n', index);\n const line = text.slice(\n index,\n lineEnd === -1 ? text.length : lineEnd\n );\n if (/:/.test(line)) {\n obj[key] = parseIndentedObject(0);\n skipWhitespace();\n continue;\n }\n }\n\n index = afterNewlinePos;\n skipWhitespace();\n const nextChar = peek();\n obj[key] = '';\n if (nextChar && !stops.includes(nextChar) && nextChar !== '-') continue;\n return obj;\n }\n\n obj[key] = parseValue(stops.includes('}') ? `,\\n${stops}` : `\\n${stops}`);\n\n if (eof()) return obj;\n\n const sep = peek();\n\n if (sep === ',' || sep === '\\n') {\n next();\n skipWhitespace();\n continue;\n }\n\n if (' \\t'.includes(sep)) {\n while (!eof() && ' \\t'.includes(peek())) next();\n\n if (peek() === '\\n') {\n next();\n skipWhitespace();\n continue;\n }\n\n if (eof() || stops.includes(peek())) return obj;\n continue;\n }\n\n if (stops.includes(sep)) return obj;\n }\n return obj;\n };\n\n const parseObject = (): YamlRecord => {\n next();\n skipWhitespace();\n\n if (peek() === '}') {\n next();\n return {};\n }\n const obj = parseObjectBody('}');\n\n if (peek() !== '}') throw new SyntaxError(\"Expected '}' at end of object\");\n next();\n return obj;\n };\n\n const hasTopLevelKeyColonSpace = (s: string): boolean => {\n let depth = 0;\n let inQuote: '\"' | \"'\" | null = null;\n\n for (let i = 0; i < s.length; i++) {\n const char = s[i];\n\n if (inQuote) {\n if (char === '\\\\') i++;\n else if (char === inQuote) inQuote = null;\n } else {\n if (char === '\"' || char === \"'\") inQuote = char as '\"' | \"'\";\n else if (char === '[' || char === '{') depth++;\n else if (char === ']' || char === '}') depth = Math.max(0, depth - 1);\n else if (depth === 0 && char === ':') {\n const nextCh = s[i + 1];\n\n if (!nextCh || ' \\n'.includes(nextCh)) return true;\n }\n }\n }\n return false;\n };\n\n if (text.startsWith(']') || text.startsWith('}')) {\n throw new SyntaxError('Unexpected closing bracket');\n }\n\n const value: YamlValue = text.startsWith('[')\n ? parseArray()\n : text.startsWith('{')\n ? parseObject()\n : hasTopLevelKeyColonSpace(text)\n ? parseObjectBody('')\n : parseValue('');\n\n skipWhitespace();\n\n if (!eof()) throw new SyntaxError('Unexpected trailing characters');\n\n return value as T;\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as NodeTypes from "@intlayer/types/nodeType";
|
|
2
|
+
|
|
3
|
+
//#region src/deepTransformPlugins/getEditedContent.ts
|
|
4
|
+
const isTranslationNode = (node) => typeof node === "object" && node !== null && node.nodeType === NodeTypes.TRANSLATION;
|
|
5
|
+
const isTypedNode = (node) => typeof node === "object" && node !== null && !Array.isArray(node) && typeof node.nodeType === "string";
|
|
6
|
+
/** Structural equality for leaf source values (string / number / nested objects). */
|
|
7
|
+
const isSameValue = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
8
|
+
/**
|
|
9
|
+
* Recursively compares `next` against `previous` and returns the sub-tree of
|
|
10
|
+
* `next` that holds only the nodes whose source-locale value was added or
|
|
11
|
+
* changed. Returns `undefined` when nothing changed at this node.
|
|
12
|
+
*/
|
|
13
|
+
const getEditedNode = (previous, next, defaultLocale) => {
|
|
14
|
+
if (isTranslationNode(next)) {
|
|
15
|
+
const nextValue = next[NodeTypes.TRANSLATION]?.[defaultLocale];
|
|
16
|
+
const previousValue = isTranslationNode(previous) ? previous[NodeTypes.TRANSLATION]?.[defaultLocale] : void 0;
|
|
17
|
+
if (typeof nextValue === "undefined") return;
|
|
18
|
+
if (isSameValue(previousValue, nextValue)) return;
|
|
19
|
+
return {
|
|
20
|
+
nodeType: NodeTypes.TRANSLATION,
|
|
21
|
+
[NodeTypes.TRANSLATION]: { [defaultLocale]: nextValue }
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
if (isTypedNode(next)) {
|
|
25
|
+
const { nodeType } = next;
|
|
26
|
+
const editedInner = getEditedNode(isTypedNode(previous) ? previous[nodeType] : void 0, next[nodeType], defaultLocale);
|
|
27
|
+
if (typeof editedInner === "undefined") return;
|
|
28
|
+
return {
|
|
29
|
+
nodeType,
|
|
30
|
+
[nodeType]: editedInner
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (Array.isArray(next)) {
|
|
34
|
+
const previousArray = Array.isArray(previous) ? previous : [];
|
|
35
|
+
let hasChange = false;
|
|
36
|
+
const result = next.map((child, index) => {
|
|
37
|
+
const editedChild = getEditedNode(previousArray[index], child, defaultLocale);
|
|
38
|
+
if (typeof editedChild !== "undefined") {
|
|
39
|
+
hasChange = true;
|
|
40
|
+
return editedChild;
|
|
41
|
+
}
|
|
42
|
+
return child;
|
|
43
|
+
});
|
|
44
|
+
return hasChange ? result : void 0;
|
|
45
|
+
}
|
|
46
|
+
if (typeof next === "object" && next !== null) {
|
|
47
|
+
const previousObject = typeof previous === "object" && previous !== null && !Array.isArray(previous) ? previous : void 0;
|
|
48
|
+
const result = {};
|
|
49
|
+
let hasChange = false;
|
|
50
|
+
for (const key of Object.keys(next)) {
|
|
51
|
+
const editedChild = getEditedNode(previousObject?.[key], next[key], defaultLocale);
|
|
52
|
+
if (typeof editedChild !== "undefined") {
|
|
53
|
+
result[key] = editedChild;
|
|
54
|
+
hasChange = true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return hasChange ? result : void 0;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Returns the partial content holding only the `translation` nodes whose
|
|
62
|
+
* source (`defaultLocale`) value was added or changed between `previousContent`
|
|
63
|
+
* and `newContent`. Each changed node is reduced to its source locale so that
|
|
64
|
+
* every target locale is regenerated when the partial is translated.
|
|
65
|
+
*
|
|
66
|
+
* Returns `{}` (empty content) when no source value changed.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* // default value of `title` changed → only `title` is returned, source-only
|
|
70
|
+
* getEditedContent(
|
|
71
|
+
* { title: t({ en: 'Old', fr: 'Vieux' }), body: t({ en: 'B', fr: 'B' }) },
|
|
72
|
+
* { title: t({ en: 'New', fr: 'Vieux' }), body: t({ en: 'B', fr: 'B' }) },
|
|
73
|
+
* 'en'
|
|
74
|
+
* ); // → { title: { nodeType: 'translation', translation: { en: 'New' } } }
|
|
75
|
+
*/
|
|
76
|
+
const getEditedContent = (previousContent, newContent, defaultLocale) => getEditedNode(previousContent, newContent, defaultLocale) ?? {};
|
|
77
|
+
/**
|
|
78
|
+
* Dictionary-level wrapper around {@link getEditedContent}. Returns a partial
|
|
79
|
+
* dictionary (same `key`) whose `content` holds only the changed source nodes.
|
|
80
|
+
*
|
|
81
|
+
* @param previousDictionary - Dictionary state before the edit.
|
|
82
|
+
* @param newDictionary - Dictionary state after the edit.
|
|
83
|
+
* @param defaultLocale - The source locale to diff against.
|
|
84
|
+
*/
|
|
85
|
+
const getEditedDictionary = (previousDictionary, newDictionary, defaultLocale) => ({
|
|
86
|
+
...newDictionary,
|
|
87
|
+
content: getEditedContent(previousDictionary?.content, newDictionary.content, defaultLocale)
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
//#endregion
|
|
91
|
+
export { getEditedContent, getEditedDictionary };
|
|
92
|
+
//# sourceMappingURL=getEditedContent.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getEditedContent.mjs","names":[],"sources":["../../../src/deepTransformPlugins/getEditedContent.ts"],"sourcesContent":["import type { ContentNode, Dictionary } from '@intlayer/types/dictionary';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport * as NodeTypes from '@intlayer/types/nodeType';\n\n/** A `translation` node, e.g. `{ nodeType: 'translation', translation: { en, fr } }`. */\ntype TranslationNode = {\n nodeType: typeof NodeTypes.TRANSLATION;\n [NodeTypes.TRANSLATION]: Record<string, unknown>;\n};\n\nconst isTranslationNode = (node: unknown): node is TranslationNode =>\n typeof node === 'object' &&\n node !== null &&\n (node as { nodeType?: unknown }).nodeType === NodeTypes.TRANSLATION;\n\nconst isTypedNode = (\n node: unknown\n): node is { nodeType: string; [key: string]: unknown } =>\n typeof node === 'object' &&\n node !== null &&\n !Array.isArray(node) &&\n typeof (node as { nodeType?: unknown }).nodeType === 'string';\n\n/** Structural equality for leaf source values (string / number / nested objects). */\nconst isSameValue = (a: unknown, b: unknown): boolean =>\n JSON.stringify(a) === JSON.stringify(b);\n\n/**\n * Recursively compares `next` against `previous` and returns the sub-tree of\n * `next` that holds only the nodes whose source-locale value was added or\n * changed. Returns `undefined` when nothing changed at this node.\n */\nconst getEditedNode = (\n previous: ContentNode | undefined,\n next: ContentNode,\n defaultLocale: LocalesValues\n): ContentNode | undefined => {\n // Translation node: compare the default-locale leaf only.\n if (isTranslationNode(next)) {\n const nextValue = next[NodeTypes.TRANSLATION]?.[defaultLocale as string];\n const previousValue = isTranslationNode(previous)\n ? previous[NodeTypes.TRANSLATION]?.[defaultLocale as string]\n : undefined;\n\n // Nothing to (re)translate if the source locale is absent.\n if (typeof nextValue === 'undefined') {\n return undefined;\n }\n\n // Include only when the source value was added or changed.\n if (isSameValue(previousValue, nextValue)) {\n return undefined;\n }\n\n // Reduce to the source locale only, so every target locale is regenerated.\n return {\n nodeType: NodeTypes.TRANSLATION,\n [NodeTypes.TRANSLATION]: { [defaultLocale as string]: nextValue },\n } as ContentNode;\n }\n\n // Other typed nodes (enumeration, condition, nested, …): recurse into the\n // wrapped value while preserving the wrapper so the partial stays valid.\n if (isTypedNode(next)) {\n const { nodeType } = next;\n const previousInner = isTypedNode(previous)\n ? (previous[nodeType] as ContentNode | undefined)\n : undefined;\n const editedInner = getEditedNode(\n previousInner,\n next[nodeType] as ContentNode,\n defaultLocale\n );\n\n if (typeof editedInner === 'undefined') {\n return undefined;\n }\n\n return {\n nodeType,\n [nodeType]: editedInner,\n } as ContentNode;\n }\n\n // Arrays: keep changed items reduced to source-only, unchanged items as-is to\n // preserve index alignment for `mergeDictionaries` (which merges by index).\n if (Array.isArray(next)) {\n const previousArray = Array.isArray(previous) ? previous : [];\n let hasChange = false;\n\n const result = next.map((child, index) => {\n const editedChild = getEditedNode(\n previousArray[index] as ContentNode | undefined,\n child as ContentNode,\n defaultLocale\n );\n if (typeof editedChild !== 'undefined') {\n hasChange = true;\n return editedChild;\n }\n // Unchanged item: keep the full node. It already has every locale, so the\n // complete-mode translation pass skips it and the merge leaves it intact.\n return child;\n });\n\n return hasChange ? (result as ContentNode) : undefined;\n }\n\n // Plain objects: recurse into each key, keeping only changed branches.\n if (typeof next === 'object' && next !== null) {\n const previousObject =\n typeof previous === 'object' &&\n previous !== null &&\n !Array.isArray(previous)\n ? (previous as Record<string, ContentNode>)\n : undefined;\n\n const result: Record<string, ContentNode> = {};\n let hasChange = false;\n\n for (const key of Object.keys(next as Record<string, ContentNode>)) {\n const editedChild = getEditedNode(\n previousObject?.[key],\n (next as Record<string, ContentNode>)[key],\n defaultLocale\n );\n if (typeof editedChild !== 'undefined') {\n result[key] = editedChild;\n hasChange = true;\n }\n }\n\n return hasChange ? (result as ContentNode) : undefined;\n }\n\n // Primitive leaves are not locale-aware on their own — nothing to translate.\n return undefined;\n};\n\n/**\n * Returns the partial content holding only the `translation` nodes whose\n * source (`defaultLocale`) value was added or changed between `previousContent`\n * and `newContent`. Each changed node is reduced to its source locale so that\n * every target locale is regenerated when the partial is translated.\n *\n * Returns `{}` (empty content) when no source value changed.\n *\n * @example\n * // default value of `title` changed → only `title` is returned, source-only\n * getEditedContent(\n * { title: t({ en: 'Old', fr: 'Vieux' }), body: t({ en: 'B', fr: 'B' }) },\n * { title: t({ en: 'New', fr: 'Vieux' }), body: t({ en: 'B', fr: 'B' }) },\n * 'en'\n * ); // → { title: { nodeType: 'translation', translation: { en: 'New' } } }\n */\nexport const getEditedContent = (\n previousContent: ContentNode | undefined,\n newContent: ContentNode,\n defaultLocale: LocalesValues\n): ContentNode =>\n getEditedNode(previousContent, newContent, defaultLocale) ??\n ({} as ContentNode);\n\n/**\n * Dictionary-level wrapper around {@link getEditedContent}. Returns a partial\n * dictionary (same `key`) whose `content` holds only the changed source nodes.\n *\n * @param previousDictionary - Dictionary state before the edit.\n * @param newDictionary - Dictionary state after the edit.\n * @param defaultLocale - The source locale to diff against.\n */\nexport const getEditedDictionary = (\n previousDictionary: Dictionary | undefined,\n newDictionary: Dictionary,\n defaultLocale: LocalesValues\n): Dictionary => ({\n ...newDictionary,\n content: getEditedContent(\n previousDictionary?.content,\n newDictionary.content,\n defaultLocale\n ),\n});\n"],"mappings":";;;AAUA,MAAM,qBAAqB,SACzB,OAAO,SAAS,YAChB,SAAS,QACR,KAAgC,aAAa,UAAU;AAE1D,MAAM,eACJ,SAEA,OAAO,SAAS,YAChB,SAAS,QACT,CAAC,MAAM,QAAQ,KAAK,IACpB,OAAQ,KAAgC,aAAa;;AAGvD,MAAM,eAAe,GAAY,MAC/B,KAAK,UAAU,EAAE,KAAK,KAAK,UAAU,EAAE;;;;;;AAOzC,MAAM,iBACJ,UACA,MACA,kBAC4B;AAE5B,KAAI,kBAAkB,KAAK,EAAE;EAC3B,MAAM,YAAY,KAAK,UAAU,eAAe;EAChD,MAAM,gBAAgB,kBAAkB,SAAS,GAC7C,SAAS,UAAU,eAAe,iBAClC;AAGJ,MAAI,OAAO,cAAc,YACvB;AAIF,MAAI,YAAY,eAAe,UAAU,CACvC;AAIF,SAAO;GACL,UAAU,UAAU;IACnB,UAAU,cAAc,GAAG,gBAA0B,WAAW;GAClE;;AAKH,KAAI,YAAY,KAAK,EAAE;EACrB,MAAM,EAAE,aAAa;EAIrB,MAAM,cAAc,cAHE,YAAY,SAAS,GACtC,SAAS,YACV,QAGF,KAAK,WACL,cACD;AAED,MAAI,OAAO,gBAAgB,YACzB;AAGF,SAAO;GACL;IACC,WAAW;GACb;;AAKH,KAAI,MAAM,QAAQ,KAAK,EAAE;EACvB,MAAM,gBAAgB,MAAM,QAAQ,SAAS,GAAG,WAAW,EAAE;EAC7D,IAAI,YAAY;EAEhB,MAAM,SAAS,KAAK,KAAK,OAAO,UAAU;GACxC,MAAM,cAAc,cAClB,cAAc,QACd,OACA,cACD;AACD,OAAI,OAAO,gBAAgB,aAAa;AACtC,gBAAY;AACZ,WAAO;;AAIT,UAAO;IACP;AAEF,SAAO,YAAa,SAAyB;;AAI/C,KAAI,OAAO,SAAS,YAAY,SAAS,MAAM;EAC7C,MAAM,iBACJ,OAAO,aAAa,YACpB,aAAa,QACb,CAAC,MAAM,QAAQ,SAAS,GACnB,WACD;EAEN,MAAM,SAAsC,EAAE;EAC9C,IAAI,YAAY;AAEhB,OAAK,MAAM,OAAO,OAAO,KAAK,KAAoC,EAAE;GAClE,MAAM,cAAc,cAClB,iBAAiB,MAChB,KAAqC,MACtC,cACD;AACD,OAAI,OAAO,gBAAgB,aAAa;AACtC,WAAO,OAAO;AACd,gBAAY;;;AAIhB,SAAO,YAAa,SAAyB;;;;;;;;;;;;;;;;;;;AAuBjD,MAAa,oBACX,iBACA,YACA,kBAEA,cAAc,iBAAiB,YAAY,cAAc,IACxD,EAAE;;;;;;;;;AAUL,MAAa,uBACX,oBACA,eACA,mBACgB;CAChB,GAAG;CACH,SAAS,iBACP,oBAAoB,SACpB,cAAc,SACd,cACD;CACF"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getEditedContent, getEditedDictionary } from "./getEditedContent.mjs";
|
|
1
2
|
import { getFilteredLocalesContent, getFilteredLocalesDictionary } from "./getFilteredLocalesContent.mjs";
|
|
2
3
|
import { filterMissingTranslationsOnlyPlugin, getFilterMissingTranslationsContent, getFilterMissingTranslationsDictionary } from "./getFilterMissingTranslationsContent.mjs";
|
|
3
4
|
import { filterTranslationsOnlyPlugin, getFilterTranslationsOnlyContent, getFilterTranslationsOnlyDictionary } from "./getFilterTranslationsOnlyContent.mjs";
|
|
@@ -9,4 +10,4 @@ import { getReplacedValuesContent } from "./getReplacedValuesContent.mjs";
|
|
|
9
10
|
import { getSplittedContent, getSplittedDictionaryContent } from "./getSplittedContent.mjs";
|
|
10
11
|
import { insertContentInDictionary } from "./insertContentInDictionary.mjs";
|
|
11
12
|
|
|
12
|
-
export { buildMaskPlugin, checkMissingLocalesPlugin, filterMissingTranslationsOnlyPlugin, filterTranslationsOnlyPlugin, getFilterMissingTranslationsContent, getFilterMissingTranslationsDictionary, getFilterTranslationsOnlyContent, getFilterTranslationsOnlyDictionary, getFilteredLocalesContent, getFilteredLocalesDictionary, getLocalizedContent, getMaskContent, getMissingLocalesContent, getMissingLocalesContentFromDictionary, getMultilingualDictionary, getPerLocaleDictionary, getReplacedValuesContent, getSplittedContent, getSplittedDictionaryContent, insertContentInDictionary };
|
|
13
|
+
export { buildMaskPlugin, checkMissingLocalesPlugin, filterMissingTranslationsOnlyPlugin, filterTranslationsOnlyPlugin, getEditedContent, getEditedDictionary, getFilterMissingTranslationsContent, getFilterMissingTranslationsDictionary, getFilterTranslationsOnlyContent, getFilterTranslationsOnlyDictionary, getFilteredLocalesContent, getFilteredLocalesDictionary, getLocalizedContent, getMaskContent, getMissingLocalesContent, getMissingLocalesContentFromDictionary, getMultilingualDictionary, getPerLocaleDictionary, getReplacedValuesContent, getSplittedContent, getSplittedDictionaryContent, insertContentInDictionary };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { COMPOSITE_ID_SEPARATOR, QUALIFIER_DYNAMIC_TYPES_KEY, QUALIFIER_ORDER, getDictionaryCompositeId, getDictionaryQualifierId, getDictionaryQualifierSegments, getDictionaryQualifierTypes, getDictionarySelectorCacheKey, isQualifiedDictionaryGroup, isQualifiedDynamicLoaderMap, parseDictionarySelector, reconstructQualifiedEntry, resolveQualifiedDictionary, resolveQualifiedDynamicContent, resolveQualifiedDynamicContentAsync } from "./qualifiedDictionary.mjs";
|
|
1
2
|
import { editDictionaryByKeyPath } from "./editDictionaryByKeyPath.mjs";
|
|
2
3
|
import { getContentNodeByKeyPath } from "./getContentNodeByKeyPath.mjs";
|
|
3
4
|
import { getDefaultNode } from "./getDefaultNode.mjs";
|
|
@@ -5,10 +6,11 @@ import { getEmptyNode } from "./getEmptyNode.mjs";
|
|
|
5
6
|
import { getNodeChildren } from "./getNodeChildren.mjs";
|
|
6
7
|
import { getNodeType } from "./getNodeType.mjs";
|
|
7
8
|
import { mergeDictionaries } from "./mergeDictionaries.mjs";
|
|
9
|
+
import { mergeQualifiedDictionaries } from "./mergeQualifiedDictionaries.mjs";
|
|
8
10
|
import { orderDictionaries } from "./orderDictionaries.mjs";
|
|
9
11
|
import { normalizeDictionaries, normalizeDictionary } from "./normalizeDictionary.mjs";
|
|
10
12
|
import { removeContentNodeByKeyPath } from "./removeContentNodeByKeyPath.mjs";
|
|
11
13
|
import { renameContentNodeByKeyPath } from "./renameContentNodeByKeyPath.mjs";
|
|
12
14
|
import { updateNodeChildren } from "./updateNodeChildren.mjs";
|
|
13
15
|
|
|
14
|
-
export { editDictionaryByKeyPath, getContentNodeByKeyPath, getDefaultNode, getEmptyNode, getNodeChildren, getNodeType, mergeDictionaries, normalizeDictionaries, normalizeDictionary, orderDictionaries, removeContentNodeByKeyPath, renameContentNodeByKeyPath, updateNodeChildren };
|
|
16
|
+
export { COMPOSITE_ID_SEPARATOR, QUALIFIER_DYNAMIC_TYPES_KEY, QUALIFIER_ORDER, editDictionaryByKeyPath, getContentNodeByKeyPath, getDefaultNode, getDictionaryCompositeId, getDictionaryQualifierId, getDictionaryQualifierSegments, getDictionaryQualifierTypes, getDictionarySelectorCacheKey, getEmptyNode, getNodeChildren, getNodeType, isQualifiedDictionaryGroup, isQualifiedDynamicLoaderMap, mergeDictionaries, mergeQualifiedDictionaries, normalizeDictionaries, normalizeDictionary, orderDictionaries, parseDictionarySelector, reconstructQualifiedEntry, removeContentNodeByKeyPath, renameContentNodeByKeyPath, resolveQualifiedDictionary, resolveQualifiedDynamicContent, resolveQualifiedDynamicContentAsync, updateNodeChildren };
|
|
@@ -47,11 +47,14 @@ const arrayMerge = (destinationArray, sourceArray) => {
|
|
|
47
47
|
};
|
|
48
48
|
const mergeDictionaries = (dictionaries) => {
|
|
49
49
|
const localIds = Array.from(new Set(dictionaries.filter((dict) => dict.localId).map((dict) => dict.localId)));
|
|
50
|
+
if (!dictionaries[0] || dictionaries.length <= 1) return dictionaries[0];
|
|
50
51
|
const dictionariesKeys = dictionaries.map((dict) => dict.key);
|
|
51
52
|
if (new Set(dictionariesKeys).size !== 1) throw new Error("All dictionaries must have the same key");
|
|
52
53
|
let mergedContent = dictionaries[0].content;
|
|
53
54
|
for (let i = 1; i < dictionaries.length; i++) {
|
|
54
|
-
const
|
|
55
|
+
const dict = dictionaries[i];
|
|
56
|
+
if (!dict) continue;
|
|
57
|
+
const currentDictionary = getMultilingualDictionary(dict);
|
|
55
58
|
checkTypesMatch(mergedContent, currentDictionary.content, currentDictionary.localId, currentDictionary.key, []);
|
|
56
59
|
mergedContent = customMerge(mergedContent, currentDictionary.content);
|
|
57
60
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mergeDictionaries.mjs","names":[],"sources":["../../../src/dictionaryManipulator/mergeDictionaries.ts"],"sourcesContent":["import { log } from '@intlayer/config/built';\nimport { colorizeKey, getAppLogger } from '@intlayer/config/logger';\nimport type {\n ContentNode,\n Dictionary,\n LocalDictionaryId,\n} from '@intlayer/types/dictionary';\nimport { getMultilingualDictionary } from '../deepTransformPlugins';\nimport { getNodeType } from './getNodeType';\n\n// Extended type that includes arrays for internal merge operations\ntype MergeableContent = ContentNode | ContentNode[];\n\nconst checkTypesMatch = (\n object1: ContentNode,\n object2: ContentNode,\n object2LocalId: LocalDictionaryId | undefined,\n dictionaryKey: string,\n path: string[] = []\n): void => {\n const appLogger = getAppLogger({ log });\n\n // If either side is missing/undefined, allow merge without error\n if (\n object1 === undefined ||\n object1 === null ||\n object2 === undefined ||\n object2 === null\n )\n return;\n\n const type1 = getNodeType(object1);\n const type2 = getNodeType(object2);\n\n // Unknown types are treated as flexible; skip strict mismatch reporting\n if (type1 === 'unknown' || type2 === 'unknown') return;\n\n if (type1 !== type2) {\n appLogger(\n [\n `Error: Dictionary ${colorizeKey(dictionaryKey)} has a multiple content files with type mismatch at path \"${path.join('.')}\": Cannot merge ${type1} with ${type2} while merging ${object2LocalId}`,\n ],\n {\n level: 'error',\n }\n );\n\n return;\n }\n};\n\n// Custom merge function that prefers destination (first dictionary) values\nconst customMerge = (\n destination: ContentNode,\n source: ContentNode\n): MergeableContent => {\n // If destination is undefined/null, use source\n if (destination === undefined || destination === null) {\n return source;\n }\n\n // If source is undefined/null, use destination\n if (source === undefined || source === null) {\n return destination;\n }\n\n // For primitive values, prefer destination (first dictionary)\n if (typeof destination !== 'object' || typeof source !== 'object') {\n return destination;\n }\n\n // For arrays, use our custom array merge\n if (Array.isArray(destination) && Array.isArray(source)) {\n return arrayMerge(\n destination as ContentNode[],\n source as ContentNode[]\n ) as MergeableContent;\n }\n\n // For objects, recursively merge with our custom logic\n if (typeof destination === 'object' && typeof source === 'object') {\n const result: Record<string, MergeableContent> = {};\n const allKeys = new Set([\n ...Object.keys(destination as unknown as Record<string, ContentNode>),\n ...Object.keys(source as unknown as Record<string, ContentNode>),\n ]);\n\n for (const key of allKeys) {\n result[key] = customMerge(\n (destination as unknown as Record<string, ContentNode>)[key],\n (source as unknown as Record<string, ContentNode>)[key]\n );\n }\n\n return result as unknown as MergeableContent;\n }\n\n // Fallback to destination\n return destination;\n};\n\n// Custom array merge strategy that merges arrays by key when present, otherwise by index\nconst arrayMerge = (\n destinationArray: ContentNode[],\n sourceArray: ContentNode[]\n): MergeableContent[] => {\n // Check if both arrays contain only primitives\n const destHasOnlyPrimitives = destinationArray.every(\n (item) => typeof item !== 'object' || item === null\n );\n const sourceHasOnlyPrimitives = sourceArray.every(\n (item) => typeof item !== 'object' || item === null\n );\n\n // If both arrays contain only primitives, use the source array (second dictionary)\n if (destHasOnlyPrimitives && sourceHasOnlyPrimitives) {\n return sourceArray;\n }\n\n // Otherwise, merge by index with object merging logic\n const result: MergeableContent[] = [];\n const maxLength = Math.max(destinationArray.length, sourceArray.length);\n\n for (let i = 0; i < maxLength; i++) {\n const destItem = destinationArray[i];\n const sourceItem = sourceArray[i];\n\n if (destItem === undefined && sourceItem === undefined) {\n } else if (destItem === undefined) {\n // Only source exists, add it\n result.push(sourceItem);\n } else if (sourceItem === undefined) {\n // Only destination exists, add it\n result.push(destItem);\n } else {\n // Both exist, merge them\n if (\n typeof destItem === 'object' &&\n typeof sourceItem === 'object' &&\n destItem !== null &&\n sourceItem !== null\n ) {\n // Check if both objects have a 'key' property for keyed merging\n if (\n 'key' in destItem &&\n 'key' in sourceItem &&\n (destItem as Record<string, string>).key ===\n (sourceItem as Record<string, string>).key\n ) {\n // Merge objects with same key, preferring destination (first dictionary) values\n result.push(customMerge(destItem, sourceItem));\n } else {\n // Merge objects by index, preferring destination (first dictionary) values\n result.push(customMerge(destItem, sourceItem));\n }\n } else {\n // For primitives or non-objects, use destination value (first dictionary)\n result.push(destItem);\n }\n }\n }\n\n return result;\n};\n\nexport const mergeDictionaries = (dictionaries: Dictionary[]): Dictionary => {\n const localIds = Array.from(\n new Set<LocalDictionaryId>(\n dictionaries.filter((dict) => dict.localId).map((dict) => dict.localId!)\n )\n );\n\n const dictionariesKeys = dictionaries.map((dict) => dict.key);\n\n // Check if all dictionaries have the same key\n if (new Set(dictionariesKeys).size !== 1) {\n throw new Error('All dictionaries must have the same key');\n }\n\n let mergedContent: Dictionary['content'] = dictionaries[0].content;\n\n for (let i = 1; i < dictionaries.length; i++) {\n // If the dictionary is a per-locale dictionary, transform it to a partial multilingual dictionary\n const currentDictionary = getMultilingualDictionary(
|
|
1
|
+
{"version":3,"file":"mergeDictionaries.mjs","names":[],"sources":["../../../src/dictionaryManipulator/mergeDictionaries.ts"],"sourcesContent":["import { log } from '@intlayer/config/built';\nimport { colorizeKey, getAppLogger } from '@intlayer/config/logger';\nimport type {\n ContentNode,\n Dictionary,\n LocalDictionaryId,\n} from '@intlayer/types/dictionary';\nimport { getMultilingualDictionary } from '../deepTransformPlugins';\nimport { getNodeType } from './getNodeType';\n\n// Extended type that includes arrays for internal merge operations\ntype MergeableContent = ContentNode | ContentNode[];\n\nconst checkTypesMatch = (\n object1: ContentNode,\n object2: ContentNode,\n object2LocalId: LocalDictionaryId | undefined,\n dictionaryKey: string,\n path: string[] = []\n): void => {\n const appLogger = getAppLogger({ log });\n\n // If either side is missing/undefined, allow merge without error\n if (\n object1 === undefined ||\n object1 === null ||\n object2 === undefined ||\n object2 === null\n )\n return;\n\n const type1 = getNodeType(object1);\n const type2 = getNodeType(object2);\n\n // Unknown types are treated as flexible; skip strict mismatch reporting\n if (type1 === 'unknown' || type2 === 'unknown') return;\n\n if (type1 !== type2) {\n appLogger(\n [\n `Error: Dictionary ${colorizeKey(dictionaryKey)} has a multiple content files with type mismatch at path \"${path.join('.')}\": Cannot merge ${type1} with ${type2} while merging ${object2LocalId}`,\n ],\n {\n level: 'error',\n }\n );\n\n return;\n }\n};\n\n// Custom merge function that prefers destination (first dictionary) values\nconst customMerge = (\n destination: ContentNode,\n source: ContentNode\n): MergeableContent => {\n // If destination is undefined/null, use source\n if (destination === undefined || destination === null) {\n return source;\n }\n\n // If source is undefined/null, use destination\n if (source === undefined || source === null) {\n return destination;\n }\n\n // For primitive values, prefer destination (first dictionary)\n if (typeof destination !== 'object' || typeof source !== 'object') {\n return destination;\n }\n\n // For arrays, use our custom array merge\n if (Array.isArray(destination) && Array.isArray(source)) {\n return arrayMerge(\n destination as ContentNode[],\n source as ContentNode[]\n ) as MergeableContent;\n }\n\n // For objects, recursively merge with our custom logic\n if (typeof destination === 'object' && typeof source === 'object') {\n const result: Record<string, MergeableContent> = {};\n const allKeys = new Set([\n ...Object.keys(destination as unknown as Record<string, ContentNode>),\n ...Object.keys(source as unknown as Record<string, ContentNode>),\n ]);\n\n for (const key of allKeys) {\n result[key] = customMerge(\n (destination as unknown as Record<string, ContentNode>)[key],\n (source as unknown as Record<string, ContentNode>)[key]\n );\n }\n\n return result as unknown as MergeableContent;\n }\n\n // Fallback to destination\n return destination;\n};\n\n// Custom array merge strategy that merges arrays by key when present, otherwise by index\nconst arrayMerge = (\n destinationArray: ContentNode[],\n sourceArray: ContentNode[]\n): MergeableContent[] => {\n // Check if both arrays contain only primitives\n const destHasOnlyPrimitives = destinationArray.every(\n (item) => typeof item !== 'object' || item === null\n );\n const sourceHasOnlyPrimitives = sourceArray.every(\n (item) => typeof item !== 'object' || item === null\n );\n\n // If both arrays contain only primitives, use the source array (second dictionary)\n if (destHasOnlyPrimitives && sourceHasOnlyPrimitives) {\n return sourceArray;\n }\n\n // Otherwise, merge by index with object merging logic\n const result: MergeableContent[] = [];\n const maxLength = Math.max(destinationArray.length, sourceArray.length);\n\n for (let i = 0; i < maxLength; i++) {\n const destItem = destinationArray[i];\n const sourceItem = sourceArray[i];\n\n if (destItem === undefined && sourceItem === undefined) {\n } else if (destItem === undefined) {\n // Only source exists, add it\n result.push(sourceItem);\n } else if (sourceItem === undefined) {\n // Only destination exists, add it\n result.push(destItem);\n } else {\n // Both exist, merge them\n if (\n typeof destItem === 'object' &&\n typeof sourceItem === 'object' &&\n destItem !== null &&\n sourceItem !== null\n ) {\n // Check if both objects have a 'key' property for keyed merging\n if (\n 'key' in destItem &&\n 'key' in sourceItem &&\n (destItem as Record<string, string>).key ===\n (sourceItem as Record<string, string>).key\n ) {\n // Merge objects with same key, preferring destination (first dictionary) values\n result.push(customMerge(destItem, sourceItem));\n } else {\n // Merge objects by index, preferring destination (first dictionary) values\n result.push(customMerge(destItem, sourceItem));\n }\n } else {\n // For primitives or non-objects, use destination value (first dictionary)\n result.push(destItem);\n }\n }\n }\n\n return result;\n};\n\nexport const mergeDictionaries = (dictionaries: Dictionary[]): Dictionary => {\n const localIds = Array.from(\n new Set<LocalDictionaryId>(\n dictionaries.filter((dict) => dict.localId).map((dict) => dict.localId!)\n )\n );\n\n if (!dictionaries[0] || dictionaries.length <= 1) {\n return dictionaries[0]!;\n }\n\n const dictionariesKeys = dictionaries.map((dict) => dict.key);\n\n // Check if all dictionaries have the same key\n if (new Set(dictionariesKeys).size !== 1) {\n throw new Error('All dictionaries must have the same key');\n }\n\n let mergedContent: Dictionary['content'] = dictionaries[0].content;\n\n for (let i = 1; i < dictionaries.length; i++) {\n const dict = dictionaries[i];\n\n if (!dict) continue;\n\n // If the dictionary is a per-locale dictionary, transform it to a partial multilingual dictionary\n const currentDictionary = getMultilingualDictionary(dict);\n\n // Check types before merging\n checkTypesMatch(\n mergedContent,\n currentDictionary.content,\n currentDictionary.localId,\n currentDictionary.key,\n []\n );\n\n mergedContent = customMerge(\n mergedContent,\n currentDictionary.content\n ) as ContentNode;\n }\n\n const mergedDictionary: Dictionary = {\n key: dictionaries[0].key,\n content: mergedContent,\n localIds,\n };\n\n return mergedDictionary;\n};\n"],"mappings":"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"}
|