@bleedingdev/modern-js-plugin-i18n 3.5.0-ultramodern.2 → 3.5.0-ultramodern.20

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.
@@ -167,6 +167,14 @@ const buildLocalizedUrl = (req, urlPath, language, languages, localisedUrls)=>{
167
167
  const localizedUrl = '/' === basePath ? newPathname + suffix : basePath + newPathname + suffix;
168
168
  return localizedUrl;
169
169
  };
170
+ const createLocaleRedirectResponse = (location)=>new Response(null, {
171
+ status: 302,
172
+ headers: {
173
+ 'Cache-Control': 'private, no-store',
174
+ Location: location,
175
+ Vary: 'Accept-Language, Cookie'
176
+ }
177
+ });
170
178
  const i18nServerPlugin = (options)=>({
171
179
  name: '@modern-js/plugin-i18n/server',
172
180
  setup: (api)=>{
@@ -235,12 +243,12 @@ const i18nServerPlugin = (options)=>({
235
243
  if (i18nextDetector) detectedLanguage = c.get('language') || null;
236
244
  const targetLanguage = detectedLanguage || fallbackLanguage;
237
245
  const localizedUrl = buildLocalizedUrl(c.req, originUrlPath, targetLanguage, languages, localisedUrls);
238
- return c.redirect(localizedUrl);
246
+ return createLocaleRedirectResponse(localizedUrl);
239
247
  }
240
248
  const localisedUrlsConfig = (0, localisedUrls_js_namespaceObject.resolveLocalisedUrlsConfig)(localisedUrls);
241
249
  if (localisedUrlsConfig.enabled) {
242
250
  const expectedUrl = buildLocalizedUrl(c.req, originUrlPath, language, languages, localisedUrls);
243
- if (expectedUrl !== `${pathname}${url.search}${url.hash}`) return c.redirect(expectedUrl);
251
+ if (expectedUrl !== `${pathname}${url.search}${url.hash}`) return createLocaleRedirectResponse(expectedUrl);
244
252
  }
245
253
  await next();
246
254
  }
@@ -132,6 +132,14 @@ const buildLocalizedUrl = (req, urlPath, language, languages, localisedUrls)=>{
132
132
  const localizedUrl = '/' === basePath ? newPathname + suffix : basePath + newPathname + suffix;
133
133
  return localizedUrl;
134
134
  };
135
+ const createLocaleRedirectResponse = (location)=>new Response(null, {
136
+ status: 302,
137
+ headers: {
138
+ 'Cache-Control': 'private, no-store',
139
+ Location: location,
140
+ Vary: 'Accept-Language, Cookie'
141
+ }
142
+ });
135
143
  const i18nServerPlugin = (options)=>({
136
144
  name: '@modern-js/plugin-i18n/server',
137
145
  setup: (api)=>{
@@ -200,12 +208,12 @@ const i18nServerPlugin = (options)=>({
200
208
  if (i18nextDetector) detectedLanguage = c.get('language') || null;
201
209
  const targetLanguage = detectedLanguage || fallbackLanguage;
202
210
  const localizedUrl = buildLocalizedUrl(c.req, originUrlPath, targetLanguage, languages, localisedUrls);
203
- return c.redirect(localizedUrl);
211
+ return createLocaleRedirectResponse(localizedUrl);
204
212
  }
205
213
  const localisedUrlsConfig = resolveLocalisedUrlsConfig(localisedUrls);
206
214
  if (localisedUrlsConfig.enabled) {
207
215
  const expectedUrl = buildLocalizedUrl(c.req, originUrlPath, language, languages, localisedUrls);
208
- if (expectedUrl !== `${pathname}${url.search}${url.hash}`) return c.redirect(expectedUrl);
216
+ if (expectedUrl !== `${pathname}${url.search}${url.hash}`) return createLocaleRedirectResponse(expectedUrl);
209
217
  }
210
218
  await next();
211
219
  }
@@ -133,6 +133,14 @@ const buildLocalizedUrl = (req, urlPath, language, languages, localisedUrls)=>{
133
133
  const localizedUrl = '/' === basePath ? newPathname + suffix : basePath + newPathname + suffix;
134
134
  return localizedUrl;
135
135
  };
136
+ const createLocaleRedirectResponse = (location)=>new Response(null, {
137
+ status: 302,
138
+ headers: {
139
+ 'Cache-Control': 'private, no-store',
140
+ Location: location,
141
+ Vary: 'Accept-Language, Cookie'
142
+ }
143
+ });
136
144
  const i18nServerPlugin = (options)=>({
137
145
  name: '@modern-js/plugin-i18n/server',
138
146
  setup: (api)=>{
@@ -201,12 +209,12 @@ const i18nServerPlugin = (options)=>({
201
209
  if (i18nextDetector) detectedLanguage = c.get('language') || null;
202
210
  const targetLanguage = detectedLanguage || fallbackLanguage;
203
211
  const localizedUrl = buildLocalizedUrl(c.req, originUrlPath, targetLanguage, languages, localisedUrls);
204
- return c.redirect(localizedUrl);
212
+ return createLocaleRedirectResponse(localizedUrl);
205
213
  }
206
214
  const localisedUrlsConfig = resolveLocalisedUrlsConfig(localisedUrls);
207
215
  if (localisedUrlsConfig.enabled) {
208
216
  const expectedUrl = buildLocalizedUrl(c.req, originUrlPath, language, languages, localisedUrls);
209
- if (expectedUrl !== `${pathname}${url.search}${url.hash}`) return c.redirect(expectedUrl);
217
+ if (expectedUrl !== `${pathname}${url.search}${url.hash}`) return createLocaleRedirectResponse(expectedUrl);
210
218
  }
211
219
  await next();
212
220
  }
@@ -1,4 +1,10 @@
1
- import type { NestedRouteForCli, PageRoute } from '@modern-js/types';
1
+ export type LocalisedRoute = {
2
+ type: 'nested' | 'page';
3
+ path?: string;
4
+ id?: string;
5
+ children?: LocalisedRoute[];
6
+ [key: string]: any;
7
+ };
2
8
  export type LocalisedUrlPathMap = Record<string, string>;
3
9
  export type LocalisedUrlsMap = Record<string, LocalisedUrlPathMap>;
4
10
  export type LocalisedUrlsOption = boolean | LocalisedUrlsMap;
@@ -21,8 +27,8 @@ export declare const normalisePathname: (pathname: string) => string;
21
27
  * failing the build for every route missing from a map they never wrote.
22
28
  */
23
29
  export declare const resolveLocalisedUrlsConfig: (option: LocalisedUrlsOption | undefined) => ResolvedLocalisedUrlsConfig;
24
- export declare const validateLocalisedUrls: (routes: (NestedRouteForCli | PageRoute)[], languages: string[], localisedUrls: LocalisedUrlsMap) => void;
25
- export declare const applyLocalisedUrlsToRoutes: (routes: (NestedRouteForCli | PageRoute)[], languages: string[], localisedUrls: LocalisedUrlsMap) => (NestedRouteForCli | PageRoute)[];
30
+ export declare const validateLocalisedUrls: (routes: LocalisedRoute[], languages: string[], localisedUrls: LocalisedUrlsMap) => void;
31
+ export declare const applyLocalisedUrlsToRoutes: (routes: LocalisedRoute[], languages: string[], localisedUrls: LocalisedUrlsMap) => LocalisedRoute[];
26
32
  export declare const matchPathPattern: (pathname: string, pattern: string) => Record<string, string> | null;
27
33
  export declare const buildPathFromPattern: (pattern: string, params: Record<string, string>) => string;
28
34
  export declare const resolveLocalisedPath: (pathname: string, targetLanguage: string, languages: string[], localisedUrls: LocalisedUrlsMap) => string;
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "modern",
18
18
  "modern.js"
19
19
  ],
20
- "version": "3.5.0-ultramodern.2",
20
+ "version": "3.5.0-ultramodern.20",
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.2",
101
- "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.5.0-ultramodern.2",
102
- "@modern-js/server-runtime": "npm:@bleedingdev/modern-js-server-runtime@3.5.0-ultramodern.2",
103
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.5.0-ultramodern.2",
104
- "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.5.0-ultramodern.2",
105
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.5.0-ultramodern.2"
100
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.5.0-ultramodern.20",
101
+ "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.5.0-ultramodern.20",
102
+ "@modern-js/server-runtime": "npm:@bleedingdev/modern-js-server-runtime@3.5.0-ultramodern.20",
103
+ "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.5.0-ultramodern.20",
104
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.5.0-ultramodern.20",
105
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.5.0-ultramodern.20"
106
106
  },
107
107
  "peerDependencies": {
108
- "@modern-js/runtime": "3.5.0-ultramodern.2",
108
+ "@modern-js/runtime": "3.5.0-ultramodern.20",
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.2",
133
- "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.5.0-ultramodern.2"
132
+ "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.5.0-ultramodern.20",
133
+ "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.5.0-ultramodern.20"
134
134
  },
135
135
  "sideEffects": false,
136
136
  "publishConfig": {
@@ -328,6 +328,16 @@ const buildLocalizedUrl = (
328
328
  return localizedUrl;
329
329
  };
330
330
 
331
+ const createLocaleRedirectResponse = (location: string): Response =>
332
+ new Response(null, {
333
+ status: 302,
334
+ headers: {
335
+ 'Cache-Control': 'private, no-store',
336
+ Location: location,
337
+ Vary: 'Accept-Language, Cookie',
338
+ },
339
+ });
340
+
331
341
  export const i18nServerPlugin = (options: I18nPluginOptions): ServerPlugin => ({
332
342
  name: '@modern-js/plugin-i18n/server',
333
343
  setup: api => {
@@ -475,7 +485,7 @@ export const i18nServerPlugin = (options: I18nPluginOptions): ServerPlugin => ({
475
485
  languages,
476
486
  localisedUrls,
477
487
  );
478
- return c.redirect(localizedUrl);
488
+ return createLocaleRedirectResponse(localizedUrl);
479
489
  }
480
490
  const localisedUrlsConfig =
481
491
  resolveLocalisedUrlsConfig(localisedUrls);
@@ -488,7 +498,7 @@ export const i18nServerPlugin = (options: I18nPluginOptions): ServerPlugin => ({
488
498
  localisedUrls,
489
499
  );
490
500
  if (expectedUrl !== `${pathname}${url.search}${url.hash}`) {
491
- return c.redirect(expectedUrl);
501
+ return createLocaleRedirectResponse(expectedUrl);
492
502
  }
493
503
  }
494
504
  await next();
@@ -1,4 +1,10 @@
1
- import type { NestedRouteForCli, PageRoute } from '@modern-js/types';
1
+ export type LocalisedRoute = {
2
+ type: 'nested' | 'page';
3
+ path?: string;
4
+ id?: string;
5
+ children?: LocalisedRoute[];
6
+ [key: string]: any;
7
+ };
2
8
 
3
9
  export type LocalisedUrlPathMap = Record<string, string>;
4
10
  export type LocalisedUrlsMap = Record<string, LocalisedUrlPathMap>;
@@ -144,11 +150,11 @@ const ensureLocalisedUrlsForPath = (
144
150
  };
145
151
 
146
152
  export const validateLocalisedUrls = (
147
- routes: (NestedRouteForCli | PageRoute)[],
153
+ routes: LocalisedRoute[],
148
154
  languages: string[],
149
155
  localisedUrls: LocalisedUrlsMap,
150
156
  ) => {
151
- const visit = (route: NestedRouteForCli | PageRoute, parentPath: string) => {
157
+ const visit = (route: LocalisedRoute, parentPath: string) => {
152
158
  const canonicalPath = joinPath(parentPath, route.path);
153
159
  if (isLocalisableRoutePath(route.path)) {
154
160
  ensureLocalisedUrlsForPath(canonicalPath, languages, localisedUrls);
@@ -191,12 +197,12 @@ const getLocalisedRoutePaths = (
191
197
  };
192
198
 
193
199
  const transformLocalisedRoute = (
194
- route: NestedRouteForCli | PageRoute,
200
+ route: LocalisedRoute,
195
201
  parentCanonicalPath: string,
196
202
  parentLocalisedPaths: Record<string, string>,
197
203
  languages: string[],
198
204
  localisedUrls: LocalisedUrlsMap,
199
- ): (NestedRouteForCli | PageRoute)[] => {
205
+ ): LocalisedRoute[] => {
200
206
  const canonicalPath = joinPath(parentCanonicalPath, route.path);
201
207
  const localisedUrlEntry = isLocalisableRoutePath(route.path)
202
208
  ? ensureLocalisedUrlsForPath(canonicalPath, languages, localisedUrls)
@@ -224,7 +230,7 @@ const transformLocalisedRoute = (
224
230
  const baseRoute = {
225
231
  ...route,
226
232
  ...(children ? { children } : {}),
227
- } as NestedRouteForCli | PageRoute;
233
+ } as LocalisedRoute;
228
234
 
229
235
  if (!localisedUrlEntry) {
230
236
  return [baseRoute];
@@ -243,7 +249,7 @@ const transformLocalisedRoute = (
243
249
  const legalRouteIdPart = (value: string): string =>
244
250
  value.replace(/[^a-zA-Z0-9_$-]+/g, '_').replace(/^_+|_+$/g, '') || 'index';
245
251
 
246
- const suffixRouteIds = <T extends NestedRouteForCli | PageRoute>(
252
+ const suffixRouteIds = <T extends LocalisedRoute>(
247
253
  route: T,
248
254
  suffix: string,
249
255
  ): T => {
@@ -260,11 +266,11 @@ const suffixRouteIds = <T extends NestedRouteForCli | PageRoute>(
260
266
  };
261
267
 
262
268
  const cloneRouteWithLocalisedPath = (
263
- route: NestedRouteForCli | PageRoute,
269
+ route: LocalisedRoute,
264
270
  path: string,
265
271
  index: number,
266
272
  canonicalPath: string,
267
- ): NestedRouteForCli | PageRoute => {
273
+ ): LocalisedRoute => {
268
274
  const leadingLocaleParam = getLeadingLocaleParam(route.path);
269
275
  const localisedPath = leadingLocaleParam
270
276
  ? normaliseRoutePath(`${leadingLocaleParam}/${path}`)
@@ -272,7 +278,7 @@ const cloneRouteWithLocalisedPath = (
272
278
  const routeWithPath = {
273
279
  ...route,
274
280
  path: localisedPath,
275
- } as NestedRouteForCli | PageRoute;
281
+ } as LocalisedRoute;
276
282
  // Language-agnostic source pattern; lets downstream codegen collapse the
277
283
  // localized physical variants back to one canonical route.
278
284
  (routeWithPath as { modernCanonicalPath?: string }).modernCanonicalPath =
@@ -284,10 +290,10 @@ const cloneRouteWithLocalisedPath = (
284
290
  };
285
291
 
286
292
  export const applyLocalisedUrlsToRoutes = (
287
- routes: (NestedRouteForCli | PageRoute)[],
293
+ routes: LocalisedRoute[],
288
294
  languages: string[],
289
295
  localisedUrls: LocalisedUrlsMap,
290
- ): (NestedRouteForCli | PageRoute)[] => {
296
+ ): LocalisedRoute[] => {
291
297
  const rootLocalisedPaths = languages.reduce<Record<string, string>>(
292
298
  (acc, language) => {
293
299
  acc[language] = '/';
@@ -3,10 +3,21 @@ import path from 'node:path';
3
3
 
4
4
  const fixtureDir = path.resolve(__dirname, 'type-fixture');
5
5
 
6
- const tsgoBin = path.join(
7
- path.dirname(require.resolve('@typescript/native-preview/package.json')),
8
- 'bin/tsgo.js',
9
- );
6
+ function resolveTsgoBin() {
7
+ const pkgPath = require.resolve('@typescript/native-preview/package.json');
8
+ const pkgDir = path.dirname(pkgPath);
9
+ const pkg = require(pkgPath) as {
10
+ bin?:
11
+ | string
12
+ | {
13
+ tsgo?: string;
14
+ };
15
+ };
16
+ const binEntry = typeof pkg.bin === 'string' ? pkg.bin : pkg.bin?.tsgo;
17
+ return path.resolve(pkgDir, binEntry ?? 'bin/tsgo.js');
18
+ }
19
+
20
+ const tsgoBin = resolveTsgoBin();
10
21
 
11
22
  describe('Link type-level tests', () => {
12
23
  test('fixture type-checks correctly: valid uses compile, invalid uses are rejected', () => {
@@ -509,7 +509,6 @@ describe('i18n server API prefix skips', () => {
509
509
  header: () => ({ host: 'localhost' }),
510
510
  },
511
511
  get: () => null,
512
- redirect: (url: string) => ({ redirectedTo: url }),
513
512
  }) as any;
514
513
 
515
514
  // Sanity: well-formed non-canonical slugs still redirect.
@@ -517,7 +516,10 @@ describe('i18n server API prefix skips', () => {
517
516
  createContext('/cs/products/bota'),
518
517
  async () => {},
519
518
  );
520
- expect(redirected).toEqual({ redirectedTo: '/cs/produkty/bota' });
519
+ expect(redirected.status).toBe(302);
520
+ expect(redirected.headers.get('location')).toBe('/cs/produkty/bota');
521
+ expect(redirected.headers.get('cache-control')).toBe('private, no-store');
522
+ expect(redirected.headers.get('vary')).toBe('Accept-Language, Cookie');
521
523
 
522
524
  // Malformed encoding must fall through to next() instead of throwing.
523
525
  let nextCalls = 0;