@bleedingdev/modern-js-plugin-i18n 3.5.0-ultramodern.30 → 3.5.0-ultramodern.31

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 (161) hide show
  1. package/dist/cjs/cli/index.js +2 -1
  2. package/dist/cjs/runtime/Link.js +8 -89
  3. package/dist/cjs/runtime/context.js +18 -82
  4. package/dist/cjs/runtime/contextHelpers.js +137 -0
  5. package/dist/cjs/runtime/core.js +29 -132
  6. package/dist/cjs/runtime/i18n/detection/cache.js +149 -0
  7. package/dist/cjs/runtime/i18n/detection/detector.js +100 -0
  8. package/dist/cjs/runtime/i18n/detection/index.js +8 -263
  9. package/dist/cjs/runtime/i18n/detection/initOptions.js +69 -0
  10. package/dist/cjs/runtime/i18n/detection/language.js +62 -0
  11. package/dist/cjs/runtime/i18n/detection/path.js +49 -0
  12. package/dist/cjs/runtime/i18n/detection/priority.js +66 -0
  13. package/dist/cjs/runtime/i18n/detection/ssr.js +70 -0
  14. package/dist/cjs/runtime/i18n/detection/types.js +18 -0
  15. package/dist/cjs/runtime/linkHelpers.js +148 -0
  16. package/dist/cjs/runtime/pluginSetup.js +102 -0
  17. package/dist/cjs/runtime/providerComposition.js +113 -0
  18. package/dist/cjs/runtime/reactI18next.js +43 -0
  19. package/dist/cjs/server/apiPrefix.js +72 -0
  20. package/dist/cjs/server/detectorOptions.js +97 -0
  21. package/dist/cjs/server/index.js +17 -152
  22. package/dist/cjs/server/redirectPolicy.js +102 -0
  23. package/dist/cjs/shared/localisedUrls/config.js +49 -0
  24. package/dist/cjs/shared/localisedUrls/index.js +75 -0
  25. package/dist/cjs/shared/localisedUrls/normalise.js +87 -0
  26. package/dist/cjs/shared/localisedUrls/pathname.js +65 -0
  27. package/dist/cjs/shared/localisedUrls/patterns.js +130 -0
  28. package/dist/cjs/shared/localisedUrls/resolve.js +91 -0
  29. package/dist/cjs/shared/localisedUrls/routes.js +129 -0
  30. package/dist/cjs/shared/localisedUrls/types.js +18 -0
  31. package/dist/cjs/shared/localisedUrls.js +13 -294
  32. package/dist/esm/cli/index.mjs +2 -1
  33. package/dist/esm/runtime/Link.mjs +1 -82
  34. package/dist/esm/runtime/context.mjs +18 -82
  35. package/dist/esm/runtime/contextHelpers.mjs +84 -0
  36. package/dist/esm/runtime/core.mjs +30 -133
  37. package/dist/esm/runtime/i18n/detection/cache.mjs +102 -0
  38. package/dist/esm/runtime/i18n/detection/detector.mjs +62 -0
  39. package/dist/esm/runtime/i18n/detection/index.mjs +4 -260
  40. package/dist/esm/runtime/i18n/detection/initOptions.mjs +28 -0
  41. package/dist/esm/runtime/i18n/detection/language.mjs +21 -0
  42. package/dist/esm/runtime/i18n/detection/path.mjs +11 -0
  43. package/dist/esm/runtime/i18n/detection/priority.mjs +28 -0
  44. package/dist/esm/runtime/i18n/detection/ssr.mjs +26 -0
  45. package/dist/esm/runtime/i18n/detection/types.mjs +0 -0
  46. package/dist/esm/runtime/linkHelpers.mjs +98 -0
  47. package/dist/esm/runtime/pluginSetup.mjs +64 -0
  48. package/dist/esm/runtime/providerComposition.mjs +75 -0
  49. package/dist/esm/runtime/reactI18next.mjs +5 -0
  50. package/dist/esm/server/apiPrefix.mjs +31 -0
  51. package/dist/esm/server/detectorOptions.mjs +59 -0
  52. package/dist/esm/server/index.mjs +4 -139
  53. package/dist/esm/server/redirectPolicy.mjs +52 -0
  54. package/dist/esm/shared/localisedUrls/config.mjs +11 -0
  55. package/dist/esm/shared/localisedUrls/index.mjs +6 -0
  56. package/dist/esm/shared/localisedUrls/normalise.mjs +37 -0
  57. package/dist/esm/shared/localisedUrls/pathname.mjs +24 -0
  58. package/dist/esm/shared/localisedUrls/patterns.mjs +86 -0
  59. package/dist/esm/shared/localisedUrls/resolve.mjs +50 -0
  60. package/dist/esm/shared/localisedUrls/routes.mjs +88 -0
  61. package/dist/esm/shared/localisedUrls/types.mjs +0 -0
  62. package/dist/esm/shared/localisedUrls.mjs +1 -283
  63. package/dist/esm-node/cli/index.mjs +2 -1
  64. package/dist/esm-node/runtime/Link.mjs +1 -82
  65. package/dist/esm-node/runtime/context.mjs +18 -82
  66. package/dist/esm-node/runtime/contextHelpers.mjs +85 -0
  67. package/dist/esm-node/runtime/core.mjs +30 -133
  68. package/dist/esm-node/runtime/i18n/detection/cache.mjs +103 -0
  69. package/dist/esm-node/runtime/i18n/detection/detector.mjs +63 -0
  70. package/dist/esm-node/runtime/i18n/detection/index.mjs +4 -260
  71. package/dist/esm-node/runtime/i18n/detection/initOptions.mjs +29 -0
  72. package/dist/esm-node/runtime/i18n/detection/language.mjs +22 -0
  73. package/dist/esm-node/runtime/i18n/detection/path.mjs +12 -0
  74. package/dist/esm-node/runtime/i18n/detection/priority.mjs +29 -0
  75. package/dist/esm-node/runtime/i18n/detection/ssr.mjs +27 -0
  76. package/dist/esm-node/runtime/i18n/detection/types.mjs +1 -0
  77. package/dist/esm-node/runtime/linkHelpers.mjs +99 -0
  78. package/dist/esm-node/runtime/pluginSetup.mjs +65 -0
  79. package/dist/esm-node/runtime/providerComposition.mjs +76 -0
  80. package/dist/esm-node/runtime/reactI18next.mjs +6 -0
  81. package/dist/esm-node/server/apiPrefix.mjs +32 -0
  82. package/dist/esm-node/server/detectorOptions.mjs +60 -0
  83. package/dist/esm-node/server/index.mjs +4 -139
  84. package/dist/esm-node/server/redirectPolicy.mjs +53 -0
  85. package/dist/esm-node/shared/localisedUrls/config.mjs +12 -0
  86. package/dist/esm-node/shared/localisedUrls/index.mjs +7 -0
  87. package/dist/esm-node/shared/localisedUrls/normalise.mjs +38 -0
  88. package/dist/esm-node/shared/localisedUrls/pathname.mjs +25 -0
  89. package/dist/esm-node/shared/localisedUrls/patterns.mjs +87 -0
  90. package/dist/esm-node/shared/localisedUrls/resolve.mjs +51 -0
  91. package/dist/esm-node/shared/localisedUrls/routes.mjs +89 -0
  92. package/dist/esm-node/shared/localisedUrls/types.mjs +1 -0
  93. package/dist/esm-node/shared/localisedUrls.mjs +1 -283
  94. package/dist/types/runtime/Link.d.ts +3 -7
  95. package/dist/types/runtime/context.d.ts +2 -2
  96. package/dist/types/runtime/contextHelpers.d.ts +28 -0
  97. package/dist/types/runtime/core.d.ts +3 -7
  98. package/dist/types/runtime/i18n/detection/cache.d.ts +14 -0
  99. package/dist/types/runtime/i18n/detection/detector.d.ts +8 -0
  100. package/dist/types/runtime/i18n/detection/index.d.ts +5 -50
  101. package/dist/types/runtime/i18n/detection/initOptions.d.ts +10 -0
  102. package/dist/types/runtime/i18n/detection/language.d.ts +11 -0
  103. package/dist/types/runtime/i18n/detection/path.d.ts +5 -0
  104. package/dist/types/runtime/i18n/detection/priority.d.ts +10 -0
  105. package/dist/types/runtime/i18n/detection/ssr.d.ts +9 -0
  106. package/dist/types/runtime/i18n/detection/types.d.ts +29 -0
  107. package/dist/types/runtime/i18n/react-i18next.d.ts +1 -6
  108. package/dist/types/runtime/linkHelpers.d.ts +17 -0
  109. package/dist/types/runtime/pluginSetup.d.ts +32 -0
  110. package/dist/types/runtime/providerComposition.d.ts +15 -0
  111. package/dist/types/runtime/reactI18next.d.ts +7 -0
  112. package/dist/types/server/apiPrefix.d.ts +7 -0
  113. package/dist/types/server/detectorOptions.d.ts +21 -0
  114. package/dist/types/server/index.d.ts +1 -6
  115. package/dist/types/server/redirectPolicy.d.ts +16 -0
  116. package/dist/types/shared/localisedUrls/config.d.ts +9 -0
  117. package/dist/types/shared/localisedUrls/index.d.ts +7 -0
  118. package/dist/types/shared/localisedUrls/normalise.d.ts +10 -0
  119. package/dist/types/shared/localisedUrls/pathname.d.ts +3 -0
  120. package/dist/types/shared/localisedUrls/patterns.d.ts +5 -0
  121. package/dist/types/shared/localisedUrls/resolve.d.ts +8 -0
  122. package/dist/types/shared/localisedUrls/routes.d.ts +3 -0
  123. package/dist/types/shared/localisedUrls/types.d.ts +14 -0
  124. package/dist/types/shared/localisedUrls.d.ts +2 -42
  125. package/package.json +10 -10
  126. package/src/cli/index.ts +3 -2
  127. package/src/runtime/Link.tsx +11 -138
  128. package/src/runtime/context.tsx +42 -196
  129. package/src/runtime/contextHelpers.ts +232 -0
  130. package/src/runtime/core.tsx +42 -273
  131. package/src/runtime/i18n/detection/cache.ts +175 -0
  132. package/src/runtime/i18n/detection/detector.ts +157 -0
  133. package/src/runtime/i18n/detection/index.ts +10 -641
  134. package/src/runtime/i18n/detection/initOptions.ts +74 -0
  135. package/src/runtime/i18n/detection/language.ts +64 -0
  136. package/src/runtime/i18n/detection/path.ts +41 -0
  137. package/src/runtime/i18n/detection/priority.ts +82 -0
  138. package/src/runtime/i18n/detection/ssr.ts +47 -0
  139. package/src/runtime/i18n/detection/types.ts +33 -0
  140. package/src/runtime/i18n/react-i18next.ts +1 -6
  141. package/src/runtime/linkHelpers.ts +174 -0
  142. package/src/runtime/pluginSetup.ts +189 -0
  143. package/src/runtime/providerComposition.tsx +142 -0
  144. package/src/runtime/reactI18next.ts +20 -0
  145. package/src/server/apiPrefix.ts +67 -0
  146. package/src/server/detectorOptions.ts +105 -0
  147. package/src/server/index.ts +12 -327
  148. package/src/server/redirectPolicy.ts +152 -0
  149. package/src/shared/localisedUrls/config.ts +18 -0
  150. package/src/shared/localisedUrls/index.ts +13 -0
  151. package/src/shared/localisedUrls/normalise.ts +67 -0
  152. package/src/shared/localisedUrls/pathname.ts +57 -0
  153. package/src/shared/localisedUrls/patterns.ts +160 -0
  154. package/src/shared/localisedUrls/resolve.ts +118 -0
  155. package/src/shared/localisedUrls/routes.ts +226 -0
  156. package/src/shared/localisedUrls/types.ts +18 -0
  157. package/src/shared/localisedUrls.ts +20 -629
  158. package/tests/link.test.tsx +111 -0
  159. package/tests/localisedUrls.test.ts +50 -0
  160. package/tests/reactI18nextRuntimeBoundary.test.ts +5 -2
  161. package/tests/routerAdapter.test.tsx +56 -0
