@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.
- package/dist/cjs/cli/index.js +1 -1
- package/dist/cjs/cli/localisedRoutes.js +3 -2
- package/dist/esm/cli/index.mjs +1 -1
- package/dist/esm/cli/localisedRoutes.mjs +3 -2
- package/dist/esm-node/cli/index.mjs +1 -1
- package/dist/esm-node/cli/localisedRoutes.mjs +3 -2
- package/dist/types/cli/localisedRoutes.d.ts +3 -2
- package/package.json +12 -12
- package/src/cli/index.ts +1 -0
- package/src/cli/localisedRoutes.ts +11 -1
- package/tests/localisedRouteGeneration.test.ts +41 -0
- package/tests/navigation.test.tsx +53 -15
package/dist/cjs/cli/index.js
CHANGED
|
@@ -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
|
|
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 === [
|
package/dist/esm/cli/index.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
126
|
-
"@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.0-ultramodern.
|
|
127
|
-
"@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.0-ultramodern.
|
|
128
|
-
"@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.9.0-ultramodern.
|
|
129
|
-
"@modern-js/server-runtime": "npm:@bleedingdev/modern-js-server-runtime@3.9.0-ultramodern.
|
|
130
|
-
"@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.
|
|
131
|
-
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.
|
|
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.
|
|
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.
|
|
156
|
-
"@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.9.0-ultramodern.
|
|
157
|
-
"@modern-js/runtime-extensions": "npm:@bleedingdev/modern-js-runtime-extensions@3.9.0-ultramodern.
|
|
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
|
@@ -2,7 +2,12 @@ import {
|
|
|
2
2
|
applyLocalisedUrlsToRoutes,
|
|
3
3
|
resolveLocalisedUrlsConfig,
|
|
4
4
|
} from '@modern-js/i18n-runtime-extensions';
|
|
5
|
-
import
|
|
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
|
|
45
|
+
/** A provider capability whose location can be republished. */
|
|
46
46
|
function createMutableTanstackRouter(pathname: string) {
|
|
47
|
-
let
|
|
48
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
65
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 = () => {
|