@intlayer/core 9.1.3 → 9.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/cjs/deepTransformPlugins/getEditedContent.cjs +10 -6
  2. package/dist/cjs/deepTransformPlugins/getEditedContent.cjs.map +1 -1
  3. package/dist/cjs/dictionaryManipulator/getNestedDictionaryReferences.cjs +153 -0
  4. package/dist/cjs/dictionaryManipulator/getNestedDictionaryReferences.cjs.map +1 -0
  5. package/dist/cjs/dictionaryManipulator/index.cjs +16 -9
  6. package/dist/cjs/dictionaryManipulator/mergeQualifiedDictionaries.cjs +4 -3
  7. package/dist/cjs/dictionaryManipulator/mergeQualifiedDictionaries.cjs.map +1 -1
  8. package/dist/cjs/dictionaryManipulator/qualifiedDictionary.cjs +1 -170
  9. package/dist/cjs/dictionaryManipulator/qualifiedDictionary.cjs.map +1 -1
  10. package/dist/cjs/dictionaryManipulator/qualifiedDictionaryDeclaration.cjs +69 -0
  11. package/dist/cjs/dictionaryManipulator/qualifiedDictionaryDeclaration.cjs.map +1 -0
  12. package/dist/cjs/dictionaryManipulator/qualifiedDynamicLoader.cjs +115 -0
  13. package/dist/cjs/dictionaryManipulator/qualifiedDynamicLoader.cjs.map +1 -0
  14. package/dist/cjs/index.cjs +20 -9
  15. package/dist/cjs/interpreter/getContent/plugins.cjs +12 -1
  16. package/dist/cjs/interpreter/getContent/plugins.cjs.map +1 -1
  17. package/dist/cjs/interpreter/getDictionary.cjs +2 -1
  18. package/dist/cjs/interpreter/getDictionary.cjs.map +1 -1
  19. package/dist/cjs/interpreter/getNesting.optimized.cjs +59 -0
  20. package/dist/cjs/interpreter/getNesting.optimized.cjs.map +1 -0
  21. package/dist/cjs/localization/index.cjs +4 -0
  22. package/dist/cjs/localization/proxyMode.cjs +92 -0
  23. package/dist/cjs/localization/proxyMode.cjs.map +1 -0
  24. package/dist/esm/deepTransformPlugins/getEditedContent.mjs +10 -6
  25. package/dist/esm/deepTransformPlugins/getEditedContent.mjs.map +1 -1
  26. package/dist/esm/dictionaryManipulator/getNestedDictionaryReferences.mjs +150 -0
  27. package/dist/esm/dictionaryManipulator/getNestedDictionaryReferences.mjs.map +1 -0
  28. package/dist/esm/dictionaryManipulator/index.mjs +5 -2
  29. package/dist/esm/dictionaryManipulator/mergeQualifiedDictionaries.mjs +2 -1
  30. package/dist/esm/dictionaryManipulator/mergeQualifiedDictionaries.mjs.map +1 -1
  31. package/dist/esm/dictionaryManipulator/qualifiedDictionary.mjs +1 -162
  32. package/dist/esm/dictionaryManipulator/qualifiedDictionary.mjs.map +1 -1
  33. package/dist/esm/dictionaryManipulator/qualifiedDictionaryDeclaration.mjs +64 -0
  34. package/dist/esm/dictionaryManipulator/qualifiedDictionaryDeclaration.mjs.map +1 -0
  35. package/dist/esm/dictionaryManipulator/qualifiedDynamicLoader.mjs +111 -0
  36. package/dist/esm/dictionaryManipulator/qualifiedDynamicLoader.mjs.map +1 -0
  37. package/dist/esm/index.mjs +6 -2
  38. package/dist/esm/interpreter/getContent/plugins.mjs +12 -1
  39. package/dist/esm/interpreter/getContent/plugins.mjs.map +1 -1
  40. package/dist/esm/interpreter/getDictionary.mjs +2 -1
  41. package/dist/esm/interpreter/getDictionary.mjs.map +1 -1
  42. package/dist/esm/interpreter/getNesting.optimized.mjs +58 -0
  43. package/dist/esm/interpreter/getNesting.optimized.mjs.map +1 -0
  44. package/dist/esm/localization/index.mjs +2 -1
  45. package/dist/esm/localization/proxyMode.mjs +87 -0
  46. package/dist/esm/localization/proxyMode.mjs.map +1 -0
  47. package/dist/types/deepTransformPlugins/getEditedContent.d.ts.map +1 -1
  48. package/dist/types/deepTransformPlugins/getFilterMissingTranslationsContent.d.ts +1 -0
  49. package/dist/types/deepTransformPlugins/getFilterTranslationsOnlyContent.d.ts +1 -0
  50. package/dist/types/deepTransformPlugins/getFilteredLocalesContent.d.ts +1 -0
  51. package/dist/types/dictionaryManipulator/getNestedDictionaryReferences.d.ts +62 -0
  52. package/dist/types/dictionaryManipulator/getNestedDictionaryReferences.d.ts.map +1 -0
  53. package/dist/types/dictionaryManipulator/index.d.ts +5 -2
  54. package/dist/types/dictionaryManipulator/mergeQualifiedDictionaries.d.ts.map +1 -1
  55. package/dist/types/dictionaryManipulator/qualifiedDictionary.d.ts +18 -126
  56. package/dist/types/dictionaryManipulator/qualifiedDictionary.d.ts.map +1 -1
  57. package/dist/types/dictionaryManipulator/qualifiedDictionaryDeclaration.d.ts +41 -0
  58. package/dist/types/dictionaryManipulator/qualifiedDictionaryDeclaration.d.ts.map +1 -0
  59. package/dist/types/dictionaryManipulator/qualifiedDynamicLoader.d.ts +93 -0
  60. package/dist/types/dictionaryManipulator/qualifiedDynamicLoader.d.ts.map +1 -0
  61. package/dist/types/index.d.ts +6 -2
  62. package/dist/types/interpreter/getContent/plugins.d.ts +10 -0
  63. package/dist/types/interpreter/getContent/plugins.d.ts.map +1 -1
  64. package/dist/types/interpreter/getNesting.optimized.d.ts +33 -0
  65. package/dist/types/interpreter/getNesting.optimized.d.ts.map +1 -0
  66. package/dist/types/localization/index.d.ts +2 -1
  67. package/dist/types/localization/proxyMode.d.ts +92 -0
  68. package/dist/types/localization/proxyMode.d.ts.map +1 -0
  69. package/package.json +7 -7