@@ -1,14 +1,9 @@
1
1
  import type { ServerPlugin } from '@modern-js/server-runtime';
2
2
  import type { LocaleDetectionOptions } from '../shared/type';
3
+ export { collectApiPrefixes, matchesApiPrefix } from './apiPrefix.js';
3
4
  export interface I18nPluginOptions {
4
5
  localeDetection: LocaleDetectionOptions;
5
6
  staticRoutePrefixes: string[];
6
7
  }
7
- type ApiPrefixInput = string | string[] | undefined;
8
- export declare const collectApiPrefixes: (routes: Array<{
9
- isApi?: boolean;
10
- urlPath?: string;
11
- }>, bffPrefix?: ApiPrefixInput) => string[];
12
- export declare const matchesApiPrefix: (pathname: string, apiPrefixes: string[]) => boolean;
13
8
  export declare const i18nServerPlugin: (options: I18nPluginOptions) => ServerPlugin;
14
9
  export default i18nServerPlugin;
@@ -0,0 +1,16 @@
1
+ import type { LocaleDetectionOptions } from '../shared/type';
2
+ /**
3
+ * Check if the given pathname should ignore automatic locale redirect
4
+ */
5
+ export declare const shouldIgnoreRedirect: (pathname: string, urlPath: string, ignoreRedirectRoutes?: string[] | ((pathname: string) => boolean)) => boolean;
6
+ /**
7
+ * Check if the given pathname is a static resource request
8
+ * This includes:
9
+ * 1. Paths matching staticRoutePrefixes (from public directories)
10
+ * 2. Standard static resource paths like /static/, /upload/
11
+ * 3. Paths with language prefix like /en/static/, /zh/static/
12
+ */
13
+ export declare const isStaticResourceRequest: (pathname: string, staticRoutePrefixes: string[], languages?: string[]) => boolean;
14
+ export declare const getLanguageFromPath: (req: any, urlPath: string, languages: string[]) => string | null;
15
+ export declare const buildLocalizedUrl: (req: any, urlPath: string, language: string, languages: string[], localisedUrls?: LocaleDetectionOptions['localisedUrls']) => string;
16
+ export declare const createLocaleRedirectResponse: (location: string) => Response;
@@ -0,0 +1,9 @@
1
+ import type { LocalisedUrlsOption, ResolvedLocalisedUrlsConfig } from './types';
2
+ /**
3
+ * Localised URLs are strictly opt-in: only an explicit, non-empty map enables
4
+ * route expansion and validation. `true`, `false`, an empty map and absence
5
+ * all resolve to disabled, so upstream-style configs (`localePathRedirect` +
6
+ * `languages` without a map) keep plain locale-prefix behavior instead of
7
+ * failing the build for every route missing from a map they never wrote.
8
+ */
9
+ export declare const resolveLocalisedUrlsConfig: (option: LocalisedUrlsOption | undefined) => ResolvedLocalisedUrlsConfig;
@@ -0,0 +1,7 @@
1
+ export { resolveLocalisedUrlsConfig } from './config';
2
+ export { normalisePathname, normalisePathPattern } from './normalise';
3
+ export { canonicalTargetPathname, localiseTargetPathname } from './pathname';
4
+ export { buildPathFromPattern, matchPathPattern } from './patterns';
5
+ export { resolveCanonicalLocalisedPath, resolveLocalisedPath } from './resolve';
6
+ export { applyLocalisedUrlsToRoutes, validateLocalisedUrls } from './routes';
7
+ export type { LocalisedRoute, LocalisedUrlPathMap, LocalisedUrlsMap, LocalisedUrlsOption, ResolvedLocalisedUrlsConfig, } from './types';
@@ -0,0 +1,10 @@
1
+ export declare const normalisePathPattern: (path: string) => string;
2
+ /**
3
+ * Normalise a concrete request pathname: slash cleanup only. Unlike
4
+ * {@link normalisePathPattern} it must not rewrite literal `[x]` segments to
5
+ * `:x` params — pathnames are values, not patterns.
6
+ */
7
+ export declare const normalisePathname: (pathname: string) => string;
8
+ export declare const normaliseRoutePath: (path: string) => string;
9
+ export declare const stripLeadingLocaleParam: (path?: string) => string | undefined;
10
+ export declare const getLeadingLocaleParam: (path?: string) => string | null;
@@ -0,0 +1,3 @@
1
+ import type { LocalisedUrlsOption } from './types';
2
+ export declare const localiseTargetPathname: (pathname: string, language: string, languages: string[], localisedUrls?: LocalisedUrlsOption) => string;
3
+ export declare const canonicalTargetPathname: (pathname: string, languages: string[], localisedUrls?: LocalisedUrlsOption) => string;
@@ -0,0 +1,5 @@
1
+ export declare const sortPatternsBySpecificity: <T extends {
2
+ pattern: string;
3
+ }>(patterns: T[]) => T[];
4
+ export declare const matchPathPattern: (pathname: string, pattern: string) => Record<string, string> | null;
5
+ export declare const buildPathFromPattern: (pattern: string, params: Record<string, string>) => string;
@@ -0,0 +1,8 @@
1
+ import type { LocalisedUrlsMap } from './types';
2
+ export declare const resolveLocalisedPath: (pathname: string, targetLanguage: string, languages: string[], localisedUrls: LocalisedUrlsMap) => string;
3
+ /**
4
+ * Reverse-map a language-specific pathname (without language prefix) back to
5
+ * the canonical, language-agnostic path: localized slug patterns are matched
6
+ * against every language variant and rebuilt from the canonical map key.
7
+ */
8
+ export declare const resolveCanonicalLocalisedPath: (pathname: string, languages: string[], localisedUrls: LocalisedUrlsMap) => string;
@@ -0,0 +1,3 @@
1
+ import type { LocalisedRoute, LocalisedUrlsMap } from './types';
2
+ export declare const validateLocalisedUrls: (routes: LocalisedRoute[], languages: string[], localisedUrls: LocalisedUrlsMap) => void;
3
+ export declare const applyLocalisedUrlsToRoutes: (routes: LocalisedRoute[], languages: string[], localisedUrls: LocalisedUrlsMap) => LocalisedRoute[];
@@ -0,0 +1,14 @@
1
+ export type LocalisedRoute = {
2
+ type: 'nested' | 'page';
3
+ path?: string;
4
+ id?: string;
5
+ children?: LocalisedRoute[];
6
+ [key: string]: any;
7
+ };
8
+ export type LocalisedUrlPathMap = Record<string, string>;
9
+ export type LocalisedUrlsMap = Record<string, LocalisedUrlPathMap>;
10
+ export type LocalisedUrlsOption = boolean | LocalisedUrlsMap;
11
+ export interface ResolvedLocalisedUrlsConfig {
12
+ enabled: boolean;
13
+ map: LocalisedUrlsMap;
14
+ }
@@ -1,42 +1,2 @@
1
- export type LocalisedRoute = {
2
- type: 'nested' | 'page';
3
- path?: string;
4
- id?: string;
5
- children?: LocalisedRoute[];
6
- [key: string]: any;
7
- };
8
- export type LocalisedUrlPathMap = Record<string, string>;
9
- export type LocalisedUrlsMap = Record<string, LocalisedUrlPathMap>;
10
- export type LocalisedUrlsOption = boolean | LocalisedUrlsMap;
11
- export interface ResolvedLocalisedUrlsConfig {
12
- enabled: boolean;
13
- map: LocalisedUrlsMap;
14
- }
15
- export declare const normalisePathPattern: (path: string) => string;
16
- /**
17
- * Normalise a concrete request pathname: slash cleanup only. Unlike
18
- * {@link normalisePathPattern} it must not rewrite literal `[x]` segments to
19
- * `:x` params — pathnames are values, not patterns.
20
- */
21
- export declare const normalisePathname: (pathname: string) => string;
22
- /**
23
- * Localised URLs are strictly opt-in: only an explicit, non-empty map enables
24
- * route expansion and validation. `true`, `false`, an empty map and absence
25
- * all resolve to disabled, so upstream-style configs (`localePathRedirect` +
26
- * `languages` without a map) keep plain locale-prefix behavior instead of
27
- * failing the build for every route missing from a map they never wrote.
28
- */
29
- export declare const resolveLocalisedUrlsConfig: (option: LocalisedUrlsOption | undefined) => ResolvedLocalisedUrlsConfig;
30
- export declare const validateLocalisedUrls: (routes: LocalisedRoute[], languages: string[], localisedUrls: LocalisedUrlsMap) => void;
31
- export declare const applyLocalisedUrlsToRoutes: (routes: LocalisedRoute[], languages: string[], localisedUrls: LocalisedUrlsMap) => LocalisedRoute[];
32
- export declare const matchPathPattern: (pathname: string, pattern: string) => Record<string, string> | null;
33
- export declare const buildPathFromPattern: (pattern: string, params: Record<string, string>) => string;
34
- export declare const resolveLocalisedPath: (pathname: string, targetLanguage: string, languages: string[], localisedUrls: LocalisedUrlsMap) => string;
35
- /**
36
- * Reverse-map a language-specific pathname (without language prefix) back to
37
- * the canonical, language-agnostic path: localized slug patterns are matched
38
- * against every language variant and rebuilt from the canonical map key.
39
- */
40
- export declare const resolveCanonicalLocalisedPath: (pathname: string, languages: string[], localisedUrls: LocalisedUrlsMap) => string;
41
- export declare const localiseTargetPathname: (pathname: string, language: string, languages: string[], localisedUrls?: LocalisedUrlsOption) => string;
42
- export declare const canonicalTargetPathname: (pathname: string, languages: string[], localisedUrls?: LocalisedUrlsOption) => string;
1
+ export type { LocalisedRoute, LocalisedUrlPathMap, LocalisedUrlsMap, LocalisedUrlsOption, ResolvedLocalisedUrlsConfig, } from './localisedUrls/index';
2
+ export { applyLocalisedUrlsToRoutes, buildPathFromPattern, canonicalTargetPathname, localiseTargetPathname, matchPathPattern, normalisePathname, normalisePathPattern, resolveCanonicalLocalisedPath, resolveLocalisedPath, resolveLocalisedUrlsConfig, validateLocalisedUrls, } from './localisedUrls/index';
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "modern",
18
18
  "modern.js"
