@intlayer/core 9.0.0-canary.4 → 9.0.0-canary.5

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 (33) hide show
  1. package/dist/cjs/interpreter/getContent/plugins.cjs +8 -8
  2. package/dist/cjs/interpreter/getContent/plugins.cjs.map +1 -1
  3. package/dist/cjs/interpreter/getIntlayer.cjs +1 -1
  4. package/dist/cjs/interpreter/getIntlayer.cjs.map +1 -1
  5. package/dist/cjs/localization/getLocalizedUrl.cjs +4 -4
  6. package/dist/cjs/localization/getLocalizedUrl.cjs.map +1 -1
  7. package/dist/cjs/localization/getPathWithoutLocale.cjs +2 -2
  8. package/dist/cjs/localization/getPathWithoutLocale.cjs.map +1 -1
  9. package/dist/cjs/localization/getPrefix.cjs +2 -2
  10. package/dist/cjs/localization/getPrefix.cjs.map +1 -1
  11. package/dist/cjs/localization/rewriteUtils.cjs +1 -1
  12. package/dist/cjs/localization/rewriteUtils.cjs.map +1 -1
  13. package/dist/cjs/localization/validatePrefix.cjs +2 -2
  14. package/dist/cjs/localization/validatePrefix.cjs.map +1 -1
  15. package/dist/cjs/utils/localeStorage.cjs +4 -4
  16. package/dist/cjs/utils/localeStorage.cjs.map +1 -1
  17. package/dist/esm/interpreter/getContent/plugins.mjs +8 -8
  18. package/dist/esm/interpreter/getContent/plugins.mjs.map +1 -1
  19. package/dist/esm/interpreter/getIntlayer.mjs +1 -1
  20. package/dist/esm/interpreter/getIntlayer.mjs.map +1 -1
  21. package/dist/esm/localization/getLocalizedUrl.mjs +4 -4
  22. package/dist/esm/localization/getLocalizedUrl.mjs.map +1 -1
  23. package/dist/esm/localization/getPathWithoutLocale.mjs +2 -2
  24. package/dist/esm/localization/getPathWithoutLocale.mjs.map +1 -1
  25. package/dist/esm/localization/getPrefix.mjs +2 -2
  26. package/dist/esm/localization/getPrefix.mjs.map +1 -1
  27. package/dist/esm/localization/rewriteUtils.mjs +1 -1
  28. package/dist/esm/localization/rewriteUtils.mjs.map +1 -1
  29. package/dist/esm/localization/validatePrefix.mjs +2 -2
  30. package/dist/esm/localization/validatePrefix.mjs.map +1 -1
  31. package/dist/esm/utils/localeStorage.mjs +4 -4
  32. package/dist/esm/utils/localeStorage.mjs.map +1 -1
  33. package/package.json +6 -6
@@ -22,7 +22,7 @@ const fallbackPlugin = {
22
22
  transform: (node) => node
23
23
  };
24
24
  /** Translation plugin. Replaces node with a locale string if nodeType = Translation. */