@@ -1 +1 @@
1
- {"version":3,"file":"plugins.cjs","names":["NodeTypes","getTranslation","getEnumeration","isInterpolableWrapperNode","transformInterpolableNode","getInsertion","getPlural","getCondition","getGender","getSelect","getNesting"],"sources":["../../../../src/interpreter/getContent/plugins.ts"],"sourcesContent":["import type { Locale } from '@intlayer/types/allLocales';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport type {\n DeclaredLocales,\n DictionaryKeys,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport type { NodeType } from '@intlayer/types/nodeType';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport type {\n ConditionContent,\n EnumerationContent,\n FileContent,\n Gender,\n GenderContent,\n InsertionContent,\n NestedContent,\n PluralContent,\n PluralContentState,\n SelectContent,\n TranslationContent,\n} from '../../transpiler';\nimport { getCondition } from '../getCondition';\nimport { getEnumeration } from '../getEnumeration';\nimport { getGender } from '../getGender';\nimport { getInsertion } from '../getInsertion';\nimport { type GetNestingResult, getNesting } from '../getNesting';\nimport { getPlural } from '../getPlural';\nimport { getSelect } from '../getSelect';\nimport { getTranslation } from '../getTranslation';\nimport {\n isInterpolableWrapperNode,\n transformInterpolableNode,\n} from '../interpolableNode';\n\n// ── Tree-shake constants ──────────────────────────────────────────────────────\n// When these env vars are injected at build time, bundlers eliminate the\n// branches guarded by these constants.\n\n/** ---------------------------------------------\n * PLUGIN DEFINITION\n * --------------------------------------------- */\n\n/**\n * A plugin/transformer that can optionally transform a node during a single DFS pass.\n * - `canHandle` decides if the node is transformable by this plugin.\n * - `transform` returns the transformed node (and does not recurse further).\n *\n * > `transformFn` is a function that can be used to deeply transform inside the plugin.\n */\nexport type Plugins = {\n id: string;\n canHandle: (node: any) => boolean;\n transform: (\n node: any,\n props: NodeProps,\n transformFn: (node: any, props: NodeProps) => any\n ) => any;\n};\n\n/** ---------------------------------------------\n * FALLBACK PLUGIN\n *\n * Used to fallback a tree-shaken plugin\n * --------------------------------------------- */\n\nexport const fallbackPlugin: Plugins = {\n id: 'fallback-plugin',\n canHandle: () => false,\n transform: (node) => node,\n};\n\n/** ---------------------------------------------\n * TRANSLATION PLUGIN\n * --------------------------------------------- */\n\nexport type UnionKeys<T> = T extends unknown ? keyof T : never;\nexport type ValueAtKey<T, K> = T extends unknown\n ? K extends keyof T\n ? T[K]\n : never\n : never;\n\nexport type TranslationCond<T, S, L extends LocalesValues> = T extends {\n nodeType: NodeType | string;\n [NodeTypes.TRANSLATION]: infer U;\n}\n ? U extends Record<PropertyKey, unknown>\n ? U[keyof U] extends Record<PropertyKey, unknown>\n ? {\n [K in UnionKeys<U[keyof U]>]: L extends keyof U\n ? K extends keyof U[L]\n ? U[L][K]\n : ValueAtKey<U[keyof U], K>\n : ValueAtKey<U[keyof U], K>;\n } extends infer Content\n ? DeepTransformContent<Content, S>\n : never\n : (L extends keyof U ? U[L] : U[keyof U]) extends infer Content\n ? DeepTransformContent<Content, S>\n : never\n : never\n : never;\n\n/** Translation plugin. Replaces node with a locale string if nodeType = Translation. */\nexport const translationPlugin = (\n locale: LocalesValues,\n fallback?: LocalesValues\n): Plugins =>\n process.env.INTLAYER_NODE_TYPE_TRANSLATION === 'false'\n ? fallbackPlugin\n : {\n id: 'translation-plugin',\n canHandle: (node) =>\n typeof node === 'object' && node?.nodeType === NodeTypes.TRANSLATION,\n transform: (node: TranslationContent, props, deepTransformNode) => {\n const original = node[NodeTypes.TRANSLATION] ?? {};\n const result: Record<string, any> = {};\n\n for (const key in original) {\n const childProps = {\n ...props,\n children: original[key as keyof typeof original],\n keyPath: [\n ...props.keyPath,\n { type: NodeTypes.TRANSLATION, key } as KeyPath,\n ],\n };\n result[key] = deepTransformNode(\n original[key as keyof typeof original],\n childProps\n );\n }\n\n return getTranslation(result, locale, fallback);\n },\n };\n\n/** ---------------------------------------------\n * ENUMERATION PLUGIN\n * --------------------------------------------- */\n\nexport type EnumerationCond<T, S, _L> = T extends {\n nodeType: NodeType | string;\n [NodeTypes.ENUMERATION]: object;\n}\n ? (\n quantity: number\n ) => DeepTransformContent<\n T[typeof NodeTypes.ENUMERATION][keyof T[typeof NodeTypes.ENUMERATION]],\n S\n >\n : never;\n\n/** Enumeration plugin. Replaces node with a function that takes quantity => string. */\nexport const enumerationPlugin: Plugins =\n process.env.INTLAYER_NODE_TYPE_ENUMERATION === 'false'\n ? fallbackPlugin\n : {\n id: 'enumeration-plugin',\n canHandle: (node) =>\n typeof node === 'object' && node?.nodeType === NodeTypes.ENUMERATION,\n transform: (node: EnumerationContent, props, deepTransformNode) => {\n const original = node[NodeTypes.ENUMERATION];\n const result: Record<string, any> = {};\n\n for (const key in original) {\n const child = original[key as unknown as keyof typeof original];\n const childProps = {\n ...props,\n children: child,\n keyPath: [\n ...props.keyPath,\n { type: NodeTypes.ENUMERATION, key } as KeyPath,\n ],\n };\n result[key] = deepTransformNode(child, childProps);\n }\n\n return (arg: number | { count: number }) => {\n const quantity = typeof arg === 'number' ? arg : arg.count;\n const subResult = getEnumeration(result, quantity);\n\n if (typeof subResult === 'function' && typeof arg === 'object') {\n return subResult(arg);\n }\n\n return subResult;\n };\n },\n };\n\n/** ---------------------------------------------\n * PLURAL PLUGIN\n * --------------------------------------------- */\n\nexport type PluralCond<T, S, _L> = T extends {\n nodeType: NodeType | string;\n [NodeTypes.PLURAL]: object;\n}\n ? (\n arg: number | { count: number; [key: string]: unknown }\n ) => DeepTransformContent<\n T[typeof NodeTypes.PLURAL][keyof T[typeof NodeTypes.PLURAL]],\n S\n >\n : never;\n\ntype SubResultFunction = (values: Record<string, string | number>) => string;\n\n/**\n * Plural plugin. Replaces node with a function that takes a count (or\n * `{ count, ...values }`) => string, picking the matching CLDR plural form\n * for the active locale and interpolating `{{count}}` (and other values).\n */\nexport const pluralPlugin = (locale?: LocalesValues): Plugins =>\n process.env.INTLAYER_NODE_TYPE_PLURAL === 'false'\n ? fallbackPlugin\n : {\n id: 'plural-plugin',\n canHandle: (node) =>\n typeof node === 'object' && node?.nodeType === NodeTypes.PLURAL,\n transform: (node: PluralContent, props, deepTransformNode) => {\n const original = node[NodeTypes.PLURAL];\n const result: Record<string, any> = {};\n\n /** String plugin for plural. Replaces string node with a component that renders the insertion. */\n const pluralStringPlugin: Plugins = {\n id: 'plural-string-plugin',\n canHandle: (node) =>\n typeof node === 'string' || isInterpolableWrapperNode(node),\n transform: (node, subProps, deepTransformNode) => {\n // `html()`/`markdown()` nodes carry their `{{count}}` placeholders\n // inside a raw string. Interpolate into that string, then re-run\n // the transform so the html/markdown renderer applies afterwards.\n if (isInterpolableWrapperNode(node)) {\n return (values: { [k: string]: string | number }) =>\n transformInterpolableNode(\n node,\n values,\n subProps,\n props.plugins,\n deepTransformNode\n );\n }\n\n const transformedResult = deepTransformNode(node, {\n ...subProps,\n children: node,\n plugins: [\n ...(props.plugins ?? ([] as Plugins[])).filter(\n (plugin) => plugin.id !== 'intlayer-node-plugin'\n ),\n ],\n });\n\n return (values: { [k: string]: string | number }) => {\n const children = getInsertion(transformedResult, values);\n\n return deepTransformNode(children, {\n ...subProps,\n plugins: props.plugins,\n children,\n });\n };\n },\n };\n\n for (const key in original) {\n const child = original[key as keyof typeof original];\n const childProps = {\n ...props,\n children: child,\n keyPath: [\n ...props.keyPath,\n { type: NodeTypes.PLURAL, key } as KeyPath,\n ],\n plugins: [pluralStringPlugin, ...(props.plugins ?? [])],\n };\n result[key] = deepTransformNode(child, childProps);\n }\n\n const effectiveLocale = String(locale ?? props.locale ?? 'en');\n\n return (arg: number | { count: number; [key: string]: unknown }) => {\n const count = typeof arg === 'number' ? arg : arg.count;\n const values =\n typeof arg === 'number'\n ? { count: arg }\n : (arg as { count: number; [key: string]: unknown });\n\n const subResult: string | SubResultFunction = getPlural(\n result as PluralContent['plural'] as PluralContentState<string>,\n count,\n effectiveLocale\n );\n\n if (typeof subResult === 'function') {\n return (subResult as SubResultFunction)(values);\n }\n\n return subResult;\n };\n },\n };\n\n/** ---------------------------------------------\n * CONDITION PLUGIN\n * --------------------------------------------- */\n\nexport type ConditionCond<T, S, _L> = T extends {\n nodeType: NodeType | string;\n [NodeTypes.CONDITION]: object;\n}\n ? (\n value: boolean | { value: boolean }\n ) => DeepTransformContent<\n T[typeof NodeTypes.CONDITION][keyof T[typeof NodeTypes.CONDITION]],\n S\n >\n : never;\n\n/** Condition plugin. Replaces node with a function that takes boolean => string. */\nexport const conditionPlugin: Plugins =\n process.env.INTLAYER_NODE_TYPE_CONDITION === 'false'\n ? fallbackPlugin\n : {\n id: 'condition-plugin',\n canHandle: (node) =>\n typeof node === 'object' && node?.nodeType === NodeTypes.CONDITION,\n transform: (node: ConditionContent, props, deepTransformNode) => {\n const original = node[NodeTypes.CONDITION];\n const result: Record<string, any> = {};\n\n for (const key in original) {\n const child = original[key as keyof typeof original];\n const childProps = {\n ...props,\n children: child,\n keyPath: [\n ...props.keyPath,\n { type: NodeTypes.CONDITION, key } as KeyPath,\n ],\n };\n result[key] = deepTransformNode(child, childProps);\n }\n\n return (arg: boolean | { value: boolean }) => {\n const value = typeof arg === 'boolean' ? arg : arg.value;\n const subResult = getCondition(result as any, value);\n\n if (typeof subResult === 'function' && typeof arg === 'object') {\n return subResult(arg);\n }\n\n return subResult;\n };\n },\n };\n\n/** ---------------------------------------------\n * INSERTION PLUGIN\n * --------------------------------------------- */\n\nexport type InsertionCond<T, S, _L> = T extends {\n nodeType: NodeType | string;\n [NodeTypes.INSERTION]: infer I;\n fields: readonly string[];\n}\n ? (\n values: {\n [K in T['fields'][number]]: string | number;\n }\n ) => I extends string\n ? DeepTransformContent<string, S>\n : DeepTransformContent<I, S>\n : never;\n\n/** Insertion plugin. Replaces node with a function that takes quantity => string. */\nexport const insertionPlugin: Plugins =\n process.env.INTLAYER_NODE_TYPE_INSERTION === 'false'\n ? fallbackPlugin\n : {\n id: 'insertion-plugin',\n canHandle: (node) =>\n typeof node === 'object' && node?.nodeType === NodeTypes.INSERTION,\n transform: (node: InsertionContent, props, deepTransformNode) => {\n const newKeyPath: KeyPath[] = [\n ...props.keyPath,\n {\n type: NodeTypes.INSERTION,\n },\n ];\n\n const children = node[NodeTypes.INSERTION];\n\n /** Insertion string plugin. Replaces string node with a component that render the insertion. */\n const insertionStringPlugin: Plugins = {\n id: 'insertion-string-plugin',\n canHandle: (node) =>\n typeof node === 'string' || isInterpolableWrapperNode(node),\n transform: (node, subProps, deepTransformNode) => {\n // `html()`/`markdown()` nodes carry their `{{ … }}` placeholders\n // inside a raw string. Interpolate into that string, then re-run\n // the transform so the html/markdown renderer applies afterwards.\n if (isInterpolableWrapperNode(node)) {\n return (\n values: {\n [K in InsertionContent['fields'][number]]: string | number;\n }\n ) =>\n transformInterpolableNode(\n node,\n values,\n subProps,\n props.plugins,\n deepTransformNode\n );\n }\n\n const transformedResult = deepTransformNode(node, {\n ...subProps,\n children: node,\n plugins: [\n ...(props.plugins ?? ([] as Plugins[])).filter(\n (plugin) => plugin.id !== 'intlayer-node-plugin'\n ),\n ],\n });\n\n return (\n values: {\n [K in InsertionContent['fields'][number]]: string | number;\n }\n ) => {\n const children = getInsertion(transformedResult, values);\n\n return deepTransformNode(children, {\n ...subProps,\n plugins: props.plugins,\n children,\n });\n };\n },\n };\n\n return deepTransformNode(children, {\n ...props,\n children,\n keyPath: newKeyPath,\n plugins: [insertionStringPlugin, ...(props.plugins ?? [])],\n });\n },\n };\n\n/** ---------------------------------------------\n * GENDER PLUGIN\n * --------------------------------------------- */\n\nexport type GenderCond<T, S, _L> = T extends {\n nodeType: NodeType | string;\n [NodeTypes.GENDER]: object;\n}\n ? (\n value: Gender\n ) => DeepTransformContent<\n T[typeof NodeTypes.GENDER][keyof T[typeof NodeTypes.GENDER]],\n S\n >\n : never;\n\n/** Gender plugin. Replaces node with a function that takes gender => string. */\nexport const genderPlugin: Plugins =\n process.env.INTLAYER_NODE_TYPE_GENDER === 'false'\n ? fallbackPlugin\n : {\n id: 'gender-plugin',\n canHandle: (node) =>\n typeof node === 'object' && node?.nodeType === NodeTypes.GENDER,\n transform: (node: GenderContent, props, deepTransformNode) => {\n const original = node[NodeTypes.GENDER];\n const result: Record<string, any> = {};\n\n for (const key in original) {\n const child = original[key as keyof typeof original];\n const childProps = {\n ...props,\n children: child,\n keyPath: [\n ...props.keyPath,\n { type: NodeTypes.GENDER, key } as KeyPath,\n ],\n };\n result[key] = deepTransformNode(child, childProps);\n }\n\n return (value: Gender) => getGender(result as any, value);\n },\n };\n\n/** ---------------------------------------------\n * SELECT PLUGIN\n * --------------------------------------------- */\n\n/**\n * Accepted selector for a select node.\n *\n * When the node declares a `fallback` case, any string is accepted (the\n * fallback covers the unmatched values) while the declared cases still\n * autocomplete. Without a `fallback`, only the declared cases are accepted.\n */\nexport type SelectSelector<States> = 'fallback' extends keyof States\n ? Exclude<keyof States & string, 'fallback'> | (string & {})\n : keyof States & string;\n\nexport type SelectCond<T, S, _L> = T extends {\n nodeType: NodeType | string;\n [NodeTypes.SELECT]: object;\n}\n ? (\n value:\n | SelectSelector<T[typeof NodeTypes.SELECT]>\n | { value: SelectSelector<T[typeof NodeTypes.SELECT]> }\n ) => DeepTransformContent<\n T[typeof NodeTypes.SELECT][keyof T[typeof NodeTypes.SELECT]],\n S\n >\n : never;\n\n/** Select plugin. Replaces node with a function that takes a string => content. */\nexport const selectPlugin: Plugins =\n process.env.INTLAYER_NODE_TYPE_SELECT === 'false'\n ? fallbackPlugin\n : {\n id: 'select-plugin',\n canHandle: (node) =>\n typeof node === 'object' && node?.nodeType === NodeTypes.SELECT,\n transform: (node: SelectContent, props, deepTransformNode) => {\n const original = node[NodeTypes.SELECT];\n const result: Record<string, any> = {};\n\n for (const key in original) {\n const child = original[key as keyof typeof original];\n const childProps = {\n ...props,\n children: child,\n keyPath: [\n ...props.keyPath,\n { type: NodeTypes.SELECT, key } as KeyPath,\n ],\n };\n result[key] = deepTransformNode(child, childProps);\n }\n\n return (arg: string | { value: string }) => {\n const value = typeof arg === 'string' ? arg : arg?.value;\n const subResult = getSelect(result, value);\n\n if (typeof subResult === 'function' && typeof arg === 'object') {\n return subResult(arg);\n }\n\n return subResult;\n };\n },\n };\n\n/** ---------------------------------------------\n * NESTED PLUGIN\n * --------------------------------------------- */\n\nexport type NestedCond<T, S, _L> = T extends {\n nodeType: NodeType | string;\n [NodeTypes.NESTED]: infer U;\n}\n ? U extends {\n dictionaryKey: infer K extends DictionaryKeys;\n path?: infer P;\n }\n ? GetNestingResult<K, P, S>\n : never\n : never;\n\n/** Nested plugin. Replaces node with the result of `getNesting`. */\nexport const nestedPlugin = (locale?: LocalesValues): Plugins =>\n process.env.INTLAYER_NODE_TYPE_NESTED === 'false'\n ? fallbackPlugin\n : {\n id: 'nested-plugin',\n canHandle: (node) =>\n typeof node === 'object' &&\n (node?.nodeType === NodeTypes.NESTED || node?.nodeType === 'n'),\n transform: (node: NestedContent, props) =>\n getNesting(\n node[NodeTypes.NESTED].dictionaryKey,\n node[NodeTypes.NESTED].path,\n {\n ...props,\n locale: (locale ?? props.locale) as Locale,\n }\n ),\n };\n\n/** ---------------------------------------------\n * FILE PLUGIN\n * --------------------------------------------- */\n\nexport type FileCond<T, S, _L> = T extends {\n nodeType: NodeType | string;\n [NodeTypes.FILE]: string;\n content?: string;\n}\n ? DeepTransformContent<string, S>\n : never;\n\n/** File plugin. Replaces node with the result of `getNesting`. */\nexport const filePlugin: Plugins =\n process.env.INTLAYER_NODE_TYPE_FILE === 'false'\n ? fallbackPlugin\n : {\n id: 'file-plugin',\n canHandle: (node) =>\n typeof node === 'object' && node?.nodeType === NodeTypes.FILE,\n transform: (node: FileContent, props, deepTransform) =>\n deepTransform(node.content, {\n ...props,\n children: node.content,\n }),\n };\n\n/**\n * PLUGIN RESULT\n */\n\n/**\n * Interface that defines the properties of a node.\n * This interface can be augmented in other packages, such as `react-intlayer`.\n */\nexport interface NodeProps {\n dictionaryKey: string;\n keyPath: KeyPath[];\n plugins?: Plugins[];\n locale?: Locale;\n dictionaryPath?: string;\n children?: any;\n /**\n * Forces eager traversal of plain objects in `deepTransformNode`. By default\n * traversal is lazy (property getters), so callers that discard the returned\n * value never trigger plugins on nested nodes. Set this when running plugins\n * for their side effects only (e.g. missing-locale detection).\n */\n eager?: boolean;\n}\n\n/**\n * Interface that defines the plugins that can be used to transform a node.\n * This interface can be augmented in other packages, such as `react-intlayer`.\n */\nexport interface IInterpreterPlugin<T, S, L extends LocalesValues> {\n translation: TranslationCond<T, S, L>;\n enumeration: EnumerationCond<T, S, L>;\n plural: PluralCond<T, S, L>;\n condition: ConditionCond<T, S, L>;\n insertion: InsertionCond<T, S, L>;\n gender: GenderCond<T, S, L>;\n select: SelectCond<T, S, L>;\n nested: NestedCond<T, S, L>;\n file: FileCond<T, S, L>;\n}\n\n/**\n * Allow to avoid overwriting import from `intlayer` package when `IInterpreterPlugin<T>` interface is augmented in another package, such as `react-intlayer`.\n */\nexport type IInterpreterPluginState = {\n translation: true;\n enumeration: true;\n plural: true;\n condition: true;\n insertion: true;\n gender: true;\n select: true;\n nested: true;\n file: true;\n};\n\n/**\n * Utility type to check if a plugin can be applied to a node.\n */\ntype CheckApplyPlugin<\n T,\n K extends keyof IInterpreterPlugin<T, S, L>,\n S,\n L extends LocalesValues = DeclaredLocales,\n> = K extends keyof S // Test if the key is a key of S.\n ? // Test if the key of S is true. Then the plugin can be applied.\n S[K] extends true\n ? // Test if the key of S exist\n IInterpreterPlugin<T, S, L>[K] extends never\n ? never\n : // Test if the plugin condition is true (if it's not, the plugin is skipped for this node)\n IInterpreterPlugin<T, S, L>[K]\n : never\n : never;\n\n/**\n * Traverse recursively through an object or array, applying each plugin as needed.\n */\ntype Traverse<T, S, L extends LocalesValues = DeclaredLocales> =\n T extends ReadonlyArray<infer U> // Turn any read-only array into a plain mutable array\n ? Array<DeepTransformContent<U, S, L>>\n : T extends object\n ? { [K in keyof T]: DeepTransformContent<T[K], S, L> }\n : T;\n\nexport type IsAny<T> = 0 extends 1 & T ? true : false;\n\n/**\n * Traverse recursively through an object or array, applying each plugin as needed.\n */\nexport type DeepTransformContent<\n T,\n S = IInterpreterPluginState,\n L extends LocalesValues = DeclaredLocales,\n> =\n IsAny<T> extends true\n ? T\n : CheckApplyPlugin<T, keyof IInterpreterPlugin<T, S, L>, S, L> extends never // Check if there is a plugin for T:\n ? // No plugin was found, so try to transform T recursively:\n Traverse<T, S, L>\n : // A plugin was found – use the plugin's transformation.\n CheckApplyPlugin<T, keyof IInterpreterPlugin<T, S, L>, S, L>;\n"],"mappings":"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"}
1
+ {"version":3,"file":"plugins.cjs","names":["NodeTypes","getTranslation","getEnumeration","isInterpolableWrapperNode","transformInterpolableNode","getInsertion","getPlural","getCondition","getGender","getSelect","getNestingOptimized","getNesting"],"sources":["../../../../src/interpreter/getContent/plugins.ts"],"sourcesContent":["import type { Locale } from '@intlayer/types/allLocales';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport type {\n DeclaredLocales,\n DictionaryKeys,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport type { NodeType } from '@intlayer/types/nodeType';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport type {\n ConditionContent,\n EnumerationContent,\n FileContent,\n Gender,\n GenderContent,\n InsertionContent,\n NestedContent,\n PluralContent,\n PluralContentState,\n SelectContent,\n TranslationContent,\n} from '../../transpiler';\nimport { getCondition } from '../getCondition';\nimport { getEnumeration } from '../getEnumeration';\nimport { getGender } from '../getGender';\nimport { getInsertion } from '../getInsertion';\nimport { type GetNestingResult, getNesting } from '../getNesting';\nimport { getNesting as getNestingOptimized } from '../getNesting.optimized';\nimport { getPlural } from '../getPlural';\nimport { getSelect } from '../getSelect';\nimport { getTranslation } from '../getTranslation';\nimport {\n isInterpolableWrapperNode,\n transformInterpolableNode,\n} from '../interpolableNode';\n\n// ── Tree-shake constants ──────────────────────────────────────────────────────\n// When these env vars are injected at build time, bundlers eliminate the\n// branches guarded by these constants.\n\n/** ---------------------------------------------\n * PLUGIN DEFINITION\n * --------------------------------------------- */\n\n/**\n * A plugin/transformer that can optionally transform a node during a single DFS pass.\n * - `canHandle` decides if the node is transformable by this plugin.\n * - `transform` returns the transformed node (and does not recurse further).\n *\n * > `transformFn` is a function that can be used to deeply transform inside the plugin.\n */\nexport type Plugins = {\n id: string;\n canHandle: (node: any) => boolean;\n transform: (\n node: any,\n props: NodeProps,\n transformFn: (node: any, props: NodeProps) => any\n ) => any;\n};\n\n/** ---------------------------------------------\n * FALLBACK PLUGIN\n *\n * Used to fallback a tree-shaken plugin\n * --------------------------------------------- */\n\nexport const fallbackPlugin: Plugins = {\n id: 'fallback-plugin',\n canHandle: () => false,\n transform: (node) => node,\n};\n\n/** ---------------------------------------------\n * TRANSLATION PLUGIN\n * --------------------------------------------- */\n\nexport type UnionKeys<T> = T extends unknown ? keyof T : never;\nexport type ValueAtKey<T, K> = T extends unknown\n ? K extends keyof T\n ? T[K]\n : never\n : never;\n\nexport type TranslationCond<T, S, L extends LocalesValues> = T extends {\n nodeType: NodeType | string;\n [NodeTypes.TRANSLATION]: infer U;\n}\n ? U extends Record<PropertyKey, unknown>\n ? U[keyof U] extends Record<PropertyKey, unknown>\n ? {\n [K in UnionKeys<U[keyof U]>]: L extends keyof U\n ? K extends keyof U[L]\n ? U[L][K]\n : ValueAtKey<U[keyof U], K>\n : ValueAtKey<U[keyof U], K>;\n } extends infer Content\n ? DeepTransformContent<Content, S>\n : never\n : (L extends keyof U ? U[L] : U[keyof U]) extends infer Content\n ? DeepTransformContent<Content, S>\n : never\n : never\n : never;\n\n/** Translation plugin. Replaces node with a locale string if nodeType = Translation. */\nexport const translationPlugin = (\n locale: LocalesValues,\n fallback?: LocalesValues\n): Plugins =>\n process.env.INTLAYER_NODE_TYPE_TRANSLATION === 'false'\n ? fallbackPlugin\n : {\n id: 'translation-plugin',\n canHandle: (node) =>\n typeof node === 'object' && node?.nodeType === NodeTypes.TRANSLATION,\n transform: (node: TranslationContent, props, deepTransformNode) => {\n const original = node[NodeTypes.TRANSLATION] ?? {};\n const result: Record<string, any> = {};\n\n for (const key in original) {\n const childProps = {\n ...props,\n children: original[key as keyof typeof original],\n keyPath: [\n ...props.keyPath,\n { type: NodeTypes.TRANSLATION, key } as KeyPath,\n ],\n };\n result[key] = deepTransformNode(\n original[key as keyof typeof original],\n childProps\n );\n }\n\n return getTranslation(result, locale, fallback);\n },\n };\n\n/** ---------------------------------------------\n * ENUMERATION PLUGIN\n * --------------------------------------------- */\n\nexport type EnumerationCond<T, S, _L> = T extends {\n nodeType: NodeType | string;\n [NodeTypes.ENUMERATION]: object;\n}\n ? (\n quantity: number\n ) => DeepTransformContent<\n T[typeof NodeTypes.ENUMERATION][keyof T[typeof NodeTypes.ENUMERATION]],\n S\n >\n : never;\n\n/** Enumeration plugin. Replaces node with a function that takes quantity => string. */\nexport const enumerationPlugin: Plugins =\n process.env.INTLAYER_NODE_TYPE_ENUMERATION === 'false'\n ? fallbackPlugin\n : {\n id: 'enumeration-plugin',\n canHandle: (node) =>\n typeof node === 'object' && node?.nodeType === NodeTypes.ENUMERATION,\n transform: (node: EnumerationContent, props, deepTransformNode) => {\n const original = node[NodeTypes.ENUMERATION];\n const result: Record<string, any> = {};\n\n for (const key in original) {\n const child = original[key as unknown as keyof typeof original];\n const childProps = {\n ...props,\n children: child,\n keyPath: [\n ...props.keyPath,\n { type: NodeTypes.ENUMERATION, key } as KeyPath,\n ],\n };\n result[key] = deepTransformNode(child, childProps);\n }\n\n return (arg: number | { count: number }) => {\n const quantity = typeof arg === 'number' ? arg : arg.count;\n const subResult = getEnumeration(result, quantity);\n\n if (typeof subResult === 'function' && typeof arg === 'object') {\n return subResult(arg);\n }\n\n return subResult;\n };\n },\n };\n\n/** ---------------------------------------------\n * PLURAL PLUGIN\n * --------------------------------------------- */\n\nexport type PluralCond<T, S, _L> = T extends {\n nodeType: NodeType | string;\n [NodeTypes.PLURAL]: object;\n}\n ? (\n arg: number | { count: number; [key: string]: unknown }\n ) => DeepTransformContent<\n T[typeof NodeTypes.PLURAL][keyof T[typeof NodeTypes.PLURAL]],\n S\n >\n : never;\n\ntype SubResultFunction = (values: Record<string, string | number>) => string;\n\n/**\n * Plural plugin. Replaces node with a function that takes a count (or\n * `{ count, ...values }`) => string, picking the matching CLDR plural form\n * for the active locale and interpolating `{{count}}` (and other values).\n */\nexport const pluralPlugin = (locale?: LocalesValues): Plugins =>\n process.env.INTLAYER_NODE_TYPE_PLURAL === 'false'\n ? fallbackPlugin\n : {\n id: 'plural-plugin',\n canHandle: (node) =>\n typeof node === 'object' && node?.nodeType === NodeTypes.PLURAL,\n transform: (node: PluralContent, props, deepTransformNode) => {\n const original = node[NodeTypes.PLURAL];\n const result: Record<string, any> = {};\n\n /** String plugin for plural. Replaces string node with a component that renders the insertion. */\n const pluralStringPlugin: Plugins = {\n id: 'plural-string-plugin',\n canHandle: (node) =>\n typeof node === 'string' || isInterpolableWrapperNode(node),\n transform: (node, subProps, deepTransformNode) => {\n // `html()`/`markdown()` nodes carry their `{{count}}` placeholders\n // inside a raw string. Interpolate into that string, then re-run\n // the transform so the html/markdown renderer applies afterwards.\n if (isInterpolableWrapperNode(node)) {\n return (values: { [k: string]: string | number }) =>\n transformInterpolableNode(\n node,\n values,\n subProps,\n props.plugins,\n deepTransformNode\n );\n }\n\n const transformedResult = deepTransformNode(node, {\n ...subProps,\n children: node,\n plugins: [\n ...(props.plugins ?? ([] as Plugins[])).filter(\n (plugin) => plugin.id !== 'intlayer-node-plugin'\n ),\n ],\n });\n\n return (values: { [k: string]: string | number }) => {\n const children = getInsertion(transformedResult, values);\n\n return deepTransformNode(children, {\n ...subProps,\n plugins: props.plugins,\n children,\n });\n };\n },\n };\n\n for (const key in original) {\n const child = original[key as keyof typeof original];\n const childProps = {\n ...props,\n children: child,\n keyPath: [\n ...props.keyPath,\n { type: NodeTypes.PLURAL, key } as KeyPath,\n ],\n plugins: [pluralStringPlugin, ...(props.plugins ?? [])],\n };\n result[key] = deepTransformNode(child, childProps);\n }\n\n const effectiveLocale = String(locale ?? props.locale ?? 'en');\n\n return (arg: number | { count: number; [key: string]: unknown }) => {\n const count = typeof arg === 'number' ? arg : arg.count;\n const values =\n typeof arg === 'number'\n ? { count: arg }\n : (arg as { count: number; [key: string]: unknown });\n\n const subResult: string | SubResultFunction = getPlural(\n result as PluralContent['plural'] as PluralContentState<string>,\n count,\n effectiveLocale\n );\n\n if (typeof subResult === 'function') {\n return (subResult as SubResultFunction)(values);\n }\n\n return subResult;\n };\n },\n };\n\n/** ---------------------------------------------\n * CONDITION PLUGIN\n * --------------------------------------------- */\n\nexport type ConditionCond<T, S, _L> = T extends {\n nodeType: NodeType | string;\n [NodeTypes.CONDITION]: object;\n}\n ? (\n value: boolean | { value: boolean }\n ) => DeepTransformContent<\n T[typeof NodeTypes.CONDITION][keyof T[typeof NodeTypes.CONDITION]],\n S\n >\n : never;\n\n/** Condition plugin. Replaces node with a function that takes boolean => string. */\nexport const conditionPlugin: Plugins =\n process.env.INTLAYER_NODE_TYPE_CONDITION === 'false'\n ? fallbackPlugin\n : {\n id: 'condition-plugin',\n canHandle: (node) =>\n typeof node === 'object' && node?.nodeType === NodeTypes.CONDITION,\n transform: (node: ConditionContent, props, deepTransformNode) => {\n const original = node[NodeTypes.CONDITION];\n const result: Record<string, any> = {};\n\n for (const key in original) {\n const child = original[key as keyof typeof original];\n const childProps = {\n ...props,\n children: child,\n keyPath: [\n ...props.keyPath,\n { type: NodeTypes.CONDITION, key } as KeyPath,\n ],\n };\n result[key] = deepTransformNode(child, childProps);\n }\n\n return (arg: boolean | { value: boolean }) => {\n const value = typeof arg === 'boolean' ? arg : arg.value;\n const subResult = getCondition(result as any, value);\n\n if (typeof subResult === 'function' && typeof arg === 'object') {\n return subResult(arg);\n }\n\n return subResult;\n };\n },\n };\n\n/** ---------------------------------------------\n * INSERTION PLUGIN\n * --------------------------------------------- */\n\nexport type InsertionCond<T, S, _L> = T extends {\n nodeType: NodeType | string;\n [NodeTypes.INSERTION]: infer I;\n fields: readonly string[];\n}\n ? (\n values: {\n [K in T['fields'][number]]: string | number;\n }\n ) => I extends string\n ? DeepTransformContent<string, S>\n : DeepTransformContent<I, S>\n : never;\n\n/** Insertion plugin. Replaces node with a function that takes quantity => string. */\nexport const insertionPlugin: Plugins =\n process.env.INTLAYER_NODE_TYPE_INSERTION === 'false'\n ? fallbackPlugin\n : {\n id: 'insertion-plugin',\n canHandle: (node) =>\n typeof node === 'object' && node?.nodeType === NodeTypes.INSERTION,\n transform: (node: InsertionContent, props, deepTransformNode) => {\n const newKeyPath: KeyPath[] = [\n ...props.keyPath,\n {\n type: NodeTypes.INSERTION,\n },\n ];\n\n const children = node[NodeTypes.INSERTION];\n\n /** Insertion string plugin. Replaces string node with a component that render the insertion. */\n const insertionStringPlugin: Plugins = {\n id: 'insertion-string-plugin',\n canHandle: (node) =>\n typeof node === 'string' || isInterpolableWrapperNode(node),\n transform: (node, subProps, deepTransformNode) => {\n // `html()`/`markdown()` nodes carry their `{{ … }}` placeholders\n // inside a raw string. Interpolate into that string, then re-run\n // the transform so the html/markdown renderer applies afterwards.\n if (isInterpolableWrapperNode(node)) {\n return (\n values: {\n [K in InsertionContent['fields'][number]]: string | number;\n }\n ) =>\n transformInterpolableNode(\n node,\n values,\n subProps,\n props.plugins,\n deepTransformNode\n );\n }\n\n const transformedResult = deepTransformNode(node, {\n ...subProps,\n children: node,\n plugins: [\n ...(props.plugins ?? ([] as Plugins[])).filter(\n (plugin) => plugin.id !== 'intlayer-node-plugin'\n ),\n ],\n });\n\n return (\n values: {\n [K in InsertionContent['fields'][number]]: string | number;\n }\n ) => {\n const children = getInsertion(transformedResult, values);\n\n return deepTransformNode(children, {\n ...subProps,\n plugins: props.plugins,\n children,\n });\n };\n },\n };\n\n return deepTransformNode(children, {\n ...props,\n children,\n keyPath: newKeyPath,\n plugins: [insertionStringPlugin, ...(props.plugins ?? [])],\n });\n },\n };\n\n/** ---------------------------------------------\n * GENDER PLUGIN\n * --------------------------------------------- */\n\nexport type GenderCond<T, S, _L> = T extends {\n nodeType: NodeType | string;\n [NodeTypes.GENDER]: object;\n}\n ? (\n value: Gender\n ) => DeepTransformContent<\n T[typeof NodeTypes.GENDER][keyof T[typeof NodeTypes.GENDER]],\n S\n >\n : never;\n\n/** Gender plugin. Replaces node with a function that takes gender => string. */\nexport const genderPlugin: Plugins =\n process.env.INTLAYER_NODE_TYPE_GENDER === 'false'\n ? fallbackPlugin\n : {\n id: 'gender-plugin',\n canHandle: (node) =>\n typeof node === 'object' && node?.nodeType === NodeTypes.GENDER,\n transform: (node: GenderContent, props, deepTransformNode) => {\n const original = node[NodeTypes.GENDER];\n const result: Record<string, any> = {};\n\n for (const key in original) {\n const child = original[key as keyof typeof original];\n const childProps = {\n ...props,\n children: child,\n keyPath: [\n ...props.keyPath,\n { type: NodeTypes.GENDER, key } as KeyPath,\n ],\n };\n result[key] = deepTransformNode(child, childProps);\n }\n\n return (value: Gender) => getGender(result as any, value);\n },\n };\n\n/** ---------------------------------------------\n * SELECT PLUGIN\n * --------------------------------------------- */\n\n/**\n * Accepted selector for a select node.\n *\n * When the node declares a `fallback` case, any string is accepted (the\n * fallback covers the unmatched values) while the declared cases still\n * autocomplete. Without a `fallback`, only the declared cases are accepted.\n */\nexport type SelectSelector<States> = 'fallback' extends keyof States\n ? Exclude<keyof States & string, 'fallback'> | (string & {})\n : keyof States & string;\n\nexport type SelectCond<T, S, _L> = T extends {\n nodeType: NodeType | string;\n [NodeTypes.SELECT]: object;\n}\n ? (\n value:\n | SelectSelector<T[typeof NodeTypes.SELECT]>\n | { value: SelectSelector<T[typeof NodeTypes.SELECT]> }\n ) => DeepTransformContent<\n T[typeof NodeTypes.SELECT][keyof T[typeof NodeTypes.SELECT]],\n S\n >\n : never;\n\n/** Select plugin. Replaces node with a function that takes a string => content. */\nexport const selectPlugin: Plugins =\n process.env.INTLAYER_NODE_TYPE_SELECT === 'false'\n ? fallbackPlugin\n : {\n id: 'select-plugin',\n canHandle: (node) =>\n typeof node === 'object' && node?.nodeType === NodeTypes.SELECT,\n transform: (node: SelectContent, props, deepTransformNode) => {\n const original = node[NodeTypes.SELECT];\n const result: Record<string, any> = {};\n\n for (const key in original) {\n const child = original[key as keyof typeof original];\n const childProps = {\n ...props,\n children: child,\n keyPath: [\n ...props.keyPath,\n { type: NodeTypes.SELECT, key } as KeyPath,\n ],\n };\n result[key] = deepTransformNode(child, childProps);\n }\n\n return (arg: string | { value: string }) => {\n const value = typeof arg === 'string' ? arg : arg?.value;\n const subResult = getSelect(result, value);\n\n if (typeof subResult === 'function' && typeof arg === 'object') {\n return subResult(arg);\n }\n\n return subResult;\n };\n },\n };\n\n/** ---------------------------------------------\n * NESTED PLUGIN\n * --------------------------------------------- */\n\nexport type NestedCond<T, S, _L> = T extends {\n nodeType: NodeType | string;\n [NodeTypes.NESTED]: infer U;\n}\n ? U extends {\n dictionaryKey: infer K extends DictionaryKeys;\n path?: infer P;\n }\n ? GetNestingResult<K, P, S>\n : never\n : never;\n\n/**\n * Resolver used to turn a nested node into content.\n *\n * Optimized builds set `INTLAYER_OPTIMIZED_NESTING` so bundlers fold this to\n * the local resolver and dead-code-eliminate the registry-based one — together\n * with its `getIntlayer` import, and therefore the whole\n * `@intlayer/dictionaries-entry` module. That is what allows the dictionaries\n * entry to be emptied while `nest()` keeps working.\n */\nconst resolveNesting =\n process.env.INTLAYER_OPTIMIZED_NESTING === 'true'\n ? getNestingOptimized\n : getNesting;\n\n/** Nested plugin. Replaces node with the result of `getNesting`. */\nexport const nestedPlugin = (locale?: LocalesValues): Plugins =>\n process.env.INTLAYER_NODE_TYPE_NESTED === 'false'\n ? fallbackPlugin\n : {\n id: 'nested-plugin',\n canHandle: (node) =>\n typeof node === 'object' &&\n (node?.nodeType === NodeTypes.NESTED || node?.nodeType === 'n'),\n transform: (node: NestedContent, props) =>\n resolveNesting(\n node[NodeTypes.NESTED].dictionaryKey,\n node[NodeTypes.NESTED].path,\n {\n ...props,\n locale: (locale ?? props.locale) as Locale,\n }\n ),\n };\n\n/** ---------------------------------------------\n * FILE PLUGIN\n * --------------------------------------------- */\n\nexport type FileCond<T, S, _L> = T extends {\n nodeType: NodeType | string;\n [NodeTypes.FILE]: string;\n content?: string;\n}\n ? DeepTransformContent<string, S>\n : never;\n\n/** File plugin. Replaces node with the result of `getNesting`. */\nexport const filePlugin: Plugins =\n process.env.INTLAYER_NODE_TYPE_FILE === 'false'\n ? fallbackPlugin\n : {\n id: 'file-plugin',\n canHandle: (node) =>\n typeof node === 'object' && node?.nodeType === NodeTypes.FILE,\n transform: (node: FileContent, props, deepTransform) =>\n deepTransform(node.content, {\n ...props,\n children: node.content,\n }),\n };\n\n/**\n * PLUGIN RESULT\n */\n\n/**\n * Interface that defines the properties of a node.\n * This interface can be augmented in other packages, such as `react-intlayer`.\n */\nexport interface NodeProps {\n dictionaryKey: string;\n keyPath: KeyPath[];\n plugins?: Plugins[];\n locale?: Locale;\n dictionaryPath?: string;\n children?: any;\n /**\n * Dictionaries referenced through `nest()`, attached to the consuming\n * dictionary by the build optimization and threaded down by\n * `deepTransformNode`.\n *\n * Present only in optimized builds, where the global dictionary registry is\n * stripped and `getNesting` resolves from this map instead. See\n * `getNesting.optimized.ts`.\n */\n nestedDictionaries?: Record<string, unknown>;\n /**\n * Forces eager traversal of plain objects in `deepTransformNode`. By default\n * traversal is lazy (property getters), so callers that discard the returned\n * value never trigger plugins on nested nodes. Set this when running plugins\n * for their side effects only (e.g. missing-locale detection).\n */\n eager?: boolean;\n}\n\n/**\n * Interface that defines the plugins that can be used to transform a node.\n * This interface can be augmented in other packages, such as `react-intlayer`.\n */\nexport interface IInterpreterPlugin<T, S, L extends LocalesValues> {\n translation: TranslationCond<T, S, L>;\n enumeration: EnumerationCond<T, S, L>;\n plural: PluralCond<T, S, L>;\n condition: ConditionCond<T, S, L>;\n insertion: InsertionCond<T, S, L>;\n gender: GenderCond<T, S, L>;\n select: SelectCond<T, S, L>;\n nested: NestedCond<T, S, L>;\n file: FileCond<T, S, L>;\n}\n\n/**\n * Allow to avoid overwriting import from `intlayer` package when `IInterpreterPlugin<T>` interface is augmented in another package, such as `react-intlayer`.\n */\nexport type IInterpreterPluginState = {\n translation: true;\n enumeration: true;\n plural: true;\n condition: true;\n insertion: true;\n gender: true;\n select: true;\n nested: true;\n file: true;\n};\n\n/**\n * Utility type to check if a plugin can be applied to a node.\n */\ntype CheckApplyPlugin<\n T,\n K extends keyof IInterpreterPlugin<T, S, L>,\n S,\n L extends LocalesValues = DeclaredLocales,\n> = K extends keyof S // Test if the key is a key of S.\n ? // Test if the key of S is true. Then the plugin can be applied.\n S[K] extends true\n ? // Test if the key of S exist\n IInterpreterPlugin<T, S, L>[K] extends never\n ? never\n : // Test if the plugin condition is true (if it's not, the plugin is skipped for this node)\n IInterpreterPlugin<T, S, L>[K]\n : never\n : never;\n\n/**\n * Traverse recursively through an object or array, applying each plugin as needed.\n */\ntype Traverse<T, S, L extends LocalesValues = DeclaredLocales> =\n T extends ReadonlyArray<infer U> // Turn any read-only array into a plain mutable array\n ? Array<DeepTransformContent<U, S, L>>\n : T extends object\n ? { [K in keyof T]: DeepTransformContent<T[K], S, L> }\n : T;\n\nexport type IsAny<T> = 0 extends 1 & T ? true : false;\n\n/**\n * Traverse recursively through an object or array, applying each plugin as needed.\n */\nexport type DeepTransformContent<\n T,\n S = IInterpreterPluginState,\n L extends LocalesValues = DeclaredLocales,\n> =\n IsAny<T> extends true\n ? T\n : CheckApplyPlugin<T, keyof IInterpreterPlugin<T, S, L>, S, L> extends never // Check if there is a plugin for T:\n ? // No plugin was found, so try to transform T recursively:\n Traverse<T, S, L>\n : // A plugin was found – use the plugin's transformation.\n CheckApplyPlugin<T, keyof IInterpreterPlugin<T, S, L>, S, L>;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAmEA,MAAa,iBAA0B;CACrC,IAAI;CACJ,iBAAiB;CACjB,YAAY,SAAS;AACvB;;AAmCA,MAAa,qBACX,QACA,aAEA,QAAQ,IAAI,mCAAmC,UAC3C,iBACA;CACE,IAAI;CACJ,YAAY,SACV,OAAO,SAAS,YAAY,MAAM,aAAaA,yBAAU;CAC3D,YAAY,MAA0B,OAAO,sBAAsB;EACjE,MAAM,WAAW,KAAKA,yBAAU,gBAAgB,CAAC;EACjD,MAAM,SAA8B,CAAC;EAErC,KAAK,MAAM,OAAO,UAAU;GAC1B,MAAM,aAAa;IACjB,GAAG;IACH,UAAU,SAAS;IACnB,SAAS,CACP,GAAG,MAAM,SACT;KAAE,MAAMA,yBAAU;KAAa;IAAI,CACrC;GACF;GACA,OAAO,OAAO,kBACZ,SAAS,MACT,UACF;EACF;EAEA,OAAOC,kDAAe,QAAQ,QAAQ,QAAQ;CAChD;AACF;;AAmBN,MAAa,oBACX,QAAQ,IAAI,mCAAmC,UAC3C,iBACA;CACE,IAAI;CACJ,YAAY,SACV,OAAO,SAAS,YAAY,MAAM,aAAaD,yBAAU;CAC3D,YAAY,MAA0B,OAAO,sBAAsB;EACjE,MAAM,WAAW,KAAKA,yBAAU;EAChC,MAAM,SAA8B,CAAC;EAErC,KAAK,MAAM,OAAO,UAAU;GAC1B,MAAM,QAAQ,SAAS;GASvB,OAAO,OAAO,kBAAkB,OAAO;IAPrC,GAAG;IACH,UAAU;IACV,SAAS,CACP,GAAG,MAAM,SACT;KAAE,MAAMA,yBAAU;KAAa;IAAI,CACrC;GAE8C,CAAC;EACnD;EAEA,QAAQ,QAAoC;GAC1C,MAAM,WAAW,OAAO,QAAQ,WAAW,MAAM,IAAI;GACrD,MAAM,YAAYE,kDAAe,QAAQ,QAAQ;GAEjD,IAAI,OAAO,cAAc,cAAc,OAAO,QAAQ,UACpD,OAAO,UAAU,GAAG;GAGtB,OAAO;EACT;CACF;AACF;;;;;;AAyBN,MAAa,gBAAgB,WAC3B,QAAQ,IAAI,8BAA8B,UACtC,iBACA;CACE,IAAI;CACJ,YAAY,SACV,OAAO,SAAS,YAAY,MAAM,aAAaF,yBAAU;CAC3D,YAAY,MAAqB,OAAO,sBAAsB;EAC5D,MAAM,WAAW,KAAKA,yBAAU;EAChC,MAAM,SAA8B,CAAC;;EAGrC,MAAM,qBAA8B;GAClC,IAAI;GACJ,YAAY,SACV,OAAO,SAAS,YAAYG,+DAA0B,IAAI;GAC5D,YAAY,MAAM,UAAU,sBAAsB;IAIhD,IAAIA,+DAA0B,IAAI,GAChC,QAAQ,WACNC,+DACE,MACA,QACA,UACA,MAAM,SACN,iBACF;IAGJ,MAAM,oBAAoB,kBAAkB,MAAM;KAChD,GAAG;KACH,UAAU;KACV,SAAS,CACP,IAAI,MAAM,WAAY,CAAC,EAAe,CAAE,QACrC,WAAW,OAAO,OAAO,sBAC5B,CACF;IACF,CAAC;IAED,QAAQ,WAA6C;KACnD,MAAM,WAAWC,8CAAa,mBAAmB,MAAM;KAEvD,OAAO,kBAAkB,UAAU;MACjC,GAAG;MACH,SAAS,MAAM;MACf;KACF,CAAC;IACH;GACF;EACF;EAEA,KAAK,MAAM,OAAO,UAAU;GAC1B,MAAM,QAAQ,SAAS;GAUvB,OAAO,OAAO,kBAAkB,OAAO;IARrC,GAAG;IACH,UAAU;IACV,SAAS,CACP,GAAG,MAAM,SACT;KAAE,MAAML,yBAAU;KAAQ;IAAI,CAChC;IACA,SAAS,CAAC,oBAAoB,GAAI,MAAM,WAAW,CAAC,CAAE;GAER,CAAC;EACnD;EAEA,MAAM,kBAAkB,OAAO,UAAU,MAAM,UAAU,IAAI;EAE7D,QAAQ,QAA4D;GAClE,MAAM,QAAQ,OAAO,QAAQ,WAAW,MAAM,IAAI;GAClD,MAAM,SACJ,OAAO,QAAQ,WACX,EAAE,OAAO,IAAI,IACZ;GAEP,MAAM,YAAwCM,wCAC5C,QACA,OACA,eACF;GAEA,IAAI,OAAO,cAAc,YACvB,OAAQ,UAAgC,MAAM;GAGhD,OAAO;EACT;CACF;AACF;;AAmBN,MAAa,kBACX,QAAQ,IAAI,iCAAiC,UACzC,iBACA;CACE,IAAI;CACJ,YAAY,SACV,OAAO,SAAS,YAAY,MAAM,aAAaN,yBAAU;CAC3D,YAAY,MAAwB,OAAO,sBAAsB;EAC/D,MAAM,WAAW,KAAKA,yBAAU;EAChC,MAAM,SAA8B,CAAC;EAErC,KAAK,MAAM,OAAO,UAAU;GAC1B,MAAM,QAAQ,SAAS;GASvB,OAAO,OAAO,kBAAkB,OAAO;IAPrC,GAAG;IACH,UAAU;IACV,SAAS,CACP,GAAG,MAAM,SACT;KAAE,MAAMA,yBAAU;KAAW;IAAI,CACnC;GAE8C,CAAC;EACnD;EAEA,QAAQ,QAAsC;GAC5C,MAAM,QAAQ,OAAO,QAAQ,YAAY,MAAM,IAAI;GACnD,MAAM,YAAYO,8CAAa,QAAe,KAAK;GAEnD,IAAI,OAAO,cAAc,cAAc,OAAO,QAAQ,UACpD,OAAO,UAAU,GAAG;GAGtB,OAAO;EACT;CACF;AACF;;AAqBN,MAAa,kBACX,QAAQ,IAAI,iCAAiC,UACzC,iBACA;CACE,IAAI;CACJ,YAAY,SACV,OAAO,SAAS,YAAY,MAAM,aAAaP,yBAAU;CAC3D,YAAY,MAAwB,OAAO,sBAAsB;EAC/D,MAAM,aAAwB,CAC5B,GAAG,MAAM,SACT,EACE,MAAMA,yBAAU,UAClB,CACF;EAEA,MAAM,WAAW,KAAKA,yBAAU;;EAGhC,MAAM,wBAAiC;GACrC,IAAI;GACJ,YAAY,SACV,OAAO,SAAS,YAAYG,+DAA0B,IAAI;GAC5D,YAAY,MAAM,UAAU,sBAAsB;IAIhD,IAAIA,+DAA0B,IAAI,GAChC,QACE,WAIAC,+DACE,MACA,QACA,UACA,MAAM,SACN,iBACF;IAGJ,MAAM,oBAAoB,kBAAkB,MAAM;KAChD,GAAG;KACH,UAAU;KACV,SAAS,CACP,IAAI,MAAM,WAAY,CAAC,EAAe,CAAE,QACrC,WAAW,OAAO,OAAO,sBAC5B,CACF;IACF,CAAC;IAED,QACE,WAGG;KACH,MAAM,WAAWC,8CAAa,mBAAmB,MAAM;KAEvD,OAAO,kBAAkB,UAAU;MACjC,GAAG;MACH,SAAS,MAAM;MACf;KACF,CAAC;IACH;GACF;EACF;EAEA,OAAO,kBAAkB,UAAU;GACjC,GAAG;GACH;GACA,SAAS;GACT,SAAS,CAAC,uBAAuB,GAAI,MAAM,WAAW,CAAC,CAAE;EAC3D,CAAC;CACH;AACF;;AAmBN,MAAa,eACX,QAAQ,IAAI,8BAA8B,UACtC,iBACA;CACE,IAAI;CACJ,YAAY,SACV,OAAO,SAAS,YAAY,MAAM,aAAaL,yBAAU;CAC3D,YAAY,MAAqB,OAAO,sBAAsB;EAC5D,MAAM,WAAW,KAAKA,yBAAU;EAChC,MAAM,SAA8B,CAAC;EAErC,KAAK,MAAM,OAAO,UAAU;GAC1B,MAAM,QAAQ,SAAS;GASvB,OAAO,OAAO,kBAAkB,OAAO;IAPrC,GAAG;IACH,UAAU;IACV,SAAS,CACP,GAAG,MAAM,SACT;KAAE,MAAMA,yBAAU;KAAQ;IAAI,CAChC;GAE8C,CAAC;EACnD;EAEA,QAAQ,UAAkBQ,wCAAU,QAAe,KAAK;CAC1D;AACF;;AAgCN,MAAa,eACX,QAAQ,IAAI,8BAA8B,UACtC,iBACA;CACE,IAAI;CACJ,YAAY,SACV,OAAO,SAAS,YAAY,MAAM,aAAaR,yBAAU;CAC3D,YAAY,MAAqB,OAAO,sBAAsB;EAC5D,MAAM,WAAW,KAAKA,yBAAU;EAChC,MAAM,SAA8B,CAAC;EAErC,KAAK,MAAM,OAAO,UAAU;GAC1B,MAAM,QAAQ,SAAS;GASvB,OAAO,OAAO,kBAAkB,OAAO;IAPrC,GAAG;IACH,UAAU;IACV,SAAS,CACP,GAAG,MAAM,SACT;KAAE,MAAMA,yBAAU;KAAQ;IAAI,CAChC;GAE8C,CAAC;EACnD;EAEA,QAAQ,QAAoC;GAC1C,MAAM,QAAQ,OAAO,QAAQ,WAAW,MAAM,KAAK;GACnD,MAAM,YAAYS,wCAAU,QAAQ,KAAK;GAEzC,IAAI,OAAO,cAAc,cAAc,OAAO,QAAQ,UACpD,OAAO,UAAU,GAAG;GAGtB,OAAO;EACT;CACF;AACF;;;;;;;;;;AA2BN,MAAM,iBACJ,QAAQ,IAAI,+BAA+B,SACvCC,sDACAC;;AAGN,MAAa,gBAAgB,WAC3B,QAAQ,IAAI,8BAA8B,UACtC,iBACA;CACE,IAAI;CACJ,YAAY,SACV,OAAO,SAAS,aACf,MAAM,aAAaX,yBAAU,UAAU,MAAM,aAAa;CAC7D,YAAY,MAAqB,UAC/B,eACE,KAAKA,yBAAU,OAAO,CAAC,eACvB,KAAKA,yBAAU,OAAO,CAAC,MACvB;EACE,GAAG;EACH,QAAS,UAAU,MAAM;CAC3B,CACF;AACJ;;AAeN,MAAa,aACX,QAAQ,IAAI,4BAA4B,UACpC,iBACA;CACE,IAAI;CACJ,YAAY,SACV,OAAO,SAAS,YAAY,MAAM,aAAaA,yBAAU;CAC3D,YAAY,MAAmB,OAAO,kBACpC,cAAc,KAAK,SAAS;EAC1B,GAAG;EACH,UAAU,KAAK;CACjB,CAAC;AACL"}
@@ -26,7 +26,8 @@ const getDictionary = (dictionary, localeOrSelector, plugins) => {
26
26
  dictionaryKey: resolvedDictionary.key,
27
27
  dictionaryPath: resolvedDictionary.filePath,
28
28
  keyPath: [],
29
- plugins: appliedPlugins
29
+ plugins: appliedPlugins,
30
+ nestedDictionaries: resolvedDictionary.nestedDictionaries
30
31
  };
31
32
  return require_interpreter_getContent_getContent.getContent(resolvedDictionary.content, props, appliedPlugins);
32
33
  };