19
19
  ],
20
- "version": "3.5.0-ultramodern.30",
20
+ "version": "3.5.0-ultramodern.31",
21
21
  "engines": {
22
22
  "node": ">=20"
23
23
  },
@@ -97,15 +97,15 @@
97
97
  "i18next-fs-backend": "^2.6.6",
98
98
  "i18next-http-backend": "^4.0.0",
99
99
  "i18next-http-middleware": "^3.9.7",
100
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.5.0-ultramodern.30",
101
- "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.5.0-ultramodern.30",
102
- "@modern-js/server-runtime": "npm:@bleedingdev/modern-js-server-runtime@3.5.0-ultramodern.30",
103
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.5.0-ultramodern.30",
104
- "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.5.0-ultramodern.30",
105
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.5.0-ultramodern.30"
100
+ "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.5.0-ultramodern.31",
101
+ "@modern-js/server-runtime": "npm:@bleedingdev/modern-js-server-runtime@3.5.0-ultramodern.31",
102
+ "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.5.0-ultramodern.31",
103
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.5.0-ultramodern.31",
104
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.5.0-ultramodern.31",
105
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.5.0-ultramodern.31"
106
106
  },
107
107
  "peerDependencies": {
108
- "@modern-js/runtime": "3.5.0-ultramodern.30",
108
+ "@modern-js/runtime": "3.5.0-ultramodern.31",
109
109
  "i18next": ">=26.3.1",
110
110
  "react": "^19.2.7",
111
111
  "react-dom": "^19.2.7",
@@ -129,8 +129,8 @@
129
129
  "react-i18next": "17.0.8",
130
130
  "ts-node": "^10.9.2",
131
131
  "typescript": "^6.0.3",
132
- "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.5.0-ultramodern.30",
133
- "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.5.0-ultramodern.30"
132
+ "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.5.0-ultramodern.31",
133
+ "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.5.0-ultramodern.31"
134
134
  },