25
- const translationPlugin = (locale, fallback) => process.env["INTLAYER_NODE_TYPE_TRANSLATION"] === "false" ? fallbackPlugin : {
25
+ const translationPlugin = (locale, fallback) => process.env.INTLAYER_NODE_TYPE_TRANSLATION === "false" ? fallbackPlugin : {
26
26
  id: "translation-plugin",
27
27
  canHandle: (node) => typeof node === "object" && node?.nodeType === _intlayer_types_nodeType.TRANSLATION,
28
28
  transform: (node, props, deepTransformNode) => {
@@ -43,7 +43,7 @@ const translationPlugin = (locale, fallback) => process.env["INTLAYER_NODE_TYPE_
43
43
  }
44
44
  };
45
45
  /** Enumeration plugin. Replaces node with a function that takes quantity => string. */
46
- const enumerationPlugin = process.env["INTLAYER_NODE_TYPE_ENUMERATION"] === "false" ? fallbackPlugin : {
46
+ const enumerationPlugin = process.env.INTLAYER_NODE_TYPE_ENUMERATION === "false" ? fallbackPlugin : {
47
47
  id: "enumeration-plugin",
48
48
  canHandle: (node) => typeof node === "object" && node?.nodeType === _intlayer_types_nodeType.ENUMERATION,
49
49
  transform: (node, props, deepTransformNode) => {
@@ -72,7 +72,7 @@ const enumerationPlugin = process.env["INTLAYER_NODE_TYPE_ENUMERATION"] === "fal
72
72
  * `{ count, ...values }`) => string, picking the matching CLDR plural form
73
73
  * for the active locale and interpolating `{{count}}` (and other values).
74
74
  */
75
- const pluralPlugin = (locale) => process.env["INTLAYER_NODE_TYPE_PLURAL"] === "false" ? fallbackPlugin : {
75
+ const pluralPlugin = (locale) => process.env.INTLAYER_NODE_TYPE_PLURAL === "false" ? fallbackPlugin : {
76
76
  id: "plural-plugin",
77
77
  canHandle: (node) => typeof node === "object" && node?.nodeType === _intlayer_types_nodeType.PLURAL,
78
78
  transform: (node, props, deepTransformNode) => {
@@ -121,7 +121,7 @@ const pluralPlugin = (locale) => process.env["INTLAYER_NODE_TYPE_PLURAL"] === "f
121
121
  }
122
122
  };
123
123
  /** Condition plugin. Replaces node with a function that takes boolean => string. */
124
- const conditionPlugin = process.env["INTLAYER_NODE_TYPE_CONDITION"] === "false" ? fallbackPlugin : {
124
+ const conditionPlugin = process.env.INTLAYER_NODE_TYPE_CONDITION === "false" ? fallbackPlugin : {
125
125
  id: "condition-plugin",
126
126
  canHandle: (node) => typeof node === "object" && node?.nodeType === _intlayer_types_nodeType.CONDITION,
127
127
  transform: (node, props, deepTransformNode) => {
@@ -146,7 +146,7 @@ const conditionPlugin = process.env["INTLAYER_NODE_TYPE_CONDITION"] === "false"
146
146
  }
147
147
  };
148
148
  /** Insertion plugin. Replaces node with a function that takes quantity => string. */
149
- const insertionPlugin = process.env["INTLAYER_NODE_TYPE_INSERTION"] === "false" ? fallbackPlugin : {
149
+ const insertionPlugin = process.env.INTLAYER_NODE_TYPE_INSERTION === "false" ? fallbackPlugin : {
150
150
  id: "insertion-plugin",
151
151
  canHandle: (node) => typeof node === "object" && node?.nodeType === _intlayer_types_nodeType.INSERTION,
152
152
  transform: (node, props, deepTransformNode) => {
@@ -181,7 +181,7 @@ const insertionPlugin = process.env["INTLAYER_NODE_TYPE_INSERTION"] === "false"
181
181
  }
182
182
  };
183
183
  /** Gender plugin. Replaces node with a function that takes gender => string. */
184
- const genderPlugin = process.env["INTLAYER_NODE_TYPE_GENDER"] === "false" ? fallbackPlugin : {
184
+ const genderPlugin = process.env.INTLAYER_NODE_TYPE_GENDER === "false" ? fallbackPlugin : {
185
185
  id: "gender-plugin",
186
186
  canHandle: (node) => typeof node === "object" && node?.nodeType === _intlayer_types_nodeType.GENDER,
187
187
  transform: (node, props, deepTransformNode) => {
@@ -202,7 +202,7 @@ const genderPlugin = process.env["INTLAYER_NODE_TYPE_GENDER"] === "false" ? fall
202
202
  }
203
203
  };
204
204
  /** Nested plugin. Replaces node with the result of `getNesting`. */
205
- const nestedPlugin = (locale) => process.env["INTLAYER_NODE_TYPE_NESTED"] === "false" ? fallbackPlugin : {
205
+ const nestedPlugin = (locale) => process.env.INTLAYER_NODE_TYPE_NESTED === "false" ? fallbackPlugin : {
206
206
  id: "nested-plugin",
207
207
  canHandle: (node) => typeof node === "object" && (node?.nodeType === _intlayer_types_nodeType.NESTED || node?.nodeType === "n"),
208
208
  transform: (node, props) => require_interpreter_getNesting.getNesting(node[_intlayer_types_nodeType.NESTED].dictionaryKey, node[_intlayer_types_nodeType.NESTED].path, {
@@ -211,7 +211,7 @@ const nestedPlugin = (locale) => process.env["INTLAYER_NODE_TYPE_NESTED"] === "f
211
211
  })
212
212
  };
213
213
  /** File plugin. Replaces node with the result of `getNesting`. */
214
- const filePlugin = process.env["INTLAYER_NODE_TYPE_FILE"] === "false" ? fallbackPlugin : {
214
+ const filePlugin = process.env.INTLAYER_NODE_TYPE_FILE === "false" ? fallbackPlugin : {
215
215
  id: "file-plugin",
216
216
  canHandle: (node) => typeof node === "object" && node?.nodeType === _intlayer_types_nodeType.FILE,
217
217
  transform: (node, props, deepTransform) => deepTransform(node.content, {
@@ -1 +1 @@
1
- {"version":3,"file":"plugins.cjs","names":["NodeTypes","getTranslation","getEnumeration","getInsertion","getPlural","getCondition","getGender","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 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 { getTranslation } from '../getTranslation';\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) => typeof node === 'string',\n transform: (node: string, subProps, deepTransformNode) => {\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) => typeof node === 'string',\n transform: (node: string, subProps, deepTransformNode) => {\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 * 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 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 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","getInsertion","getPlural","getCondition","getGender","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 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 { getTranslation } from '../getTranslation';\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) => typeof node === 'string',\n transform: (node: string, subProps, deepTransformNode) => {\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) => typeof node === 'string',\n transform: (node: string, subProps, deepTransformNode) => {\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 * 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 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 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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"}
@@ -44,7 +44,7 @@ const getIntlayer = (key, localeOrSelector, plugins) => {
44
44
  }
45
45
  let locale;
46
46
  let selectorCacheKey = "";
47
- if (process.env["INTLAYER_DICTIONARY_SELECTOR"] !== "false") {
47
+ if (process.env.INTLAYER_DICTIONARY_SELECTOR !== "false") {
48
48
  const parsed = require_dictionaryManipulator_qualifiedDictionary.parseDictionarySelector(localeOrSelector);
49
49
  locale = parsed.locale;
50
50
  selectorCacheKey = require_dictionaryManipulator_qualifiedDictionary.getDictionarySelectorCacheKey(parsed.selector);
@@ -1 +1 @@
1
- {"version":3,"file":"getIntlayer.cjs","names":["parseDictionarySelector","getDictionarySelectorCacheKey","getDictionary"],"sources":["../../../src/interpreter/getIntlayer.ts"],"sourcesContent":["import { log } from '@intlayer/config/built';\nimport { colorizeKey, getAppLogger } from '@intlayer/config/logger';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport type { DictionarySelector } from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n DictionaryKeys,\n DictionaryRegistryResult,\n ExtractSelectorLocale,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport {\n getDictionarySelectorCacheKey,\n parseDictionarySelector,\n} from '../dictionaryManipulator/qualifiedDictionary';\nimport type {\n DeepTransformContent,\n IInterpreterPluginState,\n Plugins,\n} from './getContent';\nimport { getDictionary } from './getDictionary';\n\n/**\n * Creates a Recursive Proxy that returns the path of the accessed key\n * stringified. This prevents the app from crashing on undefined access.\n */\nconst createSafeFallback = (path = ''): any => {\n return new Proxy({} as Record<string | symbol, unknown>, {\n get: (_target, prop) => {\n if (\n prop === 'toJSON' ||\n prop === Symbol.toPrimitive ||\n prop === 'toString' ||\n prop === 'valueOf'\n ) {\n return () => path;\n }\n if (prop === 'then') {\n return undefined; // Prevent it from being treated as a Promise\n }\n if (prop === Symbol.iterator) {\n return function* () {\n yield path;\n };\n }\n\n // Recursively build the path (e.g., \"myDictionary.home.title\")\n const nextPath = path ? `${path}.${String(prop)}` : String(prop);\n return createSafeFallback(nextPath);\n },\n });\n};\n\nconst dictionaryCache = new Map<string, any>();\nconst warnedMissingDictionaries = new Set<string>();\n\n/**\n * Picks one dictionary by its key and returns its content for the given\n * locale or selector.\n *\n * The second argument is either a locale (`'fr'`) or a selector object:\n * - `{ item: 2 }` — collection item (omit `item` to get every item as array)\n * - `{ variant: 'black-friday' }` — named variant (omit for the `default` one)\n * - `{ id: 'prod_abc', ...metaFields }` — meta record\n * - `locale` can be combined with any selector: `{ item: 2, locale: 'fr' }`\n */\nexport const getIntlayer = <\n const T extends DictionaryKeys,\n const A extends LocalesValues | DictionarySelector = DeclaredLocales,\n>(\n key: T,\n localeOrSelector?: A,\n plugins?: Plugins[]\n): DeepTransformContent<\n DictionaryRegistryResult<T, A>,\n IInterpreterPluginState,\n ExtractSelectorLocale<A>\n> => {\n const dictionaries = getDictionaries();\n const dictionary = dictionaries[key as T];\n\n if (!dictionary && process.env.NODE_ENV === 'development') {\n if (!warnedMissingDictionaries.has(key as string)) {\n // Log a warning instead of throwing (so developers know it's missing)\n const logger = getAppLogger({ log });\n logger(\n typeof window === 'undefined'\n ? `Dictionary ${colorizeKey(key)} was not found. Using fallback proxy.`\n : `Dictionary ${key} was not found. Using fallback proxy.`,\n {\n level: 'warn',\n }\n );\n warnedMissingDictionaries.add(key as string);\n }\n\n return createSafeFallback(key as string);\n }\n\n let locale: LocalesValues | undefined;\n let selectorCacheKey = '';\n\n if (process.env['INTLAYER_DICTIONARY_SELECTOR'] !== 'false') {\n const parsed = parseDictionarySelector(localeOrSelector);\n locale = parsed.locale;\n selectorCacheKey = getDictionarySelectorCacheKey(parsed.selector);\n } else {\n // Selectors are unused in this project (build-time flag): the second\n // argument can only be a locale, so the selector parsing is dead code.\n locale = localeOrSelector as LocalesValues | undefined;\n }\n\n const cacheKey = `${key}_${locale ?? 'default'}_${selectorCacheKey}_${plugins ? 'custom_plugins' : 'default_plugins'}`;\n\n if (dictionaryCache.has(cacheKey)) {\n return dictionaryCache.get(cacheKey);\n }\n\n const result = getDictionary(dictionary, localeOrSelector, plugins);\n\n dictionaryCache.set(cacheKey, result);\n\n return result as any;\n};\n"],"mappings":";;;;;;;;;;;;;AA0BA,MAAM,sBAAsB,OAAO,OAAY;AAC7C,QAAO,IAAI,MAAM,EAAE,EAAsC,EACvD,MAAM,SAAS,SAAS;AACtB,MACE,SAAS,YACT,SAAS,OAAO,eAChB,SAAS,cACT,SAAS,UAET,cAAa;AAEf,MAAI,SAAS,OACX;AAEF,MAAI,SAAS,OAAO,SAClB,QAAO,aAAa;AAClB,SAAM;;AAMV,SAAO,mBADU,OAAO,GAAG,KAAK,GAAG,OAAO,KAAK,KAAK,OAAO,KAAK,CAC7B;IAEtC,CAAC;;AAGJ,MAAM,kCAAkB,IAAI,KAAkB;AAC9C,MAAM,4CAA4B,IAAI,KAAa;;;;;;;;;;;AAYnD,MAAa,eAIX,KACA,kBACA,YAKG;CAEH,MAAM,gEAAyB,CAAC;AAEhC,KAAI,CAAC,cAAc,QAAQ,IAAI,aAAa,eAAe;AACzD,MAAI,CAAC,0BAA0B,IAAI,IAAc,EAAE;AAGjD,6CAD4B,EAAE,iCAAK,CAC7B,CACJ,OAAO,WAAW,cACd,uDAA0B,IAAI,CAAC,yCAC/B,cAAc,IAAI,wCACtB,EACE,OAAO,QACR,CACF;AACD,6BAA0B,IAAI,IAAc;;AAG9C,SAAO,mBAAmB,IAAc;;CAG1C,IAAI;CACJ,IAAI,mBAAmB;AAEvB,KAAI,QAAQ,IAAI,oCAAoC,SAAS;EAC3D,MAAM,SAASA,0EAAwB,iBAAiB;AACxD,WAAS,OAAO;AAChB,qBAAmBC,gFAA8B,OAAO,SAAS;OAIjE,UAAS;CAGX,MAAM,WAAW,GAAG,IAAI,GAAG,UAAU,UAAU,GAAG,iBAAiB,GAAG,UAAU,mBAAmB;AAEnG,KAAI,gBAAgB,IAAI,SAAS,CAC/B,QAAO,gBAAgB,IAAI,SAAS;CAGtC,MAAM,SAASC,gDAAc,YAAY,kBAAkB,QAAQ;AAEnE,iBAAgB,IAAI,UAAU,OAAO;AAErC,QAAO"}
1
+ {"version":3,"file":"getIntlayer.cjs","names":["parseDictionarySelector","getDictionarySelectorCacheKey","getDictionary"],"sources":["../../../src/interpreter/getIntlayer.ts"],"sourcesContent":["import { log } from '@intlayer/config/built';\nimport { colorizeKey, getAppLogger } from '@intlayer/config/logger';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport type { DictionarySelector } from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n DictionaryKeys,\n DictionaryRegistryResult,\n ExtractSelectorLocale,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport {\n getDictionarySelectorCacheKey,\n parseDictionarySelector,\n} from '../dictionaryManipulator/qualifiedDictionary';\nimport type {\n DeepTransformContent,\n IInterpreterPluginState,\n Plugins,\n} from './getContent';\nimport { getDictionary } from './getDictionary';\n\n/**\n * Creates a Recursive Proxy that returns the path of the accessed key\n * stringified. This prevents the app from crashing on undefined access.\n */\nconst createSafeFallback = (path = ''): any => {\n return new Proxy({} as Record<string | symbol, unknown>, {\n get: (_target, prop) => {\n if (\n prop === 'toJSON' ||\n prop === Symbol.toPrimitive ||\n prop === 'toString' ||\n prop === 'valueOf'\n ) {\n return () => path;\n }\n if (prop === 'then') {\n return undefined; // Prevent it from being treated as a Promise\n }\n if (prop === Symbol.iterator) {\n return function* () {\n yield path;\n };\n }\n\n // Recursively build the path (e.g., \"myDictionary.home.title\")\n const nextPath = path ? `${path}.${String(prop)}` : String(prop);\n return createSafeFallback(nextPath);\n },\n });\n};\n\nconst dictionaryCache = new Map<string, any>();\nconst warnedMissingDictionaries = new Set<string>();\n\n/**\n * Picks one dictionary by its key and returns its content for the given\n * locale or selector.\n *\n * The second argument is either a locale (`'fr'`) or a selector object:\n * - `{ item: 2 }` — collection item (omit `item` to get every item as array)\n * - `{ variant: 'black-friday' }` — named variant (omit for the `default` one)\n * - `{ id: 'prod_abc', ...metaFields }` — meta record\n * - `locale` can be combined with any selector: `{ item: 2, locale: 'fr' }`\n */\nexport const getIntlayer = <\n const T extends DictionaryKeys,\n const A extends LocalesValues | DictionarySelector = DeclaredLocales,\n>(\n key: T,\n localeOrSelector?: A,\n plugins?: Plugins[]\n): DeepTransformContent<\n DictionaryRegistryResult<T, A>,\n IInterpreterPluginState,\n ExtractSelectorLocale<A>\n> => {\n const dictionaries = getDictionaries();\n const dictionary = dictionaries[key as T];\n\n if (!dictionary && process.env.NODE_ENV === 'development') {\n if (!warnedMissingDictionaries.has(key as string)) {\n // Log a warning instead of throwing (so developers know it's missing)\n const logger = getAppLogger({ log });\n logger(\n typeof window === 'undefined'\n ? `Dictionary ${colorizeKey(key)} was not found. Using fallback proxy.`\n : `Dictionary ${key} was not found. Using fallback proxy.`,\n {\n level: 'warn',\n }\n );\n warnedMissingDictionaries.add(key as string);\n }\n\n return createSafeFallback(key as string);\n }\n\n let locale: LocalesValues | undefined;\n let selectorCacheKey = '';\n\n if (process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false') {\n const parsed = parseDictionarySelector(localeOrSelector);\n locale = parsed.locale;\n selectorCacheKey = getDictionarySelectorCacheKey(parsed.selector);\n } else {\n // Selectors are unused in this project (build-time flag): the second\n // argument can only be a locale, so the selector parsing is dead code.\n locale = localeOrSelector as LocalesValues | undefined;\n }\n\n const cacheKey = `${key}_${locale ?? 'default'}_${selectorCacheKey}_${plugins ? 'custom_plugins' : 'default_plugins'}`;\n\n if (dictionaryCache.has(cacheKey)) {\n return dictionaryCache.get(cacheKey);\n }\n\n const result = getDictionary(dictionary, localeOrSelector, plugins);\n\n dictionaryCache.set(cacheKey, result);\n\n return result as any;\n};\n"],"mappings":";;;;;;;;;;;;;AA0BA,MAAM,sBAAsB,OAAO,OAAY;AAC7C,QAAO,IAAI,MAAM,EAAE,EAAsC,EACvD,MAAM,SAAS,SAAS;AACtB,MACE,SAAS,YACT,SAAS,OAAO,eAChB,SAAS,cACT,SAAS,UAET,cAAa;AAEf,MAAI,SAAS,OACX;AAEF,MAAI,SAAS,OAAO,SAClB,QAAO,aAAa;AAClB,SAAM;;AAMV,SAAO,mBADU,OAAO,GAAG,KAAK,GAAG,OAAO,KAAK,KAAK,OAAO,KAAK,CAC7B;IAEtC,CAAC;;AAGJ,MAAM,kCAAkB,IAAI,KAAkB;AAC9C,MAAM,4CAA4B,IAAI,KAAa;;;;;;;;;;;AAYnD,MAAa,eAIX,KACA,kBACA,YAKG;CAEH,MAAM,gEAAyB,CAAC;AAEhC,KAAI,CAAC,cAAc,QAAQ,IAAI,aAAa,eAAe;AACzD,MAAI,CAAC,0BAA0B,IAAI,IAAc,EAAE;AAGjD,6CAD4B,EAAE,iCAAK,CAC7B,CACJ,OAAO,WAAW,cACd,uDAA0B,IAAI,CAAC,yCAC/B,cAAc,IAAI,wCACtB,EACE,OAAO,QACR,CACF;AACD,6BAA0B,IAAI,IAAc;;AAG9C,SAAO,mBAAmB,IAAc;;CAG1C,IAAI;CACJ,IAAI,mBAAmB;AAEvB,KAAI,QAAQ,IAAI,iCAAiC,SAAS;EACxD,MAAM,SAASA,0EAAwB,iBAAiB;AACxD,WAAS,OAAO;AAChB,qBAAmBC,gFAA8B,OAAO,SAAS;OAIjE,UAAS;CAGX,MAAM,WAAW,GAAG,IAAI,GAAG,UAAU,UAAU,GAAG,iBAAiB,GAAG,UAAU,mBAAmB;AAEnG,KAAI,gBAAgB,IAAI,SAAS,CAC/B,QAAO,gBAAgB,IAAI,SAAS;CAGtC,MAAM,SAASC,gDAAc,YAAY,kBAAkB,QAAQ;AAEnE,iBAAgB,IAAI,UAAU,OAAO;AAErC,QAAO"}
@@ -63,16 +63,16 @@ const getLocalizedUrl = (url, currentLocale = _intlayer_config_built.internation
63
63
  const { defaultLocale, mode, locales, rewrite, domains, currentDomain } = require_localization_getPrefix.resolveRoutingConfig(options);
64
64
  const urlWithoutLocale = require_localization_getPathWithoutLocale.getPathWithoutLocale(url, locales);
65
65
  const rewriteRules = require_localization_rewriteUtils.getRewriteRules(rewrite, "url");
66
- if (!(process.env["INTLAYER_ROUTING_MODE"] && process.env["INTLAYER_ROUTING_MODE"] !== "no-prefix") && mode === "no-prefix") return require_localization_rewriteUtils.getLocalizedPath(require_localization_rewriteUtils.getCanonicalPath(urlWithoutLocale, void 0, rewriteRules), currentLocale, rewriteRules).path;
66
+ if (!(process.env.INTLAYER_ROUTING_MODE && process.env.INTLAYER_ROUTING_MODE !== "no-prefix") && mode === "no-prefix") return require_localization_rewriteUtils.getLocalizedPath(require_localization_rewriteUtils.getCanonicalPath(urlWithoutLocale, void 0, rewriteRules), currentLocale, rewriteRules).path;
67
67
  const isAbsoluteUrl = require_utils_checkIsURLAbsolute.checkIsURLAbsolute(urlWithoutLocale);
68
68
  const parsedUrl = isAbsoluteUrl ? new URL(urlWithoutLocale) : new URL(urlWithoutLocale, "http://example.com");
69
69
  const translatedPathname = require_localization_rewriteUtils.getLocalizedPath(require_localization_rewriteUtils.getCanonicalPath(parsedUrl.pathname, void 0, rewriteRules), currentLocale, rewriteRules).path;
70
- const detectedCurrentHostname = process.env["INTLAYER_ROUTING_DOMAINS"] !== "false" ? extractHostname(currentDomain ?? (isAbsoluteUrl ? parsedUrl.hostname : void 0) ?? (typeof window !== "undefined" ? window?.location?.hostname : void 0) ?? "") || null : null;
71
- const localeDomain = process.env["INTLAYER_ROUTING_DOMAINS"] !== "false" ? domains?.[currentLocale] : void 0;
70
+ const detectedCurrentHostname = process.env.INTLAYER_ROUTING_DOMAINS !== "false" ? extractHostname(currentDomain ?? (isAbsoluteUrl ? parsedUrl.hostname : void 0) ?? (typeof window !== "undefined" ? window?.location?.hostname : void 0) ?? "") || null : null;
71
+ const localeDomain = process.env.INTLAYER_ROUTING_DOMAINS !== "false" ? domains?.[currentLocale] : void 0;
72
72
  const localeDomainHostname = localeDomain ? extractHostname(localeDomain) : null;
73
73
  const normalizedDomain = localeDomainHostname !== null && detectedCurrentHostname !== null && localeDomainHostname !== detectedCurrentHostname && localeDomain ? /^https?:\/\//.test(localeDomain) ? localeDomain : `https://${localeDomain}` : null;
74
74
  const baseUrl = normalizedDomain ? normalizedDomain : isAbsoluteUrl ? `${parsedUrl.protocol}//${parsedUrl.host}` : "";
75
- if (!(process.env["INTLAYER_ROUTING_MODE"] && process.env["INTLAYER_ROUTING_MODE"] !== "search-params") && mode === "search-params") {
75
+ if (!(process.env.INTLAYER_ROUTING_MODE && process.env.INTLAYER_ROUTING_MODE !== "search-params") && mode === "search-params") {
76
76
  const searchParams = new URLSearchParams(parsedUrl.search);
77
77
  searchParams.set("locale", currentLocale.toString());
78
78
  const queryParams = searchParams.toString();
@@ -1 +1 @@
1
- {"version":3,"file":"getLocalizedUrl.cjs","names":["internationalization","resolveRoutingConfig","getPathWithoutLocale","getRewriteRules","getLocalizedPath","getCanonicalPath","checkIsURLAbsolute","getPrefix"],"sources":["../../../src/localization/getLocalizedUrl.ts"],"sourcesContent":["import { internationalization } from '@intlayer/config/built';\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\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type {\n LocalesValues,\n LocalizedUrl,\n ResolvedDefaultLocale,\n} from '@intlayer/types/module_augmentation';\nimport { checkIsURLAbsolute } from '../utils/checkIsURLAbsolute';\nimport { getPathWithoutLocale } from './getPathWithoutLocale';\nimport {\n getPrefix,\n type RoutingOptions,\n resolveRoutingConfig,\n} from './getPrefix';\nimport {\n getCanonicalPath,\n getLocalizedPath,\n getRewriteRules,\n} from './rewriteUtils';\n\nexport type { RoutingOptions };\n\n/** Strips the protocol and returns the bare hostname of a domain string. */\nconst extractHostname = (domain: string): string => {\n try {\n return /^https?:\\/\\//.test(domain) ? new URL(domain).hostname : domain;\n } catch {\n return domain;\n }\n};\n\n/**\n * Generate URL by prefixing the given URL with the referenced locale or adding search parameters\n * based on the routing mode. Handles both absolute and relative URLs appropriately.\n *\n * This function gets the locales, default locale, and routing mode from the configuration if not provided.\n *\n * Example:\n *\n * ```ts\n * // prefix-no-default mode\n * getLocalizedUrl('/about', 'fr', { locales: ['en', 'fr'], defaultLocale: 'en', mode: 'prefix-no-default' });\n * // Returns '/fr/about' for the French locale\n * // Returns '/about' for the English locale (default)\n *\n * // prefix-all mode\n * getLocalizedUrl('/about', 'en', { locales: ['en', 'fr'], defaultLocale: 'en', mode: 'prefix-all' });\n * // Returns '/en/about' for the English locale\n * // Returns '/fr/about' for the French locale\n *\n * // search-params mode\n * getLocalizedUrl('/about', 'fr', { locales: ['en', 'fr'], defaultLocale: 'en', mode: 'search-params' });\n * // Returns '/about?locale=fr' for the French locale\n *\n * // no-prefix mode\n * getLocalizedUrl('/about', 'fr', { locales: ['en', 'fr'], defaultLocale: 'en', mode: 'no-prefix' });\n * // Returns '/about' for any locale\n * ```\n *\n * @param url - The original URL string to be processed.\n * @param currentLocale - The current locale.\n * @param options - Configuration options\n * @param options.locales - Optional array of supported locales. Defaults to configured locales.\n * @param options.defaultLocale - The default locale. Defaults to configured default locale.\n * @param options.mode - URL routing mode for locale handling. Defaults to configured mode.\n * @param options.currentDomain - Hostname of the page being rendered. Used to decide\n * whether to emit a relative URL (same domain) or an absolute URL (cross-domain).\n * Auto-detected from the input URL or `window.location` when omitted.\n * @returns The localized URL for the current locale.\n */\n/**\n * The return type is narrowed to a template-literal type when both `url` and\n * `currentLocale` are string literals and the routing mode / defaultLocale are\n * not overridden via `options`.\n */\nexport const getLocalizedUrl = <\n const T extends string,\n const L extends LocalesValues = ResolvedDefaultLocale,\n>(\n url: T,\n currentLocale: L = internationalization?.defaultLocale as L,\n options: RoutingOptions = {}\n): LocalizedUrl<T, L> => {\n const { defaultLocale, mode, locales, rewrite, domains, currentDomain } =\n resolveRoutingConfig(options);\n\n const urlWithoutLocale = getPathWithoutLocale(url, locales);\n const rewriteRules = getRewriteRules(rewrite, 'url');\n\n if (\n !(\n process.env['INTLAYER_ROUTING_MODE'] &&\n process.env['INTLAYER_ROUTING_MODE'] !== 'no-prefix'\n ) &&\n mode === 'no-prefix'\n ) {\n return getLocalizedPath(\n getCanonicalPath(urlWithoutLocale, undefined, rewriteRules),\n currentLocale as Locale,\n rewriteRules\n ).path as LocalizedUrl<T, L>;\n }\n\n const isAbsoluteUrl = checkIsURLAbsolute(urlWithoutLocale);\n const parsedUrl = isAbsoluteUrl\n ? new URL(urlWithoutLocale)\n : new URL(urlWithoutLocale, 'http://example.com');\n\n const translatedPathname = getLocalizedPath(\n getCanonicalPath(parsedUrl.pathname, undefined, rewriteRules),\n currentLocale as Locale,\n rewriteRules\n ).path;\n\n // ── Domain routing ────────────────────────────────────────────────────────\n // Resolve the \"current\" hostname so we can choose between a relative URL\n // (same domain) and an absolute URL (cross-domain).\n //\n // Detection priority:\n // 1. Explicit `currentDomain` option passed by the caller.\n // 2. Hostname extracted from an absolute input URL.\n // 3. `window.location.hostname` in browser environments.\n // When none of these is available we fall back to always generating an\n // absolute URL (the previous behaviour, safe for SSR/static generation).\n const detectedCurrentHostname =\n process.env['INTLAYER_ROUTING_DOMAINS'] !== 'false'\n ? extractHostname(\n currentDomain ??\n (isAbsoluteUrl ? parsedUrl.hostname : undefined) ??\n (typeof window !== 'undefined'\n ? window?.location?.hostname\n : undefined) ??\n ''\n ) || null\n : null;\n\n const localeDomain =\n process.env['INTLAYER_ROUTING_DOMAINS'] !== 'false'\n ? domains?.[currentLocale as LocalesValues]\n : undefined;\n\n const localeDomainHostname = localeDomain\n ? extractHostname(localeDomain)\n : null;\n\n // Only prepend the locale's domain when it differs from the current hostname.\n const isCrossDomain =\n localeDomainHostname !== null &&\n detectedCurrentHostname !== null &&\n localeDomainHostname !== detectedCurrentHostname;\n\n const normalizedDomain =\n isCrossDomain && localeDomain\n ? /^https?:\\/\\//.test(localeDomain)\n ? localeDomain\n : `https://${localeDomain}`\n : null;\n\n const baseUrl = normalizedDomain\n ? normalizedDomain\n : isAbsoluteUrl\n ? `${parsedUrl.protocol}//${parsedUrl.host}`\n : '';\n // ─────────────────────────────────────────────────────────────────────────\n\n if (\n !(\n process.env['INTLAYER_ROUTING_MODE'] &&\n process.env['INTLAYER_ROUTING_MODE'] !== 'search-params'\n ) &&\n mode === 'search-params'\n ) {\n const searchParams = new URLSearchParams(parsedUrl.search);\n\n searchParams.set('locale', currentLocale.toString());\n\n const queryParams = searchParams.toString();\n const path = queryParams\n ? `${translatedPathname}?${queryParams}`\n : translatedPathname;\n\n return `${baseUrl}${path}${parsedUrl.hash}` as LocalizedUrl<T, L>;\n }\n\n const { prefix } = getPrefix(currentLocale, {\n defaultLocale,\n mode,\n locales,\n domains,\n });\n\n let localizedPath = `/${prefix}${translatedPathname}`.replace(/\\/+/g, '/');\n\n if (localizedPath.length > 1 && localizedPath.endsWith('/')) {\n localizedPath = localizedPath.slice(0, -1);\n }\n\n return `${baseUrl}${localizedPath}${parsedUrl.search}${parsedUrl.hash}` as LocalizedUrl<\n T,\n L\n >;\n};\n"],"mappings":";;;;;;;;;;AA4BA,MAAM,mBAAmB,WAA2B;AAClD,KAAI;AACF,SAAO,eAAe,KAAK,OAAO,GAAG,IAAI,IAAI,OAAO,CAAC,WAAW;SAC1D;AACN,SAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDX,MAAa,mBAIX,KACA,gBAAmBA,6CAAsB,eACzC,UAA0B,EAAE,KACL;CACvB,MAAM,EAAE,eAAe,MAAM,SAAS,SAAS,SAAS,kBACtDC,oDAAqB,QAAQ;CAE/B,MAAM,mBAAmBC,+DAAqB,KAAK,QAAQ;CAC3D,MAAM,eAAeC,kDAAgB,SAAS,MAAM;AAEpD,KACE,EACE,QAAQ,IAAI,4BACZ,QAAQ,IAAI,6BAA6B,gBAE3C,SAAS,YAET,QAAOC,mDACLC,mDAAiB,kBAAkB,QAAW,aAAa,EAC3D,eACA,aACD,CAAC;CAGJ,MAAM,gBAAgBC,oDAAmB,iBAAiB;CAC1D,MAAM,YAAY,gBACd,IAAI,IAAI,iBAAiB,GACzB,IAAI,IAAI,kBAAkB,qBAAqB;CAEnD,MAAM,qBAAqBF,mDACzBC,mDAAiB,UAAU,UAAU,QAAW,aAAa,EAC7D,eACA,aACD,CAAC;CAYF,MAAM,0BACJ,QAAQ,IAAI,gCAAgC,UACxC,gBACE,kBACG,gBAAgB,UAAU,WAAW,YACrC,OAAO,WAAW,cACf,QAAQ,UAAU,WAClB,WACJ,GACH,IAAI,OACL;CAEN,MAAM,eACJ,QAAQ,IAAI,gCAAgC,UACxC,UAAU,iBACV;CAEN,MAAM,uBAAuB,eACzB,gBAAgB,aAAa,GAC7B;CAQJ,MAAM,mBAJJ,yBAAyB,QACzB,4BAA4B,QAC5B,yBAAyB,2BAGR,eACb,eAAe,KAAK,aAAa,GAC/B,eACA,WAAW,iBACb;CAEN,MAAM,UAAU,mBACZ,mBACA,gBACE,GAAG,UAAU,SAAS,IAAI,UAAU,SACpC;AAGN,KACE,EACE,QAAQ,IAAI,4BACZ,QAAQ,IAAI,6BAA6B,oBAE3C,SAAS,iBACT;EACA,MAAM,eAAe,IAAI,gBAAgB,UAAU,OAAO;AAE1D,eAAa,IAAI,UAAU,cAAc,UAAU,CAAC;EAEpD,MAAM,cAAc,aAAa,UAAU;AAK3C,SAAO,GAAG,UAJG,cACT,GAAG,mBAAmB,GAAG,gBACzB,qBAEuB,UAAU;;CAGvC,MAAM,EAAE,WAAWE,yCAAU,eAAe;EAC1C;EACA;EACA;EACA;EACD,CAAC;CAEF,IAAI,gBAAgB,IAAI,SAAS,qBAAqB,QAAQ,QAAQ,IAAI;AAE1E,KAAI,cAAc,SAAS,KAAK,cAAc,SAAS,IAAI,CACzD,iBAAgB,cAAc,MAAM,GAAG,GAAG;AAG5C,QAAO,GAAG,UAAU,gBAAgB,UAAU,SAAS,UAAU"}
1
+ {"version":3,"file":"getLocalizedUrl.cjs","names":["internationalization","resolveRoutingConfig","getPathWithoutLocale","getRewriteRules","getLocalizedPath","getCanonicalPath","checkIsURLAbsolute","getPrefix"],"sources":["../../../src/localization/getLocalizedUrl.ts"],"sourcesContent":["import { internationalization } from '@intlayer/config/built';\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\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type {\n LocalesValues,\n LocalizedUrl,\n ResolvedDefaultLocale,\n} from '@intlayer/types/module_augmentation';\nimport { checkIsURLAbsolute } from '../utils/checkIsURLAbsolute';\nimport { getPathWithoutLocale } from './getPathWithoutLocale';\nimport {\n getPrefix,\n type RoutingOptions,\n resolveRoutingConfig,\n} from './getPrefix';\nimport {\n getCanonicalPath,\n getLocalizedPath,\n getRewriteRules,\n} from './rewriteUtils';\n\nexport type { RoutingOptions };\n\n/** Strips the protocol and returns the bare hostname of a domain string. */\nconst extractHostname = (domain: string): string => {\n try {\n return /^https?:\\/\\//.test(domain) ? new URL(domain).hostname : domain;\n } catch {\n return domain;\n }\n};\n\n/**\n * Generate URL by prefixing the given URL with the referenced locale or adding search parameters\n * based on the routing mode. Handles both absolute and relative URLs appropriately.\n *\n * This function gets the locales, default locale, and routing mode from the configuration if not provided.\n *\n * Example:\n *\n * ```ts\n * // prefix-no-default mode\n * getLocalizedUrl('/about', 'fr', { locales: ['en', 'fr'], defaultLocale: 'en', mode: 'prefix-no-default' });\n * // Returns '/fr/about' for the French locale\n * // Returns '/about' for the English locale (default)\n *\n * // prefix-all mode\n * getLocalizedUrl('/about', 'en', { locales: ['en', 'fr'], defaultLocale: 'en', mode: 'prefix-all' });\n * // Returns '/en/about' for the English locale\n * // Returns '/fr/about' for the French locale\n *\n * // search-params mode\n * getLocalizedUrl('/about', 'fr', { locales: ['en', 'fr'], defaultLocale: 'en', mode: 'search-params' });\n * // Returns '/about?locale=fr' for the French locale\n *\n * // no-prefix mode\n * getLocalizedUrl('/about', 'fr', { locales: ['en', 'fr'], defaultLocale: 'en', mode: 'no-prefix' });\n * // Returns '/about' for any locale\n * ```\n *\n * @param url - The original URL string to be processed.\n * @param currentLocale - The current locale.\n * @param options - Configuration options\n * @param options.locales - Optional array of supported locales. Defaults to configured locales.\n * @param options.defaultLocale - The default locale. Defaults to configured default locale.\n * @param options.mode - URL routing mode for locale handling. Defaults to configured mode.\n * @param options.currentDomain - Hostname of the page being rendered. Used to decide\n * whether to emit a relative URL (same domain) or an absolute URL (cross-domain).\n * Auto-detected from the input URL or `window.location` when omitted.\n * @returns The localized URL for the current locale.\n */\n/**\n * The return type is narrowed to a template-literal type when both `url` and\n * `currentLocale` are string literals and the routing mode / defaultLocale are\n * not overridden via `options`.\n */\nexport const getLocalizedUrl = <\n const T extends string,\n const L extends LocalesValues = ResolvedDefaultLocale,\n>(\n url: T,\n currentLocale: L = internationalization?.defaultLocale as L,\n options: RoutingOptions = {}\n): LocalizedUrl<T, L> => {\n const { defaultLocale, mode, locales, rewrite, domains, currentDomain } =\n resolveRoutingConfig(options);\n\n const urlWithoutLocale = getPathWithoutLocale(url, locales);\n const rewriteRules = getRewriteRules(rewrite, 'url');\n\n if (\n !(\n process.env.INTLAYER_ROUTING_MODE &&\n process.env.INTLAYER_ROUTING_MODE !== 'no-prefix'\n ) &&\n mode === 'no-prefix'\n ) {\n return getLocalizedPath(\n getCanonicalPath(urlWithoutLocale, undefined, rewriteRules),\n currentLocale as Locale,\n rewriteRules\n ).path as LocalizedUrl<T, L>;\n }\n\n const isAbsoluteUrl = checkIsURLAbsolute(urlWithoutLocale);\n const parsedUrl = isAbsoluteUrl\n ? new URL(urlWithoutLocale)\n : new URL(urlWithoutLocale, 'http://example.com');\n\n const translatedPathname = getLocalizedPath(\n getCanonicalPath(parsedUrl.pathname, undefined, rewriteRules),\n currentLocale as Locale,\n rewriteRules\n ).path;\n\n // ── Domain routing ────────────────────────────────────────────────────────\n // Resolve the \"current\" hostname so we can choose between a relative URL\n // (same domain) and an absolute URL (cross-domain).\n //\n // Detection priority:\n // 1. Explicit `currentDomain` option passed by the caller.\n // 2. Hostname extracted from an absolute input URL.\n // 3. `window.location.hostname` in browser environments.\n // When none of these is available we fall back to always generating an\n // absolute URL (the previous behaviour, safe for SSR/static generation).\n const detectedCurrentHostname =\n process.env.INTLAYER_ROUTING_DOMAINS !== 'false'\n ? extractHostname(\n currentDomain ??\n (isAbsoluteUrl ? parsedUrl.hostname : undefined) ??\n (typeof window !== 'undefined'\n ? window?.location?.hostname\n : undefined) ??\n ''\n ) || null\n : null;\n\n const localeDomain =\n process.env.INTLAYER_ROUTING_DOMAINS !== 'false'\n ? domains?.[currentLocale as LocalesValues]\n : undefined;\n\n const localeDomainHostname = localeDomain\n ? extractHostname(localeDomain)\n : null;\n\n // Only prepend the locale's domain when it differs from the current hostname.\n const isCrossDomain =\n localeDomainHostname !== null &&\n detectedCurrentHostname !== null &&\n localeDomainHostname !== detectedCurrentHostname;\n\n const normalizedDomain =\n isCrossDomain && localeDomain\n ? /^https?:\\/\\//.test(localeDomain)\n ? localeDomain\n : `https://${localeDomain}`\n : null;\n\n const baseUrl = normalizedDomain\n ? normalizedDomain\n : isAbsoluteUrl\n ? `${parsedUrl.protocol}//${parsedUrl.host}`\n : '';\n // ─────────────────────────────────────────────────────────────────────────\n\n if (\n !(\n process.env.INTLAYER_ROUTING_MODE &&\n process.env.INTLAYER_ROUTING_MODE !== 'search-params'\n ) &&\n mode === 'search-params'\n ) {\n const searchParams = new URLSearchParams(parsedUrl.search);\n\n searchParams.set('locale', currentLocale.toString());\n\n const queryParams = searchParams.toString();\n const path = queryParams\n ? `${translatedPathname}?${queryParams}`\n : translatedPathname;\n\n return `${baseUrl}${path}${parsedUrl.hash}` as LocalizedUrl<T, L>;\n }\n\n const { prefix } = getPrefix(currentLocale, {\n defaultLocale,\n mode,\n locales,\n domains,\n });\n\n let localizedPath = `/${prefix}${translatedPathname}`.replace(/\\/+/g, '/');\n\n if (localizedPath.length > 1 && localizedPath.endsWith('/')) {\n localizedPath = localizedPath.slice(0, -1);\n }\n\n return `${baseUrl}${localizedPath}${parsedUrl.search}${parsedUrl.hash}` as LocalizedUrl<\n T,\n L\n >;\n};\n"],"mappings":";;;;;;;;;;AA4BA,MAAM,mBAAmB,WAA2B;AAClD,KAAI;AACF,SAAO,eAAe,KAAK,OAAO,GAAG,IAAI,IAAI,OAAO,CAAC,WAAW;SAC1D;AACN,SAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDX,MAAa,mBAIX,KACA,gBAAmBA,6CAAsB,eACzC,UAA0B,EAAE,KACL;CACvB,MAAM,EAAE,eAAe,MAAM,SAAS,SAAS,SAAS,kBACtDC,oDAAqB,QAAQ;CAE/B,MAAM,mBAAmBC,+DAAqB,KAAK,QAAQ;CAC3D,MAAM,eAAeC,kDAAgB,SAAS,MAAM;AAEpD,KACE,EACE,QAAQ,IAAI,yBACZ,QAAQ,IAAI,0BAA0B,gBAExC,SAAS,YAET,QAAOC,mDACLC,mDAAiB,kBAAkB,QAAW,aAAa,EAC3D,eACA,aACD,CAAC;CAGJ,MAAM,gBAAgBC,oDAAmB,iBAAiB;CAC1D,MAAM,YAAY,gBACd,IAAI,IAAI,iBAAiB,GACzB,IAAI,IAAI,kBAAkB,qBAAqB;CAEnD,MAAM,qBAAqBF,mDACzBC,mDAAiB,UAAU,UAAU,QAAW,aAAa,EAC7D,eACA,aACD,CAAC;CAYF,MAAM,0BACJ,QAAQ,IAAI,6BAA6B,UACrC,gBACE,kBACG,gBAAgB,UAAU,WAAW,YACrC,OAAO,WAAW,cACf,QAAQ,UAAU,WAClB,WACJ,GACH,IAAI,OACL;CAEN,MAAM,eACJ,QAAQ,IAAI,6BAA6B,UACrC,UAAU,iBACV;CAEN,MAAM,uBAAuB,eACzB,gBAAgB,aAAa,GAC7B;CAQJ,MAAM,mBAJJ,yBAAyB,QACzB,4BAA4B,QAC5B,yBAAyB,2BAGR,eACb,eAAe,KAAK,aAAa,GAC/B,eACA,WAAW,iBACb;CAEN,MAAM,UAAU,mBACZ,mBACA,gBACE,GAAG,UAAU,SAAS,IAAI,UAAU,SACpC;AAGN,KACE,EACE,QAAQ,IAAI,yBACZ,QAAQ,IAAI,0BAA0B,oBAExC,SAAS,iBACT;EACA,MAAM,eAAe,IAAI,gBAAgB,UAAU,OAAO;AAE1D,eAAa,IAAI,UAAU,cAAc,UAAU,CAAC;EAEpD,MAAM,cAAc,aAAa,UAAU;AAK3C,SAAO,GAAG,UAJG,cACT,GAAG,mBAAmB,GAAG,gBACzB,qBAEuB,UAAU;;CAGvC,MAAM,EAAE,WAAWE,yCAAU,eAAe;EAC1C;EACA;EACA;EACA;EACD,CAAC;CAEF,IAAI,gBAAgB,IAAI,SAAS,qBAAqB,QAAQ,QAAQ,IAAI;AAE1E,KAAI,cAAc,SAAS,KAAK,cAAc,SAAS,IAAI,CACzD,iBAAgB,cAAc,MAAM,GAAG,GAAG;AAG5C,QAAO,GAAG,UAAU,gBAAgB,UAAU,SAAS,UAAU"}
@@ -8,11 +8,11 @@ let _intlayer_config_built = require("@intlayer/config/built");
8
8
  * True when the build-time routing mode is known and is not a prefix-based
9
9
  * mode (neither 'prefix-all' nor 'prefix-no-default').
10
10
  */
11
- const TREE_SHAKE_PREFIX_MODES = process.env["INTLAYER_ROUTING_MODE"] && process.env["INTLAYER_ROUTING_MODE"] !== "prefix-all" && process.env["INTLAYER_ROUTING_MODE"] !== "prefix-no-default";
11
+ const TREE_SHAKE_PREFIX_MODES = process.env.INTLAYER_ROUTING_MODE && process.env.INTLAYER_ROUTING_MODE !== "prefix-all" && process.env.INTLAYER_ROUTING_MODE !== "prefix-no-default";
12
12
  /**
13
13
  * True when the build-time routing mode is known and is NOT 'search-params'.
14
14
  */
15
- const TREE_SHAKE_SEARCH_PARAMS = process.env["INTLAYER_ROUTING_MODE"] && process.env["INTLAYER_ROUTING_MODE"] !== "search-params";
15
+ const TREE_SHAKE_SEARCH_PARAMS = process.env.INTLAYER_ROUTING_MODE && process.env.INTLAYER_ROUTING_MODE !== "search-params";
16
16
  /**
17
17
  * Removes the locale segment from the given URL or pathname if present.
18
18
  * Also removes locale from search parameters if present.
@@ -1 +1 @@
1
- {"version":3,"file":"getPathWithoutLocale.cjs","names":["internationalization","checkIsURLAbsolute"],"sources":["../../../src/localization/getPathWithoutLocale.ts"],"sourcesContent":["import { internationalization } from '@intlayer/config/built';\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 * True when the build-time routing mode is known and is not a prefix-based\n * mode (neither 'prefix-all' nor 'prefix-no-default').\n */\nconst TREE_SHAKE_PREFIX_MODES =\n process.env['INTLAYER_ROUTING_MODE'] &&\n process.env['INTLAYER_ROUTING_MODE'] !== 'prefix-all' &&\n process.env['INTLAYER_ROUTING_MODE'] !== 'prefix-no-default';\n\n/**\n * True when the build-time routing mode is known and is NOT 'search-params'.\n */\nconst TREE_SHAKE_SEARCH_PARAMS =\n process.env['INTLAYER_ROUTING_MODE'] &&\n process.env['INTLAYER_ROUTING_MODE'] !== 'search-params';\n\nimport type {\n DeclaredLocales,\n LocalesValues,\n PathWithoutLocale,\n} from '@intlayer/types/module_augmentation';\nimport { checkIsURLAbsolute } from '../utils/checkIsURLAbsolute';\n\n/**\n * Removes the locale segment from the given URL or pathname if present.\n * Also removes locale from search parameters if present.\n *\n * This function get the locales from the configuration if not provided.\n *\n * Example:\n *\n * ```ts\n * getPathWithoutLocale('/en/dashboard') // Returns '/dashboard'\n * getPathWithoutLocale('/fr/dashboard') // Returns '/dashboard'\n * getPathWithoutLocale('/dashboard') // Returns '/dashboard'\n * getPathWithoutLocale('dashboard') // Returns 'dashboard'\n * getPathWithoutLocale('/dashboard?locale=fr') // Returns '/dashboard'\n * getPathWithoutLocale('https://example.com/en/dashboard') // Returns 'https://example.com/dashboard'\n * getPathWithoutLocale('https://example.com/fr/dashboard') // Returns 'https://example.com/dashboard'\n * getPathWithoutLocale('https://example.com/dashboard') // Returns 'https://example.com/dashboard'\n * getPathWithoutLocale('https://example.com/dashboard?locale=fr') // Returns 'https://example.com/dashboard'\n * ```\n *\n * @param inputUrl - The complete URL string or pathname to process.\n * @param locales - Optional array of supported locales. Defaults to `localesDefault`.\n * @returns The URL string or pathname without the locale segment or locale search parameter.\n */\n/**\n * The return type is narrowed to a template-literal type when both `inputUrl`\n * and `locales` are string literals known at compile time. Absolute URLs fall\n * back to `string`.\n */\nexport const getPathWithoutLocale = <\n const T extends string,\n const L extends LocalesValues = DeclaredLocales,\n>(\n inputUrl: T,\n locales: L[] = internationalization?.locales as L[]\n): PathWithoutLocale<T, L> => {\n // Determine if the original URL is absolute (includes protocol)\n const isAbsoluteUrl = checkIsURLAbsolute(inputUrl);\n\n let fixedInputUrl = inputUrl;\n\n if (inputUrl?.endsWith('/')) {\n fixedInputUrl = inputUrl.slice(0, -1);\n }\n\n // Initialize a URL object if the URL is absolute\n // For relative URLs, use a dummy base to leverage the URL API\n const url = isAbsoluteUrl\n ? new URL(fixedInputUrl)\n : new URL(fixedInputUrl, 'http://example.com');\n\n const pathname = url.pathname;\n\n // Ensure the pathname starts with '/'\n if (!pathname.startsWith('/')) {\n // If not, return the URL as is\n url.pathname = `/${pathname}`;\n }\n\n // Only strip locale path prefix in prefix-based routing modes\n if (!TREE_SHAKE_PREFIX_MODES) {\n // Split the pathname to extract the first segment\n const pathSegments = pathname.split('/');\n const firstSegment = pathSegments[1]; // The segment after the first '/'\n\n // Check if the first segment is a supported locale\n if (locales?.includes(firstSegment as L)) {\n // Remove the locale segment from the pathname\n pathSegments.splice(1, 1); // Remove the first segment\n\n // Reconstruct the pathname\n const newPathname = pathSegments.join('/') ?? '/';\n url.pathname = newPathname;\n }\n }\n\n // Only strip locale from search parameters in search-params routing mode\n if (!TREE_SHAKE_SEARCH_PARAMS) {\n const searchParams = new URLSearchParams(url.search);\n if (searchParams.has('locale')) {\n searchParams.delete('locale');\n url.search = searchParams.toString();\n }\n }\n\n if (isAbsoluteUrl) {\n return url.toString() as PathWithoutLocale<T, L>;\n }\n\n return url.toString().replace('http://example.com', '') as PathWithoutLocale<\n T,\n L\n >;\n};\n"],"mappings":";;;;;;;;;;AAUA,MAAM,0BACJ,QAAQ,IAAI,4BACZ,QAAQ,IAAI,6BAA6B,gBACzC,QAAQ,IAAI,6BAA6B;;;;AAK3C,MAAM,2BACJ,QAAQ,IAAI,4BACZ,QAAQ,IAAI,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsC3C,MAAa,wBAIX,UACA,UAAeA,6CAAsB,YACT;CAE5B,MAAM,gBAAgBC,oDAAmB,SAAS;CAElD,IAAI,gBAAgB;AAEpB,KAAI,UAAU,SAAS,IAAI,CACzB,iBAAgB,SAAS,MAAM,GAAG,GAAG;CAKvC,MAAM,MAAM,gBACR,IAAI,IAAI,cAAc,GACtB,IAAI,IAAI,eAAe,qBAAqB;CAEhD,MAAM,WAAW,IAAI;AAGrB,KAAI,CAAC,SAAS,WAAW,IAAI,CAE3B,KAAI,WAAW,IAAI;AAIrB,KAAI,CAAC,yBAAyB;EAE5B,MAAM,eAAe,SAAS,MAAM,IAAI;EACxC,MAAM,eAAe,aAAa;AAGlC,MAAI,SAAS,SAAS,aAAkB,EAAE;AAExC,gBAAa,OAAO,GAAG,EAAE;AAIzB,OAAI,WADgB,aAAa,KAAK,IAAI,IAAI;;;AAMlD,KAAI,CAAC,0BAA0B;EAC7B,MAAM,eAAe,IAAI,gBAAgB,IAAI,OAAO;AACpD,MAAI,aAAa,IAAI,SAAS,EAAE;AAC9B,gBAAa,OAAO,SAAS;AAC7B,OAAI,SAAS,aAAa,UAAU;;;AAIxC,KAAI,cACF,QAAO,IAAI,UAAU;AAGvB,QAAO,IAAI,UAAU,CAAC,QAAQ,sBAAsB,GAAG"}
1
+ {"version":3,"file":"getPathWithoutLocale.cjs","names":["internationalization","checkIsURLAbsolute"],"sources":["../../../src/localization/getPathWithoutLocale.ts"],"sourcesContent":["import { internationalization } from '@intlayer/config/built';\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 * True when the build-time routing mode is known and is not a prefix-based\n * mode (neither 'prefix-all' nor 'prefix-no-default').\n */\nconst TREE_SHAKE_PREFIX_MODES =\n process.env.INTLAYER_ROUTING_MODE &&\n process.env.INTLAYER_ROUTING_MODE !== 'prefix-all' &&\n process.env.INTLAYER_ROUTING_MODE !== 'prefix-no-default';\n\n/**\n * True when the build-time routing mode is known and is NOT 'search-params'.\n */\nconst TREE_SHAKE_SEARCH_PARAMS =\n process.env.INTLAYER_ROUTING_MODE &&\n process.env.INTLAYER_ROUTING_MODE !== 'search-params';\n\nimport type {\n DeclaredLocales,\n LocalesValues,\n PathWithoutLocale,\n} from '@intlayer/types/module_augmentation';\nimport { checkIsURLAbsolute } from '../utils/checkIsURLAbsolute';\n\n/**\n * Removes the locale segment from the given URL or pathname if present.\n * Also removes locale from search parameters if present.\n *\n * This function get the locales from the configuration if not provided.\n *\n * Example:\n *\n * ```ts\n * getPathWithoutLocale('/en/dashboard') // Returns '/dashboard'\n * getPathWithoutLocale('/fr/dashboard') // Returns '/dashboard'\n * getPathWithoutLocale('/dashboard') // Returns '/dashboard'\n * getPathWithoutLocale('dashboard') // Returns 'dashboard'\n * getPathWithoutLocale('/dashboard?locale=fr') // Returns '/dashboard'\n * getPathWithoutLocale('https://example.com/en/dashboard') // Returns 'https://example.com/dashboard'\n * getPathWithoutLocale('https://example.com/fr/dashboard') // Returns 'https://example.com/dashboard'\n * getPathWithoutLocale('https://example.com/dashboard') // Returns 'https://example.com/dashboard'\n * getPathWithoutLocale('https://example.com/dashboard?locale=fr') // Returns 'https://example.com/dashboard'\n * ```\n *\n * @param inputUrl - The complete URL string or pathname to process.\n * @param locales - Optional array of supported locales. Defaults to `localesDefault`.\n * @returns The URL string or pathname without the locale segment or locale search parameter.\n */\n/**\n * The return type is narrowed to a template-literal type when both `inputUrl`\n * and `locales` are string literals known at compile time. Absolute URLs fall\n * back to `string`.\n */\nexport const getPathWithoutLocale = <\n const T extends string,\n const L extends LocalesValues = DeclaredLocales,\n>(\n inputUrl: T,\n locales: L[] = internationalization?.locales as L[]\n): PathWithoutLocale<T, L> => {\n // Determine if the original URL is absolute (includes protocol)\n const isAbsoluteUrl = checkIsURLAbsolute(inputUrl);\n\n let fixedInputUrl = inputUrl;\n\n if (inputUrl?.endsWith('/')) {\n fixedInputUrl = inputUrl.slice(0, -1);\n }\n\n // Initialize a URL object if the URL is absolute\n // For relative URLs, use a dummy base to leverage the URL API\n const url = isAbsoluteUrl\n ? new URL(fixedInputUrl)\n : new URL(fixedInputUrl, 'http://example.com');\n\n const pathname = url.pathname;\n\n // Ensure the pathname starts with '/'\n if (!pathname.startsWith('/')) {\n // If not, return the URL as is\n url.pathname = `/${pathname}`;\n }\n\n // Only strip locale path prefix in prefix-based routing modes\n if (!TREE_SHAKE_PREFIX_MODES) {\n // Split the pathname to extract the first segment\n const pathSegments = pathname.split('/');\n const firstSegment = pathSegments[1]; // The segment after the first '/'\n\n // Check if the first segment is a supported locale\n if (locales?.includes(firstSegment as L)) {\n // Remove the locale segment from the pathname\n pathSegments.splice(1, 1); // Remove the first segment\n\n // Reconstruct the pathname\n const newPathname = pathSegments.join('/') ?? '/';\n url.pathname = newPathname;\n }\n }\n\n // Only strip locale from search parameters in search-params routing mode\n if (!TREE_SHAKE_SEARCH_PARAMS) {\n const searchParams = new URLSearchParams(url.search);\n if (searchParams.has('locale')) {\n searchParams.delete('locale');\n url.search = searchParams.toString();\n }\n }\n\n if (isAbsoluteUrl) {\n return url.toString() as PathWithoutLocale<T, L>;\n }\n\n return url.toString().replace('http://example.com', '') as PathWithoutLocale<\n T,\n L\n >;\n};\n"],"mappings":";;;;;;;;;;AAUA,MAAM,0BACJ,QAAQ,IAAI,yBACZ,QAAQ,IAAI,0BAA0B,gBACtC,QAAQ,IAAI,0BAA0B;;;;AAKxC,MAAM,2BACJ,QAAQ,IAAI,yBACZ,QAAQ,IAAI,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCxC,MAAa,wBAIX,UACA,UAAeA,6CAAsB,YACT;CAE5B,MAAM,gBAAgBC,oDAAmB,SAAS;CAElD,IAAI,gBAAgB;AAEpB,KAAI,UAAU,SAAS,IAAI,CACzB,iBAAgB,SAAS,MAAM,GAAG,GAAG;CAKvC,MAAM,MAAM,gBACR,IAAI,IAAI,cAAc,GACtB,IAAI,IAAI,eAAe,qBAAqB;CAEhD,MAAM,WAAW,IAAI;AAGrB,KAAI,CAAC,SAAS,WAAW,IAAI,CAE3B,KAAI,WAAW,IAAI;AAIrB,KAAI,CAAC,yBAAyB;EAE5B,MAAM,eAAe,SAAS,MAAM,IAAI;EACxC,MAAM,eAAe,aAAa;AAGlC,MAAI,SAAS,SAAS,aAAkB,EAAE;AAExC,gBAAa,OAAO,GAAG,EAAE;AAIzB,OAAI,WADgB,aAAa,KAAK,IAAI,IAAI;;;AAMlD,KAAI,CAAC,0BAA0B;EAC7B,MAAM,eAAe,IAAI,gBAAgB,IAAI,OAAO;AACpD,MAAI,aAAa,IAAI,SAAS,EAAE;AAC9B,gBAAa,OAAO,SAAS;AAC7B,OAAI,SAAS,aAAa,UAAU;;;AAIxC,KAAI,cACF,QAAO,IAAI,UAAU;AAGvB,QAAO,IAAI,UAAU,CAAC,QAAQ,sBAAsB,GAAG"}
@@ -51,11 +51,11 @@ const resolveRoutingConfig = (options = {}) => ({
51
51
  */
52
52
  const getPrefix = (locale, options = {}) => {
53
53
  const { defaultLocale, mode, locales, domains } = resolveRoutingConfig(options);
54
- if (process.env["INTLAYER_ROUTING_MODE"] && process.env["INTLAYER_ROUTING_MODE"] !== "prefix-all" && process.env["INTLAYER_ROUTING_MODE"] !== "prefix-no-default" || !locale || !locales.includes(locale)) return {
54
+ if (process.env.INTLAYER_ROUTING_MODE && process.env.INTLAYER_ROUTING_MODE !== "prefix-all" && process.env.INTLAYER_ROUTING_MODE !== "prefix-no-default" || !locale || !locales.includes(locale)) return {
55
55
  prefix: "",
56
56
  localePrefix: void 0
57
57
  };
58
- if (process.env["INTLAYER_ROUTING_DOMAINS"] !== "false" && domains) {
58
+ if (process.env.INTLAYER_ROUTING_DOMAINS !== "false" && domains) {
59
59
  const localeDomain = domains[locale];
60
60
  if (localeDomain) {
61
61
  if (Object.values(domains).filter((domain) => domain === localeDomain).length === 1) return {
@@ -1 +1 @@
1
- {"version":3,"file":"getPrefix.cjs","names":["internationalization","DEFAULT_LOCALE","routing","ROUTING_MODE","LOCALES"],"sources":["../../../src/localization/getPrefix.ts"],"sourcesContent":["import { internationalization, routing } from '@intlayer/config/built';\nimport {\n DEFAULT_LOCALE,\n LOCALES,\n ROUTING_MODE,\n} from '@intlayer/config/defaultValues';\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\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { RoutingConfig } from '@intlayer/types/config';\nimport type {\n DeclaredLocales,\n LocalesValues,\n ResolvedDefaultLocale,\n ResolvedRoutingMode,\n} from '@intlayer/types/module_augmentation';\n\n/**\n * Shared routing options used across all URL localization functions.\n */\nexport type RoutingOptions = {\n locales?: LocalesValues[];\n defaultLocale?: LocalesValues;\n mode?: RoutingConfig['mode'];\n rewrite?: RoutingConfig['rewrite'];\n domains?: RoutingConfig['domains'];\n /**\n * The hostname of the page currently being rendered (e.g. `'intlayer.org'`).\n * When provided, `getLocalizedUrl` returns a relative URL for locales whose\n * configured domain matches `currentDomain`, and an absolute URL only when\n * the target locale lives on a different domain.\n *\n * When omitted the function tries to infer it from:\n * 1. The domain of an absolute input URL.\n * 2. `window.location.hostname` in browser environments.\n * Falls back to always generating absolute URLs when neither is available.\n */\n currentDomain?: string;\n};\n\n/**\n * Resolves routing configuration by merging provided options with configuration defaults.\n * Single source of truth for default routing config resolution across all localization functions.\n */\nexport const resolveRoutingConfig = (\n options: RoutingOptions = {}\n): Omit<RoutingOptions, 'defaultLocale' | 'mode' | 'locales'> & {\n defaultLocale: LocalesValues;\n mode: RoutingConfig['mode'];\n locales: LocalesValues[];\n} => ({\n defaultLocale: internationalization?.defaultLocale ?? DEFAULT_LOCALE,\n mode: routing?.mode ?? ROUTING_MODE,\n locales: internationalization?.locales ?? LOCALES,\n rewrite: routing?.rewrite,\n domains: routing?.domains,\n ...options,\n});\n\nexport type GetPrefixOptions = {\n defaultLocale?: LocalesValues;\n mode?: RoutingConfig['mode'];\n};\n\nexport type GetPrefixResult = {\n /**\n * The locale path segment appended to `/`, with a trailing slash (e.g. `'fr/'`).\n * Empty string when no prefix is needed.\n */\n prefix: string;\n /**\n * The bare locale identifier (e.g. `'fr'`), or `undefined` when no prefix is applied.\n */\n localePrefix: Locale | undefined;\n};\n\n/**\n * Narrowed return type for {@link getPrefix} that carries the locale literal through.\n *\n * Distributes over union locales — calling `getPrefix('fr')` in `prefix-no-default`\n * mode with `defaultLocale = 'en'` resolves to `{ prefix: 'fr/'; localePrefix: 'fr' }`.\n *\n * Note: domain-based routing and \"locale not in locales\" edge cases may return an\n * empty result at runtime regardless of what this type reports.\n */\nexport type GetPrefixResultNarrowed<\n L extends LocalesValues | undefined,\n Mode extends string = ResolvedRoutingMode,\n Default extends LocalesValues = ResolvedDefaultLocale,\n> = L extends string\n ? [string] extends [L] // L is wide (string / LocalesValues) → distribute over declared locales\n ? GetPrefixResultNarrowed<DeclaredLocales, Mode, Default>\n : [string] extends [Mode]\n ? GetPrefixResult // mode is wide → fall back to generic result\n : Mode extends 'prefix-all'\n ? { prefix: `${L}/`; localePrefix: L }\n : Mode extends 'prefix-no-default'\n ? L extends Default\n ? { prefix: ''; localePrefix: undefined }\n : { prefix: `${L}/`; localePrefix: L }\n : { prefix: ''; localePrefix: undefined } // no-prefix / search-params\n : { prefix: ''; localePrefix: undefined }; // locale is undefined\n\n/**\n * Determines the URL prefix for a given locale based on the routing mode configuration.\n *\n * Example:\n *\n * ```ts\n * // prefix-no-default mode with default locale\n * getPrefix('en', { defaultLocale: 'en', mode: 'prefix-no-default' })\n * // Returns { prefix: '', localePrefix: undefined }\n *\n * // prefix-no-default mode with non-default locale\n * getPrefix('fr', { defaultLocale: 'en', mode: 'prefix-no-default' })\n * // Returns { prefix: '/fr', localePrefix: 'fr' }\n *\n * // prefix-all mode\n * getPrefix('en', { defaultLocale: 'en', mode: 'prefix-all' })\n * // Returns { prefix: '/en', localePrefix: locale }\n *\n * // search-params mode\n * getPrefix('en', { defaultLocale: 'en', mode: 'search-params' })\n * // Returns { prefix: '', localePrefix: undefined }\n *\n * // no-prefix mode\n * getPrefix('en', { defaultLocale: 'en', mode: 'no-prefix' })\n * // Returns { prefix: '', localePrefix: undefined }\n * ```\n *\n * @param locale - The locale to check for prefix. If not provided, uses configured default locale.\n * @param options - Configuration options\n * @param options.defaultLocale - The default locale. Defaults to configured default locale.\n * @param options.mode - URL routing mode for locale handling. Defaults to configured mode.\n * @returns An object containing pathPrefix, prefix, and localePrefix for the given locale.\n */\nexport const getPrefix = <const L extends LocalesValues | undefined>(\n locale: L,\n options: RoutingOptions = {}\n): GetPrefixResultNarrowed<L> => {\n const { defaultLocale, mode, locales, domains } =\n resolveRoutingConfig(options);\n\n if (\n (process.env['INTLAYER_ROUTING_MODE'] &&\n process.env['INTLAYER_ROUTING_MODE'] !== 'prefix-all' &&\n process.env['INTLAYER_ROUTING_MODE'] !== 'prefix-no-default') ||\n !locale ||\n !locales.includes(locale)\n ) {\n return {\n prefix: '',\n localePrefix: undefined,\n } as GetPrefixResultNarrowed<L>;\n }\n\n // If this locale is the only one assigned to its domain, no URL prefix is needed\n // (the domain itself identifies the locale). Shared domains use normal prefix logic.\n if (process.env['INTLAYER_ROUTING_DOMAINS'] !== 'false' && domains) {\n const localeDomain = domains[locale as LocalesValues];\n\n if (localeDomain) {\n const localesOnSameDomain = Object.values(domains).filter(\n (domain) => domain === localeDomain\n ).length;\n\n if (localesOnSameDomain === 1) {\n return {\n prefix: '',\n localePrefix: undefined,\n } as GetPrefixResultNarrowed<L>;\n }\n }\n }\n\n // Handle prefix-based modes (prefix-all or prefix-no-default)\n const shouldPrefix =\n mode === 'prefix-all' ||\n (mode === 'prefix-no-default' && defaultLocale !== locale);\n\n if (shouldPrefix) {\n return {\n prefix: `${locale}/`,\n localePrefix: locale as Locale,\n } as GetPrefixResultNarrowed<L>;\n }\n\n return {\n prefix: '',\n localePrefix: undefined,\n } as GetPrefixResultNarrowed<L>;\n};\n"],"mappings":";;;;;;;;;;AA+CA,MAAa,wBACX,UAA0B,EAAE,MAKxB;CACJ,eAAeA,6CAAsB,iBAAiBC;CACtD,MAAMC,gCAAS,QAAQC;CACvB,SAASH,6CAAsB,WAAWI;CAC1C,SAASF,gCAAS;CAClB,SAASA,gCAAS;CAClB,GAAG;CACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+ED,MAAa,aACX,QACA,UAA0B,EAAE,KACG;CAC/B,MAAM,EAAE,eAAe,MAAM,SAAS,YACpC,qBAAqB,QAAQ;AAE/B,KACG,QAAQ,IAAI,4BACX,QAAQ,IAAI,6BAA6B,gBACzC,QAAQ,IAAI,6BAA6B,uBAC3C,CAAC,UACD,CAAC,QAAQ,SAAS,OAAO,CAEzB,QAAO;EACL,QAAQ;EACR,cAAc;EACf;AAKH,KAAI,QAAQ,IAAI,gCAAgC,WAAW,SAAS;EAClE,MAAM,eAAe,QAAQ;AAE7B,MAAI,cAKF;OAJ4B,OAAO,OAAO,QAAQ,CAAC,QAChD,WAAW,WAAW,aACxB,CAAC,WAE0B,EAC1B,QAAO;IACL,QAAQ;IACR,cAAc;IACf;;;AAUP,KAHE,SAAS,gBACR,SAAS,uBAAuB,kBAAkB,OAGnD,QAAO;EACL,QAAQ,GAAG,OAAO;EAClB,cAAc;EACf;AAGH,QAAO;EACL,QAAQ;EACR,cAAc;EACf"}
1
+ {"version":3,"file":"getPrefix.cjs","names":["internationalization","DEFAULT_LOCALE","routing","ROUTING_MODE","LOCALES"],"sources":["../../../src/localization/getPrefix.ts"],"sourcesContent":["import { internationalization, routing } from '@intlayer/config/built';\nimport {\n DEFAULT_LOCALE,\n LOCALES,\n ROUTING_MODE,\n} from '@intlayer/config/defaultValues';\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\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { RoutingConfig } from '@intlayer/types/config';\nimport type {\n DeclaredLocales,\n LocalesValues,\n ResolvedDefaultLocale,\n ResolvedRoutingMode,\n} from '@intlayer/types/module_augmentation';\n\n/**\n * Shared routing options used across all URL localization functions.\n */\nexport type RoutingOptions = {\n locales?: LocalesValues[];\n defaultLocale?: LocalesValues;\n mode?: RoutingConfig['mode'];\n rewrite?: RoutingConfig['rewrite'];\n domains?: RoutingConfig['domains'];\n /**\n * The hostname of the page currently being rendered (e.g. `'intlayer.org'`).\n * When provided, `getLocalizedUrl` returns a relative URL for locales whose\n * configured domain matches `currentDomain`, and an absolute URL only when\n * the target locale lives on a different domain.\n *\n * When omitted the function tries to infer it from:\n * 1. The domain of an absolute input URL.\n * 2. `window.location.hostname` in browser environments.\n * Falls back to always generating absolute URLs when neither is available.\n */\n currentDomain?: string;\n};\n\n/**\n * Resolves routing configuration by merging provided options with configuration defaults.\n * Single source of truth for default routing config resolution across all localization functions.\n */\nexport const resolveRoutingConfig = (\n options: RoutingOptions = {}\n): Omit<RoutingOptions, 'defaultLocale' | 'mode' | 'locales'> & {\n defaultLocale: LocalesValues;\n mode: RoutingConfig['mode'];\n locales: LocalesValues[];\n} => ({\n defaultLocale: internationalization?.defaultLocale ?? DEFAULT_LOCALE,\n mode: routing?.mode ?? ROUTING_MODE,\n locales: internationalization?.locales ?? LOCALES,\n rewrite: routing?.rewrite,\n domains: routing?.domains,\n ...options,\n});\n\nexport type GetPrefixOptions = {\n defaultLocale?: LocalesValues;\n mode?: RoutingConfig['mode'];\n};\n\nexport type GetPrefixResult = {\n /**\n * The locale path segment appended to `/`, with a trailing slash (e.g. `'fr/'`).\n * Empty string when no prefix is needed.\n */\n prefix: string;\n /**\n * The bare locale identifier (e.g. `'fr'`), or `undefined` when no prefix is applied.\n */\n localePrefix: Locale | undefined;\n};\n\n/**\n * Narrowed return type for {@link getPrefix} that carries the locale literal through.\n *\n * Distributes over union locales — calling `getPrefix('fr')` in `prefix-no-default`\n * mode with `defaultLocale = 'en'` resolves to `{ prefix: 'fr/'; localePrefix: 'fr' }`.\n *\n * Note: domain-based routing and \"locale not in locales\" edge cases may return an\n * empty result at runtime regardless of what this type reports.\n */\nexport type GetPrefixResultNarrowed<\n L extends LocalesValues | undefined,\n Mode extends string = ResolvedRoutingMode,\n Default extends LocalesValues = ResolvedDefaultLocale,\n> = L extends string\n ? [string] extends [L] // L is wide (string / LocalesValues) → distribute over declared locales\n ? GetPrefixResultNarrowed<DeclaredLocales, Mode, Default>\n : [string] extends [Mode]\n ? GetPrefixResult // mode is wide → fall back to generic result\n : Mode extends 'prefix-all'\n ? { prefix: `${L}/`; localePrefix: L }\n : Mode extends 'prefix-no-default'\n ? L extends Default\n ? { prefix: ''; localePrefix: undefined }\n : { prefix: `${L}/`; localePrefix: L }\n : { prefix: ''; localePrefix: undefined } // no-prefix / search-params\n : { prefix: ''; localePrefix: undefined }; // locale is undefined\n\n/**\n * Determines the URL prefix for a given locale based on the routing mode configuration.\n *\n * Example:\n *\n * ```ts\n * // prefix-no-default mode with default locale\n * getPrefix('en', { defaultLocale: 'en', mode: 'prefix-no-default' })\n * // Returns { prefix: '', localePrefix: undefined }\n *\n * // prefix-no-default mode with non-default locale\n * getPrefix('fr', { defaultLocale: 'en', mode: 'prefix-no-default' })\n * // Returns { prefix: '/fr', localePrefix: 'fr' }\n *\n * // prefix-all mode\n * getPrefix('en', { defaultLocale: 'en', mode: 'prefix-all' })\n * // Returns { prefix: '/en', localePrefix: locale }\n *\n * // search-params mode\n * getPrefix('en', { defaultLocale: 'en', mode: 'search-params' })\n * // Returns { prefix: '', localePrefix: undefined }\n *\n * // no-prefix mode\n * getPrefix('en', { defaultLocale: 'en', mode: 'no-prefix' })\n * // Returns { prefix: '', localePrefix: undefined }\n * ```\n *\n * @param locale - The locale to check for prefix. If not provided, uses configured default locale.\n * @param options - Configuration options\n * @param options.defaultLocale - The default locale. Defaults to configured default locale.\n * @param options.mode - URL routing mode for locale handling. Defaults to configured mode.\n * @returns An object containing pathPrefix, prefix, and localePrefix for the given locale.\n */\nexport const getPrefix = <const L extends LocalesValues | undefined>(\n locale: L,\n options: RoutingOptions = {}\n): GetPrefixResultNarrowed<L> => {\n const { defaultLocale, mode, locales, domains } =\n resolveRoutingConfig(options);\n\n if (\n (process.env.INTLAYER_ROUTING_MODE &&\n process.env.INTLAYER_ROUTING_MODE !== 'prefix-all' &&\n process.env.INTLAYER_ROUTING_MODE !== 'prefix-no-default') ||\n !locale ||\n !locales.includes(locale)\n ) {\n return {\n prefix: '',\n localePrefix: undefined,\n } as GetPrefixResultNarrowed<L>;\n }\n\n // If this locale is the only one assigned to its domain, no URL prefix is needed\n // (the domain itself identifies the locale). Shared domains use normal prefix logic.\n if (process.env.INTLAYER_ROUTING_DOMAINS !== 'false' && domains) {\n const localeDomain = domains[locale as LocalesValues];\n\n if (localeDomain) {\n const localesOnSameDomain = Object.values(domains).filter(\n (domain) => domain === localeDomain\n ).length;\n\n if (localesOnSameDomain === 1) {\n return {\n prefix: '',\n localePrefix: undefined,\n } as GetPrefixResultNarrowed<L>;\n }\n }\n }\n\n // Handle prefix-based modes (prefix-all or prefix-no-default)\n const shouldPrefix =\n mode === 'prefix-all' ||\n (mode === 'prefix-no-default' && defaultLocale !== locale);\n\n if (shouldPrefix) {\n return {\n prefix: `${locale}/`,\n localePrefix: locale as Locale,\n } as GetPrefixResultNarrowed<L>;\n }\n\n return {\n prefix: '',\n localePrefix: undefined,\n } as GetPrefixResultNarrowed<L>;\n};\n"],"mappings":";;;;;;;;;;AA+CA,MAAa,wBACX,UAA0B,EAAE,MAKxB;CACJ,eAAeA,6CAAsB,iBAAiBC;CACtD,MAAMC,gCAAS,QAAQC;CACvB,SAASH,6CAAsB,WAAWI;CAC1C,SAASF,gCAAS;CAClB,SAASA,gCAAS;CAClB,GAAG;CACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+ED,MAAa,aACX,QACA,UAA0B,EAAE,KACG;CAC/B,MAAM,EAAE,eAAe,MAAM,SAAS,YACpC,qBAAqB,QAAQ;AAE/B,KACG,QAAQ,IAAI,yBACX,QAAQ,IAAI,0BAA0B,gBACtC,QAAQ,IAAI,0BAA0B,uBACxC,CAAC,UACD,CAAC,QAAQ,SAAS,OAAO,CAEzB,QAAO;EACL,QAAQ;EACR,cAAc;EACf;AAKH,KAAI,QAAQ,IAAI,6BAA6B,WAAW,SAAS;EAC/D,MAAM,eAAe,QAAQ;AAE7B,MAAI,cAKF;OAJ4B,OAAO,OAAO,QAAQ,CAAC,QAChD,WAAW,WAAW,aACxB,CAAC,WAE0B,EAC1B,QAAO;IACL,QAAQ;IACR,cAAc;IACf;;;AAUP,KAHE,SAAS,gBACR,SAAS,uBAAuB,kBAAkB,OAGnD,QAAO;EACL,QAAQ,GAAG,OAAO;EAClB,cAAc;EACf;AAGH,QAAO;EACL,QAAQ;EACR,cAAc;EACf"}
@@ -5,7 +5,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
5
5
  * True when rewrite rules are explicitly disabled at build time
6
6
  * (INTLAYER_ROUTING_REWRITE_RULES === 'false').
7
7
  */
8
- const TREE_SHAKE_REWRITE = process.env["INTLAYER_ROUTING_REWRITE_RULES"] === "false";
8
+ const TREE_SHAKE_REWRITE = process.env.INTLAYER_ROUTING_REWRITE_RULES === "false";
9
9
  /**
10
10
  * Normalizes legacy Record format or extracts specialized rules from RewriteObject.
11
11
  */
@@ -1 +1 @@
1
- {"version":3,"file":"rewriteUtils.cjs","names":[],"sources":["../../../src/localization/rewriteUtils.ts"],"sourcesContent":["// ── 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 * True when rewrite rules are explicitly disabled at build time\n * (INTLAYER_ROUTING_REWRITE_RULES === 'false').\n */\nconst TREE_SHAKE_REWRITE =\n process.env['INTLAYER_ROUTING_REWRITE_RULES'] === 'false';\n\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type {\n RewriteObject,\n RewriteRules,\n RoutingConfig,\n} from '@intlayer/types/config';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\n\nexport type LocalizedPathResult = {\n path: string;\n isRewritten: boolean;\n};\n\n/**\n * Normalizes legacy Record format or extracts specialized rules from RewriteObject.\n */\nexport const getRewriteRules = (\n rewrite: RoutingConfig['rewrite'],\n context: keyof RewriteObject = 'url'\n): RewriteRules | undefined => {\n if (!rewrite || TREE_SHAKE_REWRITE) return undefined;\n\n if ('url' in rewrite) {\n return (rewrite as RewriteObject)[context];\n }\n\n // Normalize legacy format\n return {\n rules: Object.entries(rewrite).map(([canonical, localized]) => ({\n // Normalize canonical path\n canonical: canonical.startsWith('/')\n ? canonical.replace(/\\[([^\\]]+)\\]/g, ':$1')\n : `/${canonical.replace(/\\[([^\\]]+)\\]/g, ':$1')}`,\n\n // Normalize localized path\n localized: Object.fromEntries(\n Object.entries(localized).map(([locale, pattern]) => {\n const normalizedPattern = pattern?.startsWith('/')\n ? pattern.replace(/\\[([^\\]]+)\\]/g, ':$1')\n : `/${(pattern || '').replace(/\\[([^\\]]+)\\]/g, ':$1')}`;\n return [locale, normalizedPattern];\n })\n ),\n })),\n };\n};\n\n/**\n * Converts normalized pattern to Regex.\n * Internal syntax supports:\n * - :param -> ([^/]+) (one segment)\n * - :param* -> (.*) (zero or more segments)\n * - :param+ -> (.+) (one or more segments)\n * - :param? -> ([^/]*) (zero or one segment)\n */\nconst patternToRegex = (pattern: string) => {\n const regexString = pattern\n .replace(/\\//g, '\\\\/') // Escape slashes\n .replace(/\\\\\\/:(?:[^/\\\\*+?]+)\\*/g, '(?:\\\\/(.*))?') // /:param*\n .replace(/\\\\\\/:(?:[^/\\\\*+?]+)\\?/g, '(?:\\\\/([^\\\\/]+))?') // /:param?\n .replace(/:([^/\\\\*+?]+)\\*/g, '(.*)') // :param* (if no leading slash)\n .replace(/:([^/\\\\*+?]+)\\?/g, '([^\\\\/]*)') // :param? (if no leading slash)\n .replace(/:([^/\\\\*+?]+)\\+/g, '(.+)') // :param+\n .replace(/:([^/\\\\*+?]+)/g, '([^\\\\/]+)'); // :param\n\n return new RegExp(`^${regexString}$`);\n};\n\n/**\n * Replaces route parameters in a path with provided values.\n */\nconst fillPath = (pattern: string, params: string[]) => {\n let index = 0;\n return (\n pattern\n .replace(/:([^/\\\\*+?]+)[*+?]?/g, () => params[index++] ?? '')\n .replace(/\\/+/g, '/')\n .replace(/\\/$/, '') || '/'\n );\n};\n\n/**\n * Extract values from a URL based on a pattern.\n */\nconst extractParams = (url: string, pattern: string): string[] | null => {\n const regex = patternToRegex(pattern);\n const match = url.match(regex);\n return match ? match.slice(1) : null;\n};\n\n/**\n * Given a localized URL (e.g., \"/produits/123\"), finds the canonical internal path (e.g., \"/products/123\").\n * If locale is provided, only check for that locale. Otherwise, check for all locales.\n */\nexport const getCanonicalPath = (\n localizedPath: string,\n locale?: Locale,\n rewriteRules?: RewriteRules\n): string => {\n if (!rewriteRules || TREE_SHAKE_REWRITE) return localizedPath;\n\n for (const rule of rewriteRules.rules) {\n const { canonical, localized } = rule;\n const localesToCheck = locale ? [locale] : Object.keys(localized);\n\n for (const loc of localesToCheck) {\n const localizedPattern = localized[loc as keyof typeof localized];\n\n if (!localizedPattern) continue;\n\n const params = extractParams(localizedPath, localizedPattern);\n\n if (params) {\n return fillPath(canonical, params);\n }\n }\n }\n\n return localizedPath;\n};\n\n/**\n * Given a canonical path (e.g., \"/products/123\"), finds the localized URL pattern (e.g., \"/produits/123\").\n */\nexport const getLocalizedPath = (\n canonicalPath: string,\n locale: LocalesValues,\n rewriteRules?: RewriteRules\n): LocalizedPathResult => {\n if (!rewriteRules || TREE_SHAKE_REWRITE)\n return { path: canonicalPath, isRewritten: false };\n\n for (const rule of rewriteRules.rules) {\n const { canonical, localized } = rule;\n\n // Check if the input path matches a configured canonical pattern\n const params = extractParams(canonicalPath, canonical);\n\n if (params) {\n const targetPattern = localized[locale as keyof typeof localized];\n\n if (targetPattern) {\n return {\n path: fillPath(targetPattern, params),\n isRewritten: true,\n };\n }\n }\n }\n\n return { path: canonicalPath, isRewritten: false };\n};\n\n/**\n * Returns the internal path for a given canonical path and locale.\n * Ensures the locale prefix is present exactly once.\n */\nexport const getInternalPath = (\n canonicalPath: string,\n locale: Locale\n): string => {\n const pathWithLeadingSlash = canonicalPath.startsWith('/')\n ? canonicalPath\n : `/${canonicalPath}`;\n\n if (\n pathWithLeadingSlash.startsWith(`/${locale}/`) ||\n pathWithLeadingSlash === `/${locale}`\n ) {\n return pathWithLeadingSlash;\n }\n\n return `/${locale}${pathWithLeadingSlash === '/' ? '' : pathWithLeadingSlash}`;\n};\n\n/**\n * Given a current pathname and locale, returns the pretty localized path if a rewrite rule exists and the path is not already localized.\n */\nexport const getRewritePath = (\n pathname: string,\n locale: Locale,\n rewrite?: RoutingConfig['rewrite']\n): string | undefined => {\n if (TREE_SHAKE_REWRITE) return undefined;\n const rules = getRewriteRules(rewrite, 'url');\n if (!rules) return undefined;\n\n // Identify canonical path (relative to root, no locale prefix expected in 'url' context)\n const canonicalPath = getCanonicalPath(pathname, undefined, rules);\n\n // Get the localized path for the current locale\n const { path: localizedPath, isRewritten } = getLocalizedPath(\n canonicalPath,\n locale,\n rules\n );\n\n if (isRewritten && localizedPath !== pathname) {\n return localizedPath;\n }\n\n return undefined;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"rewriteUtils.cjs","names":[],"sources":["../../../src/localization/rewriteUtils.ts"],"sourcesContent":["// ── 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 * True when rewrite rules are explicitly disabled at build time\n * (INTLAYER_ROUTING_REWRITE_RULES === 'false').\n */\nconst TREE_SHAKE_REWRITE =\n process.env.INTLAYER_ROUTING_REWRITE_RULES === 'false';\n\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type {\n RewriteObject,\n RewriteRules,\n RoutingConfig,\n} from '@intlayer/types/config';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\n\nexport type LocalizedPathResult = {\n path: string;\n isRewritten: boolean;\n};\n\n/**\n * Normalizes legacy Record format or extracts specialized rules from RewriteObject.\n */\nexport const getRewriteRules = (\n rewrite: RoutingConfig['rewrite'],\n context: keyof RewriteObject = 'url'\n): RewriteRules | undefined => {\n if (!rewrite || TREE_SHAKE_REWRITE) return undefined;\n\n if ('url' in rewrite) {\n return (rewrite as RewriteObject)[context];\n }\n\n // Normalize legacy format\n return {\n rules: Object.entries(rewrite).map(([canonical, localized]) => ({\n // Normalize canonical path\n canonical: canonical.startsWith('/')\n ? canonical.replace(/\\[([^\\]]+)\\]/g, ':$1')\n : `/${canonical.replace(/\\[([^\\]]+)\\]/g, ':$1')}`,\n\n // Normalize localized path\n localized: Object.fromEntries(\n Object.entries(localized).map(([locale, pattern]) => {\n const normalizedPattern = pattern?.startsWith('/')\n ? pattern.replace(/\\[([^\\]]+)\\]/g, ':$1')\n : `/${(pattern || '').replace(/\\[([^\\]]+)\\]/g, ':$1')}`;\n return [locale, normalizedPattern];\n })\n ),\n })),\n };\n};\n\n/**\n * Converts normalized pattern to Regex.\n * Internal syntax supports:\n * - :param -> ([^/]+) (one segment)\n * - :param* -> (.*) (zero or more segments)\n * - :param+ -> (.+) (one or more segments)\n * - :param? -> ([^/]*) (zero or one segment)\n */\nconst patternToRegex = (pattern: string) => {\n const regexString = pattern\n .replace(/\\//g, '\\\\/') // Escape slashes\n .replace(/\\\\\\/:(?:[^/\\\\*+?]+)\\*/g, '(?:\\\\/(.*))?') // /:param*\n .replace(/\\\\\\/:(?:[^/\\\\*+?]+)\\?/g, '(?:\\\\/([^\\\\/]+))?') // /:param?\n .replace(/:([^/\\\\*+?]+)\\*/g, '(.*)') // :param* (if no leading slash)\n .replace(/:([^/\\\\*+?]+)\\?/g, '([^\\\\/]*)') // :param? (if no leading slash)\n .replace(/:([^/\\\\*+?]+)\\+/g, '(.+)') // :param+\n .replace(/:([^/\\\\*+?]+)/g, '([^\\\\/]+)'); // :param\n\n return new RegExp(`^${regexString}$`);\n};\n\n/**\n * Replaces route parameters in a path with provided values.\n */\nconst fillPath = (pattern: string, params: string[]) => {\n let index = 0;\n return (\n pattern\n .replace(/:([^/\\\\*+?]+)[*+?]?/g, () => params[index++] ?? '')\n .replace(/\\/+/g, '/')\n .replace(/\\/$/, '') || '/'\n );\n};\n\n/**\n * Extract values from a URL based on a pattern.\n */\nconst extractParams = (url: string, pattern: string): string[] | null => {\n const regex = patternToRegex(pattern);\n const match = url.match(regex);\n return match ? match.slice(1) : null;\n};\n\n/**\n * Given a localized URL (e.g., \"/produits/123\"), finds the canonical internal path (e.g., \"/products/123\").\n * If locale is provided, only check for that locale. Otherwise, check for all locales.\n */\nexport const getCanonicalPath = (\n localizedPath: string,\n locale?: Locale,\n rewriteRules?: RewriteRules\n): string => {\n if (!rewriteRules || TREE_SHAKE_REWRITE) return localizedPath;\n\n for (const rule of rewriteRules.rules) {\n const { canonical, localized } = rule;\n const localesToCheck = locale ? [locale] : Object.keys(localized);\n\n for (const loc of localesToCheck) {\n const localizedPattern = localized[loc as keyof typeof localized];\n\n if (!localizedPattern) continue;\n\n const params = extractParams(localizedPath, localizedPattern);\n\n if (params) {\n return fillPath(canonical, params);\n }\n }\n }\n\n return localizedPath;\n};\n\n/**\n * Given a canonical path (e.g., \"/products/123\"), finds the localized URL pattern (e.g., \"/produits/123\").\n */\nexport const getLocalizedPath = (\n canonicalPath: string,\n locale: LocalesValues,\n rewriteRules?: RewriteRules\n): LocalizedPathResult => {\n if (!rewriteRules || TREE_SHAKE_REWRITE)\n return { path: canonicalPath, isRewritten: false };\n\n for (const rule of rewriteRules.rules) {\n const { canonical, localized } = rule;\n\n // Check if the input path matches a configured canonical pattern\n const params = extractParams(canonicalPath, canonical);\n\n if (params) {\n const targetPattern = localized[locale as keyof typeof localized];\n\n if (targetPattern) {\n return {\n path: fillPath(targetPattern, params),\n isRewritten: true,\n };\n }\n }\n }\n\n return { path: canonicalPath, isRewritten: false };\n};\n\n/**\n * Returns the internal path for a given canonical path and locale.\n * Ensures the locale prefix is present exactly once.\n */\nexport const getInternalPath = (\n canonicalPath: string,\n locale: Locale\n): string => {\n const pathWithLeadingSlash = canonicalPath.startsWith('/')\n ? canonicalPath\n : `/${canonicalPath}`;\n\n if (\n pathWithLeadingSlash.startsWith(`/${locale}/`) ||\n pathWithLeadingSlash === `/${locale}`\n ) {\n return pathWithLeadingSlash;\n }\n\n return `/${locale}${pathWithLeadingSlash === '/' ? '' : pathWithLeadingSlash}`;\n};\n\n/**\n * Given a current pathname and locale, returns the pretty localized path if a rewrite rule exists and the path is not already localized.\n */\nexport const getRewritePath = (\n pathname: string,\n locale: Locale,\n rewrite?: RoutingConfig['rewrite']\n): string | undefined => {\n if (TREE_SHAKE_REWRITE) return undefined;\n const rules = getRewriteRules(rewrite, 'url');\n if (!rules) return undefined;\n\n // Identify canonical path (relative to root, no locale prefix expected in 'url' context)\n const canonicalPath = getCanonicalPath(pathname, undefined, rules);\n\n // Get the localized path for the current locale\n const { path: localizedPath, isRewritten } = getLocalizedPath(\n canonicalPath,\n locale,\n rules\n );\n\n if (isRewritten && localizedPath !== pathname) {\n return localizedPath;\n }\n\n return undefined;\n};\n"],"mappings":"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"}
@@ -5,11 +5,11 @@ const require_localization_getPrefix = require('./getPrefix.cjs');
5
5
  /**
6
6
  * True when the build-time routing mode is known and is NOT 'no-prefix'.
7
7
  */
8
- const TREE_SHAKE_NO_PREFIX = process.env["INTLAYER_ROUTING_MODE"] && process.env["INTLAYER_ROUTING_MODE"] !== "no-prefix";
8
+ const TREE_SHAKE_NO_PREFIX = process.env.INTLAYER_ROUTING_MODE && process.env.INTLAYER_ROUTING_MODE !== "no-prefix";
9
9
  /**
10
10
  * True when the build-time routing mode is known and is NOT 'search-params'.
11
11
  */
12
- const TREE_SHAKE_SEARCH_PARAMS = process.env["INTLAYER_ROUTING_MODE"] && process.env["INTLAYER_ROUTING_MODE"] !== "search-params";
12
+ const TREE_SHAKE_SEARCH_PARAMS = process.env.INTLAYER_ROUTING_MODE && process.env.INTLAYER_ROUTING_MODE !== "search-params";
13
13
  /**
14
14
  * Checks whether a given locale is valid based on the configured locales.
15
15
  *
@@ -1 +1 @@
1
- {"version":3,"file":"validatePrefix.cjs","names":["resolveRoutingConfig","getPrefix"],"sources":["../../../src/localization/validatePrefix.ts"],"sourcesContent":["// ── 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 * True when the build-time routing mode is known and is NOT 'no-prefix'.\n */\nconst TREE_SHAKE_NO_PREFIX =\n process.env['INTLAYER_ROUTING_MODE'] &&\n process.env['INTLAYER_ROUTING_MODE'] !== 'no-prefix';\n\n/**\n * True when the build-time routing mode is known and is NOT 'search-params'.\n */\nconst TREE_SHAKE_SEARCH_PARAMS =\n process.env['INTLAYER_ROUTING_MODE'] &&\n process.env['INTLAYER_ROUTING_MODE'] !== 'search-params';\n\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport {\n getPrefix,\n type RoutingOptions,\n resolveRoutingConfig,\n} from './getPrefix';\n\nexport type ValidatePrefixResult = {\n isValid: boolean;\n localePrefix: string | undefined;\n};\n\n/**\n * Checks whether a given locale is valid based on the configured locales.\n *\n * @param locale - The locale value to validate. Can be `undefined` or `null`.\n * @param options - Optional configuration to override default settings.\n * @param options.locales - Array of valid locales. Defaults to the configured internationalization locales.\n * @param options.defaultLocale - The default locale to use as fallback. Defaults to the configured default locale.\n * @param options.mode - The routing mode (`'prefix'`, `'prefix-all'`, or `'no-prefix'`). Defaults to the configured routing mode.\n * @returns An object containing the validation result and the locale prefix.\n *\n * @example\n * // Check if 'en' is a valid locale\n * const { isValid, localePrefix } = validatePrefix('en');\n *\n * @example\n * // Check with custom options\n * const { isValid, localePrefix } = validatePrefix('fr', {\n * locales: ['en', 'fr', 'es'],\n * defaultLocale: 'en',\n * mode: 'prefix-all',\n * });\n */\nexport const validatePrefix = (\n locale: LocalesValues | undefined | null,\n options?: RoutingOptions\n): ValidatePrefixResult => {\n const { defaultLocale, mode, locales } = resolveRoutingConfig(options);\n\n if (\n (!TREE_SHAKE_NO_PREFIX && mode === 'no-prefix') ||\n (!TREE_SHAKE_SEARCH_PARAMS && mode === 'search-params')\n ) {\n return { isValid: true, localePrefix: undefined };\n }\n\n const { localePrefix } = getPrefix(locale || defaultLocale, {\n mode,\n locales,\n defaultLocale,\n });\n\n if (localePrefix === locale && locale === undefined) {\n return { isValid: true, localePrefix: undefined };\n }\n\n // A segment that doesn't match a locale code pattern (e.g. 'concept', 'compiler')\n // is a content slug from a default-locale URL, not an invalid locale attempt.\n if (locale && !/^[a-z]{2,3}(-[a-zA-Z]{2,4})?$/.test(locale)) {\n return { isValid: true, localePrefix: undefined };\n }\n\n const isValid = locales.some((localeEl) => localeEl === locale);\n\n return { isValid, localePrefix };\n};\n"],"mappings":";;;;;;;AAOA,MAAM,uBACJ,QAAQ,IAAI,4BACZ,QAAQ,IAAI,6BAA6B;;;;AAK3C,MAAM,2BACJ,QAAQ,IAAI,4BACZ,QAAQ,IAAI,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;AAoC3C,MAAa,kBACX,QACA,YACyB;CACzB,MAAM,EAAE,eAAe,MAAM,YAAYA,oDAAqB,QAAQ;AAEtE,KACG,CAAC,wBAAwB,SAAS,eAClC,CAAC,4BAA4B,SAAS,gBAEvC,QAAO;EAAE,SAAS;EAAM,cAAc;EAAW;CAGnD,MAAM,EAAE,iBAAiBC,yCAAU,UAAU,eAAe;EAC1D;EACA;EACA;EACD,CAAC;AAEF,KAAI,iBAAiB,UAAU,WAAW,OACxC,QAAO;EAAE,SAAS;EAAM,cAAc;EAAW;AAKnD,KAAI,UAAU,CAAC,gCAAgC,KAAK,OAAO,CACzD,QAAO;EAAE,SAAS;EAAM,cAAc;EAAW;AAKnD,QAAO;EAAE,SAFO,QAAQ,MAAM,aAAa,aAAa,OAExC;EAAE;EAAc"}
1
+ {"version":3,"file":"validatePrefix.cjs","names":["resolveRoutingConfig","getPrefix"],"sources":["../../../src/localization/validatePrefix.ts"],"sourcesContent":["// ── 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 * True when the build-time routing mode is known and is NOT 'no-prefix'.\n */\nconst TREE_SHAKE_NO_PREFIX =\n process.env.INTLAYER_ROUTING_MODE &&\n process.env.INTLAYER_ROUTING_MODE !== 'no-prefix';\n\n/**\n * True when the build-time routing mode is known and is NOT 'search-params'.\n */\nconst TREE_SHAKE_SEARCH_PARAMS =\n process.env.INTLAYER_ROUTING_MODE &&\n process.env.INTLAYER_ROUTING_MODE !== 'search-params';\n\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport {\n getPrefix,\n type RoutingOptions,\n resolveRoutingConfig,\n} from './getPrefix';\n\nexport type ValidatePrefixResult = {\n isValid: boolean;\n localePrefix: string | undefined;\n};\n\n/**\n * Checks whether a given locale is valid based on the configured locales.\n *\n * @param locale - The locale value to validate. Can be `undefined` or `null`.\n * @param options - Optional configuration to override default settings.\n * @param options.locales - Array of valid locales. Defaults to the configured internationalization locales.\n * @param options.defaultLocale - The default locale to use as fallback. Defaults to the configured default locale.\n * @param options.mode - The routing mode (`'prefix'`, `'prefix-all'`, or `'no-prefix'`). Defaults to the configured routing mode.\n * @returns An object containing the validation result and the locale prefix.\n *\n * @example\n * // Check if 'en' is a valid locale\n * const { isValid, localePrefix } = validatePrefix('en');\n *\n * @example\n * // Check with custom options\n * const { isValid, localePrefix } = validatePrefix('fr', {\n * locales: ['en', 'fr', 'es'],\n * defaultLocale: 'en',\n * mode: 'prefix-all',\n * });\n */\nexport const validatePrefix = (\n locale: LocalesValues | undefined | null,\n options?: RoutingOptions\n): ValidatePrefixResult => {\n const { defaultLocale, mode, locales } = resolveRoutingConfig(options);\n\n if (\n (!TREE_SHAKE_NO_PREFIX && mode === 'no-prefix') ||\n (!TREE_SHAKE_SEARCH_PARAMS && mode === 'search-params')\n ) {\n return { isValid: true, localePrefix: undefined };\n }\n\n const { localePrefix } = getPrefix(locale || defaultLocale, {\n mode,\n locales,\n defaultLocale,\n });\n\n if (localePrefix === locale && locale === undefined) {\n return { isValid: true, localePrefix: undefined };\n }\n\n // A segment that doesn't match a locale code pattern (e.g. 'concept', 'compiler')\n // is a content slug from a default-locale URL, not an invalid locale attempt.\n if (locale && !/^[a-z]{2,3}(-[a-zA-Z]{2,4})?$/.test(locale)) {\n return { isValid: true, localePrefix: undefined };\n }\n\n const isValid = locales.some((localeEl) => localeEl === locale);\n\n return { isValid, localePrefix };\n};\n"],"mappings":";;;;;;;AAOA,MAAM,uBACJ,QAAQ,IAAI,yBACZ,QAAQ,IAAI,0BAA0B;;;;AAKxC,MAAM,2BACJ,QAAQ,IAAI,yBACZ,QAAQ,IAAI,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;AAoCxC,MAAa,kBACX,QACA,YACyB;CACzB,MAAM,EAAE,eAAe,MAAM,YAAYA,oDAAqB,QAAQ;AAEtE,KACG,CAAC,wBAAwB,SAAS,eAClC,CAAC,4BAA4B,SAAS,gBAEvC,QAAO;EAAE,SAAS;EAAM,cAAc;EAAW;CAGnD,MAAM,EAAE,iBAAiBC,yCAAU,UAAU,eAAe;EAC1D;EACA;EACA;EACD,CAAC;AAEF,KAAI,iBAAiB,UAAU,WAAW,OACxC,QAAO;EAAE,SAAS;EAAM,cAAc;EAAW;AAKnD,KAAI,UAAU,CAAC,gCAAgC,KAAK,OAAO,CACzD,QAAO;EAAE,SAAS;EAAM,cAAc;EAAW;AAKnD,QAAO;EAAE,SAFO,QAAQ,MAAM,aAAa,aAAa,OAExC;EAAE;EAAc"}
@@ -8,19 +8,19 @@ let _intlayer_config_built = require("@intlayer/config/built");
8
8
  /**
9
9
  * True when cookie storage is explicitly disabled at build time.
10
10
  */
11
- const TREE_SHAKE_STORAGE_COOKIES = process.env["INTLAYER_ROUTING_STORAGE_COOKIES"] === "false";
11
+ const TREE_SHAKE_STORAGE_COOKIES = process.env.INTLAYER_ROUTING_STORAGE_COOKIES === "false";
12
12
  /**
13
13
  * True when localStorage is explicitly disabled at build time.
14
14
  */
15
- const TREE_SHAKE_STORAGE_LOCAL_STORAGE = process.env["INTLAYER_ROUTING_STORAGE_LOCALSTORAGE"] === "false";
15
+ const TREE_SHAKE_STORAGE_LOCAL_STORAGE = process.env.INTLAYER_ROUTING_STORAGE_LOCALSTORAGE === "false";
16
16
  /**
17
17
  * True when sessionStorage is explicitly disabled at build time.
18
18
  */
19
- const TREE_SHAKE_STORAGE_SESSION_STORAGE = process.env["INTLAYER_ROUTING_STORAGE_SESSIONSTORAGE"] === "false";
19
+ const TREE_SHAKE_STORAGE_SESSION_STORAGE = process.env.INTLAYER_ROUTING_STORAGE_SESSIONSTORAGE === "false";
20
20
  /**
21
21
  * True when header storage is explicitly disabled at build time.
22
22
  */
23
- const TREE_SHAKE_STORAGE_HEADERS = process.env["INTLAYER_ROUTING_STORAGE_HEADERS"] === "false";
23
+ const TREE_SHAKE_STORAGE_HEADERS = process.env.INTLAYER_ROUTING_STORAGE_HEADERS === "false";
24
24
  const localeStorageOptions = {
25
25
  getCookie: (name) => document.cookie.split(";").find((c) => c.trim().startsWith(`${name}=`))?.split("=")[1],
26
26
  getLocaleStorage: (name) => localStorage.getItem(name),