@bleedingdev/modern-js-plugin-i18n 3.9.0-ultramodern.11 → 3.9.0-ultramodern.12

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.
@@ -84,7 +84,7 @@ const i18nPlugin = (options = {})=>({
84
84
  });
85
85
  api.modifyFileSystemRoutes(({ entrypoint, routes })=>({
86
86
  entrypoint,
87
- routes: (0, external_localisedRoutes_js_namespaceObject.applyLocalisedRoutes)(routes, localeDetection ? (0, utils_js_namespaceObject.getLocaleDetectionOptions)(entrypoint.entryName, localeDetection) : void 0)
87
+ routes: (0, external_localisedRoutes_js_namespaceObject.applyLocalisedRoutes)(routes, localeDetection ? (0, utils_js_namespaceObject.getLocaleDetectionOptions)(entrypoint.entryName, localeDetection) : void 0, entrypoint)
88
88
  }));
89
89
  api._internalServerPlugins(({ plugins })=>{
90
90
  const { serverRoutes, metaName } = api.getAppContext();
@@ -31,11 +31,12 @@ __webpack_require__.d(__webpack_exports__, {
31
31
  applyLocalisedRoutes: ()=>applyLocalisedRoutes
32
32
  });
33
33
  const i18n_runtime_extensions_namespaceObject = require("@modern-js/i18n-runtime-extensions");
34
- const applyLocalisedRoutes = (routes, localeDetection)=>{
34
+ const cli_namespaceObject = require("@modern-js/runtime/cli");
35
+ const applyLocalisedRoutes = (routes, localeDetection, entrypoint)=>{
35
36
  const { localePathRedirect, languages = [], localisedUrls } = localeDetection ?? {};
36
37
  const resolved = (0, i18n_runtime_extensions_namespaceObject.resolveLocalisedUrlsConfig)(localisedUrls);
37
38
  if (!localePathRedirect || !languages.length || !resolved.enabled) return routes;
38
- return (0, i18n_runtime_extensions_namespaceObject.applyLocalisedUrlsToRoutes)(routes, languages, resolved.map);
39
+ return (0, i18n_runtime_extensions_namespaceObject.applyLocalisedUrlsToRoutes)(routes, languages, resolved.map, entrypoint && '@modern-js/plugin-tanstack' === (0, cli_namespaceObject.getEntrypointRoutesOwner)(entrypoint) ? 'canonical' : 'physical');
39
40
  };
40
41
  exports.applyLocalisedRoutes = __webpack_exports__.applyLocalisedRoutes;
41
42
  for(var __rspack_i in __webpack_exports__)if (-1 === [
@@ -51,7 +51,7 @@ const i18nPlugin = (options = {})=>({
51
51
  });
52
52
  api.modifyFileSystemRoutes(({ entrypoint, routes })=>({
53
53
  entrypoint,
54
- routes: applyLocalisedRoutes(routes, localeDetection ? getLocaleDetectionOptions(entrypoint.entryName, localeDetection) : void 0)
54
+ routes: applyLocalisedRoutes(routes, localeDetection ? getLocaleDetectionOptions(entrypoint.entryName, localeDetection) : void 0, entrypoint)
55
55
  }));
56
56
  api._internalServerPlugins(({ plugins })=>{
57
57
  const { serverRoutes, metaName } = api.getAppContext();
@@ -1,8 +1,9 @@
1
1
  import { applyLocalisedUrlsToRoutes, resolveLocalisedUrlsConfig } from "@modern-js/i18n-runtime-extensions";
2
- const applyLocalisedRoutes = (routes, localeDetection)=>{
2
+ import { getEntrypointRoutesOwner } from "@modern-js/runtime/cli";
3
+ const applyLocalisedRoutes = (routes, localeDetection, entrypoint)=>{
3
4
  const { localePathRedirect, languages = [], localisedUrls } = localeDetection ?? {};
4
5
  const resolved = resolveLocalisedUrlsConfig(localisedUrls);
5
6
  if (!localePathRedirect || !languages.length || !resolved.enabled) return routes;
6
- return applyLocalisedUrlsToRoutes(routes, languages, resolved.map);
7
+ return applyLocalisedUrlsToRoutes(routes, languages, resolved.map, entrypoint && '@modern-js/plugin-tanstack' === getEntrypointRoutesOwner(entrypoint) ? 'canonical' : 'physical');
7
8
  };
8
9
  export { applyLocalisedRoutes };
@@ -52,7 +52,7 @@ const i18nPlugin = (options = {})=>({
52
52
  });
53
53
  api.modifyFileSystemRoutes(({ entrypoint, routes })=>({
54
54
  entrypoint,
55
- routes: applyLocalisedRoutes(routes, localeDetection ? getLocaleDetectionOptions(entrypoint.entryName, localeDetection) : void 0)
55
+ routes: applyLocalisedRoutes(routes, localeDetection ? getLocaleDetectionOptions(entrypoint.entryName, localeDetection) : void 0, entrypoint)
56
56
  }));
57
57
  api._internalServerPlugins(({ plugins })=>{
58
58
  const { serverRoutes, metaName } = api.getAppContext();
@@ -1,9 +1,10 @@
1
1
  import "node:module";
2
2
  import { applyLocalisedUrlsToRoutes, resolveLocalisedUrlsConfig } from "@modern-js/i18n-runtime-extensions";
3
- const applyLocalisedRoutes = (routes, localeDetection)=>{
3
+ import { getEntrypointRoutesOwner } from "@modern-js/runtime/cli";
4
+ const applyLocalisedRoutes = (routes, localeDetection, entrypoint)=>{
4
5
  const { localePathRedirect, languages = [], localisedUrls } = localeDetection ?? {};
5
6
  const resolved = resolveLocalisedUrlsConfig(localisedUrls);
6
7
  if (!localePathRedirect || !languages.length || !resolved.enabled) return routes;
7
- return applyLocalisedUrlsToRoutes(routes, languages, resolved.map);
8
+ return applyLocalisedUrlsToRoutes(routes, languages, resolved.map, entrypoint && '@modern-js/plugin-tanstack' === getEntrypointRoutesOwner(entrypoint) ? 'canonical' : 'physical');
8
9
  };
9
10
  export { applyLocalisedRoutes };
@@ -1,4 +1,5 @@
1
- import type { NestedRouteForCli, PageRoute } from '@modern-js/types';
1
+ import { getEntrypointRoutesOwner } from '@modern-js/runtime/cli';
2
+ import type { Entrypoint, NestedRouteForCli, PageRoute } from '@modern-js/types';
2
3
  import type { BaseLocaleDetectionOptions } from '../shared/type.js';
3
4
  type FileSystemRoutes = (NestedRouteForCli | PageRoute)[];
4
5
  /**
@@ -14,5 +15,5 @@ type FileSystemRoutes = (NestedRouteForCli | PageRoute)[];
14
15
  * with `localePathRedirect` and `languages` but no map keeps plain
15
16
  * locale-prefix behaviour.
16
17
  */
17
- export declare const applyLocalisedRoutes: (routes: FileSystemRoutes, localeDetection: BaseLocaleDetectionOptions | undefined) => FileSystemRoutes;
18
+ export declare const applyLocalisedRoutes: (routes: FileSystemRoutes, localeDetection: BaseLocaleDetectionOptions | undefined, entrypoint?: Entrypoint & Parameters<typeof getEntrypointRoutesOwner>[0]) => FileSystemRoutes;
18
19
  export {};
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "modern",
18
18
  "modern.js"
19
19
  ],
20
- "version": "3.9.0-ultramodern.11",
20
+ "version": "3.9.0-ultramodern.12",
21
21
  "engines": {
22
22
  "node": ">=20"
23
23
  },
@@ -122,13 +122,13 @@
122
122
  }
123
123
  },
124
124
  "dependencies": {
125
- "@modern-js/i18n-runtime-extensions": "npm:@bleedingdev/modern-js-i18n-runtime-extensions@3.9.0-ultramodern.11",
126
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.0-ultramodern.11",
127
- "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.0-ultramodern.11",
128
- "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.9.0-ultramodern.11",
129
- "@modern-js/server-runtime": "npm:@bleedingdev/modern-js-server-runtime@3.9.0-ultramodern.11",
130
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.11",
131
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.11",
125
+ "@modern-js/i18n-runtime-extensions": "npm:@bleedingdev/modern-js-i18n-runtime-extensions@3.9.0-ultramodern.12",
126
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.0-ultramodern.12",
127
+ "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.0-ultramodern.12",
128
+ "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.9.0-ultramodern.12",
129
+ "@modern-js/server-runtime": "npm:@bleedingdev/modern-js-server-runtime@3.9.0-ultramodern.12",
130
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.12",
131
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.12",
132
132
  "@swc/helpers": "^0.5.23",
133
133
  "i18next-browser-languagedetector": "^8.2.1",
134
134
  "i18next-chained-backend": "^5.0.6",
@@ -137,7 +137,7 @@
137
137
  "i18next-http-middleware": "^3.9.8"
138
138
  },
139
139
  "peerDependencies": {
140
- "@modern-js/runtime": "3.9.0-ultramodern.11",
140
+ "@modern-js/runtime": "3.9.0-ultramodern.12",
141
141
  "i18next": ">=25.7.4",
142
142
  "react": "^19.2.8",
143
143
  "react-dom": "^19.2.8",
@@ -152,9 +152,9 @@
152
152
  }
153
153
  },
154
154
  "devDependencies": {
155
- "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.9.0-ultramodern.11",
156
- "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.9.0-ultramodern.11",
157
- "@modern-js/runtime-extensions": "npm:@bleedingdev/modern-js-runtime-extensions@3.9.0-ultramodern.11",
155
+ "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.9.0-ultramodern.12",
156
+ "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.9.0-ultramodern.12",
157
+ "@modern-js/runtime-extensions": "npm:@bleedingdev/modern-js-runtime-extensions@3.9.0-ultramodern.12",
158
158
  "@rslib/core": "1.0.0",
159
159
  "@types/node": "^26.4.1",
160
160
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
package/src/cli/index.ts CHANGED
@@ -174,6 +174,7 @@ export const i18nPlugin = (
174
174
  localeDetection
175
175
  ? getLocaleDetectionOptions(entrypoint.entryName, localeDetection)
176
176
  : undefined,
177
+ entrypoint,
177
178
  ) as typeof routes,
178
179
  }));
179
180
 
@@ -2,7 +2,12 @@ import {
2
2
  applyLocalisedUrlsToRoutes,
3
3
  resolveLocalisedUrlsConfig,
4
4
  } from '@modern-js/i18n-runtime-extensions';
5
- import type { NestedRouteForCli, PageRoute } from '@modern-js/types';
5
+ import { getEntrypointRoutesOwner } from '@modern-js/runtime/cli';
6
+ import type {
7
+ Entrypoint,
8
+ NestedRouteForCli,
9
+ PageRoute,
10
+ } from '@modern-js/types';
6
11
  import type { BaseLocaleDetectionOptions } from '../shared/type';
7
12
 
8
13
  type FileSystemRoutes = (NestedRouteForCli | PageRoute)[];
@@ -23,6 +28,7 @@ type FileSystemRoutes = (NestedRouteForCli | PageRoute)[];
23
28
  export const applyLocalisedRoutes = (
24
29
  routes: FileSystemRoutes,
25
30
  localeDetection: BaseLocaleDetectionOptions | undefined,
31
+ entrypoint?: Entrypoint & Parameters<typeof getEntrypointRoutesOwner>[0],
26
32
  ): FileSystemRoutes => {
27
33
  const {
28
34
  localePathRedirect,
@@ -39,5 +45,9 @@ export const applyLocalisedRoutes = (
39
45
  routes,
40
46
  languages,
41
47
  resolved.map,
48
+ entrypoint &&
49
+ getEntrypointRoutesOwner(entrypoint) === '@modern-js/plugin-tanstack'
50
+ ? 'canonical'
51
+ : 'physical',
42
52
  ) as FileSystemRoutes;
43
53
  };
@@ -100,6 +100,47 @@ describe('native localised route generation', () => {
100
100
  expect(paths).toContain('produkty/:slug');
101
101
  });
102
102
 
103
+ test('keeps one source identity for TanStack entries while validating locale mappings', async () => {
104
+ const modifyRoutes = await createNativeOnlyHarness({
105
+ localeDetection: {
106
+ localePathRedirect: true,
107
+ languages: ['en', 'cs'],
108
+ localisedUrls,
109
+ },
110
+ });
111
+ const result = await modifyRoutes({
112
+ entrypoint: {
113
+ ...entrypoint,
114
+ __modernRoutesOwner: '@modern-js/plugin-tanstack',
115
+ },
116
+ routes: [
117
+ createRoute(':lang', [
118
+ createRoute('about'),
119
+ createRoute('products/:slug'),
120
+ ]),
121
+ ],
122
+ });
123
+ expect(result.routes[0].children).toMatchObject([
124
+ {
125
+ id: 'about',
126
+ path: 'about',
127
+ modernLocalisedRoute: {
128
+ canonicalPath: '/about',
129
+ paths: localisedUrls['/about'],
130
+ },
131
+ },
132
+ {
133
+ id: 'products/:slug',
134
+ path: 'products/:slug',
135
+ modernLocalisedRoute: {
136
+ canonicalPath: '/products/:slug',
137
+ paths: localisedUrls['/products/:slug'],
138
+ },
139
+ },
140
+ ]);
141
+ expect(result.routes[0].children).toHaveLength(2);
142
+ });
143
+
103
144
  test('leaves routes untouched when no map is declared', async () => {
104
145
  const modifyRoutes = await createNativeOnlyHarness({
105
146
  localeDetection: {
@@ -42,27 +42,30 @@ function createI18nInstance(language = 'en'): I18nInstance {
42
42
  };
43
43
  }
44
44
 
45
- /** A TanStack-shaped router whose location store can be republished. */
45
+ /** A provider capability whose location can be republished. */
46
46
  function createMutableTanstackRouter(pathname: string) {
47
- let location = { pathname, searchStr: '', hash: '' };
48
- let matches = [{ params: { lang: pathname.split('/')[1] ?? '' } }];
47
+ let snapshot = {
48
+ location: { pathname, search: '', hash: '' },
49
+ params: { lang: pathname.split('/')[1] ?? '' },
50
+ };
49
51
  const listeners = new Set<() => void>();
50
52
 
51
53
  return {
52
54
  navigate: rstest.fn(async () => undefined),
53
- stores: {
54
- location: {
55
- get: () => location,
56
- subscribe: (listener: () => void) => {
57
- listeners.add(listener);
58
- return () => listeners.delete(listener);
59
- },
55
+ navigation: {
56
+ getSnapshot: () => snapshot,
57
+ Link: TanstackLink,
58
+ navigate: rstest.fn(async () => undefined),
59
+ subscribe: (listener: () => void) => {
60
+ listeners.add(listener);
61
+ return () => listeners.delete(listener);
60
62
  },
61
- matches: { get: () => matches },
62
63
  },
63
64
  publishPathname(nextPathname: string) {
64
- location = { pathname: nextPathname, searchStr: '', hash: '' };
65
- matches = [{ params: { lang: nextPathname.split('/')[1] ?? '' } }];
65
+ snapshot = {
66
+ location: { pathname: nextPathname, search: '', hash: '' },
67
+ params: { lang: nextPathname.split('/')[1] ?? '' },
68
+ };
66
69
  for (const listener of listeners) {
67
70
  listener();
68
71
  }
@@ -129,6 +132,7 @@ function createTanstackRuntimeContext(
129
132
  applyRouterRuntimeState(runtimeContext, {
130
133
  framework: 'tanstack',
131
134
  instance: router,
135
+ navigation: router.navigation,
132
136
  } as any);
133
137
  return runtimeContext;
134
138
  }
@@ -270,7 +274,8 @@ describe('bare plugin-i18n navigation adapter', () => {
270
274
  await act(async () => {
271
275
  applyRouterRuntimeState(runtimeContext, {
272
276
  framework: 'tanstack',
273
- instance: createMutableTanstackRouter('/en/terms-of-service'),
277
+ navigation: createMutableTanstackRouter('/en/terms-of-service')
278
+ .navigation,
274
279
  } as any);
275
280
  });
276
281
 
@@ -329,13 +334,46 @@ describe('bare plugin-i18n navigation adapter', () => {
329
334
  await act(async () => {
330
335
  applyRouterRuntimeState(runtimeContext, {
331
336
  framework: 'tanstack',
332
- instance: createMutableTanstackRouter('/cs/obchodni-podminky'),
337
+ navigation: createMutableTanstackRouter('/cs/obchodni-podminky')
338
+ .navigation,
333
339
  } as any);
334
340
  });
335
341
 
336
342
  expect(anchor()?.getAttribute('data-router-link')).toBe('tanstack');
337
343
  });
338
344
 
345
+ test('replaces providers and stops observing the previous router', async () => {
346
+ const first = createMutableTanstackRouter('/en/products/shoe');
347
+ const second = createMutableTanstackRouter('/cs/produkty/bota');
348
+ const runtimeContext = createTanstackRuntimeContext(first);
349
+ const App = () => {
350
+ const adapter = useIntegratedRouterAdapter();
351
+ return <span data-testid="path">{adapter.location?.pathname}</span>;
352
+ };
353
+ rendered = await renderBareRuntime(
354
+ App,
355
+ runtimeContext,
356
+ createI18nInstance('en'),
357
+ );
358
+ await act(async () => {
359
+ applyRouterRuntimeState(runtimeContext, {
360
+ framework: 'custom-provider',
361
+ navigation: second.navigation,
362
+ });
363
+ });
364
+ expect(
365
+ rendered.container.querySelector('[data-testid="path"]')?.textContent,
366
+ ).toBe('/cs/produkty/bota');
367
+ await act(async () => first.publishPathname('/en/stale'));
368
+ expect(
369
+ rendered.container.querySelector('[data-testid="path"]')?.textContent,
370
+ ).toBe('/cs/produkty/bota');
371
+ await act(async () => second.publishPathname('/en/current'));
372
+ expect(
373
+ rendered.container.querySelector('[data-testid="path"]')?.textContent,
374
+ ).toBe('/en/current');
375
+ });
376
+
339
377
  test('follows the router location so the language tracks a client navigation', async () => {
340
378
  const router = createMutableTanstackRouter('/en/products/shoe');
341
379
  const App = () => {