@@ -1 +1 @@
1
- {"version":3,"file":"getDictionary.cjs","names":["parseDictionarySelector","getBasePlugins","resolveQualifiedDictionary","getContent"],"sources":["../../../src/interpreter/getDictionary.ts"],"sourcesContent":["import type {\n Dictionary,\n DictionarySelector,\n QualifiedDictionaryGroup,\n ResolveQualifiedDictionaryContent,\n} from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n ExtractSelectorLocale,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport {\n parseDictionarySelector,\n resolveQualifiedDictionary,\n} from '../dictionaryManipulator/qualifiedDictionary';\nimport type {\n DeepTransformContent,\n IInterpreterPluginState,\n NodeProps,\n Plugins,\n} from './getContent';\nimport { getBasePlugins, getContent } from './getContent/getContent';\n\n/**\n * Transforms a dictionary in a single pass, applying each plugin as needed.\n *\n * Also accepts a `QualifiedDictionaryGroup` (collections, variants) together\n * with a selector as second argument — the group is resolved to a single entry\n * (or an ordered array of entries for collections without an `item` selector)\n * before transformation.\n *\n * @param dictionary The dictionary (or qualified dictionary group) to transform.\n * @param localeOrSelector The locale, or a selector object (`{ item }`,\n * `{ variant }`, optionally with `locale`).\n * @param plugins An array of NodeTransformer that define how to transform recognized nodes.\n * If omitted, we’ll use a default set of plugins.\n */\nexport const getDictionary = <\n const T extends Dictionary | QualifiedDictionaryGroup,\n const A extends LocalesValues | DictionarySelector = DeclaredLocales,\n>(\n dictionary: T,\n localeOrSelector?: A,\n plugins?: Plugins[]\n): DeepTransformContent<\n ResolveQualifiedDictionaryContent<T, A>,\n IInterpreterPluginState,\n ExtractSelectorLocale<A>\n> => {\n const { locale, selector } = parseDictionarySelector(localeOrSelector);\n const appliedPlugins = plugins ?? getBasePlugins(locale);\n\n const resolved = resolveQualifiedDictionary(dictionary, selector);\n\n const transformDictionary = (resolvedDictionary: Dictionary) => {\n const props: NodeProps = {\n dictionaryKey: resolvedDictionary.key,\n dictionaryPath: resolvedDictionary.filePath,\n keyPath: [],\n plugins: appliedPlugins,\n };\n\n return getContent(resolvedDictionary.content, props, appliedPlugins);\n };\n\n if (resolved === null) return null as any;\n\n if (Array.isArray(resolved)) {\n return resolved.map(transformDictionary) as any;\n }\n\n return transformDictionary(resolved) as any;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAqCA,MAAa,iBAIX,YACA,kBACA,YAKG;CACH,MAAM,EAAE,QAAQ,aAAaA,0EAAwB,gBAAgB;CACrE,MAAM,iBAAiB,WAAWC,yDAAe,MAAM;CAEvD,MAAM,WAAWC,6EAA2B,YAAY,QAAQ;CAEhE,MAAM,uBAAuB,uBAAmC;EAC9D,MAAM,QAAmB;GACvB,eAAe,mBAAmB;GAClC,gBAAgB,mBAAmB;GACnC,SAAS,CAAC;GACV,SAAS;EACX;EAEA,OAAOC,qDAAW,mBAAmB,SAAS,OAAO,cAAc;CACrE;CAEA,IAAI,aAAa,MAAM,OAAO;CAE9B,IAAI,MAAM,QAAQ,QAAQ,GACxB,OAAO,SAAS,IAAI,mBAAmB;CAGzC,OAAO,oBAAoB,QAAQ;AACrC"}
1
+ {"version":3,"file":"getDictionary.cjs","names":["parseDictionarySelector","getBasePlugins","resolveQualifiedDictionary","getContent"],"sources":["../../../src/interpreter/getDictionary.ts"],"sourcesContent":["import type {\n Dictionary,\n DictionarySelector,\n QualifiedDictionaryGroup,\n ResolveQualifiedDictionaryContent,\n} from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n ExtractSelectorLocale,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport {\n parseDictionarySelector,\n resolveQualifiedDictionary,\n} from '../dictionaryManipulator/qualifiedDictionary';\nimport type {\n DeepTransformContent,\n IInterpreterPluginState,\n NodeProps,\n Plugins,\n} from './getContent';\nimport { getBasePlugins, getContent } from './getContent/getContent';\n\n/**\n * Transforms a dictionary in a single pass, applying each plugin as needed.\n *\n * Also accepts a `QualifiedDictionaryGroup` (collections, variants) together\n * with a selector as second argument — the group is resolved to a single entry\n * (or an ordered array of entries for collections without an `item` selector)\n * before transformation.\n *\n * @param dictionary The dictionary (or qualified dictionary group) to transform.\n * @param localeOrSelector The locale, or a selector object (`{ item }`,\n * `{ variant }`, optionally with `locale`).\n * @param plugins An array of NodeTransformer that define how to transform recognized nodes.\n * If omitted, we’ll use a default set of plugins.\n */\nexport const getDictionary = <\n const T extends Dictionary | QualifiedDictionaryGroup,\n const A extends LocalesValues | DictionarySelector = DeclaredLocales,\n>(\n dictionary: T,\n localeOrSelector?: A,\n plugins?: Plugins[]\n): DeepTransformContent<\n ResolveQualifiedDictionaryContent<T, A>,\n IInterpreterPluginState,\n ExtractSelectorLocale<A>\n> => {\n const { locale, selector } = parseDictionarySelector(localeOrSelector);\n const appliedPlugins = plugins ?? getBasePlugins(locale);\n\n const resolved = resolveQualifiedDictionary(dictionary, selector);\n\n const transformDictionary = (resolvedDictionary: Dictionary) => {\n const props: NodeProps = {\n dictionaryKey: resolvedDictionary.key,\n dictionaryPath: resolvedDictionary.filePath,\n keyPath: [],\n plugins: appliedPlugins,\n // Attached by the build optimization so `nest()` resolves without the\n // global registry. Undefined in unoptimized builds, where the\n // registry-based resolver is used instead.\n nestedDictionaries: resolvedDictionary.nestedDictionaries,\n };\n\n return getContent(resolvedDictionary.content, props, appliedPlugins);\n };\n\n if (resolved === null) return null as any;\n\n if (Array.isArray(resolved)) {\n return resolved.map(transformDictionary) as any;\n }\n\n return transformDictionary(resolved) as any;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAqCA,MAAa,iBAIX,YACA,kBACA,YAKG;CACH,MAAM,EAAE,QAAQ,aAAaA,0EAAwB,gBAAgB;CACrE,MAAM,iBAAiB,WAAWC,yDAAe,MAAM;CAEvD,MAAM,WAAWC,6EAA2B,YAAY,QAAQ;CAEhE,MAAM,uBAAuB,uBAAmC;EAC9D,MAAM,QAAmB;GACvB,eAAe,mBAAmB;GAClC,gBAAgB,mBAAmB;GACnC,SAAS,CAAC;GACV,SAAS;GAIT,oBAAoB,mBAAmB;EACzC;EAEA,OAAOC,qDAAW,mBAAmB,SAAS,OAAO,cAAc;CACrE;CAEA,IAAI,aAAa,MAAM,OAAO;CAE9B,IAAI,MAAM,QAAQ,QAAQ,GACxB,OAAO,SAAS,IAAI,mBAAmB;CAGzC,OAAO,oBAAoB,QAAQ;AACrC"}
@@ -0,0 +1,59 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_interpreter_getDictionary = require('./getDictionary.cjs');
3
+
4
+ //#region src/interpreter/getNesting.optimized.ts
5
+ /**
6
+ * Walks a dot-separated path on an already-interpreted dictionary content.
7
+ *
8
+ * Mirrors the registry-based resolver: an unresolvable segment yields the whole
9
+ * content rather than `undefined`, so a stale path degrades to the parent
10
+ * object instead of crashing the render.
11
+ */
12
+ const walkPath = (content, path) => {
13
+ let current = content;
14
+ for (const segment of path.split(".")) {
15
+ current = current?.[segment];
16
+ if (current === void 0) return content;
17
+ }
18
+ return current;
19
+ };
20
+ /**
21
+ * Optimized-build replacement for `getNesting`.
22
+ *
23
+ * Resolves a `nest()` reference from the dictionaries the compiler attached to
24
+ * the consuming dictionary (`props.nestedDictionaries`) instead of looking the
25
+ * key up in the global registry exposed by `@intlayer/dictionaries-entry`.
26
+ *
27
+ * That is what lets the build optimization erase the registry entirely: this
28
+ * module never imports `getIntlayer`, so no dictionary reaches the bundle
29
+ * except through the static import injected at the call site. The nest target
30
+ * then lands in the same chunk as the dictionary referencing it — and, in
31
+ * `dynamic` / `fetch` mode, is loaded by the same lazy per-locale loader.
32
+ *
33
+ * The compiler guarantees the attachment for every dictionary holding a nested
34
+ * node, so a missing entry means the dictionary was not produced by the
35
+ * optimized pipeline; the reference then resolves to `undefined`.
36
+ *
37
+ * @param dictionaryKey - The key of the dictionary to nest.
38
+ * @param path - Optional dot-separated path within the nested dictionary.
39
+ * @param props - Interpreter node props, carrying the attached dictionaries.
40
+ * @returns The nested content.
41
+ */
42
+ const getNesting = (dictionaryKey, path, props) => {
43
+ const nestedDictionaries = props?.nestedDictionaries;
44
+ const nestedDictionary = nestedDictionaries?.[dictionaryKey];
45
+ if (!nestedDictionary) return void 0;
46
+ const content = require_interpreter_getDictionary.getDictionary({
47
+ ...nestedDictionary,
48
+ nestedDictionaries: {
49
+ ...nestedDictionaries,
50
+ ...nestedDictionary.nestedDictionaries
51
+ }
52
+ }, props?.locale, props?.plugins);
53
+ if (typeof path !== "string") return content;
54
+ return walkPath(content, path);
55
+ };
56
+
57
+ //#endregion
58
+ exports.getNesting = getNesting;
59
+ //# sourceMappingURL=getNesting.optimized.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getNesting.optimized.cjs","names":["getDictionary"],"sources":["../../../src/interpreter/getNesting.optimized.ts"],"sourcesContent":["import type { Dictionary } from '@intlayer/types/dictionary';\nimport type {\n DictionaryKeys,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport type { ValidDotPathsFor } from '../transpiler';\nimport type { NodeProps } from './getContent';\nimport { getDictionary } from './getDictionary';\nimport type { GetNestingResult } from './getNesting';\n\n/**\n * Walks a dot-separated path on an already-interpreted dictionary content.\n *\n * Mirrors the registry-based resolver: an unresolvable segment yields the whole\n * content rather than `undefined`, so a stale path degrades to the parent\n * object instead of crashing the render.\n */\nconst walkPath = (content: unknown, path: string): unknown => {\n let current: unknown = content;\n\n for (const segment of path.split('.')) {\n current = (current as Record<string, unknown> | undefined)?.[segment];\n\n if (current === undefined) return content;\n }\n\n return current;\n};\n\n/**\n * Optimized-build replacement for `getNesting`.\n *\n * Resolves a `nest()` reference from the dictionaries the compiler attached to\n * the consuming dictionary (`props.nestedDictionaries`) instead of looking the\n * key up in the global registry exposed by `@intlayer/dictionaries-entry`.\n *\n * That is what lets the build optimization erase the registry entirely: this\n * module never imports `getIntlayer`, so no dictionary reaches the bundle\n * except through the static import injected at the call site. The nest target\n * then lands in the same chunk as the dictionary referencing it — and, in\n * `dynamic` / `fetch` mode, is loaded by the same lazy per-locale loader.\n *\n * The compiler guarantees the attachment for every dictionary holding a nested\n * node, so a missing entry means the dictionary was not produced by the\n * optimized pipeline; the reference then resolves to `undefined`.\n *\n * @param dictionaryKey - The key of the dictionary to nest.\n * @param path - Optional dot-separated path within the nested dictionary.\n * @param props - Interpreter node props, carrying the attached dictionaries.\n * @returns The nested content.\n */\nexport const getNesting = <const K extends DictionaryKeys, const P>(\n dictionaryKey: K,\n path?: P extends ValidDotPathsFor<K> ? P : never,\n props?: NodeProps\n): GetNestingResult<K, P> => {\n const nestedDictionaries = props?.nestedDictionaries;\n const nestedDictionary = nestedDictionaries?.[dictionaryKey as string];\n\n if (!nestedDictionary) return undefined as GetNestingResult<K, P>;\n\n // The attached dictionaries are the raw compiled artifacts, which carry no\n // attachment of their own. Hand the consumer's map down so a nest target that\n // nests further dictionaries resolves too — the map is the transitive\n // closure, so one level of propagation covers any depth, and cycles simply\n // re-read the same finite map.\n const content = getDictionary(\n {\n ...(nestedDictionary as Dictionary),\n nestedDictionaries: {\n ...nestedDictionaries,\n ...(nestedDictionary as Dictionary).nestedDictionaries,\n },\n } as Dictionary,\n props?.locale as LocalesValues,\n props?.plugins\n );\n\n if (typeof path !== 'string') return content as GetNestingResult<K, P>;\n\n return walkPath(content, path) as GetNestingResult<K, P>;\n};\n"],"mappings":";;;;;;;;;;;AAiBA,MAAM,YAAY,SAAkB,SAA0B;CAC5D,IAAI,UAAmB;CAEvB,KAAK,MAAM,WAAW,KAAK,MAAM,GAAG,GAAG;EACrC,UAAW,UAAkD;EAE7D,IAAI,YAAY,QAAW,OAAO;CACpC;CAEA,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAa,cACX,eACA,MACA,UAC2B;CAC3B,MAAM,qBAAqB,OAAO;CAClC,MAAM,mBAAmB,qBAAqB;CAE9C,IAAI,CAAC,kBAAkB,OAAO;CAO9B,MAAM,UAAUA,gDACd;EACE,GAAI;EACJ,oBAAoB;GAClB,GAAG;GACH,GAAI,iBAAgC;EACtC;CACF,GACA,OAAO,QACP,OAAO,OACT;CAEA,IAAI,OAAO,SAAS,UAAU,OAAO;CAErC,OAAO,SAAS,SAAS,IAAI;AAC/B"}
@@ -16,9 +16,11 @@ const require_localization_getLocaleFromPath = require('./getLocaleFromPath.cjs'
16
16
  const require_localization_getLocaleLang = require('./getLocaleLang.cjs');
17
17
  const require_localization_getLocaleName = require('./getLocaleName.cjs');
18
18
  const require_localization_localeMapper = require('./localeMapper.cjs');
19
+ const require_localization_proxyMode = require('./proxyMode.cjs');
19
20
  const require_localization_validatePrefix = require('./validatePrefix.cjs');
20
21
 
21
22
  exports.comparePaths = require_localization_comparePaths.comparePaths;
23
+ exports.formatProxyEnabledMessage = require_localization_proxyMode.formatProxyEnabledMessage;
22
24
  exports.generateSitemap = require_localization_generateSitemap.generateSitemap;
23
25
  exports.generateSitemapUrl = require_localization_generateSitemap.generateSitemapUrl;
24
26
  exports.getBrowserLocale = require_localization_getBrowserLocale.getBrowserLocale;
@@ -40,10 +42,12 @@ exports.getPrefix = require_localization_getPrefix.getPrefix;
40
42
  exports.getRewritePath = require_localization_rewriteUtils.getRewritePath;
41
43
  exports.getRewriteRules = require_localization_rewriteUtils.getRewriteRules;
42
44
  exports.isLocaleExclusiveOnDomain = require_localization_domainUtils.isLocaleExclusiveOnDomain;
45
+ exports.isProxyStorageLocaleEnabled = require_localization_proxyMode.isProxyStorageLocaleEnabled;
43
46
  exports.localeDetector = require_localization_localeDetector.localeDetector;
44
47
  exports.localeFlatMap = require_localization_localeMapper.localeFlatMap;
45
48
  exports.localeMap = require_localization_localeMapper.localeMap;
46
49
  exports.localeRecord = require_localization_localeMapper.localeRecord;
47
50
  exports.localeResolver = require_localization_localeResolver.localeResolver;
48
51
  exports.normalizePath = require_localization_comparePaths.normalizePath;
52
+ exports.resolveProxyMode = require_localization_proxyMode.resolveProxyMode;
49
53
  exports.validatePrefix = require_localization_validatePrefix.validatePrefix;
@@ -0,0 +1,92 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
3
+ let _intlayer_config_logger = require("@intlayer/config/logger");
4
+ let _intlayer_config_colors = require("@intlayer/config/colors");
5
+ _intlayer_config_colors = require_runtime.__toESM(_intlayer_config_colors);
6
+
7
+ //#region src/localization/proxyMode.ts
8
+ /**
9
+ * Resolves the effective {@link ProxyMode} from the `routing.enableProxy`
10
+ * configuration value.
11
+ *
12
+ * `process.env.INTLAYER_ROUTING_ENABLE_PROXY` is injected at build time by
13
+ * `getConfigEnvVars` and takes precedence, so bundlers can dead-code-eliminate
14
+ * the branches guarded by the resolved mode. The variable is only emitted for
15
+ * the two explicit states; its absence means `'auto'` and defers to the
16
+ * configuration value read at runtime.
17
+ *
18
+ * @param enableProxy - The `routing.enableProxy` value; `undefined` means auto.
19
+ * @returns The resolved proxy mode.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * resolveProxyMode(undefined); // 'auto'
24
+ * resolveProxyMode(true); // 'forced'
25
+ * resolveProxyMode(false); // 'disabled'
26
+ * ```
27
+ */
28
+ const resolveProxyMode = (enableProxy) => {
29
+ if (process.env.INTLAYER_ROUTING_ENABLE_PROXY === "false") return "disabled";
30
+ if (process.env.INTLAYER_ROUTING_ENABLE_PROXY === "true") return "forced";
31
+ if (enableProxy === false) return "disabled";
32
+ if (enableProxy === true) return "forced";
33
+ return "auto";
34
+ };
35
+ /**
36
+ * Indicates whether the proxy may use the locale held in storage (cookie or
37
+ * header) as a source when deciding which locale a request resolves to.
38
+ *
39
+ * Auto mode suppresses it on development and preview servers only. Every other
40
+ * combination keeps the stored locale active, which notably preserves the
41
+ * behaviour of `createIntlayerProxyHandler` when it is mounted manually in a
42
+ * production Nitro server.
43
+ *
44
+ * Suppressing the read affects redirect *sources* only — the locale is still
45
+ * persisted onto responses, the URL locale prefix still wins over everything,
46
+ * and `Accept-Language` detection still applies as the fallback.
47
+ *
48
+ * @param proxyMode - The resolved proxy mode.
49
+ * @param isDevServer - Whether a development or preview server is serving the app.
50
+ * @returns `true` when the stored locale may drive locale resolution.
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * isProxyStorageLocaleEnabled('auto', true); // false — dev server, URL-driven only
55
+ * isProxyStorageLocaleEnabled('auto', false); // true — production
56
+ * isProxyStorageLocaleEnabled('forced', true); // true — explicitly opted in
57
+ * ```
58
+ */
59
+ const isProxyStorageLocaleEnabled = (proxyMode, isDevServer) => !(proxyMode === "auto" && isDevServer);
60
+ /**
61
+ * Builds the line announcing that a server has mounted the locale-routing
62
+ * proxy.
63
+ *
64
+ * Shared by every integration (Vite plugin, Next.js middleware) so the reported
65
+ * state cannot drift between them: when the stored locale is suppressed the
66
+ * message says so, which would otherwise look like a broken proxy to anyone
67
+ * testing locale switching with a cookie already set.
68
+ *
69
+ * Takes the suppression flag rather than the {@link ProxyMode} on purpose. Auto
70
+ * mode only suppresses storage on a dev server, so a mode-based signature would
71
+ * let a production caller — such as the Nitro production handler — announce a
72
+ * suppression that is not actually in effect.
73
+ *
74
+ * @param isStorageLocaleSuppressed - Whether the stored locale is barred from
75
+ * driving redirects, i.e. the negation of {@link isProxyStorageLocaleEnabled}.
76
+ * @returns An ANSI-coloured, ready-to-log message.
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * formatProxyEnabledMessage(true);
81
+ * // Intlayer proxy enabled - storage redirection disabled for dev purpose
82
+ * formatProxyEnabledMessage(false);
83
+ * // Intlayer proxy enabled
84
+ * ```
85
+ */
86
+ const formatProxyEnabledMessage = (isStorageLocaleSuppressed) => [`Intlayer proxy ${(0, _intlayer_config_logger.colorize)("enabled", _intlayer_config_colors.GREEN)}`, isStorageLocaleSuppressed && (0, _intlayer_config_logger.colorize)("- storage redirection disabled for dev purpose", _intlayer_config_colors.GREY)].filter(Boolean).join(" ");
87
+
88
+ //#endregion
89
+ exports.formatProxyEnabledMessage = formatProxyEnabledMessage;
90
+ exports.isProxyStorageLocaleEnabled = isProxyStorageLocaleEnabled;
91
+ exports.resolveProxyMode = resolveProxyMode;
92
+ //# sourceMappingURL=proxyMode.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxyMode.cjs","names":["ANSIColors"],"sources":["../../../src/localization/proxyMode.ts"],"sourcesContent":["import * as ANSIColors from '@intlayer/config/colors';\nimport { colorize } from '@intlayer/config/logger';\n\n/**\n * Effective mode of the Intlayer locale-routing proxy, resolved from the\n * `routing.enableProxy` configuration option.\n *\n * - `'auto'` — the option was left unset. The proxy is registered, but it stays\n * URL-driven while a development or preview server is serving the app: the\n * stored locale (cookie / header) is not used as a redirect source, so a\n * stale cookie cannot silently pull every navigation to another locale. In\n * production the mode behaves exactly like `'forced'`.\n * - `'forced'` — the option was explicitly set to `true`. Full proxy behaviour\n * in every environment, storage-driven redirects included.\n * - `'disabled'` — the option was explicitly set to `false`. The proxy is not\n * registered at all (Vite) or becomes a pass-through (Next.js).\n */\nexport type ProxyMode = 'auto' | 'forced' | 'disabled';\n\n/**\n * Resolves the effective {@link ProxyMode} from the `routing.enableProxy`\n * configuration value.\n *\n * `process.env.INTLAYER_ROUTING_ENABLE_PROXY` is injected at build time by\n * `getConfigEnvVars` and takes precedence, so bundlers can dead-code-eliminate\n * the branches guarded by the resolved mode. The variable is only emitted for\n * the two explicit states; its absence means `'auto'` and defers to the\n * configuration value read at runtime.\n *\n * @param enableProxy - The `routing.enableProxy` value; `undefined` means auto.\n * @returns The resolved proxy mode.\n *\n * @example\n * ```ts\n * resolveProxyMode(undefined); // 'auto'\n * resolveProxyMode(true); // 'forced'\n * resolveProxyMode(false); // 'disabled'\n * ```\n */\nexport const resolveProxyMode = (enableProxy?: boolean): ProxyMode => {\n if (process.env.INTLAYER_ROUTING_ENABLE_PROXY === 'false') return 'disabled';\n if (process.env.INTLAYER_ROUTING_ENABLE_PROXY === 'true') return 'forced';\n\n if (enableProxy === false) return 'disabled';\n if (enableProxy === true) return 'forced';\n\n return 'auto';\n};\n\n/**\n * Indicates whether the proxy may use the locale held in storage (cookie or\n * header) as a source when deciding which locale a request resolves to.\n *\n * Auto mode suppresses it on development and preview servers only. Every other\n * combination keeps the stored locale active, which notably preserves the\n * behaviour of `createIntlayerProxyHandler` when it is mounted manually in a\n * production Nitro server.\n *\n * Suppressing the read affects redirect *sources* only — the locale is still\n * persisted onto responses, the URL locale prefix still wins over everything,\n * and `Accept-Language` detection still applies as the fallback.\n *\n * @param proxyMode - The resolved proxy mode.\n * @param isDevServer - Whether a development or preview server is serving the app.\n * @returns `true` when the stored locale may drive locale resolution.\n *\n * @example\n * ```ts\n * isProxyStorageLocaleEnabled('auto', true); // false — dev server, URL-driven only\n * isProxyStorageLocaleEnabled('auto', false); // true — production\n * isProxyStorageLocaleEnabled('forced', true); // true — explicitly opted in\n * ```\n */\nexport const isProxyStorageLocaleEnabled = (\n proxyMode: ProxyMode,\n isDevServer: boolean\n): boolean => !(proxyMode === 'auto' && isDevServer);\n\n/**\n * Builds the line announcing that a server has mounted the locale-routing\n * proxy.\n *\n * Shared by every integration (Vite plugin, Next.js middleware) so the reported\n * state cannot drift between them: when the stored locale is suppressed the\n * message says so, which would otherwise look like a broken proxy to anyone\n * testing locale switching with a cookie already set.\n *\n * Takes the suppression flag rather than the {@link ProxyMode} on purpose. Auto\n * mode only suppresses storage on a dev server, so a mode-based signature would\n * let a production caller — such as the Nitro production handler — announce a\n * suppression that is not actually in effect.\n *\n * @param isStorageLocaleSuppressed - Whether the stored locale is barred from\n * driving redirects, i.e. the negation of {@link isProxyStorageLocaleEnabled}.\n * @returns An ANSI-coloured, ready-to-log message.\n *\n * @example\n * ```ts\n * formatProxyEnabledMessage(true);\n * // Intlayer proxy enabled - storage redirection disabled for dev purpose\n * formatProxyEnabledMessage(false);\n * // Intlayer proxy enabled\n * ```\n */\nexport const formatProxyEnabledMessage = (\n isStorageLocaleSuppressed: boolean\n): string =>\n [\n `Intlayer proxy ${colorize('enabled', ANSIColors.GREEN)}`,\n isStorageLocaleSuppressed &&\n colorize(\n '- storage redirection disabled for dev purpose',\n ANSIColors.GREY\n ),\n ]\n .filter(Boolean)\n .join(' ');\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCA,MAAa,oBAAoB,gBAAqC;CACpE,IAAI,QAAQ,IAAI,kCAAkC,SAAS,OAAO;CAClE,IAAI,QAAQ,IAAI,kCAAkC,QAAQ,OAAO;CAEjE,IAAI,gBAAgB,OAAO,OAAO;CAClC,IAAI,gBAAgB,MAAM,OAAO;CAEjC,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,MAAa,+BACX,WACA,gBACY,EAAE,cAAc,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BxC,MAAa,6BACX,8BAEA,CACE,wDAA2B,WAAWA,wBAAW,KAAK,KACtD,mEAEI,kDACAA,wBAAW,IACb,CACJ,CAAC,CACE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG"}
@@ -1,8 +1,12 @@
1
1
  import * as NodeTypes from "@intlayer/types/nodeType";
2
2
 
3
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";
4
+ /** Whether `node` is a plain (non-array) object, i.e. a content branch. */
5
+ const isRecordNode = (node) => typeof node === "object" && node !== null && !Array.isArray(node);
6
+ /** Whether `node` is a typed node, whatever its `nodeType`. */
7
+ const isTypedNode = (node) => isRecordNode(node) && typeof node.nodeType === "string";
8
+ /** Whether `node` is a `translation` node holding a locale map. */
9
+ const isTranslationNode = (node) => isTypedNode(node) && node.nodeType === NodeTypes.TRANSLATION && isRecordNode(node[NodeTypes.TRANSLATION]);
6
10
  /** Structural equality for leaf source values (string / number / nested objects). */
7
11
  const isSameValue = (a, b) => JSON.stringify(a) === JSON.stringify(b);
8
12
  /**
@@ -12,8 +16,8 @@ const isSameValue = (a, b) => JSON.stringify(a) === JSON.stringify(b);
12
16
  */
13
17
  const getEditedNode = (previous, next, defaultLocale) => {
14
18
  if (isTranslationNode(next)) {
15
- const nextValue = next[NodeTypes.TRANSLATION]?.[defaultLocale];
16
- const previousValue = isTranslationNode(previous) ? previous[NodeTypes.TRANSLATION]?.[defaultLocale] : void 0;
19
+ const nextValue = next[NodeTypes.TRANSLATION][defaultLocale];
20
+ const previousValue = isTranslationNode(previous) ? previous[NodeTypes.TRANSLATION][defaultLocale] : void 0;
17
21
  if (typeof nextValue === "undefined") return;
18
22
  if (isSameValue(previousValue, nextValue)) return;
19
23
  return {
@@ -44,8 +48,8 @@ const getEditedNode = (previous, next, defaultLocale) => {
44
48
  });
45
49
  return hasChange ? result : void 0;
46
50
  }
47
- if (typeof next === "object" && next !== null) {
48
- const previousObject = typeof previous === "object" && previous !== null && !Array.isArray(previous) ? previous : void 0;
51
+ if (isRecordNode(next)) {
52
+ const previousObject = isRecordNode(previous) ? previous : void 0;
49
53
  const result = {};
50
54
  let hasChange = false;
51
55
  for (const key of Object.keys(next)) {
@@ -1 +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,IAAI,KACnB,OAAQ,KAAgC,aAAa;;AAGvD,MAAM,eAAe,GAAY,MAC/B,KAAK,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC;;;;;;AAOxC,MAAM,iBACJ,UACA,MACA,kBAC4B;CAE5B,IAAI,kBAAkB,IAAI,GAAG;EAC3B,MAAM,YAAY,KAAK,UAAU,YAAY,GAAG;EAChD,MAAM,gBAAgB,kBAAkB,QAAQ,IAC5C,SAAS,UAAU,YAAY,GAAG,iBAClC;EAGJ,IAAI,OAAO,cAAc,aACvB;EAIF,IAAI,YAAY,eAAe,SAAS,GACtC;EAIF,OAAO;GACL,UAAU,UAAU;IACnB,UAAU,cAAc,GAAG,gBAA0B,UAAU;EAClE;CACF;CAIA,IAAI,YAAY,IAAI,GAAG;EACrB,MAAM,EAAE,aAAa;EACrB,MAAM,gBAAgB,YAAY,QAAQ,IACrC,SAAS,YACV;EACJ,MAAM,cAAc,cAClB,eACA,KAAK,WACL,aACF;EAEA,IAAI,OAAO,gBAAgB,aACzB;EAGF,OAAO;GACL;IACC,WAAW;EACd;CACF;CAIA,IAAI,MAAM,QAAQ,IAAI,GAAG;EACvB,MAAM,gBAAgB,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC;EAC5D,IAAI,YAAY;EAEhB,MAAM,SAAS,KAAK,KAAK,OAAO,UAAU;GACxC,MAAM,cAAc,cAClB,cAAc,QACd,OACA,aACF;GACA,IAAI,OAAO,gBAAgB,aAAa;IACtC,YAAY;IACZ,OAAO;GACT;GAGA,OAAO;EACT,CAAC;EAED,OAAO,YAAa,SAAyB;CAC/C;CAGA,IAAI,OAAO,SAAS,YAAY,SAAS,MAAM;EAC7C,MAAM,iBACJ,OAAO,aAAa,YACpB,aAAa,QACb,CAAC,MAAM,QAAQ,QAAQ,IAClB,WACD;EAEN,MAAM,SAAsC,CAAC;EAC7C,IAAI,YAAY;EAEhB,KAAK,MAAM,OAAO,OAAO,KAAK,IAAmC,GAAG;GAClE,MAAM,cAAc,cAClB,iBAAiB,MAChB,KAAqC,MACtC,aACF;GACA,IAAI,OAAO,gBAAgB,aAAa;IACtC,OAAO,OAAO;IACd,YAAY;GACd;EACF;EAEA,OAAO,YAAa,SAAyB;CAC/C;AAIF;;;;;;;;;;;;;;;;;AAkBA,MAAa,oBACX,iBACA,YACA,kBAEA,cAAc,iBAAiB,YAAY,aAAa,KACvD,CAAC;;;;;;;;;AAUJ,MAAa,uBACX,oBACA,eACA,mBACgB;CAChB,GAAG;CACH,SAAS,iBACP,oBAAoB,SACpB,cAAc,SACd,aACF;AACF"}
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 type { TypedNodeModel } from '@intlayer/types/nodeType';\nimport * as NodeTypes from '@intlayer/types/nodeType';\n\n/**\n * Structural view of a dictionary content tree.\n *\n * `ContentNode` describes leaves and typed nodes only — the plain objects and\n * arrays that hold them are typed as `any` by the public `Dictionary['content']`.\n * Declaring those containers explicitly lets the diff walk a whole content tree\n * without casting at every step.\n */\ntype ContentTree = ContentNode | ContentTree[] | { [key: string]: ContentTree };\n\n/** A `translation` node, e.g. `{ nodeType: 'translation', translation: { en, fr } }`. */\ntype TranslationNode = TypedNodeModel<\n typeof NodeTypes.TRANSLATION,\n Record<string, ContentTree>\n>;\n\n/**\n * Any typed node — a wrapper holding its value under the key named by its own\n * `nodeType` (e.g. `{ nodeType: 'plural', plural: … }`).\n */\ntype TypedContentNode = {\n nodeType: string;\n [wrappedValueKey: string]: ContentTree;\n};\n\n/** Whether `node` is a plain (non-array) object, i.e. a content branch. */\nconst isRecordNode = (node: ContentTree): node is Record<string, ContentTree> =>\n typeof node === 'object' && node !== null && !Array.isArray(node);\n\n/** Whether `node` is a typed node, whatever its `nodeType`. */\nconst isTypedNode = (node: ContentTree): node is TypedContentNode =>\n isRecordNode(node) && typeof node.nodeType === 'string';\n\n/** Whether `node` is a `translation` node holding a locale map. */\nconst isTranslationNode = (node: ContentTree): node is TranslationNode =>\n isTypedNode(node) &&\n node.nodeType === NodeTypes.TRANSLATION &&\n isRecordNode(node[NodeTypes.TRANSLATION]);\n\n/** Structural equality for leaf source values (string / number / nested objects). */\nconst isSameValue = (a: ContentTree, b: ContentTree): 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: ContentTree,\n next: ContentTree,\n defaultLocale: LocalesValues\n): ContentTree | undefined => {\n // Translation node: compare the default-locale leaf only.\n if (isTranslationNode(next)) {\n const nextValue = next[NodeTypes.TRANSLATION][defaultLocale];\n const previousValue = isTranslationNode(previous)\n ? previous[NodeTypes.TRANSLATION][defaultLocale]\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]: nextValue },\n } satisfies TranslationNode;\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]\n : undefined;\n const editedInner = getEditedNode(\n previousInner,\n next[nodeType],\n defaultLocale\n );\n\n if (typeof editedInner === 'undefined') {\n return undefined;\n }\n\n return {\n nodeType,\n [nodeType]: editedInner,\n } satisfies TypedContentNode;\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],\n child,\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 : undefined;\n }\n\n // Plain objects: recurse into each key, keeping only changed branches.\n if (isRecordNode(next)) {\n const previousObject = isRecordNode(previous) ? previous : undefined;\n\n const result: Record<string, ContentTree> = {};\n let hasChange = false;\n\n for (const key of Object.keys(next)) {\n const editedChild = getEditedNode(\n previousObject?.[key],\n next[key],\n defaultLocale\n );\n if (typeof editedChild !== 'undefined') {\n result[key] = editedChild;\n hasChange = true;\n }\n }\n\n return hasChange ? result : 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 // The result is a sub-tree of `newContent`, so narrowing the structural\n // `ContentTree` back to the public `ContentNode` is sound: only the plain\n // containers `ContentNode` leaves untyped are dropped from the type.\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":";;;;AA+BA,MAAM,gBAAgB,SACpB,OAAO,SAAS,YAAY,SAAS,QAAQ,CAAC,MAAM,QAAQ,IAAI;;AAGlE,MAAM,eAAe,SACnB,aAAa,IAAI,KAAK,OAAO,KAAK,aAAa;;AAGjD,MAAM,qBAAqB,SACzB,YAAY,IAAI,KAChB,KAAK,aAAa,UAAU,eAC5B,aAAa,KAAK,UAAU,YAAY;;AAG1C,MAAM,eAAe,GAAgB,MACnC,KAAK,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC;;;;;;AAOxC,MAAM,iBACJ,UACA,MACA,kBAC4B;CAE5B,IAAI,kBAAkB,IAAI,GAAG;EAC3B,MAAM,YAAY,KAAK,UAAU,YAAY,CAAC;EAC9C,MAAM,gBAAgB,kBAAkB,QAAQ,IAC5C,SAAS,UAAU,YAAY,CAAC,iBAChC;EAGJ,IAAI,OAAO,cAAc,aACvB;EAIF,IAAI,YAAY,eAAe,SAAS,GACtC;EAIF,OAAO;GACL,UAAU,UAAU;IACnB,UAAU,cAAc,GAAG,gBAAgB,UAAU;EACxD;CACF;CAIA,IAAI,YAAY,IAAI,GAAG;EACrB,MAAM,EAAE,aAAa;EACrB,MAAM,gBAAgB,YAAY,QAAQ,IACtC,SAAS,YACT;EACJ,MAAM,cAAc,cAClB,eACA,KAAK,WACL,aACF;EAEA,IAAI,OAAO,gBAAgB,aACzB;EAGF,OAAO;GACL;IACC,WAAW;EACd;CACF;CAIA,IAAI,MAAM,QAAQ,IAAI,GAAG;EACvB,MAAM,gBAAgB,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC;EAC5D,IAAI,YAAY;EAEhB,MAAM,SAAS,KAAK,KAAK,OAAO,UAAU;GACxC,MAAM,cAAc,cAClB,cAAc,QACd,OACA,aACF;GACA,IAAI,OAAO,gBAAgB,aAAa;IACtC,YAAY;IACZ,OAAO;GACT;GAGA,OAAO;EACT,CAAC;EAED,OAAO,YAAY,SAAS;CAC9B;CAGA,IAAI,aAAa,IAAI,GAAG;EACtB,MAAM,iBAAiB,aAAa,QAAQ,IAAI,WAAW;EAE3D,MAAM,SAAsC,CAAC;EAC7C,IAAI,YAAY;EAEhB,KAAK,MAAM,OAAO,OAAO,KAAK,IAAI,GAAG;GACnC,MAAM,cAAc,cAClB,iBAAiB,MACjB,KAAK,MACL,aACF;GACA,IAAI,OAAO,gBAAgB,aAAa;IACtC,OAAO,OAAO;IACd,YAAY;GACd;EACF;EAEA,OAAO,YAAY,SAAS;CAC9B;AAIF;;;;;;;;;;;;;;;;;AAkBA,MAAa,oBACX,iBACA,YACA,kBAKC,cAAc,iBAAiB,YAAY,aAAa,KACvD,CAAC;;;;;;;;;AAUL,MAAa,uBACX,oBACA,eACA,mBACgB;CAChB,GAAG;CACH,SAAS,iBACP,oBAAoB,SACpB,cAAc,SACd,aACF;AACF"}
@@ -0,0 +1,150 @@
1
+ import { NESTED } from "@intlayer/types/nodeType";
2
+
3
+ //#region src/dictionaryManipulator/getNestedDictionaryReferences.ts
4
+ /**
5
+ * Compact alias some minified pipelines emit in place of `nodeType: 'nested'`.
6
+ * Matched defensively so the scan keeps working on already-minified content.
7
+ */
8
+ const MINIFIED_NESTED_NODE_TYPE = "n";
9
+ /**
10
+ * Narrows a value to a traversable object (plain object or array). `null` is
11
+ * excluded even though `typeof null === 'object'`.
12
+ */
13
+ const isTraversable = (value) => typeof value === "object" && value !== null;
14
+ /**
15
+ * Records one `nest()` reference, widening the entry to `'all'` as soon as a
16
+ * reference targets the whole dictionary.
17
+ */
18
+ const addNestedReference = (references, dictionaryKey, path) => {
19
+ const existingReference = references.get(dictionaryKey);
20
+ if (existingReference === "all") return;
21
+ const rootField = path?.split(".")[0];
22
+ if (!rootField) {
23
+ references.set(dictionaryKey, "all");
24
+ return;
25
+ }
26
+ if (existingReference) {
27
+ existingReference.add(rootField);
28
+ return;
29
+ }
30
+ references.set(dictionaryKey, /* @__PURE__ */ new Set([rootField]));
31
+ };
32
+ /**
33
+ * Walks a dictionary tree and collects every dictionary referenced through a
34
+ * `nest()` node, together with the top-level fields those references read.
35
+ *
36
+ * The walk is iterative (no recursion depth limit) and guards against shared
37
+ * or circular references, so it is safe on both compiled JSON and in-memory
38
+ * dictionaries.
39
+ *
40
+ * @param dictionaries - Dictionary objects (or any content subtree) to scan.
41
+ * @returns Map of nest-target dictionary key → referenced top-level fields.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * // dashboard.content.ts → { period: nest('common', 'period') }
46
+ * getNestedDictionaryReferences([dashboardDictionary]);
47
+ * // Map { 'common' => Set { 'period' } }
48
+ * ```
49
+ */
50
+ const getNestedDictionaryReferences = (dictionaries) => {
51
+ const references = /* @__PURE__ */ new Map();
52
+ const stack = [...dictionaries];
53
+ const visitedNodes = /* @__PURE__ */ new Set();
54
+ while (stack.length > 0) {
55
+ const node = stack.pop();
56
+ if (!isTraversable(node)) continue;
57
+ if (visitedNodes.has(node)) continue;
58
+ visitedNodes.add(node);
59
+ if (Array.isArray(node)) {
60
+ stack.push(...node);
61
+ continue;
62
+ }
63
+ const { nodeType } = node;
64
+ if (nodeType === NESTED || nodeType === MINIFIED_NESTED_NODE_TYPE) {
65
+ const nestedState = node[nodeType];
66
+ if (isTraversable(nestedState)) {
67
+ const { dictionaryKey, path } = nestedState;
68
+ if (typeof dictionaryKey === "string") addNestedReference(references, dictionaryKey, typeof path === "string" ? path : void 0);
69
+ }
70
+ continue;
71
+ }
72
+ stack.push(...Object.values(node));
73
+ }
74
+ return references;
75
+ };
76
+ /**
77
+ * Keys of every dictionary referenced through a `nest()` node.
78
+ *
79
+ * @param dictionaries - Dictionary objects (or any content subtree) to scan.
80
+ * @returns Set of nest-target dictionary keys.
81
+ */
82
+ const getNestedDictionaryKeys = (dictionaries) => new Set(getNestedDictionaryReferences(dictionaries).keys());
83
+ /**
84
+ * Expands direct nest edges into their transitive closure, so a consumer that
85
+ * nests a dictionary which itself nests another ends up depending on both.
86
+ *
87
+ * Self-references and cycles are absorbed: a key never appears in its own
88
+ * dependency set, and the walk terminates on cyclic graphs.
89
+ */
90
+ const closeOverNestedEdges = (directEdges) => {
91
+ const closedEdges = /* @__PURE__ */ new Map();
92
+ for (const [consumerKey, directTargets] of directEdges) {
93
+ const allTargets = /* @__PURE__ */ new Set();
94
+ const pending = [...directTargets];
95
+ while (pending.length > 0) {
96
+ const targetKey = pending.pop();
97
+ if (targetKey === consumerKey) continue;
98
+ if (allTargets.has(targetKey)) continue;
99
+ allTargets.add(targetKey);
100
+ const transitiveTargets = directEdges.get(targetKey);
101
+ if (transitiveTargets) pending.push(...transitiveTargets);
102
+ }
103
+ closedEdges.set(consumerKey, allTargets);
104
+ }
105
+ return closedEdges;
106
+ };
107
+ /**
108
+ * Builds the `nest()` dependency graph of a set of dictionaries: for each
109
+ * dictionary key, the keys of every dictionary it needs in order to resolve its
110
+ * nested nodes, transitively.
111
+ *
112
+ * The build optimization uses this to attach the nest targets to the dictionary
113
+ * artifact itself, so `nest()` resolves from a local reference and the target
114
+ * lands in the same chunk (or the same lazy per-locale loader) as the
115
+ * dictionary referencing it.
116
+ *
117
+ * Dictionaries without nested nodes are absent from the result.
118
+ *
119
+ * @param dictionaries - Compiled dictionaries to scan; each must carry its `key`.
120
+ * @returns Map of dictionary key → keys it depends on through `nest()`.
121
+ *
122
+ * @example
123
+ * ```ts
124
+ * // dashboard nests common, common nests labels
125
+ * getNestedDictionaryGraph([dashboard, common]);
126
+ * // Map { 'dashboard' => Set { 'common', 'labels' }, 'common' => Set { 'labels' } }
127
+ * ```
128
+ */
129
+ const getNestedDictionaryGraph = (dictionaries) => {
130
+ const directEdges = /* @__PURE__ */ new Map();
131
+ for (const dictionary of dictionaries) {
132
+ if (!isTraversable(dictionary)) continue;
133
+ const { key } = dictionary;
134
+ if (typeof key !== "string") continue;
135
+ const targets = getNestedDictionaryKeys([dictionary]);
136
+ targets.delete(key);
137
+ if (targets.size === 0) continue;
138
+ const existingTargets = directEdges.get(key);
139
+ if (existingTargets) {
140
+ for (const target of targets) existingTargets.add(target);
141
+ continue;
142
+ }
143
+ directEdges.set(key, targets);
144
+ }
145
+ return closeOverNestedEdges(directEdges);
146
+ };
147
+
148
+ //#endregion
149
+ export { getNestedDictionaryGraph, getNestedDictionaryKeys, getNestedDictionaryReferences };
150
+ //# sourceMappingURL=getNestedDictionaryReferences.mjs.map