@bleedingdev/modern-js-plugin-i18n 3.5.0-ultramodern.9 → 3.5.0-ultramodern.91

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 (221) hide show
  1. package/dist/cjs/cli/index.js +2 -1
  2. package/dist/cjs/cli/locales.js +15 -15
  3. package/dist/cjs/runtime/Link.js +8 -89
  4. package/dist/cjs/runtime/context.js +78 -91
  5. package/dist/cjs/runtime/contextHelpers.js +143 -0
  6. package/dist/cjs/runtime/core.js +32 -132
  7. package/dist/cjs/runtime/hooks.js +54 -21
  8. package/dist/cjs/runtime/i18n/backend/defaults.node.js +7 -7
  9. package/dist/cjs/runtime/i18n/detection/cache.js +150 -0
  10. package/dist/cjs/runtime/i18n/detection/detector.js +100 -0
  11. package/dist/cjs/runtime/i18n/detection/index.js +8 -263
  12. package/dist/cjs/runtime/i18n/detection/initOptions.js +69 -0
  13. package/dist/cjs/runtime/i18n/detection/language.js +62 -0
  14. package/dist/cjs/runtime/i18n/detection/path.js +49 -0
  15. package/dist/cjs/runtime/i18n/detection/priority.js +66 -0
  16. package/dist/cjs/runtime/i18n/detection/ssr.js +70 -0
  17. package/dist/cjs/runtime/i18n/detection/types.js +18 -0
  18. package/dist/cjs/runtime/i18n/instance.js +34 -10
  19. package/dist/cjs/runtime/linkHelpers.js +148 -0
  20. package/dist/cjs/runtime/pluginSetup.js +102 -0
  21. package/dist/cjs/runtime/providerComposition.js +115 -0
  22. package/dist/cjs/runtime/reactI18next.js +43 -0
  23. package/dist/cjs/runtime/routerAdapter.js +16 -8
  24. package/dist/cjs/runtime/utils.js +1 -9
  25. package/dist/cjs/server/apiPrefix.js +72 -0
  26. package/dist/cjs/server/detectorOptions.js +97 -0
  27. package/dist/cjs/server/index.js +17 -144
  28. package/dist/cjs/server/redirectPolicy.js +96 -0
  29. package/dist/cjs/shared/detection.js +2 -104
  30. package/dist/cjs/shared/localisedUrls/config.js +49 -0
  31. package/dist/cjs/shared/localisedUrls/index.js +91 -0
  32. package/dist/cjs/shared/localisedUrls/normalise.js +87 -0
  33. package/dist/cjs/shared/localisedUrls/pathname.js +68 -0
  34. package/dist/cjs/shared/localisedUrls/patterns.js +130 -0
  35. package/dist/cjs/shared/localisedUrls/redirect.js +103 -0
  36. package/dist/cjs/shared/localisedUrls/resolve.js +88 -0
  37. package/dist/cjs/shared/localisedUrls/routes.js +130 -0
  38. package/dist/cjs/shared/localisedUrls/types.js +18 -0
  39. package/dist/cjs/shared/localisedUrls.js +28 -294
  40. package/dist/cjs/shared/utils.js +2 -8
  41. package/dist/esm/cli/index.mjs +2 -1
  42. package/dist/esm/cli/locales.mjs +13 -13
  43. package/dist/esm/runtime/Link.mjs +1 -82
  44. package/dist/esm/runtime/context.mjs +76 -92
  45. package/dist/esm/runtime/contextHelpers.mjs +87 -0
  46. package/dist/esm/runtime/core.mjs +30 -133
  47. package/dist/esm/runtime/hooks.mjs +56 -23
  48. package/dist/esm/runtime/i18n/backend/defaults.node.mjs +5 -5
  49. package/dist/esm/runtime/i18n/detection/cache.mjs +103 -0
  50. package/dist/esm/runtime/i18n/detection/detector.mjs +62 -0
  51. package/dist/esm/runtime/i18n/detection/index.mjs +4 -260
  52. package/dist/esm/runtime/i18n/detection/initOptions.mjs +28 -0
  53. package/dist/esm/runtime/i18n/detection/language.mjs +21 -0
  54. package/dist/esm/runtime/i18n/detection/path.mjs +11 -0
  55. package/dist/esm/runtime/i18n/detection/priority.mjs +28 -0
  56. package/dist/esm/runtime/i18n/detection/ssr.mjs +26 -0
  57. package/dist/esm/runtime/i18n/detection/types.mjs +0 -0
  58. package/dist/esm/runtime/i18n/instance.mjs +35 -8
  59. package/dist/esm/runtime/linkHelpers.mjs +98 -0
  60. package/dist/esm/runtime/pluginSetup.mjs +64 -0
  61. package/dist/esm/runtime/providerComposition.mjs +77 -0
  62. package/dist/esm/runtime/reactI18next.mjs +5 -0
  63. package/dist/esm/runtime/routerAdapter.mjs +16 -8
  64. package/dist/esm/runtime/utils.mjs +2 -10
  65. package/dist/esm/server/apiPrefix.mjs +31 -0
  66. package/dist/esm/server/detectorOptions.mjs +59 -0
  67. package/dist/esm/server/index.mjs +8 -135
  68. package/dist/esm/server/redirectPolicy.mjs +46 -0
  69. package/dist/esm/shared/detection.mjs +1 -71
  70. package/dist/esm/shared/localisedUrls/config.mjs +11 -0
  71. package/dist/esm/shared/localisedUrls/index.mjs +7 -0
  72. package/dist/esm/shared/localisedUrls/normalise.mjs +37 -0
  73. package/dist/esm/shared/localisedUrls/pathname.mjs +24 -0
  74. package/dist/esm/shared/localisedUrls/patterns.mjs +86 -0
  75. package/dist/esm/shared/localisedUrls/redirect.mjs +56 -0
  76. package/dist/esm/shared/localisedUrls/resolve.mjs +47 -0
  77. package/dist/esm/shared/localisedUrls/routes.mjs +89 -0
  78. package/dist/esm/shared/localisedUrls/types.mjs +0 -0
  79. package/dist/esm/shared/localisedUrls.mjs +1 -283
  80. package/dist/esm/shared/utils.mjs +1 -1
  81. package/dist/esm-node/cli/index.mjs +2 -1
  82. package/dist/esm-node/cli/locales.mjs +13 -13
  83. package/dist/esm-node/runtime/Link.mjs +1 -82
  84. package/dist/esm-node/runtime/context.mjs +76 -92
  85. package/dist/esm-node/runtime/contextHelpers.mjs +88 -0
  86. package/dist/esm-node/runtime/core.mjs +30 -133
  87. package/dist/esm-node/runtime/hooks.mjs +56 -23
  88. package/dist/esm-node/runtime/i18n/backend/defaults.node.mjs +5 -5
  89. package/dist/esm-node/runtime/i18n/detection/cache.mjs +104 -0
  90. package/dist/esm-node/runtime/i18n/detection/detector.mjs +63 -0
  91. package/dist/esm-node/runtime/i18n/detection/index.mjs +4 -260
  92. package/dist/esm-node/runtime/i18n/detection/initOptions.mjs +29 -0
  93. package/dist/esm-node/runtime/i18n/detection/language.mjs +22 -0
  94. package/dist/esm-node/runtime/i18n/detection/path.mjs +12 -0
  95. package/dist/esm-node/runtime/i18n/detection/priority.mjs +29 -0
  96. package/dist/esm-node/runtime/i18n/detection/ssr.mjs +27 -0
  97. package/dist/esm-node/runtime/i18n/detection/types.mjs +1 -0
  98. package/dist/esm-node/runtime/i18n/instance.mjs +35 -8
  99. package/dist/esm-node/runtime/linkHelpers.mjs +99 -0
  100. package/dist/esm-node/runtime/pluginSetup.mjs +65 -0
  101. package/dist/esm-node/runtime/providerComposition.mjs +78 -0
  102. package/dist/esm-node/runtime/reactI18next.mjs +6 -0
  103. package/dist/esm-node/runtime/routerAdapter.mjs +16 -8
  104. package/dist/esm-node/runtime/utils.mjs +2 -10
  105. package/dist/esm-node/server/apiPrefix.mjs +32 -0
  106. package/dist/esm-node/server/detectorOptions.mjs +60 -0
  107. package/dist/esm-node/server/index.mjs +8 -135
  108. package/dist/esm-node/server/redirectPolicy.mjs +47 -0
  109. package/dist/esm-node/shared/detection.mjs +1 -71
  110. package/dist/esm-node/shared/localisedUrls/config.mjs +12 -0
  111. package/dist/esm-node/shared/localisedUrls/index.mjs +8 -0
  112. package/dist/esm-node/shared/localisedUrls/normalise.mjs +38 -0
  113. package/dist/esm-node/shared/localisedUrls/pathname.mjs +25 -0
  114. package/dist/esm-node/shared/localisedUrls/patterns.mjs +87 -0
  115. package/dist/esm-node/shared/localisedUrls/redirect.mjs +57 -0
  116. package/dist/esm-node/shared/localisedUrls/resolve.mjs +48 -0
  117. package/dist/esm-node/shared/localisedUrls/routes.mjs +90 -0
  118. package/dist/esm-node/shared/localisedUrls/types.mjs +1 -0
  119. package/dist/esm-node/shared/localisedUrls.mjs +1 -283
  120. package/dist/esm-node/shared/utils.mjs +1 -1
  121. package/dist/types/cli/locales.d.ts +1 -1
  122. package/dist/types/runtime/Link.d.ts +3 -7
  123. package/dist/types/runtime/context.d.ts +21 -5
  124. package/dist/types/runtime/contextHelpers.d.ts +29 -0
  125. package/dist/types/runtime/core.d.ts +4 -8
  126. package/dist/types/runtime/hooks.d.ts +3 -7
  127. package/dist/types/runtime/i18n/backend/middleware.common.d.ts +2 -1
  128. package/dist/types/runtime/i18n/detection/cache.d.ts +14 -0
  129. package/dist/types/runtime/i18n/detection/detector.d.ts +8 -0
  130. package/dist/types/runtime/i18n/detection/index.d.ts +5 -50
  131. package/dist/types/runtime/i18n/detection/initOptions.d.ts +10 -0
  132. package/dist/types/runtime/i18n/detection/language.d.ts +11 -0
  133. package/dist/types/runtime/i18n/detection/middleware.d.ts +4 -4
  134. package/dist/types/runtime/i18n/detection/middleware.node.d.ts +4 -4
  135. package/dist/types/runtime/i18n/detection/path.d.ts +5 -0
  136. package/dist/types/runtime/i18n/detection/priority.d.ts +10 -0
  137. package/dist/types/runtime/i18n/detection/ssr.d.ts +9 -0
  138. package/dist/types/runtime/i18n/detection/types.d.ts +32 -0
  139. package/dist/types/runtime/i18n/instance.d.ts +31 -27
  140. package/dist/types/runtime/i18n/react-i18next.d.ts +1 -6
  141. package/dist/types/runtime/i18n/utils.d.ts +14 -5
  142. package/dist/types/runtime/linkHelpers.d.ts +17 -0
  143. package/dist/types/runtime/pluginSetup.d.ts +32 -0
  144. package/dist/types/runtime/providerComposition.d.ts +16 -0
  145. package/dist/types/runtime/reactI18next.d.ts +7 -0
  146. package/dist/types/runtime/routerAdapter.d.ts +7 -6
  147. package/dist/types/runtime/utils.d.ts +7 -2
  148. package/dist/types/server/apiPrefix.d.ts +7 -0
  149. package/dist/types/server/detectorOptions.d.ts +21 -0
  150. package/dist/types/server/index.d.ts +1 -6
  151. package/dist/types/server/redirectPolicy.d.ts +22 -0
  152. package/dist/types/shared/detection.d.ts +1 -11
  153. package/dist/types/shared/localisedUrls/config.d.ts +9 -0
  154. package/dist/types/shared/localisedUrls/index.d.ts +9 -0
  155. package/dist/types/shared/localisedUrls/normalise.d.ts +10 -0
  156. package/dist/types/shared/localisedUrls/pathname.d.ts +4 -0
  157. package/dist/types/shared/localisedUrls/patterns.d.ts +5 -0
  158. package/dist/types/shared/localisedUrls/redirect.d.ts +11 -0
  159. package/dist/types/shared/localisedUrls/resolve.d.ts +7 -0
  160. package/dist/types/shared/localisedUrls/routes.d.ts +3 -0
  161. package/dist/types/shared/localisedUrls/types.d.ts +14 -0
  162. package/dist/types/shared/localisedUrls.d.ts +2 -36
  163. package/dist/types/shared/utils.d.ts +0 -2
  164. package/package.json +17 -20
  165. package/rstest.config.mts +8 -1
  166. package/src/cli/index.ts +5 -4
  167. package/src/cli/locales.ts +3 -3
  168. package/src/runtime/Link.tsx +11 -138
  169. package/src/runtime/context.tsx +170 -207
  170. package/src/runtime/contextHelpers.ts +242 -0
  171. package/src/runtime/core.tsx +48 -275
  172. package/src/runtime/hooks.ts +86 -24
  173. package/src/runtime/i18n/backend/defaults.node.ts +2 -2
  174. package/src/runtime/i18n/backend/middleware.common.ts +5 -4
  175. package/src/runtime/i18n/detection/cache.ts +189 -0
  176. package/src/runtime/i18n/detection/detector.ts +166 -0
  177. package/src/runtime/i18n/detection/index.ts +10 -641
  178. package/src/runtime/i18n/detection/initOptions.ts +74 -0
  179. package/src/runtime/i18n/detection/language.ts +64 -0
  180. package/src/runtime/i18n/detection/middleware.node.ts +26 -7
  181. package/src/runtime/i18n/detection/middleware.ts +15 -7
  182. package/src/runtime/i18n/detection/path.ts +41 -0
  183. package/src/runtime/i18n/detection/priority.ts +82 -0
  184. package/src/runtime/i18n/detection/ssr.ts +47 -0
  185. package/src/runtime/i18n/detection/types.ts +41 -0
  186. package/src/runtime/i18n/instance.ts +98 -34
  187. package/src/runtime/i18n/react-i18next.ts +1 -6
  188. package/src/runtime/i18n/utils.ts +18 -9
  189. package/src/runtime/linkHelpers.ts +174 -0
  190. package/src/runtime/pluginSetup.ts +189 -0
  191. package/src/runtime/providerComposition.tsx +148 -0
  192. package/src/runtime/reactI18next.ts +20 -0
  193. package/src/runtime/routerAdapter.tsx +36 -15
  194. package/src/runtime/utils.ts +13 -35
  195. package/src/server/apiPrefix.ts +67 -0
  196. package/src/server/detectorOptions.ts +105 -0
  197. package/src/server/index.ts +14 -319
  198. package/src/server/redirectPolicy.ts +127 -0
  199. package/src/shared/detection.ts +1 -1
  200. package/src/shared/localisedUrls/config.ts +18 -0
  201. package/src/shared/localisedUrls/index.ts +24 -0
  202. package/src/shared/localisedUrls/normalise.ts +67 -0
  203. package/src/shared/localisedUrls/pathname.ts +60 -0
  204. package/src/shared/localisedUrls/patterns.ts +160 -0
  205. package/src/shared/localisedUrls/redirect.ts +93 -0
  206. package/src/shared/localisedUrls/resolve.ts +135 -0
  207. package/src/shared/localisedUrls/routes.ts +234 -0
  208. package/src/shared/localisedUrls/types.ts +18 -0
  209. package/src/shared/localisedUrls.ts +26 -623
  210. package/src/shared/utils.ts +2 -2
  211. package/tests/federatedI18nBoundary.client.test.tsx +112 -0
  212. package/tests/federatedI18nBoundary.test.tsx +120 -0
  213. package/tests/fixtures/localised-routes.golden.json +156 -0
  214. package/tests/link.test.tsx +111 -0
  215. package/tests/localisedUrlRewriteMatrix.fork.test.ts +147 -0
  216. package/tests/localisedUrls.test.ts +148 -2
  217. package/tests/reactI18nextRuntimeBoundary.test.ts +6 -3
  218. package/tests/redirectPolicy.test.ts +45 -0
  219. package/tests/routerAdapter.test.tsx +603 -0
  220. package/tests/type-fixture/fixture-globals.d.ts +11 -0
  221. package/tests/type-fixture/tsconfig.json +1 -0