135
135
  "sideEffects": false,
136
136
  "publishConfig": {
package/src/cli/index.ts CHANGED
@@ -126,9 +126,10 @@ export const i18nPlugin = (
126
126
  backend: backendOptions,
127
127
  ...extendedConfig,
128
128
  };
129
+ const { reactI18next } = extendedConfig as { reactI18next?: boolean };
129
130
  const runtimePluginPath =
130
131
  customPlugin?.runtime?.path ||
131
- (config.reactI18next === false
132
+ (reactI18next === false
132
133
  ? `@${metaName}/plugin-i18n/runtime/no-react-i18next`
133
134
  : `@${metaName}/plugin-i18n/runtime`);
134
135
 
@@ -173,7 +174,7 @@ export const i18nPlugin = (
173
174
  routes as (NestedRouteForCli | PageRoute)[],
174
175
  languages,
175
176
  localisedUrlsConfig.map,
176
- ),
177
+ ) as (NestedRouteForCli | PageRoute)[],
177
178
  };
178
179
  });
179
180
 
@@ -6,96 +6,22 @@ import type {
6
6
  ValidateLinkTo,
7
7
  } from './canonicalRoutes';
8
8
  import { useModernI18n } from './context';
9
+ import {
10
+ interpolateRouteParams,
11
+ type LinkParams,
12
+ mergeClassNames,
13
+ normalizeSearch,
14
+ splitActiveProps,
15
+ warnOnce,
16
+ } from './linkHelpers';
9
17
  import { canonicalPath, type LocalizedPathsConfig } from './localizedPaths';
