@intlayer/core 9.2.0 → 9.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -24
- package/dist/cjs/_virtual/_rolldown/runtime.cjs +1 -2
- package/dist/cjs/deepTransformPlugins/getEditedContent.cjs +10 -6
- package/dist/cjs/deepTransformPlugins/getEditedContent.cjs.map +1 -1
- package/dist/cjs/deepTransformPlugins/getReplacedValuesContent.cjs +2 -1
- package/dist/cjs/deepTransformPlugins/getReplacedValuesContent.cjs.map +1 -1
- package/dist/cjs/deepTransformPlugins/insertContentInDictionary.cjs +41 -32
- package/dist/cjs/deepTransformPlugins/insertContentInDictionary.cjs.map +1 -1
- package/dist/cjs/dictionaryManipulator/editDictionaryByKeyPath.cjs +6 -5
- package/dist/cjs/dictionaryManipulator/editDictionaryByKeyPath.cjs.map +1 -1
- package/dist/cjs/dictionaryManipulator/getNestedDictionaryReferences.cjs +153 -0
- package/dist/cjs/dictionaryManipulator/getNestedDictionaryReferences.cjs.map +1 -0
- package/dist/cjs/dictionaryManipulator/index.cjs +16 -9
- package/dist/cjs/dictionaryManipulator/mergeDictionaries.cjs +4 -3
- package/dist/cjs/dictionaryManipulator/mergeDictionaries.cjs.map +1 -1
- package/dist/cjs/dictionaryManipulator/mergeQualifiedDictionaries.cjs +4 -3
- package/dist/cjs/dictionaryManipulator/mergeQualifiedDictionaries.cjs.map +1 -1
- package/dist/cjs/dictionaryManipulator/qualifiedDictionary.cjs +1 -171
- package/dist/cjs/dictionaryManipulator/qualifiedDictionary.cjs.map +1 -1
- package/dist/cjs/dictionaryManipulator/qualifiedDictionary.test-d.cjs.map +1 -1
- package/dist/cjs/dictionaryManipulator/qualifiedDictionaryDeclaration.cjs +69 -0
- package/dist/cjs/dictionaryManipulator/qualifiedDictionaryDeclaration.cjs.map +1 -0
- package/dist/cjs/dictionaryManipulator/qualifiedDynamicLoader.cjs +115 -0
- package/dist/cjs/dictionaryManipulator/qualifiedDynamicLoader.cjs.map +1 -0
- package/dist/cjs/dictionaryManipulator/removeContentNodeByKeyPath.cjs +4 -2
- package/dist/cjs/dictionaryManipulator/removeContentNodeByKeyPath.cjs.map +1 -1
- package/dist/cjs/dictionaryManipulator/renameContentNodeByKeyPath.cjs +11 -9
- package/dist/cjs/dictionaryManipulator/renameContentNodeByKeyPath.cjs.map +1 -1
- package/dist/cjs/formatters/date.cjs +2 -1
- package/dist/cjs/formatters/date.cjs.map +1 -1
- package/dist/cjs/formatters/relativeTime.cjs +3 -3
- package/dist/cjs/formatters/relativeTime.cjs.map +1 -1
- package/dist/cjs/index.cjs +16 -9
- package/dist/cjs/interpreter/getCondition.cjs +0 -1
- package/dist/cjs/interpreter/getCondition.cjs.map +1 -1
- package/dist/cjs/interpreter/getContent/plugins.cjs +12 -1
- package/dist/cjs/interpreter/getContent/plugins.cjs.map +1 -1
- package/dist/cjs/interpreter/getDictionary.cjs +2 -1
- package/dist/cjs/interpreter/getDictionary.cjs.map +1 -1
- package/dist/cjs/interpreter/getEnumeration.cjs +0 -1
- package/dist/cjs/interpreter/getEnumeration.cjs.map +1 -1
- package/dist/cjs/interpreter/getGender.cjs +0 -1
- package/dist/cjs/interpreter/getGender.cjs.map +1 -1
- package/dist/cjs/interpreter/getHTML.cjs +0 -1
- package/dist/cjs/interpreter/getHTML.cjs.map +1 -1
- package/dist/cjs/interpreter/getInsertion.cjs +0 -1
- package/dist/cjs/interpreter/getInsertion.cjs.map +1 -1
- package/dist/cjs/interpreter/getIntlayer.cjs.map +1 -1
- package/dist/cjs/interpreter/getIntlayer.test-d.cjs.map +1 -1
- package/dist/cjs/interpreter/getNesting.optimized.cjs +59 -0
- package/dist/cjs/interpreter/getNesting.optimized.cjs.map +1 -0
- package/dist/cjs/interpreter/getSelect.cjs +0 -1
- package/dist/cjs/interpreter/getSelect.cjs.map +1 -1
- package/dist/cjs/interpreter/getTranslation.cjs +0 -1
- package/dist/cjs/interpreter/getTranslation.cjs.map +1 -1
- package/dist/cjs/interpreter/splitAndJoinInsertion.cjs +0 -1
- package/dist/cjs/interpreter/splitAndJoinInsertion.cjs.map +1 -1
- package/dist/cjs/localization/domainUtils.cjs +0 -1
- package/dist/cjs/localization/domainUtils.cjs.map +1 -1
- package/dist/cjs/localization/getBrowserLocale.cjs +2 -1
- package/dist/cjs/localization/getBrowserLocale.cjs.map +1 -1
- package/dist/cjs/localization/getHTMLTextDir.cjs +0 -1
- package/dist/cjs/localization/getHTMLTextDir.cjs.map +1 -1
- package/dist/cjs/localization/getLocale.cjs +2 -1
- package/dist/cjs/localization/getLocale.cjs.map +1 -1
- package/dist/cjs/localization/getLocaleLang.cjs +0 -1
- package/dist/cjs/localization/getLocaleLang.cjs.map +1 -1
- package/dist/cjs/localization/localeDetector.cjs +2 -1
- package/dist/cjs/localization/localeDetector.cjs.map +1 -1
- package/dist/cjs/localization/proxyMode.cjs.map +1 -1
- package/dist/cjs/localization/rewriteUtils.cjs +0 -1
- package/dist/cjs/localization/rewriteUtils.cjs.map +1 -1
- package/dist/cjs/markdown/compiler.cjs +9 -6
- package/dist/cjs/markdown/compiler.cjs.map +1 -1
- package/dist/cjs/markdown/constants.cjs +0 -1
- package/dist/cjs/markdown/constants.cjs.map +1 -1
- package/dist/cjs/markdown/utils.cjs.map +1 -1
- package/dist/cjs/messageFormat/ICU.cjs +17 -14
- package/dist/cjs/messageFormat/ICU.cjs.map +1 -1
- package/dist/cjs/messageFormat/i18next.cjs +4 -2
- package/dist/cjs/messageFormat/i18next.cjs.map +1 -1
- package/dist/cjs/messageFormat/navigatePath.cjs +0 -1
- package/dist/cjs/messageFormat/navigatePath.cjs.map +1 -1
- package/dist/cjs/messageFormat/resolveMessage.cjs +2 -1
- package/dist/cjs/messageFormat/resolveMessage.cjs.map +1 -1
- package/dist/cjs/messageFormat/verify-icu-format.cjs +3 -2
- package/dist/cjs/messageFormat/verify-icu-format.cjs.map +1 -1
- package/dist/cjs/messageFormat/vue-i18n.cjs +2 -1
- package/dist/cjs/messageFormat/vue-i18n.cjs.map +1 -1
- package/dist/cjs/transpiler/condition/condition.cjs.map +1 -1
- package/dist/cjs/transpiler/enumeration/enumeration.cjs.map +1 -1
- package/dist/cjs/transpiler/file/file.cjs +2 -1
- package/dist/cjs/transpiler/file/file.cjs.map +1 -1
- package/dist/cjs/transpiler/file/fileBrowser.cjs +0 -1
- package/dist/cjs/transpiler/file/fileBrowser.cjs.map +1 -1
- package/dist/cjs/transpiler/gender/gender.cjs.map +1 -1
- package/dist/cjs/transpiler/html/getHTMLCustomComponents.cjs +0 -1
- package/dist/cjs/transpiler/html/getHTMLCustomComponents.cjs.map +1 -1
- package/dist/cjs/transpiler/html/html.cjs.map +1 -1
- package/dist/cjs/transpiler/html/htmlTags.cjs +0 -1
- package/dist/cjs/transpiler/html/htmlTags.cjs.map +1 -1
- package/dist/cjs/transpiler/html/validateHTML.cjs +12 -12
- package/dist/cjs/transpiler/html/validateHTML.cjs.map +1 -1
- package/dist/cjs/transpiler/insertion/getInsertionValues.cjs +0 -1
- package/dist/cjs/transpiler/insertion/getInsertionValues.cjs.map +1 -1
- package/dist/cjs/transpiler/insertion/insertion.cjs.map +1 -1
- package/dist/cjs/transpiler/markdown/getMarkdownMetadata.cjs +2 -1
- package/dist/cjs/transpiler/markdown/getMarkdownMetadata.cjs.map +1 -1
- package/dist/cjs/transpiler/markdown/markdown.cjs +2 -1
- package/dist/cjs/transpiler/markdown/markdown.cjs.map +1 -1
- package/dist/cjs/transpiler/markdown/validateMarkdown.cjs +7 -6
- package/dist/cjs/transpiler/markdown/validateMarkdown.cjs.map +1 -1
- package/dist/cjs/transpiler/nesting/nesting.cjs.map +1 -1
- package/dist/cjs/transpiler/plural/plural.cjs.map +1 -1
- package/dist/cjs/transpiler/select/select.cjs.map +1 -1
- package/dist/cjs/transpiler/translation/translation.cjs.map +1 -1
- package/dist/cjs/utils/checkIsURLAbsolute.cjs +0 -1
- package/dist/cjs/utils/checkIsURLAbsolute.cjs.map +1 -1
- package/dist/cjs/utils/cookieExpiry.cjs +0 -1
- package/dist/cjs/utils/cookieExpiry.cjs.map +1 -1
- package/dist/cjs/utils/getCookie.cjs +0 -1
- package/dist/cjs/utils/getCookie.cjs.map +1 -1
- package/dist/cjs/utils/isSameKeyPath.cjs +0 -1
- package/dist/cjs/utils/isSameKeyPath.cjs.map +1 -1
- package/dist/cjs/utils/isValidReactElement.cjs +0 -1
- package/dist/cjs/utils/isValidReactElement.cjs.map +1 -1
- package/dist/cjs/utils/parseYaml.cjs +0 -1
- package/dist/cjs/utils/parseYaml.cjs.map +1 -1
- package/dist/cjs/utils/stringifyYaml.cjs +0 -1
- package/dist/cjs/utils/stringifyYaml.cjs.map +1 -1
- package/dist/esm/deepTransformPlugins/getEditedContent.mjs +10 -6
- package/dist/esm/deepTransformPlugins/getEditedContent.mjs.map +1 -1
- package/dist/esm/deepTransformPlugins/getReplacedValuesContent.mjs +2 -1
- package/dist/esm/deepTransformPlugins/getReplacedValuesContent.mjs.map +1 -1
- package/dist/esm/deepTransformPlugins/insertContentInDictionary.mjs +41 -32
- package/dist/esm/deepTransformPlugins/insertContentInDictionary.mjs.map +1 -1
- package/dist/esm/dictionaryManipulator/editDictionaryByKeyPath.mjs +6 -5
- package/dist/esm/dictionaryManipulator/editDictionaryByKeyPath.mjs.map +1 -1
- package/dist/esm/dictionaryManipulator/getNestedDictionaryReferences.mjs +150 -0
- package/dist/esm/dictionaryManipulator/getNestedDictionaryReferences.mjs.map +1 -0
- package/dist/esm/dictionaryManipulator/index.mjs +5 -2
- package/dist/esm/dictionaryManipulator/mergeDictionaries.mjs +4 -3
- package/dist/esm/dictionaryManipulator/mergeDictionaries.mjs.map +1 -1
- package/dist/esm/dictionaryManipulator/mergeQualifiedDictionaries.mjs +2 -1
- package/dist/esm/dictionaryManipulator/mergeQualifiedDictionaries.mjs.map +1 -1
- package/dist/esm/dictionaryManipulator/qualifiedDictionary.mjs +1 -162
- package/dist/esm/dictionaryManipulator/qualifiedDictionary.mjs.map +1 -1
- package/dist/esm/dictionaryManipulator/qualifiedDictionaryDeclaration.mjs +64 -0
- package/dist/esm/dictionaryManipulator/qualifiedDictionaryDeclaration.mjs.map +1 -0
- package/dist/esm/dictionaryManipulator/qualifiedDynamicLoader.mjs +111 -0
- package/dist/esm/dictionaryManipulator/qualifiedDynamicLoader.mjs.map +1 -0
- package/dist/esm/dictionaryManipulator/removeContentNodeByKeyPath.mjs +4 -2
- package/dist/esm/dictionaryManipulator/removeContentNodeByKeyPath.mjs.map +1 -1
- package/dist/esm/dictionaryManipulator/renameContentNodeByKeyPath.mjs +11 -9
- package/dist/esm/dictionaryManipulator/renameContentNodeByKeyPath.mjs.map +1 -1
- package/dist/esm/formatters/date.mjs +2 -1
- package/dist/esm/formatters/date.mjs.map +1 -1
- package/dist/esm/formatters/relativeTime.mjs +3 -3
- package/dist/esm/formatters/relativeTime.mjs.map +1 -1
- package/dist/esm/index.mjs +5 -2
- package/dist/esm/interpreter/getContent/plugins.mjs +12 -1
- package/dist/esm/interpreter/getContent/plugins.mjs.map +1 -1
- package/dist/esm/interpreter/getDictionary.mjs +2 -1
- package/dist/esm/interpreter/getDictionary.mjs.map +1 -1
- package/dist/esm/interpreter/getNesting.optimized.mjs +58 -0
- package/dist/esm/interpreter/getNesting.optimized.mjs.map +1 -0
- package/dist/esm/localization/getBrowserLocale.mjs +2 -1
- package/dist/esm/localization/getBrowserLocale.mjs.map +1 -1
- package/dist/esm/localization/getLocale.mjs +2 -1
- package/dist/esm/localization/getLocale.mjs.map +1 -1
- package/dist/esm/localization/localeDetector.mjs +2 -1
- package/dist/esm/localization/localeDetector.mjs.map +1 -1
- package/dist/esm/markdown/compiler.mjs +9 -6
- package/dist/esm/markdown/compiler.mjs.map +1 -1
- package/dist/esm/markdown/utils.mjs.map +1 -1
- package/dist/esm/messageFormat/ICU.mjs +17 -14
- package/dist/esm/messageFormat/ICU.mjs.map +1 -1
- package/dist/esm/messageFormat/i18next.mjs +4 -2
- package/dist/esm/messageFormat/i18next.mjs.map +1 -1
- package/dist/esm/messageFormat/resolveMessage.mjs +2 -1
- package/dist/esm/messageFormat/resolveMessage.mjs.map +1 -1
- package/dist/esm/messageFormat/verify-icu-format.mjs +3 -2
- package/dist/esm/messageFormat/verify-icu-format.mjs.map +1 -1
- package/dist/esm/messageFormat/vue-i18n.mjs +2 -1
- package/dist/esm/messageFormat/vue-i18n.mjs.map +1 -1
- package/dist/esm/transpiler/file/file.mjs +2 -1
- package/dist/esm/transpiler/file/file.mjs.map +1 -1
- package/dist/esm/transpiler/html/validateHTML.mjs +12 -11
- package/dist/esm/transpiler/html/validateHTML.mjs.map +1 -1
- package/dist/esm/transpiler/markdown/getMarkdownMetadata.mjs +2 -1
- package/dist/esm/transpiler/markdown/getMarkdownMetadata.mjs.map +1 -1
- package/dist/esm/transpiler/markdown/markdown.mjs +2 -1
- package/dist/esm/transpiler/markdown/markdown.mjs.map +1 -1
- package/dist/esm/transpiler/markdown/validateMarkdown.mjs +7 -6
- package/dist/esm/transpiler/markdown/validateMarkdown.mjs.map +1 -1
- package/dist/esm/utils/parseYaml.mjs.map +1 -1
- package/dist/types/deepTransformPlugins/getEditedContent.d.ts.map +1 -1
- package/dist/types/deepTransformPlugins/getFilterMissingTranslationsContent.d.ts +1 -0
- package/dist/types/deepTransformPlugins/getFilterTranslationsOnlyContent.d.ts +1 -0
- package/dist/types/deepTransformPlugins/getFilteredLocalesContent.d.ts +1 -0
- package/dist/types/dictionaryManipulator/getNestedDictionaryReferences.d.ts +62 -0
- package/dist/types/dictionaryManipulator/getNestedDictionaryReferences.d.ts.map +1 -0
- package/dist/types/dictionaryManipulator/index.d.ts +5 -2
- package/dist/types/dictionaryManipulator/mergeQualifiedDictionaries.d.ts.map +1 -1
- package/dist/types/dictionaryManipulator/qualifiedDictionary.d.ts +18 -126
- package/dist/types/dictionaryManipulator/qualifiedDictionary.d.ts.map +1 -1
- package/dist/types/dictionaryManipulator/qualifiedDictionaryDeclaration.d.ts +41 -0
- package/dist/types/dictionaryManipulator/qualifiedDictionaryDeclaration.d.ts.map +1 -0
- package/dist/types/dictionaryManipulator/qualifiedDynamicLoader.d.ts +93 -0
- package/dist/types/dictionaryManipulator/qualifiedDynamicLoader.d.ts.map +1 -0
- package/dist/types/index.d.ts +5 -2
- package/dist/types/interpreter/getContent/plugins.d.ts +10 -0
- package/dist/types/interpreter/getContent/plugins.d.ts.map +1 -1
- package/dist/types/interpreter/getNesting.optimized.d.ts +33 -0
- package/dist/types/interpreter/getNesting.optimized.d.ts.map +1 -0
- package/package.json +7 -7
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
let _intlayer_types_nodeType = require("@intlayer/types/nodeType");
|
|
3
|
+
|
|
4
|
+
//#region src/dictionaryManipulator/getNestedDictionaryReferences.ts
|
|
5
|
+
/**
|
|
6
|
+
* Compact alias some minified pipelines emit in place of `nodeType: 'nested'`.
|
|
7
|
+
* Matched defensively so the scan keeps working on already-minified content.
|
|
8
|
+
*/
|
|
9
|
+
const MINIFIED_NESTED_NODE_TYPE = "n";
|
|
10
|
+
/**
|
|
11
|
+
* Narrows a value to a traversable object (plain object or array). `null` is
|
|
12
|
+
* excluded even though `typeof null === 'object'`.
|
|
13
|
+
*/
|
|
14
|
+
const isTraversable = (value) => typeof value === "object" && value !== null;
|
|
15
|
+
/**
|
|
16
|
+
* Records one `nest()` reference, widening the entry to `'all'` as soon as a
|
|
17
|
+
* reference targets the whole dictionary.
|
|
18
|
+
*/
|
|
19
|
+
const addNestedReference = (references, dictionaryKey, path) => {
|
|
20
|
+
const existingReference = references.get(dictionaryKey);
|
|
21
|
+
if (existingReference === "all") return;
|
|
22
|
+
const rootField = path?.split(".")[0];
|
|
23
|
+
if (!rootField) {
|
|
24
|
+
references.set(dictionaryKey, "all");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (existingReference) {
|
|
28
|
+
existingReference.add(rootField);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
references.set(dictionaryKey, /* @__PURE__ */ new Set([rootField]));
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Walks a dictionary tree and collects every dictionary referenced through a
|
|
35
|
+
* `nest()` node, together with the top-level fields those references read.
|
|
36
|
+
*
|
|
37
|
+
* The walk is iterative (no recursion depth limit) and guards against shared
|
|
38
|
+
* or circular references, so it is safe on both compiled JSON and in-memory
|
|
39
|
+
* dictionaries.
|
|
40
|
+
*
|
|
41
|
+
* @param dictionaries - Dictionary objects (or any content subtree) to scan.
|
|
42
|
+
* @returns Map of nest-target dictionary key → referenced top-level fields.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* // dashboard.content.ts → { period: nest('common', 'period') }
|
|
47
|
+
* getNestedDictionaryReferences([dashboardDictionary]);
|
|
48
|
+
* // Map { 'common' => Set { 'period' } }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
const getNestedDictionaryReferences = (dictionaries) => {
|
|
52
|
+
const references = /* @__PURE__ */ new Map();
|
|
53
|
+
const stack = [...dictionaries];
|
|
54
|
+
const visitedNodes = /* @__PURE__ */ new Set();
|
|
55
|
+
while (stack.length > 0) {
|
|
56
|
+
const node = stack.pop();
|
|
57
|
+
if (!isTraversable(node)) continue;
|
|
58
|
+
if (visitedNodes.has(node)) continue;
|
|
59
|
+
visitedNodes.add(node);
|
|
60
|
+
if (Array.isArray(node)) {
|
|
61
|
+
stack.push(...node);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const { nodeType } = node;
|
|
65
|
+
if (nodeType === _intlayer_types_nodeType.NESTED || nodeType === MINIFIED_NESTED_NODE_TYPE) {
|
|
66
|
+
const nestedState = node[nodeType];
|
|
67
|
+
if (isTraversable(nestedState)) {
|
|
68
|
+
const { dictionaryKey, path } = nestedState;
|
|
69
|
+
if (typeof dictionaryKey === "string") addNestedReference(references, dictionaryKey, typeof path === "string" ? path : void 0);
|
|
70
|
+
}
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
stack.push(...Object.values(node));
|
|
74
|
+
}
|
|
75
|
+
return references;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Keys of every dictionary referenced through a `nest()` node.
|
|
79
|
+
*
|
|
80
|
+
* @param dictionaries - Dictionary objects (or any content subtree) to scan.
|
|
81
|
+
* @returns Set of nest-target dictionary keys.
|
|
82
|
+
*/
|
|
83
|
+
const getNestedDictionaryKeys = (dictionaries) => new Set(getNestedDictionaryReferences(dictionaries).keys());
|
|
84
|
+
/**
|
|
85
|
+
* Expands direct nest edges into their transitive closure, so a consumer that
|
|
86
|
+
* nests a dictionary which itself nests another ends up depending on both.
|
|
87
|
+
*
|
|
88
|
+
* Self-references and cycles are absorbed: a key never appears in its own
|
|
89
|
+
* dependency set, and the walk terminates on cyclic graphs.
|
|
90
|
+
*/
|
|
91
|
+
const closeOverNestedEdges = (directEdges) => {
|
|
92
|
+
const closedEdges = /* @__PURE__ */ new Map();
|
|
93
|
+
for (const [consumerKey, directTargets] of directEdges) {
|
|
94
|
+
const allTargets = /* @__PURE__ */ new Set();
|
|
95
|
+
const pending = [...directTargets];
|
|
96
|
+
while (pending.length > 0) {
|
|
97
|
+
const targetKey = pending.pop();
|
|
98
|
+
if (targetKey === consumerKey) continue;
|
|
99
|
+
if (allTargets.has(targetKey)) continue;
|
|
100
|
+
allTargets.add(targetKey);
|
|
101
|
+
const transitiveTargets = directEdges.get(targetKey);
|
|
102
|
+
if (transitiveTargets) pending.push(...transitiveTargets);
|
|
103
|
+
}
|
|
104
|
+
closedEdges.set(consumerKey, allTargets);
|
|
105
|
+
}
|
|
106
|
+
return closedEdges;
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Builds the `nest()` dependency graph of a set of dictionaries: for each
|
|
110
|
+
* dictionary key, the keys of every dictionary it needs in order to resolve its
|
|
111
|
+
* nested nodes, transitively.
|
|
112
|
+
*
|
|
113
|
+
* The build optimization uses this to attach the nest targets to the dictionary
|
|
114
|
+
* artifact itself, so `nest()` resolves from a local reference and the target
|
|
115
|
+
* lands in the same chunk (or the same lazy per-locale loader) as the
|
|
116
|
+
* dictionary referencing it.
|
|
117
|
+
*
|
|
118
|
+
* Dictionaries without nested nodes are absent from the result.
|
|
119
|
+
*
|
|
120
|
+
* @param dictionaries - Compiled dictionaries to scan; each must carry its `key`.
|
|
121
|
+
* @returns Map of dictionary key → keys it depends on through `nest()`.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```ts
|
|
125
|
+
* // dashboard nests common, common nests labels
|
|
126
|
+
* getNestedDictionaryGraph([dashboard, common]);
|
|
127
|
+
* // Map { 'dashboard' => Set { 'common', 'labels' }, 'common' => Set { 'labels' } }
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
const getNestedDictionaryGraph = (dictionaries) => {
|
|
131
|
+
const directEdges = /* @__PURE__ */ new Map();
|
|
132
|
+
for (const dictionary of dictionaries) {
|
|
133
|
+
if (!isTraversable(dictionary)) continue;
|
|
134
|
+
const { key } = dictionary;
|
|
135
|
+
if (typeof key !== "string") continue;
|
|
136
|
+
const targets = getNestedDictionaryKeys([dictionary]);
|
|
137
|
+
targets.delete(key);
|
|
138
|
+
if (targets.size === 0) continue;
|
|
139
|
+
const existingTargets = directEdges.get(key);
|
|
140
|
+
if (existingTargets) {
|
|
141
|
+
for (const target of targets) existingTargets.add(target);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
directEdges.set(key, targets);
|
|
145
|
+
}
|
|
146
|
+
return closeOverNestedEdges(directEdges);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
//#endregion
|
|
150
|
+
exports.getNestedDictionaryGraph = getNestedDictionaryGraph;
|
|
151
|
+
exports.getNestedDictionaryKeys = getNestedDictionaryKeys;
|
|
152
|
+
exports.getNestedDictionaryReferences = getNestedDictionaryReferences;
|
|
153
|
+
//# sourceMappingURL=getNestedDictionaryReferences.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getNestedDictionaryReferences.cjs","names":["NESTED"],"sources":["../../../src/dictionaryManipulator/getNestedDictionaryReferences.ts"],"sourcesContent":["import { NESTED } from '@intlayer/types/nodeType';\n\n/**\n * Top-level content fields of a nest target that are referenced through\n * `nest()` nodes.\n *\n * `'all'` means at least one reference targets the whole dictionary\n * (`nest('common')` without a path), so no field of that dictionary may be\n * dropped.\n */\nexport type NestedFieldReferences = Set<string> | 'all';\n\n/**\n * Compact alias some minified pipelines emit in place of `nodeType: 'nested'`.\n * Matched defensively so the scan keeps working on already-minified content.\n */\nconst MINIFIED_NESTED_NODE_TYPE = 'n';\n\ntype UnknownRecord = Record<string, unknown>;\n\n/**\n * Narrows a value to a traversable object (plain object or array). `null` is\n * excluded even though `typeof null === 'object'`.\n */\nconst isTraversable = (value: unknown): value is UnknownRecord =>\n typeof value === 'object' && value !== null;\n\n/**\n * Records one `nest()` reference, widening the entry to `'all'` as soon as a\n * reference targets the whole dictionary.\n */\nconst addNestedReference = (\n references: Map<string, NestedFieldReferences>,\n dictionaryKey: string,\n path: string | undefined\n): void => {\n const existingReference = references.get(dictionaryKey);\n\n if (existingReference === 'all') return;\n\n // `getNesting` returns the whole dictionary when no path is given, and also\n // falls back to it when the path cannot be resolved — both cases require\n // every field to stay available.\n const rootField = path?.split('.')[0];\n\n if (!rootField) {\n references.set(dictionaryKey, 'all');\n return;\n }\n\n if (existingReference) {\n existingReference.add(rootField);\n return;\n }\n\n references.set(dictionaryKey, new Set([rootField]));\n};\n\n/**\n * Walks a dictionary tree and collects every dictionary referenced through a\n * `nest()` node, together with the top-level fields those references read.\n *\n * The walk is iterative (no recursion depth limit) and guards against shared\n * or circular references, so it is safe on both compiled JSON and in-memory\n * dictionaries.\n *\n * @param dictionaries - Dictionary objects (or any content subtree) to scan.\n * @returns Map of nest-target dictionary key → referenced top-level fields.\n *\n * @example\n * ```ts\n * // dashboard.content.ts → { period: nest('common', 'period') }\n * getNestedDictionaryReferences([dashboardDictionary]);\n * // Map { 'common' => Set { 'period' } }\n * ```\n */\nexport const getNestedDictionaryReferences = (\n dictionaries: Iterable<unknown>\n): Map<string, NestedFieldReferences> => {\n const references = new Map<string, NestedFieldReferences>();\n const stack: unknown[] = [...dictionaries];\n const visitedNodes = new Set<object>();\n\n while (stack.length > 0) {\n const node = stack.pop();\n\n if (!isTraversable(node)) continue;\n if (visitedNodes.has(node)) continue;\n visitedNodes.add(node);\n\n if (Array.isArray(node)) {\n stack.push(...node);\n continue;\n }\n\n const { nodeType } = node;\n\n if (nodeType === NESTED || nodeType === MINIFIED_NESTED_NODE_TYPE) {\n const nestedState = node[nodeType];\n\n if (isTraversable(nestedState)) {\n const { dictionaryKey, path } = nestedState;\n\n if (typeof dictionaryKey === 'string') {\n addNestedReference(\n references,\n dictionaryKey,\n typeof path === 'string' ? path : undefined\n );\n }\n }\n\n // A nested node only carries the reference descriptor — no sub-content.\n continue;\n }\n\n stack.push(...Object.values(node));\n }\n\n return references;\n};\n\n/**\n * Keys of every dictionary referenced through a `nest()` node.\n *\n * @param dictionaries - Dictionary objects (or any content subtree) to scan.\n * @returns Set of nest-target dictionary keys.\n */\nexport const getNestedDictionaryKeys = (\n dictionaries: Iterable<unknown>\n): Set<string> => new Set(getNestedDictionaryReferences(dictionaries).keys());\n\n/**\n * Expands direct nest edges into their transitive closure, so a consumer that\n * nests a dictionary which itself nests another ends up depending on both.\n *\n * Self-references and cycles are absorbed: a key never appears in its own\n * dependency set, and the walk terminates on cyclic graphs.\n */\nconst closeOverNestedEdges = (\n directEdges: Map<string, Set<string>>\n): Map<string, Set<string>> => {\n const closedEdges = new Map<string, Set<string>>();\n\n for (const [consumerKey, directTargets] of directEdges) {\n const allTargets = new Set<string>();\n const pending = [...directTargets];\n\n while (pending.length > 0) {\n const targetKey = pending.pop()!;\n\n if (targetKey === consumerKey) continue;\n if (allTargets.has(targetKey)) continue;\n allTargets.add(targetKey);\n\n const transitiveTargets = directEdges.get(targetKey);\n if (transitiveTargets) pending.push(...transitiveTargets);\n }\n\n closedEdges.set(consumerKey, allTargets);\n }\n\n return closedEdges;\n};\n\n/**\n * Builds the `nest()` dependency graph of a set of dictionaries: for each\n * dictionary key, the keys of every dictionary it needs in order to resolve its\n * nested nodes, transitively.\n *\n * The build optimization uses this to attach the nest targets to the dictionary\n * artifact itself, so `nest()` resolves from a local reference and the target\n * lands in the same chunk (or the same lazy per-locale loader) as the\n * dictionary referencing it.\n *\n * Dictionaries without nested nodes are absent from the result.\n *\n * @param dictionaries - Compiled dictionaries to scan; each must carry its `key`.\n * @returns Map of dictionary key → keys it depends on through `nest()`.\n *\n * @example\n * ```ts\n * // dashboard nests common, common nests labels\n * getNestedDictionaryGraph([dashboard, common]);\n * // Map { 'dashboard' => Set { 'common', 'labels' }, 'common' => Set { 'labels' } }\n * ```\n */\nexport const getNestedDictionaryGraph = (\n dictionaries: Iterable<unknown>\n): Map<string, Set<string>> => {\n const directEdges = new Map<string, Set<string>>();\n\n for (const dictionary of dictionaries) {\n if (!isTraversable(dictionary)) continue;\n\n const { key } = dictionary;\n if (typeof key !== 'string') continue;\n\n const targets = getNestedDictionaryKeys([dictionary]);\n targets.delete(key);\n\n if (targets.size === 0) continue;\n\n // Several compiled entries can share a key (qualified groups); merge them.\n const existingTargets = directEdges.get(key);\n if (existingTargets) {\n for (const target of targets) existingTargets.add(target);\n continue;\n }\n\n directEdges.set(key, targets);\n }\n\n return closeOverNestedEdges(directEdges);\n};\n"],"mappings":";;;;;;;;AAgBA,MAAM,4BAA4B;;;;;AAQlC,MAAM,iBAAiB,UACrB,OAAO,UAAU,YAAY,UAAU;;;;;AAMzC,MAAM,sBACJ,YACA,eACA,SACS;CACT,MAAM,oBAAoB,WAAW,IAAI,aAAa;CAEtD,IAAI,sBAAsB,OAAO;CAKjC,MAAM,YAAY,MAAM,MAAM,GAAG,CAAC,CAAC;CAEnC,IAAI,CAAC,WAAW;EACd,WAAW,IAAI,eAAe,KAAK;EACnC;CACF;CAEA,IAAI,mBAAmB;EACrB,kBAAkB,IAAI,SAAS;EAC/B;CACF;CAEA,WAAW,IAAI,+BAAe,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;AACpD;;;;;;;;;;;;;;;;;;;AAoBA,MAAa,iCACX,iBACuC;CACvC,MAAM,6BAAa,IAAI,IAAmC;CAC1D,MAAM,QAAmB,CAAC,GAAG,YAAY;CACzC,MAAM,+BAAe,IAAI,IAAY;CAErC,OAAO,MAAM,SAAS,GAAG;EACvB,MAAM,OAAO,MAAM,IAAI;EAEvB,IAAI,CAAC,cAAc,IAAI,GAAG;EAC1B,IAAI,aAAa,IAAI,IAAI,GAAG;EAC5B,aAAa,IAAI,IAAI;EAErB,IAAI,MAAM,QAAQ,IAAI,GAAG;GACvB,MAAM,KAAK,GAAG,IAAI;GAClB;EACF;EAEA,MAAM,EAAE,aAAa;EAErB,IAAI,aAAaA,mCAAU,aAAa,2BAA2B;GACjE,MAAM,cAAc,KAAK;GAEzB,IAAI,cAAc,WAAW,GAAG;IAC9B,MAAM,EAAE,eAAe,SAAS;IAEhC,IAAI,OAAO,kBAAkB,UAC3B,mBACE,YACA,eACA,OAAO,SAAS,WAAW,OAAO,MACpC;GAEJ;GAGA;EACF;EAEA,MAAM,KAAK,GAAG,OAAO,OAAO,IAAI,CAAC;CACnC;CAEA,OAAO;AACT;;;;;;;AAQA,MAAa,2BACX,iBACgB,IAAI,IAAI,8BAA8B,YAAY,CAAC,CAAC,KAAK,CAAC;;;;;;;;AAS5E,MAAM,wBACJ,gBAC6B;CAC7B,MAAM,8BAAc,IAAI,IAAyB;CAEjD,KAAK,MAAM,CAAC,aAAa,kBAAkB,aAAa;EACtD,MAAM,6BAAa,IAAI,IAAY;EACnC,MAAM,UAAU,CAAC,GAAG,aAAa;EAEjC,OAAO,QAAQ,SAAS,GAAG;GACzB,MAAM,YAAY,QAAQ,IAAI;GAE9B,IAAI,cAAc,aAAa;GAC/B,IAAI,WAAW,IAAI,SAAS,GAAG;GAC/B,WAAW,IAAI,SAAS;GAExB,MAAM,oBAAoB,YAAY,IAAI,SAAS;GACnD,IAAI,mBAAmB,QAAQ,KAAK,GAAG,iBAAiB;EAC1D;EAEA,YAAY,IAAI,aAAa,UAAU;CACzC;CAEA,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAa,4BACX,iBAC6B;CAC7B,MAAM,8BAAc,IAAI,IAAyB;CAEjD,KAAK,MAAM,cAAc,cAAc;EACrC,IAAI,CAAC,cAAc,UAAU,GAAG;EAEhC,MAAM,EAAE,QAAQ;EAChB,IAAI,OAAO,QAAQ,UAAU;EAE7B,MAAM,UAAU,wBAAwB,CAAC,UAAU,CAAC;EACpD,QAAQ,OAAO,GAAG;EAElB,IAAI,QAAQ,SAAS,GAAG;EAGxB,MAAM,kBAAkB,YAAY,IAAI,GAAG;EAC3C,IAAI,iBAAiB;GACnB,KAAK,MAAM,UAAU,SAAS,gBAAgB,IAAI,MAAM;GACxD;EACF;EAEA,YAAY,IAAI,KAAK,OAAO;CAC9B;CAEA,OAAO,qBAAqB,WAAW;AACzC"}
|
|
@@ -4,33 +4,39 @@ const require_dictionaryManipulator_editDictionaryByKeyPath = require('./editDic
|
|
|
4
4
|
const require_dictionaryManipulator_getContentNodeByKeyPath = require('./getContentNodeByKeyPath.cjs');
|
|
5
5
|
const require_dictionaryManipulator_getDefaultNode = require('./getDefaultNode.cjs');
|
|
6
6
|
const require_dictionaryManipulator_getEmptyNode = require('./getEmptyNode.cjs');
|
|
7
|
+
const require_dictionaryManipulator_getNestedDictionaryReferences = require('./getNestedDictionaryReferences.cjs');
|
|
7
8
|
const require_dictionaryManipulator_getNodeChildren = require('./getNodeChildren.cjs');
|
|
8
9
|
const require_dictionaryManipulator_getNodeType = require('./getNodeType.cjs');
|
|
9
10
|
const require_dictionaryManipulator_mergeDictionaries = require('./mergeDictionaries.cjs');
|
|
11
|
+
const require_dictionaryManipulator_qualifiedDictionaryDeclaration = require('./qualifiedDictionaryDeclaration.cjs');
|
|
10
12
|
const require_dictionaryManipulator_mergeQualifiedDictionaries = require('./mergeQualifiedDictionaries.cjs');
|
|
11
13
|
const require_dictionaryManipulator_orderDictionaries = require('./orderDictionaries.cjs');
|
|
12
14
|
const require_dictionaryManipulator_normalizeDictionary = require('./normalizeDictionary.cjs');
|
|
15
|
+
const require_dictionaryManipulator_qualifiedDynamicLoader = require('./qualifiedDynamicLoader.cjs');
|
|
13
16
|
const require_dictionaryManipulator_removeContentNodeByKeyPath = require('./removeContentNodeByKeyPath.cjs');
|
|
14
17
|
const require_dictionaryManipulator_renameContentNodeByKeyPath = require('./renameContentNodeByKeyPath.cjs');
|
|
15
18
|
const require_dictionaryManipulator_updateNodeChildren = require('./updateNodeChildren.cjs');
|
|
16
19
|
|
|
17
20
|
exports.COMPOSITE_ID_SEPARATOR = require_dictionaryManipulator_qualifiedDictionary.COMPOSITE_ID_SEPARATOR;
|
|
18
21
|
exports.DEFAULT_VARIANT_ID = require_dictionaryManipulator_qualifiedDictionary.DEFAULT_VARIANT_ID;
|
|
19
|
-
exports.QUALIFIER_DYNAMIC_TYPES_KEY =
|
|
20
|
-
exports.QUALIFIER_ORDER =
|
|
22
|
+
exports.QUALIFIER_DYNAMIC_TYPES_KEY = require_dictionaryManipulator_qualifiedDynamicLoader.QUALIFIER_DYNAMIC_TYPES_KEY;
|
|
23
|
+
exports.QUALIFIER_ORDER = require_dictionaryManipulator_qualifiedDictionaryDeclaration.QUALIFIER_ORDER;
|
|
21
24
|
exports.editDictionaryByKeyPath = require_dictionaryManipulator_editDictionaryByKeyPath.editDictionaryByKeyPath;
|
|
22
25
|
exports.getContentNodeByKeyPath = require_dictionaryManipulator_getContentNodeByKeyPath.getContentNodeByKeyPath;
|
|
23
26
|
exports.getDefaultNode = require_dictionaryManipulator_getDefaultNode.getDefaultNode;
|
|
24
|
-
exports.getDictionaryCompositeIds =
|
|
25
|
-
exports.getDictionaryQualifierIds =
|
|
26
|
-
exports.getDictionaryQualifierTypes =
|
|
27
|
+
exports.getDictionaryCompositeIds = require_dictionaryManipulator_qualifiedDictionaryDeclaration.getDictionaryCompositeIds;
|
|
28
|
+
exports.getDictionaryQualifierIds = require_dictionaryManipulator_qualifiedDictionaryDeclaration.getDictionaryQualifierIds;
|
|
29
|
+
exports.getDictionaryQualifierTypes = require_dictionaryManipulator_qualifiedDictionaryDeclaration.getDictionaryQualifierTypes;
|
|
27
30
|
exports.getDictionarySelectorCacheKey = require_dictionaryManipulator_qualifiedDictionary.getDictionarySelectorCacheKey;
|
|
28
31
|
exports.getEmptyNode = require_dictionaryManipulator_getEmptyNode.getEmptyNode;
|
|
32
|
+
exports.getNestedDictionaryGraph = require_dictionaryManipulator_getNestedDictionaryReferences.getNestedDictionaryGraph;
|
|
33
|
+
exports.getNestedDictionaryKeys = require_dictionaryManipulator_getNestedDictionaryReferences.getNestedDictionaryKeys;
|
|
34
|
+
exports.getNestedDictionaryReferences = require_dictionaryManipulator_getNestedDictionaryReferences.getNestedDictionaryReferences;
|
|
29
35
|
exports.getNodeChildren = require_dictionaryManipulator_getNodeChildren.getNodeChildren;
|
|
30
36
|
exports.getNodeType = require_dictionaryManipulator_getNodeType.getNodeType;
|
|
31
|
-
exports.getVariantIds =
|
|
37
|
+
exports.getVariantIds = require_dictionaryManipulator_qualifiedDictionaryDeclaration.getVariantIds;
|
|
32
38
|
exports.isQualifiedDictionaryGroup = require_dictionaryManipulator_qualifiedDictionary.isQualifiedDictionaryGroup;
|
|
33
|
-
exports.isQualifiedDynamicLoaderMap =
|
|
39
|
+
exports.isQualifiedDynamicLoaderMap = require_dictionaryManipulator_qualifiedDynamicLoader.isQualifiedDynamicLoaderMap;
|
|
34
40
|
exports.mergeDictionaries = require_dictionaryManipulator_mergeDictionaries.mergeDictionaries;
|
|
35
41
|
exports.mergeQualifiedDictionaries = require_dictionaryManipulator_mergeQualifiedDictionaries.mergeQualifiedDictionaries;
|
|
36
42
|
exports.normalizeDictionaries = require_dictionaryManipulator_normalizeDictionary.normalizeDictionaries;
|
|
@@ -41,10 +47,11 @@ exports.reconstructQualifiedEntry = require_dictionaryManipulator_qualifiedDicti
|
|
|
41
47
|
exports.removeContentNodeByKeyPath = require_dictionaryManipulator_removeContentNodeByKeyPath.removeContentNodeByKeyPath;
|
|
42
48
|
exports.renameContentNodeByKeyPath = require_dictionaryManipulator_renameContentNodeByKeyPath.renameContentNodeByKeyPath;
|
|
43
49
|
exports.resolveDictionaryArgument = require_dictionaryManipulator_qualifiedDictionary.resolveDictionaryArgument;
|
|
50
|
+
exports.resolveEffectiveVariantId = require_dictionaryManipulator_qualifiedDictionary.resolveEffectiveVariantId;
|
|
44
51
|
exports.resolveProviderVariant = require_dictionaryManipulator_qualifiedDictionary.resolveProviderVariant;
|
|
45
52
|
exports.resolveQualifiedDictionary = require_dictionaryManipulator_qualifiedDictionary.resolveQualifiedDictionary;
|
|
46
|
-
exports.resolveQualifiedDynamicContent =
|
|
47
|
-
exports.resolveQualifiedDynamicContentAsync =
|
|
53
|
+
exports.resolveQualifiedDynamicContent = require_dictionaryManipulator_qualifiedDynamicLoader.resolveQualifiedDynamicContent;
|
|
54
|
+
exports.resolveQualifiedDynamicContentAsync = require_dictionaryManipulator_qualifiedDynamicLoader.resolveQualifiedDynamicContentAsync;
|
|
48
55
|
exports.serializeVariant = require_dictionaryManipulator_qualifiedDictionary.serializeVariant;
|
|
49
56
|
exports.serializeVariantChain = require_dictionaryManipulator_qualifiedDictionary.serializeVariantChain;
|
|
50
57
|
exports.updateNodeChildren = require_dictionaryManipulator_updateNodeChildren.updateNodeChildren;
|
|
@@ -40,9 +40,10 @@ const arrayMerge = (destinationArray, sourceArray) => {
|
|
|
40
40
|
const sourceItem = sourceArray[i];
|
|
41
41
|
if (destItem === void 0 && sourceItem === void 0) {} else if (destItem === void 0) result.push(sourceItem);
|
|
42
42
|
else if (sourceItem === void 0) result.push(destItem);
|
|
43
|
-
else if (typeof destItem === "object" && typeof sourceItem === "object" && destItem !== null && sourceItem !== null)
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
else if (typeof destItem === "object" && typeof sourceItem === "object" && destItem !== null && sourceItem !== null) {
|
|
44
|
+
if ("key" in destItem && "key" in sourceItem && destItem.key === sourceItem.key) result.push(customMerge(destItem, sourceItem));
|
|
45
|
+
else result.push(customMerge(destItem, sourceItem));
|
|
46
|
+
} else result.push(destItem);
|
|
46
47
|
}
|
|
47
48
|
return result;
|
|
48
49
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mergeDictionaries.cjs","names":["getNodeType","getMultilingualDictionary"],"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":";;;;;;;AAaA,MAAM,mBACJ,SACA,SACA,gBACA,eACA,OAAiB,CAAC,MACT;CACT,MAAM,
|
|
1
|
+
{"version":3,"file":"mergeDictionaries.cjs","names":["getAppLogger","getNodeType","colorizeKey","getMultilingualDictionary"],"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":";;;;;;;AAaA,MAAM,mBACJ,SACA,SACA,gBACA,eACA,OAAiB,CAAC,MACT;CACT,MAAM,gBAAYA,sCAAa,EAAE,gCAAI,CAAC;CAGtC,IACE,YAAY,UACZ,YAAY,QACZ,YAAY,UACZ,YAAY,MAEZ;CAEF,MAAM,QAAQC,sDAAY,OAAO;CACjC,MAAM,QAAQA,sDAAY,OAAO;CAGjC,IAAI,UAAU,aAAa,UAAU,WAAW;CAEhD,IAAI,UAAU,OAAO;EACnB,UACE,CACE,yBAAqBC,qCAAY,aAAa,EAAE,4DAA4D,KAAK,KAAK,GAAG,EAAE,kBAAkB,MAAM,QAAQ,MAAM,iBAAiB,gBACpL,GACA,EACE,OAAO,QACT,CACF;EAEA;CACF;AACF;AAGA,MAAM,eACJ,aACA,WACqB;CAErB,IAAI,gBAAgB,UAAa,gBAAgB,MAC/C,OAAO;CAIT,IAAI,WAAW,UAAa,WAAW,MACrC,OAAO;CAIT,IAAI,OAAO,gBAAgB,YAAY,OAAO,WAAW,UACvD,OAAO;CAIT,IAAI,MAAM,QAAQ,WAAW,KAAK,MAAM,QAAQ,MAAM,GACpD,OAAO,WACL,aACA,MACF;CAIF,IAAI,OAAO,gBAAgB,YAAY,OAAO,WAAW,UAAU;EACjE,MAAM,SAA2C,CAAC;EAClD,MAAM,0BAAU,IAAI,IAAI,CACtB,GAAG,OAAO,KAAK,WAAqD,GACpE,GAAG,OAAO,KAAK,MAAgD,CACjE,CAAC;EAED,KAAK,MAAM,OAAO,SAChB,OAAO,OAAO,YACX,YAAuD,MACvD,OAAkD,IACrD;EAGF,OAAO;CACT;CAGA,OAAO;AACT;AAGA,MAAM,cACJ,kBACA,gBACuB;CAEvB,MAAM,wBAAwB,iBAAiB,OAC5C,SAAS,OAAO,SAAS,YAAY,SAAS,IACjD;CACA,MAAM,0BAA0B,YAAY,OACzC,SAAS,OAAO,SAAS,YAAY,SAAS,IACjD;CAGA,IAAI,yBAAyB,yBAC3B,OAAO;CAIT,MAAM,SAA6B,CAAC;CACpC,MAAM,YAAY,KAAK,IAAI,iBAAiB,QAAQ,YAAY,MAAM;CAEtE,KAAK,IAAI,IAAI,GAAG,IAAI,WAAW,KAAK;EAClC,MAAM,WAAW,iBAAiB;EAClC,MAAM,aAAa,YAAY;EAE/B,IAAI,aAAa,UAAa,eAAe,QAAW,CACxD,OAAO,IAAI,aAAa,QAEtB,OAAO,KAAK,UAAU;OACjB,IAAI,eAAe,QAExB,OAAO,KAAK,QAAQ;OAGpB,IACE,OAAO,aAAa,YACpB,OAAO,eAAe,YACtB,aAAa,QACb,eAAe,MACf;GAEA,IACE,SAAS,YACT,SAAS,cACR,SAAoC,QAClC,WAAsC,KAGzC,OAAO,KAAK,YAAY,UAAU,UAAU,CAAC;QAG7C,OAAO,KAAK,YAAY,UAAU,UAAU,CAAC;EAEjD,OAEE,OAAO,KAAK,QAAQ;CAG1B;CAEA,OAAO;AACT;AAEA,MAAa,qBAAqB,iBAA2C;CAC3E,MAAM,WAAW,MAAM,KACrB,IAAI,IACF,aAAa,QAAQ,SAAS,KAAK,OAAO,CAAC,CAAC,KAAK,SAAS,KAAK,OAAQ,CACzE,CACF;CAEA,IAAI,CAAC,aAAa,MAAM,aAAa,UAAU,GAC7C,OAAO,aAAa;CAGtB,MAAM,mBAAmB,aAAa,KAAK,SAAS,KAAK,GAAG;CAG5D,IAAI,IAAI,IAAI,gBAAgB,CAAC,CAAC,SAAS,GACrC,MAAM,IAAI,MAAM,yCAAyC;CAG3D,IAAI,gBAAuC,aAAa,EAAE,CAAC;CAE3D,KAAK,IAAI,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK;EAC5C,MAAM,OAAO,aAAa;EAE1B,IAAI,CAAC,MAAM;EAGX,MAAM,oBAAoBC,iFAA0B,IAAI;EAGxD,gBACE,eACA,kBAAkB,SAClB,kBAAkB,SAClB,kBAAkB,KAClB,CAAC,CACH;EAEA,gBAAgB,YACd,eACA,kBAAkB,OACpB;CACF;CAQA,OAAO;EALL,KAAK,aAAa,EAAE,CAAC;EACrB,SAAS;EACT;CAGoB;AACxB"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
const require_dictionaryManipulator_qualifiedDictionary = require('./qualifiedDictionary.cjs');
|
|
3
3
|
const require_dictionaryManipulator_mergeDictionaries = require('./mergeDictionaries.cjs');
|
|
4
|
+
const require_dictionaryManipulator_qualifiedDictionaryDeclaration = require('./qualifiedDictionaryDeclaration.cjs');
|
|
4
5
|
let _intlayer_config_built = require("@intlayer/config/built");
|
|
5
6
|
let _intlayer_config_logger = require("@intlayer/config/logger");
|
|
6
7
|
|
|
@@ -24,10 +25,10 @@ let _intlayer_config_logger = require("@intlayer/config/logger");
|
|
|
24
25
|
* declares only a subset is ambiguous and is rejected with an error log.
|
|
25
26
|
*/
|
|
26
27
|
const mergeQualifiedDictionaries = (dictionaries) => {
|
|
27
|
-
const perDictionaryTypes = dictionaries.map(
|
|
28
|
+
const perDictionaryTypes = dictionaries.map(require_dictionaryManipulator_qualifiedDictionaryDeclaration.getDictionaryQualifierTypes);
|
|
28
29
|
const declaredDimensions = /* @__PURE__ */ new Set();
|
|
29
30
|
for (const types of perDictionaryTypes) for (const type of types) declaredDimensions.add(type);
|
|
30
|
-
const groupQualifierTypes =
|
|
31
|
+
const groupQualifierTypes = require_dictionaryManipulator_qualifiedDictionaryDeclaration.QUALIFIER_ORDER.filter((qualifierType) => declaredDimensions.has(qualifierType));
|
|
31
32
|
if (groupQualifierTypes.length === 0) return require_dictionaryManipulator_mergeDictionaries.mergeDictionaries(dictionaries);
|
|
32
33
|
const appLogger = (0, _intlayer_config_logger.getAppLogger)({ log: _intlayer_config_built.log });
|
|
33
34
|
const baseDictionaries = [];
|
|
@@ -37,7 +38,7 @@ const mergeQualifiedDictionaries = (dictionaries) => {
|
|
|
37
38
|
baseDictionaries.push(dictionary);
|
|
38
39
|
return;
|
|
39
40
|
}
|
|
40
|
-
const compositeIds =
|
|
41
|
+
const compositeIds = require_dictionaryManipulator_qualifiedDictionaryDeclaration.getDictionaryCompositeIds(dictionary, groupQualifierTypes);
|
|
41
42
|
if (compositeIds === void 0) {
|
|
42
43
|
appLogger(`Dictionary ${(0, _intlayer_config_logger.colorizeKey)(dictionary.key)} declares (${perDictionaryTypes[index].join(", ")}) but the key's dimensions are (${groupQualifierTypes.join(", ")}); every entry must declare all of them. Entry ignored${dictionary.filePath ? ` - ${dictionary.filePath}` : ""}.`, { level: "error" });
|
|
43
44
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mergeQualifiedDictionaries.cjs","names":["getDictionaryQualifierTypes","QUALIFIER_ORDER","mergeDictionaries","getDictionaryCompositeIds","DEFAULT_VARIANT_ID"],"sources":["../../../src/dictionaryManipulator/mergeQualifiedDictionaries.ts"],"sourcesContent":["import { log } from '@intlayer/config/built';\nimport { colorizeKey, getAppLogger } from '@intlayer/config/logger';\nimport type {\n Dictionary,\n DictionaryQualifierType,\n QualifiedDictionaryGroup,\n} from '@intlayer/types/dictionary';\nimport { mergeDictionaries } from './mergeDictionaries';\nimport {\n COMPOSITE_ID_SEPARATOR,\n DEFAULT_VARIANT_ID,\n getDictionaryCompositeIds,\n getDictionaryQualifierTypes,\n QUALIFIER_ORDER,\n} from './
|
|
1
|
+
{"version":3,"file":"mergeQualifiedDictionaries.cjs","names":["getDictionaryQualifierTypes","QUALIFIER_ORDER","mergeDictionaries","getAppLogger","getDictionaryCompositeIds","colorizeKey","DEFAULT_VARIANT_ID"],"sources":["../../../src/dictionaryManipulator/mergeQualifiedDictionaries.ts"],"sourcesContent":["import { log } from '@intlayer/config/built';\nimport { colorizeKey, getAppLogger } from '@intlayer/config/logger';\nimport type {\n Dictionary,\n DictionaryQualifierType,\n QualifiedDictionaryGroup,\n} from '@intlayer/types/dictionary';\nimport { mergeDictionaries } from './mergeDictionaries';\nimport {\n COMPOSITE_ID_SEPARATOR,\n DEFAULT_VARIANT_ID,\n} from './qualifiedDictionary';\nimport {\n getDictionaryCompositeIds,\n getDictionaryQualifierTypes,\n QUALIFIER_ORDER,\n} from './qualifiedDictionaryDeclaration';\n\n/**\n * Merges sibling dictionaries sharing the same key, honouring qualifiers.\n *\n * - No dictionary declares a qualifier → behaves exactly like\n * `mergeDictionaries` (single merged dictionary).\n * - At least one dictionary declares a qualifier → the group's dimension set is\n * the union of every declared dimension (in canonical order\n * `variant → item`). Dictionaries are grouped by their composite id\n * (one segment per dimension), merged within each group (locale completion /\n * priority overrides preserved), and a `QualifiedDictionaryGroup` is returned.\n * Unqualified siblings act as shared base content merged into every entry —\n * and, on a variant-only key, also materialize the `default` entry when no\n * declaration claims it. Every other variant entry then inherits from that\n * `default` entry, so a variant only declares the fields it overrides.\n *\n * Every qualified entry must declare ALL dimensions of the group; an entry that\n * declares only a subset is ambiguous and is rejected with an error log.\n */\nexport const mergeQualifiedDictionaries = (\n dictionaries: Dictionary[]\n): Dictionary | QualifiedDictionaryGroup => {\n const perDictionaryTypes = dictionaries.map(getDictionaryQualifierTypes);\n\n const declaredDimensions = new Set<DictionaryQualifierType>();\n for (const types of perDictionaryTypes) {\n for (const type of types) declaredDimensions.add(type);\n }\n\n // Canonical order, restricted to the dimensions actually declared.\n const groupQualifierTypes = QUALIFIER_ORDER.filter((qualifierType) =>\n declaredDimensions.has(qualifierType)\n );\n\n if (groupQualifierTypes.length === 0) {\n return mergeDictionaries(dictionaries);\n }\n\n const appLogger = getAppLogger({ log });\n\n const baseDictionaries: Dictionary[] = [];\n const entriesDictionaries = new Map<string, Dictionary[]>();\n\n dictionaries.forEach((dictionary, index) => {\n if (perDictionaryTypes[index]?.length === 0) {\n baseDictionaries.push(dictionary);\n return;\n }\n\n // A dictionary may map to several composite ids (array variant fan-out):\n // its content is registered under every id it lists.\n const compositeIds = getDictionaryCompositeIds(\n dictionary,\n groupQualifierTypes\n );\n\n if (compositeIds === undefined) {\n appLogger(\n `Dictionary ${colorizeKey(dictionary.key)} declares (${perDictionaryTypes[index].join(', ')}) but the key's dimensions are (${groupQualifierTypes.join(', ')}); every entry must declare all of them. Entry ignored${dictionary.filePath ? ` - ${dictionary.filePath}` : ''}.`,\n { level: 'error' }\n );\n return;\n }\n\n for (const compositeId of compositeIds) {\n const existingEntries = entriesDictionaries.get(compositeId) ?? [];\n existingEntries.push(dictionary);\n entriesDictionaries.set(compositeId, existingEntries);\n }\n });\n\n // Unqualified siblings are the key's base content. On a variant key they also\n // materialize the `default` entry when no declaration claims it, so the key\n // still resolves when no variant is selected — and so variants that declare\n // no entry of their own can fall back to it. Registering an empty entry list\n // is enough: the merge loop below appends the base dictionaries to it.\n //\n // Composite (variant × item) keys are excluded: base content carries no item\n // index, so it cannot be placed on the collection axis.\n const isVariantOnlyGroup =\n groupQualifierTypes.length === 1 && groupQualifierTypes[0] === 'variant';\n\n if (\n isVariantOnlyGroup &&\n baseDictionaries.length > 0 &&\n !entriesDictionaries.has(DEFAULT_VARIANT_ID)\n ) {\n entriesDictionaries.set(DEFAULT_VARIANT_ID, []);\n }\n\n // `content` maps each composite id to its resolved content node directly; the\n // qualifier coordinates live in the key, not in a per-entry wrapper. For an\n // object variant the variant segment is the canonical serialization of the\n // object, so it fully identifies the entry — no side-map is needed.\n const content: Record<string, unknown> = {};\n\n let importMode: Dictionary['importMode'];\n\n const variantIndex = groupQualifierTypes.indexOf('variant');\n\n /**\n * The id of the entry a composite id inherits from — itself with its variant\n * segment swapped for `default` (`'promo/2'` → `'default/2'`). `undefined`\n * for the default entries themselves and for keys with no variant dimension.\n */\n const getInheritedEntryId = (compositeId: string): string | undefined => {\n if (variantIndex === -1) return undefined;\n\n const segments = compositeId.split(COMPOSITE_ID_SEPARATOR);\n if (segments[variantIndex] === DEFAULT_VARIANT_ID) return undefined;\n\n segments[variantIndex] = DEFAULT_VARIANT_ID;\n return segments.join(COMPOSITE_ID_SEPARATOR);\n };\n\n for (const [compositeId, qualifiedDictionaries] of entriesDictionaries) {\n // Precedence, highest first: the entry's own declarations, then the\n // `default` variant it inherits from, then the unqualified siblings shared\n // by the whole key (mergeDictionaries prefers the first occurrence).\n //\n // Inheriting from `default` is what makes a variant declaration partial: it\n // only has to declare the fields whose wording actually differs, and every\n // other field is completed from the default entry.\n const inheritedEntryId = getInheritedEntryId(compositeId);\n const inheritedDictionaries = inheritedEntryId\n ? (entriesDictionaries.get(inheritedEntryId) ?? [])\n : [];\n\n const mergedEntry = mergeDictionaries([\n ...qualifiedDictionaries,\n ...inheritedDictionaries,\n ...baseDictionaries,\n ]);\n\n content[compositeId] = mergedEntry.content;\n\n const [firstQualified] = qualifiedDictionaries;\n\n importMode ??= firstQualified?.importMode;\n }\n\n const localIds = Array.from(\n new Set(\n dictionaries\n .filter((dictionary) => dictionary.localId)\n .map((dictionary) => dictionary.localId!)\n )\n );\n\n return {\n key: dictionaries[0]!.key,\n qualifierTypes: groupQualifierTypes,\n content,\n ...(importMode !== undefined && { importMode }),\n localIds,\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAoCA,MAAa,8BACX,iBAC0C;CAC1C,MAAM,qBAAqB,aAAa,IAAIA,wFAA2B;CAEvE,MAAM,qCAAqB,IAAI,IAA6B;CAC5D,KAAK,MAAM,SAAS,oBAClB,KAAK,MAAM,QAAQ,OAAO,mBAAmB,IAAI,IAAI;CAIvD,MAAM,sBAAsBC,6EAAgB,QAAQ,kBAClD,mBAAmB,IAAI,aAAa,CACtC;CAEA,IAAI,oBAAoB,WAAW,GACjC,OAAOC,kEAAkB,YAAY;CAGvC,MAAM,gBAAYC,sCAAa,EAAE,gCAAI,CAAC;CAEtC,MAAM,mBAAiC,CAAC;CACxC,MAAM,sCAAsB,IAAI,IAA0B;CAE1D,aAAa,SAAS,YAAY,UAAU;EAC1C,IAAI,mBAAmB,MAAM,EAAE,WAAW,GAAG;GAC3C,iBAAiB,KAAK,UAAU;GAChC;EACF;EAIA,MAAM,eAAeC,uFACnB,YACA,mBACF;EAEA,IAAI,iBAAiB,QAAW;GAC9B,UACE,kBAAcC,qCAAY,WAAW,GAAG,EAAE,aAAa,mBAAmB,MAAM,CAAC,KAAK,IAAI,EAAE,kCAAkC,oBAAoB,KAAK,IAAI,EAAE,wDAAwD,WAAW,WAAW,MAAM,WAAW,aAAa,GAAG,IAC5Q,EAAE,OAAO,QAAQ,CACnB;GACA;EACF;EAEA,KAAK,MAAM,eAAe,cAAc;GACtC,MAAM,kBAAkB,oBAAoB,IAAI,WAAW,KAAK,CAAC;GACjE,gBAAgB,KAAK,UAAU;GAC/B,oBAAoB,IAAI,aAAa,eAAe;EACtD;CACF,CAAC;CAaD,IAFE,oBAAoB,WAAW,KAAK,oBAAoB,OAAO,aAI/D,iBAAiB,SAAS,KAC1B,CAAC,oBAAoB,aAAsB,GAE3C,oBAAoB,IAAIC,sEAAoB,CAAC,CAAC;CAOhD,MAAM,UAAmC,CAAC;CAE1C,IAAI;CAEJ,MAAM,eAAe,oBAAoB,QAAQ,SAAS;;;;;;CAO1D,MAAM,uBAAuB,gBAA4C;EACvE,IAAI,iBAAiB,IAAI,OAAO;EAEhC,MAAM,WAAW,YAAY,SAA4B;EACzD,IAAI,SAAS,6BAAsC,OAAO;EAE1D,SAAS,gBAAgBA;EACzB,OAAO,SAAS,QAA2B;CAC7C;CAEA,KAAK,MAAM,CAAC,aAAa,0BAA0B,qBAAqB;EAQtE,MAAM,mBAAmB,oBAAoB,WAAW;EACxD,MAAM,wBAAwB,mBACzB,oBAAoB,IAAI,gBAAgB,KAAK,CAAC,IAC/C,CAAC;EAQL,QAAQ,eANYJ,kEAAkB;GACpC,GAAG;GACH,GAAG;GACH,GAAG;EACL,CAEiC,CAAC,CAAC;EAEnC,MAAM,CAAC,kBAAkB;EAEzB,eAAe,gBAAgB;CACjC;CAEA,MAAM,WAAW,MAAM,KACrB,IAAI,IACF,aACG,QAAQ,eAAe,WAAW,OAAO,CAAC,CAC1C,KAAK,eAAe,WAAW,OAAQ,CAC5C,CACF;CAEA,OAAO;EACL,KAAK,aAAa,EAAE,CAAE;EACtB,gBAAgB;EAChB;EACA,GAAI,eAAe,UAAa,EAAE,WAAW;EAC7C;CACF;AACF"}
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
|
|
3
2
|
//#region src/dictionaryManipulator/qualifiedDictionary.ts
|
|
4
3
|
/**
|
|
5
|
-
* Canonical order of qualifier dimensions. A key that declares both dimensions
|
|
6
|
-
* always nests them in this order, with `item` innermost so it can act as the
|
|
7
|
-
* collection (array) axis.
|
|
8
|
-
*/
|
|
9
|
-
const QUALIFIER_ORDER = ["variant", "item"];
|
|
10
|
-
/**
|
|
11
4
|
* Separator joining per-dimension ids into a composite entry id. Also used as
|
|
12
5
|
* the chunk path separator in dynamic mode.
|
|
13
6
|
*/
|
|
@@ -62,57 +55,6 @@ const serializeVariant = (variant) => {
|
|
|
62
55
|
return Object.keys(variant).sort().map((field) => `${encodeSegmentText(field, COMPONENT_UNSAFE_CHARS)}=${encodeSegmentText(String(variant[field]), COMPONENT_UNSAFE_CHARS)}`).join("&");
|
|
63
56
|
};
|
|
64
57
|
/**
|
|
65
|
-
* Normalizes the `variant` field of a dictionary into the list of variant ids
|
|
66
|
-
* the declaration registers under. A single value yields one id; an **array**
|
|
67
|
-
* fans out into one id per element (duplicates collapsed). Returns `undefined`
|
|
68
|
-
* when the dictionary does not declare the variant dimension (no `variant`
|
|
69
|
-
* field, or an empty array).
|
|
70
|
-
*/
|
|
71
|
-
const getVariantIds = (variant) => {
|
|
72
|
-
if (variant === void 0) return void 0;
|
|
73
|
-
const values = Array.isArray(variant) ? variant : [variant];
|
|
74
|
-
if (values.length === 0) return void 0;
|
|
75
|
-
return [...new Set(values.map(serializeVariant))];
|
|
76
|
-
};
|
|
77
|
-
/**
|
|
78
|
-
* Returns the qualifier dimensions declared on a dictionary, in canonical
|
|
79
|
-
* order (`variant → item`). Empty when the dictionary is unqualified
|
|
80
|
-
* (plain dictionary or shared base content of a qualified group).
|
|
81
|
-
*/
|
|
82
|
-
const getDictionaryQualifierTypes = (dictionary) => {
|
|
83
|
-
const declaredQualifiers = [];
|
|
84
|
-
if (getVariantIds(dictionary.variant) !== void 0) declaredQualifiers.push("variant");
|
|
85
|
-
if (typeof dictionary.item === "number") declaredQualifiers.push("item");
|
|
86
|
-
return declaredQualifiers;
|
|
87
|
-
};
|
|
88
|
-
/**
|
|
89
|
-
* Returns the qualifier identifiers of a dictionary for the given qualifier
|
|
90
|
-
* dimension — the candidate segments of the composite entry ids.
|
|
91
|
-
*
|
|
92
|
-
* - 'variant' → the serialized variant id(s); an array variant yields one id
|
|
93
|
-
* per element (declaration-side fan-out)
|
|
94
|
-
* - 'item' → the item index as a single-element list
|
|
95
|
-
*/
|
|
96
|
-
const getDictionaryQualifierIds = (dictionary, qualifierType) => {
|
|
97
|
-
if (qualifierType === "variant") return getVariantIds(dictionary.variant);
|
|
98
|
-
return dictionary.item === void 0 ? void 0 : [String(dictionary.item)];
|
|
99
|
-
};
|
|
100
|
-
/**
|
|
101
|
-
* Builds every composite entry id of a dictionary — the cartesian product of
|
|
102
|
-
* its per-dimension id lists, joined in canonical order. A dictionary with a
|
|
103
|
-
* plain (non-array) variant yields exactly one id; an array variant fans out
|
|
104
|
-
* into one id per element. `undefined` when a dimension of the set is missing.
|
|
105
|
-
*/
|
|
106
|
-
const getDictionaryCompositeIds = (dictionary, qualifierTypes) => {
|
|
107
|
-
let compositeIds = [""];
|
|
108
|
-
for (const qualifierType of qualifierTypes) {
|
|
109
|
-
const ids = getDictionaryQualifierIds(dictionary, qualifierType);
|
|
110
|
-
if (ids === void 0) return void 0;
|
|
111
|
-
compositeIds = compositeIds.flatMap((prefix) => ids.map((id) => prefix === "" ? id : `${prefix}${"/"}${id}`));
|
|
112
|
-
}
|
|
113
|
-
return compositeIds;
|
|
114
|
-
};
|
|
115
|
-
/**
|
|
116
58
|
* Serializes the variant coordinate of a selector into the ordered list of
|
|
117
59
|
* candidate ids to try, so a single value and a preference chain share one code
|
|
118
60
|
* path downstream.
|
|
@@ -290,130 +232,18 @@ const getDictionarySelectorCacheKey = (selector) => {
|
|
|
290
232
|
return `${selectorKey}:${selectorKey === "variant" ? serializeVariantChain(value).join(",") : String(value)}`;
|
|
291
233
|
}).join("|");
|
|
292
234
|
};
|
|
293
|
-
/**
|
|
294
|
-
* Marker property carrying the ordered qualifier dimensions on a dynamic loader
|
|
295
|
-
* map. Its presence distinguishes a qualified group loader map (a nested tree
|
|
296
|
-
* of chunks) from a plain dynamic loader map (one chunk per `locale`). Prefixed
|
|
297
|
-
* and unlikely to collide with a real locale code.
|
|
298
|
-
*/
|
|
299
|
-
const QUALIFIER_DYNAMIC_TYPES_KEY = "__intlayerQualifierTypes";
|
|
300
|
-
/**
|
|
301
|
-
* Type guard discriminating a qualified dynamic loader map (collections /
|
|
302
|
-
* variants, possibly combined) from a plain dynamic loader map.
|
|
303
|
-
*/
|
|
304
|
-
const isQualifiedDynamicLoaderMap = (value) => typeof value === "object" && value !== null && "__intlayerQualifierTypes" in value;
|
|
305
|
-
/**
|
|
306
|
-
* Walks the loader tree following the selector and collects the chunk loaders
|
|
307
|
-
* it targets — shared by the sync ({@link resolveQualifiedDynamicContent}) and
|
|
308
|
-
* async ({@link resolveQualifiedDynamicContentAsync}) resolvers.
|
|
309
|
-
*/
|
|
310
|
-
const collectQualifiedChunks = (loaderMap, key, locale, selector) => {
|
|
311
|
-
const qualifierTypes = loaderMap[QUALIFIER_DYNAMIC_TYPES_KEY];
|
|
312
|
-
const localeTree = loaderMap[locale];
|
|
313
|
-
const itemAxisOpen = qualifierTypes.includes("item") && selector?.item === void 0;
|
|
314
|
-
if (!localeTree) return {
|
|
315
|
-
itemAxisOpen,
|
|
316
|
-
missed: true,
|
|
317
|
-
chunks: []
|
|
318
|
-
};
|
|
319
|
-
const chunks = [];
|
|
320
|
-
const walk = (node, dimensions, segments) => {
|
|
321
|
-
if (dimensions.length === 0) {
|
|
322
|
-
chunks.push({
|
|
323
|
-
cacheKey: `${key}.${locale}.${segments.join("/")}`,
|
|
324
|
-
loader: node
|
|
325
|
-
});
|
|
326
|
-
return true;
|
|
327
|
-
}
|
|
328
|
-
const [dimension, ...rest] = dimensions;
|
|
329
|
-
const tree = node;
|
|
330
|
-
if (dimension === "item" && selector?.item === void 0) {
|
|
331
|
-
for (const segment of Object.keys(tree).sort((left, right) => Number(left) - Number(right))) walk(tree[segment], rest, [...segments, segment]);
|
|
332
|
-
return true;
|
|
333
|
-
}
|
|
334
|
-
const segment = dimension === "variant" ? resolveEffectiveVariantId(serializeVariantChain(selector?.variant), (variantId) => tree[variantId] !== void 0) : String(selector?.item);
|
|
335
|
-
const child = tree[segment];
|
|
336
|
-
if (!child) return false;
|
|
337
|
-
return walk(child, rest, [...segments, segment]);
|
|
338
|
-
};
|
|
339
|
-
return {
|
|
340
|
-
itemAxisOpen,
|
|
341
|
-
missed: !walk(localeTree, qualifierTypes, []),
|
|
342
|
-
chunks
|
|
343
|
-
};
|
|
344
|
-
};
|
|
345
|
-
/**
|
|
346
|
-
* Resolves the content of a qualified dynamic loader map against a selector,
|
|
347
|
-
* loading only the chunk(s) the selector actually targets.
|
|
348
|
-
*
|
|
349
|
-
* Walks the nested loader tree one dimension at a time (canonical order
|
|
350
|
-
* `variant → item`): `variant` descends by the serialized id, falling back to
|
|
351
|
-
* `default` when the selected variant has no chunk of its own, and `item`
|
|
352
|
-
* either narrows to the selected index or — when no item is given — expands
|
|
353
|
-
* into every sibling chunk (the collection axis). Semantics mirror
|
|
354
|
-
* {@link resolveQualifiedDictionary} so dynamic and static modes behave alike.
|
|
355
|
-
*
|
|
356
|
-
* The Suspense mechanism is injected through `loadChunk` so the same logic
|
|
357
|
-
* serves both the client (suspender cache) and the server (`react.use`). Every
|
|
358
|
-
* targeted loader is started before the first chunk is read, so sibling chunks
|
|
359
|
-
* load in parallel rather than waterfalling.
|
|
360
|
-
*
|
|
361
|
-
* @param loaderMap - The qualified dynamic loader map (entry point default export).
|
|
362
|
-
* @param key - The dictionary key (used to build stable chunk cache keys).
|
|
363
|
-
* @param locale - The resolved locale to load chunks for.
|
|
364
|
-
* @param selector - The selector splitting the qualifier dimensions.
|
|
365
|
-
* @param loadChunk - Reads a started chunk promise, suspending until it resolves.
|
|
366
|
-
* @param transform - Turns a resolved chunk dictionary into final content.
|
|
367
|
-
*/
|
|
368
|
-
const resolveQualifiedDynamicContent = (params) => {
|
|
369
|
-
const { loaderMap, key, locale, selector, loadChunk, transform } = params;
|
|
370
|
-
const { itemAxisOpen, missed, chunks } = collectQualifiedChunks(loaderMap, key, locale, selector);
|
|
371
|
-
if (missed) return itemAxisOpen ? [] : null;
|
|
372
|
-
const dictionaries = chunks.map(({ cacheKey, loader }) => loadChunk(cacheKey, loader()));
|
|
373
|
-
if (itemAxisOpen) return dictionaries.map(transform);
|
|
374
|
-
const [dictionary] = dictionaries;
|
|
375
|
-
return dictionary ? transform(dictionary) : null;
|
|
376
|
-
};
|
|
377
|
-
/**
|
|
378
|
-
* Async counterpart of {@link resolveQualifiedDynamicContent} for frameworks
|
|
379
|
-
* that load dictionaries with `await` instead of Suspense (Vue, Svelte, Lit,
|
|
380
|
-
* vanilla). Awaits every targeted chunk in parallel, then resolves identically.
|
|
381
|
-
*
|
|
382
|
-
* @param loaderMap - The qualified dynamic loader map.
|
|
383
|
-
* @param key - The dictionary key (used to build stable chunk cache keys).
|
|
384
|
-
* @param locale - The resolved locale to load chunks for.
|
|
385
|
-
* @param selector - The selector splitting the qualifier dimensions.
|
|
386
|
-
* @param transform - Turns a resolved chunk dictionary into final content.
|
|
387
|
-
*/
|
|
388
|
-
const resolveQualifiedDynamicContentAsync = async (params) => {
|
|
389
|
-
const { loaderMap, key, locale, selector, transform } = params;
|
|
390
|
-
const { itemAxisOpen, missed, chunks } = collectQualifiedChunks(loaderMap, key, locale, selector);
|
|
391
|
-
if (missed) return itemAxisOpen ? [] : null;
|
|
392
|
-
const dictionaries = await Promise.all(chunks.map(({ loader }) => loader()));
|
|
393
|
-
if (itemAxisOpen) return dictionaries.map(transform);
|
|
394
|
-
const [dictionary] = dictionaries;
|
|
395
|
-
return dictionary ? transform(dictionary) : null;
|
|
396
|
-
};
|
|
397
235
|
|
|
398
236
|
//#endregion
|
|
399
237
|
exports.COMPOSITE_ID_SEPARATOR = COMPOSITE_ID_SEPARATOR;
|
|
400
238
|
exports.DEFAULT_VARIANT_ID = DEFAULT_VARIANT_ID;
|
|
401
|
-
exports.QUALIFIER_DYNAMIC_TYPES_KEY = QUALIFIER_DYNAMIC_TYPES_KEY;
|
|
402
|
-
exports.QUALIFIER_ORDER = QUALIFIER_ORDER;
|
|
403
|
-
exports.getDictionaryCompositeIds = getDictionaryCompositeIds;
|
|
404
|
-
exports.getDictionaryQualifierIds = getDictionaryQualifierIds;
|
|
405
|
-
exports.getDictionaryQualifierTypes = getDictionaryQualifierTypes;
|
|
406
239
|
exports.getDictionarySelectorCacheKey = getDictionarySelectorCacheKey;
|
|
407
|
-
exports.getVariantIds = getVariantIds;
|
|
408
240
|
exports.isQualifiedDictionaryGroup = isQualifiedDictionaryGroup;
|
|
409
|
-
exports.isQualifiedDynamicLoaderMap = isQualifiedDynamicLoaderMap;
|
|
410
241
|
exports.parseDictionarySelector = parseDictionarySelector;
|
|
411
242
|
exports.reconstructQualifiedEntry = reconstructQualifiedEntry;
|
|
412
243
|
exports.resolveDictionaryArgument = resolveDictionaryArgument;
|
|
244
|
+
exports.resolveEffectiveVariantId = resolveEffectiveVariantId;
|
|
413
245
|
exports.resolveProviderVariant = resolveProviderVariant;
|
|
414
246
|
exports.resolveQualifiedDictionary = resolveQualifiedDictionary;
|
|
415
|
-
exports.resolveQualifiedDynamicContent = resolveQualifiedDynamicContent;
|
|
416
|
-
exports.resolveQualifiedDynamicContentAsync = resolveQualifiedDynamicContentAsync;
|
|
417
247
|
exports.serializeVariant = serializeVariant;
|
|
418
248
|
exports.serializeVariantChain = serializeVariantChain;
|
|
419
249
|
//# sourceMappingURL=qualifiedDictionary.cjs.map
|