@@ -1,3 +1,5 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
1
3
  import type { NestedRouteForCli } from '@modern-js/types';
2
4
  import { describe, expect, test } from '@rstest/core';
3
5
  import { i18nPlugin as i18nCliPlugin } from '../src/cli';
@@ -17,6 +19,12 @@ import {
17
19
  validateLocalisedUrls,
18
20
  } from '../src/shared/localisedUrls';
19
21
 
22
+ const localisedRoutesGoldenPath = path.join(
23
+ __dirname,
24
+ 'fixtures',
25
+ 'localised-routes.golden.json',
26
+ );
27
+
20
28
  const createRoute = (
21
29
  path: string,
22
30
  children?: NestedRouteForCli[],
@@ -37,6 +45,9 @@ const createRequestContext = (pathname: string) =>
37
45
  },
38
46
  }) as any;
39
47
 
48
+ const serializeRoutesForGolden = (routes: unknown): string =>
49
+ `${JSON.stringify(routes, null, 2)}\n`;
50
+
40
51
  describe('resolveLocalisedUrlsConfig', () => {
41
52
  test('is opt-in: only a non-empty map enables the feature', () => {
42
53
  const map = { '/about': { en: '/about', cs: '/o-nas' } };
@@ -108,6 +119,70 @@ describe('cli modifyFileSystemRoutes', () => {
108
119
  return modifyRoutes!;
109
120
  };
110
121
 
122
+ const generateLocalisedRoutesGolden = (): string => {
123
+ const modifyRoutes = setupModifyRoutes({
124
+ localePathRedirect: true,
125
+ languages: ['en', 'cs', 'de'],
126
+ localisedUrls: {
127
+ '/about': {
128
+ en: '/about',
129
+ cs: '/o-nas',
130
+ de: '/ueber-uns',
131
+ },
132
+ '/products': {
133
+ en: '/products',
134
+ cs: '/produkty',
135
+ de: '/produkte',
136
+ },
137
+ '/products/:slug': {
138
+ en: '/products/:slug',
139
+ cs: '/produkty/:slug',
140
+ de: '/produkte/:slug',
141
+ },
142
+ '/docs': {
143
+ en: '/docs',
144
+ cs: '/dokumenty',
145
+ de: '/dokumente',
146
+ },
147
+ '/docs/*': {
148
+ en: '/docs/*',
149
+ cs: '/dokumenty/*',
150
+ de: '/dokumente/*',
151
+ },
152
+ },
153
+ });
154
+ const routes = [
155
+ createRoute(':lang', [
156
+ createRoute('about'),
157
+ createRoute('products', [createRoute(':slug')]),
158
+ createRoute('docs', [createRoute('*')]),
159
+ ]),
160
+ ];
161
+
162
+ const result = modifyRoutes({
163
+ entrypoint: { entryName: 'main' },
164
+ routes,
165
+ });
166
+
167
+ return serializeRoutesForGolden(result.routes);
168
+ };
169
+
170
+ test('matches checked-in localised routes golden output', async () => {
171
+ const localisedRoutes = generateLocalisedRoutesGolden();
172
+ const repeatedLocalisedRoutes = generateLocalisedRoutesGolden();
173
+
174
+ expect(repeatedLocalisedRoutes).toBe(localisedRoutes);
175
+
176
+ if (process.env.UPDATE_I18N_LOCALISED_ROUTES_GOLDEN === '1') {
177
+ await mkdir(path.dirname(localisedRoutesGoldenPath), { recursive: true });
178
+ await writeFile(localisedRoutesGoldenPath, localisedRoutes);
179
+ }
180
+
181
+ await expect(readFile(localisedRoutesGoldenPath, 'utf8')).resolves.toBe(
182
+ localisedRoutes,
183
+ );
184
+ });
185
+
111
186
  test('upstream-style configs without a map keep routes untouched', () => {
112
187
  const modifyRoutes = setupModifyRoutes({
113
188
  localePathRedirect: true,
@@ -141,6 +216,25 @@ describe('cli modifyFileSystemRoutes', () => {
141
216
  });
142
217
 
143
218
  describe('localisedUrls', () => {
219
+ test('leaves framework-internal Module Federation routes unlocalised', () => {
220
+ const internalRoute = createRoute('_mf', [
221
+ createRoute('fragment', [createRoute('product-card')]),
222
+ ]);
223
+ const publicRoute = createRoute(':lang', [createRoute('products')]);
224
+ const routes = [internalRoute, publicRoute];
225
+
226
+ expect(() =>
227
+ validateLocalisedUrls(routes, ['en', 'cs'], {
228
+ '/products': { en: '/products', cs: '/produkty' },
229
+ }),
230
+ ).not.toThrow();
231
+ expect(
232
+ applyLocalisedUrlsToRoutes(routes, ['en', 'cs'], {
233
+ '/products': { en: '/products', cs: '/produkty' },
234
+ })[0],
235
+ ).toEqual(internalRoute);
236
+ });
237
+
144
238
  test('requires every localisable route path to define every language', () => {
145
239
  const routes = [createRoute(':lang', [createRoute('terms-of-service')])];
146
240
 
@@ -272,6 +366,31 @@ describe('localisedUrls', () => {
272
366
  ).toBe('/products/cervena-bota');
273
367
  });
274
368
 
369
+ test('encodes wildcard params while preserving path separators', () => {
370
+ const localisedUrls = {
371
+ '/docs/*': {
372
+ en: '/docs/*',
373
+ cs: '/dokumenty/*',
374
+ },
375
+ };
376
+
377
+ expect(
378
+ resolveLocalisedPath(
379
+ '/docs/a%20b/c%23d',
380
+ 'cs',
381
+ ['en', 'cs'],
382
+ localisedUrls,
383
+ ),
384
+ ).toBe('/dokumenty/a%20b/c%23d');
385
+ expect(
386
+ resolveCanonicalLocalisedPath(
387
+ '/dokumenty/a%20b/c%23d',
388
+ ['en', 'cs'],
389
+ localisedUrls,
390
+ ),
391
+ ).toBe('/docs/a%20b/c%23d');
392
+ });
393
+
275
394
  test('resolves static patterns before param patterns', () => {
276
395
  const localisedUrls = {
277
396
  '/products/:slug': {
@@ -321,6 +440,31 @@ describe('localisedUrls', () => {
321
440
  ).toBe('/products/cervena-bota');
322
441
  });
323
442
 
443
+ test('strips case-insensitive locale prefixes before relocalising full pathnames', () => {
444
+ const localisedUrls = {
445
+ '/products/:slug': {
446
+ en: '/products/:slug',
447
+ cs: '/produkty/:slug',
448
+ },
449
+ };
450
+
451
+ expect(
452
+ localiseTargetPathname(
453
+ '/CS/produkty/cervena-bota',
454
+ 'en',
455
+ ['en', 'cs'],
456
+ localisedUrls,
457
+ ),
458
+ ).toBe('/en/products/cervena-bota');
459
+ expect(
460
+ canonicalTargetPathname(
461
+ '/CS/produkty/cervena-bota',
462
+ ['en', 'cs'],
463
+ localisedUrls,
464
+ ),
465
+ ).toBe('/products/cervena-bota');
466
+ });
467
+
324
468
  test('resolves nested optional route params with translated ancestors', () => {
325
469
  const localisedUrls = {
326
470
  '/checkout': {
@@ -509,7 +653,6 @@ describe('i18n server API prefix skips', () => {
509
653
  header: () => ({ host: 'localhost' }),
510
654
  },
511
655
  get: () => null,
512
- redirect: (url: string) => ({ redirectedTo: url }),
513
656
  }) as any;
514
657
 
515
658
  // Sanity: well-formed non-canonical slugs still redirect.
@@ -517,7 +660,10 @@ describe('i18n server API prefix skips', () => {
517
660
  createContext('/cs/products/bota'),
518
661
  async () => {},
519
662
  );
520
- expect(redirected).toEqual({ redirectedTo: '/cs/produkty/bota' });
663
+ expect(redirected.status).toBe(302);
664
+ expect(redirected.headers.get('location')).toBe('/cs/produkty/bota');
665
+ expect(redirected.headers.get('cache-control')).toBe('private, no-store');
666
+ expect(redirected.headers.get('vary')).toBe('Accept-Language, Cookie');
521
667
 
522
668
  // Malformed encoding must fall through to next() instead of throwing.
523
669
  let nextCalls = 0;
@@ -7,7 +7,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
7
7
  const runtimeRoot = resolve(__dirname, '../src/runtime');
8
8
 
9
9
  const readRuntimeSource = (file: string) =>
10
- readFileSync(resolve(runtimeRoot, file), 'utf8');
10
+ readFileSync(resolve(runtimeRoot, file), 'utf8').replace(/\r\n/gu, '\n');
11
11
 
12
12
  describe('react-i18next runtime boundary', () => {
13
13
  test('keeps the disabled runtime entry free of the optional adapter edge', () => {
@@ -21,11 +21,14 @@ describe('react-i18next runtime boundary', () => {
21
21
 
22
22
  test('keeps the runtime plugin factory entry synchronous for federation', () => {
23
23
  const core = readRuntimeSource('core.tsx');
24
+ const pluginSetup = readRuntimeSource('pluginSetup.ts');
24
25
 
25
26
  expect(core).not.toContain("from './i18n/backend/middleware'");
26
27
  expect(core).not.toContain("from './i18n/utils'");
27
- expect(core).toContain("import('./i18n/backend/middleware')");
28
- expect(core).toContain("import('./i18n/utils')");
28
+ expect(pluginSetup).not.toContain("from './i18n/backend/middleware'");
29
+ expect(pluginSetup).not.toContain("from './i18n/utils'");
30
+ expect(pluginSetup).toContain("import('./i18n/backend/middleware')");
31
+ expect(pluginSetup).toContain("import('./i18n/utils')");
29
32
  });
30
33
 
31
34
  test('keeps the Modern i18n context stable across federated runtime copies', () => {
@@ -0,0 +1,45 @@
1
+ import { describe, expect, test } from '@rstest/core';
2
+ import { shouldIgnoreRedirect as shouldIgnoreRuntimeRedirect } from '../src/runtime/utils';
3
+ import {
4
+ isStaticResourceRequest,
5
+ shouldIgnoreRedirect as shouldIgnoreServerRedirect,
6
+ } from '../src/server/redirectPolicy';
7
+
8
+ const languages = ['en', 'cs'];
9
+
10
+ const defaultSkippedPaths = [
11
+ '/backend-mf-manifest.json',
12
+ '/backendRemoteEntry.cjs',
13
+ '/mf-manifest.json',
14
+ '/mf-stats.json',
15
+ '/remoteEntry.js',
16
+ '/static/app.js',
17
+ '/upload/avatar.png',
18
+ ];
19
+
20
+ describe('locale redirect default skip policy', () => {
21
+ test('skips ADR-0002 Module Federation and static endpoints on the server', () => {
22
+ for (const pathname of defaultSkippedPaths) {
23
+ expect(isStaticResourceRequest(pathname, [], languages)).toBe(true);
24
+ expect(shouldIgnoreServerRedirect(pathname, '/', undefined)).toBe(true);
25
+ }
26
+ });
27
+
28
+ test('skips language-prefixed static and upload endpoints', () => {
29
+ expect(isStaticResourceRequest('/cs/static/app.js', [], languages)).toBe(
30
+ true,
31
+ );
32
+ expect(
33
+ isStaticResourceRequest('/en/upload/avatar.png', [], languages),
34
+ ).toBe(true);
35
+ });
36
+
37
+ test('uses the same default skip policy in runtime redirects', () => {
38
+ for (const pathname of defaultSkippedPaths) {
39
+ expect(shouldIgnoreRuntimeRedirect(pathname, languages)).toBe(true);
40
+ expect(shouldIgnoreRuntimeRedirect(`/cs${pathname}`, languages)).toBe(
41
+ true,
42
+ );
43
+ }
44
+ });
45
+ });