10
18
  import { useI18nRouterAdapter } from './routerAdapter';
11
19
  import { buildLocalizedUrl, splitUrlTarget } from './utils';
12
20
 
13
- const EXTERNAL_TARGET_RE = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
14
-
15
- const warnedTargets = new Set<string>();
16
-
17
- const warnOnce = (key: string, message: string) => {
18
- if (process.env.NODE_ENV !== 'development' || warnedTargets.has(key)) {
19
- return;
20
- }
21
- warnedTargets.add(key);
22
- console.warn(message);
23
- };
24
-
25
- export type LinkParams = Record<string, string | number | undefined>;
26
-
27
- /**
28
- * Interpolate `$param`, `:param`, optional (`{-$param}` / `:param?`) and splat
29
- * (`$` / `*`) segments with concrete values before localization, so
30
- * pattern-mapped slugs localize correctly.
31
- */
32
- export const interpolateRouteParams = (
33
- pathname: string,
34
- params?: LinkParams,
35
- ): string => {
36
- if (!/[$:*{]/.test(pathname)) {
37
- return pathname;
38
- }
39
-
40
- const resolveParam = (name: string): string | undefined => {
41
- const value = params?.[name];
42
- return value === undefined ? undefined : String(value);
43
- };
44
-
45
- const segments = pathname
46
- .split('/')
47
- .map(segment => {
48
- if (!segment) {
49
- return segment;
50
- }
51
-
52
- if (segment.startsWith('{-$') && segment.endsWith('}')) {
53
- const value = resolveParam(segment.slice(3, -1));
54
- return value === undefined ? null : encodeURIComponent(value);
55
- }
56
-
57
- if (segment === '$' || segment === '*') {
58
- const value = resolveParam('_splat') ?? resolveParam('*');
59
- return value === undefined
60
- ? null
61
- : value.split('/').map(encodeURIComponent).join('/');
62
- }
63
-
64
- if (segment.startsWith('$')) {
65
- const value = resolveParam(segment.slice(1));
66
- if (value === undefined) {
67
- warnOnce(
68
- `missing-param:${pathname}:${segment}`,
69
- `[plugin-i18n] <Link to="${pathname}"> is missing required param "${segment.slice(1)}".`,
70
- );
71
- return segment;
72
- }
73
- return encodeURIComponent(value);
74
- }
75
-
76
- if (segment.startsWith(':')) {
77
- const optional = segment.endsWith('?');
78
- const name = segment.slice(1, optional ? -1 : undefined);
79
- const value = resolveParam(name);
80
- if (value === undefined) {
81
- if (optional) {
82
- return null;
83
- }
84
- warnOnce(
85
- `missing-param:${pathname}:${segment}`,
86
- `[plugin-i18n] <Link to="${pathname}"> is missing required param "${name}".`,
87
- );
88
- return segment;
89
- }
90
- return encodeURIComponent(value);
91
- }
21
+ export type { LinkParams };
22
+ export { interpolateRouteParams };
92
23
 
93
- return segment;
94
- })
95
- .filter(segment => segment !== null);
96
-
97
- return segments.join('/') || '/';
98
- };
24
+ const EXTERNAL_TARGET_RE = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
99
25
 
100
26
  export interface LinkActiveOptions {
101
27
  /**
@@ -142,59 +68,6 @@ export type LinkProps<TTo extends string = string> = LinkBaseProps & {
142
68
  } & ValidateLinkTo<TTo> &
143
69
  LinkParamsProp<LinkTargetPathname<TTo>>;
144
70
 
145
- const normalizeSearch = (
146
- search: string | Record<string, unknown> | undefined,
147
- searchFromTo: string,
148
- ): {
149
- searchString: string;
150
- searchObject: Record<string, string> | undefined;
151
- } => {
152
- if (search && typeof search === 'object') {
153
- const entries = Object.entries(search).filter(
154
- ([, value]) => value !== undefined && value !== null,
155
- );
156
- const searchObject = Object.fromEntries(
157
- entries.map(([key, value]) => [key, String(value)]),
158
- );
159
- const params = new URLSearchParams(searchObject);
160
- const serialized = params.toString();
161
- return {
162
- searchString: serialized ? `?${serialized}` : '',
163
- searchObject,
164
- };
165
- }
166
-
167
- const raw = typeof search === 'string' && search ? search : searchFromTo;
168
- if (!raw) {
169
- return { searchString: '', searchObject: undefined };
170
- }
171
-
172
- const searchString = raw.startsWith('?') ? raw : `?${raw}`;
173
- const searchObject: Record<string, string> = {};
174
- new URLSearchParams(searchString).forEach((value, key) => {
175
- searchObject[key] = value;
176
- });
177
-
178
- return { searchString, searchObject };
179
- };
180
-
181
- const splitActiveProps = (
182
- active: boolean,
183
- activeProps?: LinkBaseProps['activeProps'],
184
- ) => {
185
- if (!active || !activeProps) {
186
- return {};
187
- }
188
- return activeProps;
189
- };
190
-
191
- const mergeClassNames = (...values: Array<unknown>): string | undefined => {
192
- const classNames = values.filter(
193
- (value): value is string => typeof value === 'string' && value.length > 0,
194
- );
195
- return classNames.length > 0 ? classNames.join(' ') : undefined;
196
- };
197
-
198
71
  /**
199
72
  * The standard UltraModern link: a vanilla link in every respect except that
200
73
  * it localizes canonical, language-agnostic paths automatically.